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 + "</#{name}>"
else
"<#{name}></#{name}>"
end
end
end
send(name, *args, &block)
end
end
x = XmlDoc.new
p '1111111111111111111'
p x.hello
#expected output "<hello></hello>"
p x.hello{"dolly"}
#expected output "<hello>dolly</hello>"
-----------------------------------------------------------------------
Thanks,
Aashish
on 2013-03-13 13:30
on 2013-03-13 13:45
This works for me
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}>" + yield + "</#{name}>"
else
"<#{name}></#{name}>"
end
end
end
send(name, *args, &block)
end
end
x = XmlDoc.new
p x.hello{ "dolly" }
on 2013-03-13 14:02
Ah heres the issue then:
x = XmlDoc.new
p x.hello
p x.hello{ "dolly" }
gives
"<hello></hello>"
"<hello></hello>"
but
x = XmlDoc.new
p x.hello{ "dolly" }
p x.hello
gives
"<hello>dolly</hello>"
"<hello>dolly</hello>"
This also sort of happens for Aashish's code as well
x = XmlDoc.new
p x.hello
p x.hello{ "dolly" }
gives
"<hello></hello>"
"<hello></hello>"
and
x = XmlDoc.new
p x.hello{ "dolly" }
p x.hello
"<hello>dolly</hello>"
NoMethodError: undefined method call for nil:NilClass
on 2013-03-13 14:06
On Wed, Mar 13, 2013 at 7:30 AM, Aashish Kiran <lists@ruby-forum.com> wrote: > if block_given? > x = XmlDoc.new > > 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 + "</#{name}>" end end def method_missing(name, *args, &block) self.class.define_node name __send__ name, *args, &block end end x = XmlDoc.new x.hello # => "<hello></hello>" x.hello{"dolly"} # => "<hello>dolly</hello>" -Josh
on 2013-03-13 14:08
On Wed, Mar 13, 2013 at 7:58 AM, Josh Cheek <josh.cheek@gmail.com> wrote: >> self.class.class_eval do >> end >> > method_missing received a block. Since it didn't the first time you called > instead of send (to avoid potential namespace conflicts). > end > x.hello{"dolly"} # => "<hello>dolly</hello>" > > > -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}</#{name}>" end
on 2013-03-13 19:04
On Wed, Mar 13, 2013 at 2:05 PM, Josh Cheek <josh.cheek@gmail.com> wrote: > 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). Are you insinuating that I cannot define a method which will happily accept a block using define_method and block_given?? You cannot do it as the example shows: irb(main):001:0> class X irb(main):002:1> def self.doit irb(main):003:2> define_method :foo do |a| irb(main):004:3* b = block_given? irb(main):005:3> printf "block: %p\n", b irb(main):006:3> yield if b irb(main):007:3> end irb(main):008:2> end irb(main):009:1> end => nil irb(main):010:0> x = X.new => #<X:0x92f057c> irb(main):011:0> x.foo NoMethodError: undefined method `foo' for #<X:0x92f057c> from (irb):11 from /usr/bin/irb:12:in `<main>' irb(main):012:0> X.doit => #<Proc:0x92ed458@(irb):3 (lambda)> irb(main):013:0> x.foo ArgumentError: wrong number of arguments (0 for 1) from (irb):4:in `block in doit' from (irb):13 from /usr/bin/irb:12:in `<main>' irb(main):014:0> x.foo 1 block: false => nil irb(main):015:0> x.foo(1) { puts "block called" } block: false => nil irb(main):016:0> X.doit { puts "x" } => #<Proc:0x936bd44@(irb):3 (lambda)> irb(main):017:0> x.foo(1) { puts "block called" } block: true x => nil irb(main):018:0> x.foo(1) block: true x => nil Amazing: the block will be captured _even though it is never referenced explicitly_. Learn something new every day. :-) Thank you for that! > Also, you should move the method definition out to a helper method, because In this particular case I would not define a new method at all. Basically what happens here is that the class stores the history of tag names used as method names. I'd rather put the code of the new method in #method_missing class XmlDoc def method_missing(name, *args, &block) "<#{name}>".tap do |s| s << yield.to_s if block_given? end << "</#{name}>" end end irb(main):028:0> xd = XmlDoc.new => #<XmlDoc:0x848b374> irb(main):029:0> xd.foo { xd.bar { 123 } } => "<foo><bar>123</bar></foo>" Btw, I believe there exists a similar mechanism in Ruby already. http://builder.rubyforge.org/ And btw the reference to temp in the String (original solution) is useless because the variable is not yet defined at that location. Cheers robert
Please log in before posting. Registration is free and takes only a minute.
Existing account
(Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
Log in with Google account | Log in with Yahoo account
No account? Register here.