質問

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
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top