tags:

views:

256

answers:

2

If block is a closure, why this code does not work? And how to make it work?

 def R(arg)
 Class.new do
  def foo
     puts arg
  end
 end
end

class A < R("Hello!")
end

A.new.foo #throws undefined local variable or method `arg' for #<A:0x2840538>
+19  A: 

Blocks are closures and arg is indeed available inside the Class.new block. It's just not available inside the foo method because def starts a new scope. If you replace def with define_method, which takes a block, you'll see the result you want:

def R(arg)
    Class.new do
        define_method(:foo) do
           puts arg
        end
    end
end

class A < R("Hello!")
end

A.new.foo # Prints: Hello!
sepp2k
how would a method that takes parameters be defined?
Geo
@Geo: Using a block which takes parameters. E.g. `define_method(:add_one) do |x| x+1 end`
sepp2k
+5  A: 

If you define the class dynamically, you can alter it as you like:

def R(arg)
  c = Class.new

  # Send the block through as a closure, not as an inline method
  # definition which interprets variables always as local to the block.
  c.send(:define_method, :foo) do
    arg
  end

  c
end

class A < R("Hello!")
end

puts A.new.foo.inspect
# => "Hello!"
tadman