0

I'm trying to create classes using meta-programming in Ruby as per this question: Dynamically define named classes in Ruby. Everything is going well, except it seems like in the block from Class.new can't reference method parameters which is odd.

I have this

class A; end

module B
  def self.class_with_method(class_name, method_return)
    klass = Class.new(A) do
      def example
        method_return
      end
    end
    B.const_set class_name, klass
  end
end

But when I have the above and then test it with

B.class_with_method 'ExampleClass', 23
B::ExampleClass.new.example

Gives me

undefined local variable or method `method_return' for #<B::ExampleClass:0x00007ff1d7130d00> (NameError)

This is odd because if I were to do

def add_number(number)
  [1, 2, 3, 4].map {|i| i + number}
end

add_number(2)
# => [3, 4, 5, 6]

So clearly blocks can take method arguments.

Is there a way to pass method_return to def example within the block?

Eli Sadoff
  • 7,173
  • 6
  • 33
  • 61

3 Answers3

1

As method_return is a variable you can't propagate this down to the example method unless you use it in another context. You could capture this in a class-level variable:

klass = Class.new(A) do
  @@method_return = method_return

  def example
    @@method_return
  end
end

You could also dynamically define the method so that the method_return variable is captured by the block as a closure:

klass = Class.new(A) do
  define_method(:example) do
    method_return
  end
end

When using def example then the block scope shifts and becomes something completely different.

tadman
  • 208,517
  • 23
  • 234
  • 262
  • Ahhh that's not a class instance variable, that's a _class variable_! Totally different thing! – Max Feb 13 '18 at 21:56
  • @max I meant that as in "instance of Class variable" but perhaps that could be phrased better. Edited. – tadman Feb 14 '18 at 17:03
1

This isn't odd at all, it's the regular behavior of method definitions:

class A
  x = 3
  def foo
    x
  end
end

A.new.foo # NameError: undefined local variable or method `x'

Method definitions don't capture local variables from their outer scope. This is generally good: normally you don't want the result of a method to depend on some random state that existed at the time of its definition, you want it to depend on the object you called it on!

The exception of course is when you are metaprogramming. In that case it's useful to capture local variables so you explicitly capture them using the same mechanism you use elsewhere: a block.

define_method :example do
  method_return
end
Max
  • 21,123
  • 5
  • 49
  • 71
0

The def construction closes its scope. Local variables cannot seek beyond that. To do what you want, use define_method instead.

sawa
  • 165,429
  • 45
  • 277
  • 381