I understand that TOPLEVEL_BINDING is the Binding object for main. The following code confirms it:
def name
:outer
end
module Test
class Binder
def self.name
:inner
end
def self.test_it
eval 'name', TOPLEVEL_BINDING
end
end
end
p Test::Binder.test_it # => :outer
I got confused while looking at the source for rack. The problem was in understanding this code in the file lib/rack/builder.rb
def self.new_from_string(builder_script, file="(rackup)")
eval "Rack::Builder.new {\n" + builder_script + "\n}.to_app",
TOPLEVEL_BINDING, file, 0
end
def run(app)
end
The new_from_string method is passed the contents of a config.ru file which will be something like
run DemoApp::Application
Here it seems like TOPLEVEL_BINDING is referring to a Builder object, since the method run
is defined for Builder but not for Object. However the earlier experiment establishes that TOPLEVEL_BINDING refers to main's binding. I do not understand how run method is working here. Please help me in understanding this code.
TOPLEVEL_BINDING
is the top level binding.
That method is passing the string "run ..." into Builder.new { run ... }
Builder.new then does an instance_eval (https://github.com/rack/rack/blob/df1506b0825a096514fcb3821563bf9e8fd52743/lib/rack/builder.rb#L53-L55) on the block, thereby giving the code inside the block direct access to the instance's methods.
def initialize(default_app = nil,&block)
@use, @map, @run, @warmup = [], nil, default_app, nil
instance_eval(&block) if block_given?
end
run
is an instance method of the Builder class, defined here -> https://github.com/rack/rack/blob/df1506b0825a096514fcb3821563bf9e8fd52743/lib/rack/builder.rb#L103-L105
def run(app)
@run = app
end
In short, "run DemoApp::Application"
becomes:
Rack::Builder.new {
run DemoApp::Application
}.to_app
Edit: A simple example to illustrate the point:
class Builder
def initialize(&block)
instance_eval(&block)
end
def run(what)
puts "Running #{what}"
end
end
TOPLEVEL_BINDING.eval "Builder.new { run 10 }"
prints
Running 10