From: Josh Cheek Date: 2013-03-13T22:01:27+09:00 Subject: Re: cannot get blocks working --047d7b3441c4cb3a8c04d7ce0070 Content-Type: text/plain; charset=ISO-8859-1 On Wed, Mar 13, 2013 at 7:58 AM, Josh Cheek wrote: > On Wed, Mar 13, 2013 at 7:30 AM, Aashish Kiran wrote: > >> Hi, >> Below is the piece of code. Can anyone help in getting expected output. >> >> >> ------------------------------------------------------------ >> >> class XmlDoc >> def method_missing(name, *args, &block) >> self.class.class_eval do >> define_method(name) do |*args, &block| >> if block_given? >> temp = "<#{name}#{temp}>" + block.call + "" >> else >> "<#{name}>" >> end >> end >> end >> send(name, *args, &block) >> end >> end >> >> x = XmlDoc.new >> >> >> p '1111111111111111111' >> p x.hello >> #expected output "" >> >> p x.hello{"dolly"} >> #expected output "dolly" >> >> ----------------------------------------------------------------------- >> >> Thanks, >> Aashish >> >> -- >> Posted via http://www.ruby-forum.com/. >> >> > Change `block_given?` to `block`. The block_given? macro is checking if > method_missing received a block. Since it didn't the first time you called > x.hello, it never will (that scope was enclosed). > > Also, you should move the method definition out to a helper method, > because the methods that this defines will capture the block sent to > method_missing, which in turn capture their environments. Since they're > becoming methods, they won't be garbage collected, so you run the risk of > memory leaks. Moving it to a helper method will prevent capturing of the > block. > > Also, when doing metaprogramming, it's usually best to use __send__ > instead of send (to avoid potential namespace conflicts). > > I would probably write it like this (well, at this point in my life, I > probably wouldn't write anything this dynamic, but whatever) > > > class XmlDoc > def self.define_node(name) > define_method name do |*args, &block| > block ||= lambda { '' } > "<#{name}>" + block.call + "" > end > end > > def method_missing(name, *args, &block) > self.class.define_node name > __send__ name, *args, &block > end > end > > x = XmlDoc.new > x.hello # => "" > x.hello{"dolly"} # => "dolly" > > > -Josh > I guess I would also interpolate the block.call, which would allow nil to be returned, which would allow you to get rid of the reassignment to the empty lambda. define_method name do |*args, &block| "<#{name}>#{block.call if block}" end --047d7b3441c4cb3a8c04d7ce0070 Content-Type: text/html; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable On Wed, Mar 13, 2013 at 7:58 AM, Josh Cheek <josh.cheek@gmail.com= > wrote:
On Wed, Mar 13, 2013 at 7:30 AM, Aa= shish Kiran <lists@ruby-forum.com> wrote:
Hi,
Below is the piece of code. Can anyone help in getting expected output.


------------------------------------------------------------

class XmlDoc
=A0 def method_missing(name, *args, &block)
=A0 =A0 self.class.class_eval do
=A0 =A0 =A0 define_method(name) do |*args, &block|
=A0 =A0 =A0 =A0 if block_given?
=A0 =A0 =A0 =A0 =A0 temp =3D "<#{name}#{temp}>" + block.cal= l + "</#{name}>"
=A0 =A0 =A0 =A0 else
=A0 =A0 =A0 =A0 =A0 "<#{name}></#{name}>"
=A0 =A0 =A0 =A0 end
=A0 =A0 =A0 end
=A0 =A0 end
=A0 =A0 send(name, *args, &block)
=A0 end
end

x =3D XmlDoc.new


p '1111111111111111111'
p x.hello
#expected output "<hello></hello>"

p x.hello{"dolly"}
#expected output =A0 "<hello>dolly</hello>"

-----------------------------------------------------------------------

Thanks,
Aashish

--
Posted via http://= www.ruby-forum.com/.


Change `block_given?`= to `block`. The block_given? macro is checking if method_missing received = a block. Since it didn't the first time you called x.hello, it never wi= ll (that scope was enclosed).

Also, you should move the method definition out to a he= lper method, because the methods that this defines will capture the block s= ent to method_missing, which in turn capture their environments. Since they= 're becoming methods, they won't be garbage collected, so you run t= he risk of memory leaks. Moving it to a helper method will prevent capturin= g of the block.

Also, when doing metaprogramming, it's usually best= to use __send__ instead of send (to avoid potential namespace conflicts).<= /div>

I would probably write it like this (well, at this= point in my life, I probably wouldn't write anything this dynamic, but= whatever)


class XmlDoc
=A0 def self= .define_node(name)
=A0 =A0 define_method name do |*args, &blo= ck|
=A0 =A0 =A0 block ||=3D lambda { '' }
=A0 = =A0 =A0 "<#{name}>" + block.call + "</#{name}>&q= uot;
=A0 =A0 end
=A0 end
=A0=A0
=A0 def metho= d_missing(name, *args, &block)
=A0 =A0 self.class.define_node= name
=A0 =A0 __send__ name, *args, &block
=A0 end
end

x =3D XmlDoc.new
x.hello =A0 =A0 =A0 = =A0 =A0 # =3D> "<hello></hello>"
x.hello= {"dolly"} =A0# =3D> "<hello>dolly</hello>&quo= t;


-Josh

I guess I would also interpolate = the block.call, which would allow nil to be returned, which would allow you= to get rid of the reassignment to the empty lambda.

=A0 =A0 define_method name do |*args, &block|
=A0 = =A0 =A0 "<#{name}>#{block.call if block}</#{name}>"
=A0 =A0 end
--047d7b3441c4cb3a8c04d7ce0070--