Cannot get blocks working

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

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

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" }

···

--
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 Wed, Mar 13, 2013 at 7:30 AM, Aashish Kiran <lists@ruby-forum.com> 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 + "</#{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

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

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

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 Wed, Mar 13, 2013 at 7:58 AM, Josh Cheek <josh.cheek@gmail.com> wrote:

On Wed, Mar 13, 2013 at 7:30 AM, Aashish Kiran <lists@ruby-forum.com>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 + "</#{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

--
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

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. :slight_smile: 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

···

On Wed, Mar 13, 2013 at 2:05 PM, Josh Cheek <josh.cheek@gmail.com> wrote:

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/