DEV Community

loading...

`Eval`ing twice to define toplevel module from String

OKURA Masafumi
Ruby/Rails developer from Japan. The chief organizer of Kaigi on Rails ( https://kaigionrails.org ). Vimmer. He/Him
・1 min read

Sometimes we want to define modules using String and instance_eval.

class A
end

code = <<-CODE
  module B
    module_function
    def b
      puts 'b'
    end
  end
CODE
a = A.new
a.instance_eval code

B.b # => Should print 'b'
Enter fullscreen mode Exit fullscreen mode

However, this code causes NameError saying uninitialized constant B.

Where has it gone? To know where module B is defined, we use ObjectSpace class.

# Replace `B.b` in the snippet above with this
ObjectSpace.each_object(Module) {|m| p m if m.name =~ /::B/ }
# => You see something like "#<Class:0x00007f861b8c8e50>::B"
Enter fullscreen mode Exit fullscreen mode

OK, with instance_eval and String combination, the defined module is put in Anonymous class namespace, the way which we don't like. How to define a toplevel module with instance_eval?

So here's the answer.

class A
end

code = <<-CODE
  module B
    module_function
    def b
      puts 'b'
    end
  end
CODE
a = A.new
a.instance_eval do
  eval code
end
B.b # => "b" is printed
Enter fullscreen mode Exit fullscreen mode

What happened? We eval code string in instance_eval block. I don't know why but this code just does what we want to do.

Discussion (0)