What Is Toplevel_Binding in Ruby

What is TOPLEVEL_BINDING in ruby?

The fact is, TOPLEVEL_BINDING always refers to a predefined global instance of Binding, while Kernel#binding creates a new instance of Binding that encapsulates the current execution context every time. At top level, they both contain the same bindings, but they are not the same object and you cannot test their binding equality with == or ===.

puts TOPLEVEL_BINDING
puts TOPLEVEL_BINDING
puts binding
puts binding
puts binding == binding

# =>
#<Binding:0x9769ea0>
#<Binding:0x9769ea0>
#<Binding:0x9941ea8>
#<Binding:0x9941e58>
false

Understanding ruby TOPLEVEL_BINDING

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

What's the difference between TOPLEVEL_BINDING and binding?

TOPLEVEL_BINDING is a constant.

binding is a method which creates a new binding specific to the place where it is called.

> TOPLEVEL_BINDING #=> #<Binding:0x0000564e7226f1d0>
> TOPLEVEL_BINDING #=> #<Binding:0x0000564e7226f1d0> -- same object
> binding #=> #<Binding:0x0000564e72892aa8>
> binding #=> #<Binding:0x0000564e728907f8> -- new object
> TOPLEVEL_BINDING.source_location #=> ["<main>", 0]
> binding.source_location #=> ["(irb)", 6]
> binding == binding #=> false
> x = 2
> b = binding
> z = 4
> TOPLEVEL_BINDING.eval('x') #=> NameError
> TOPLEVEL_BINDING.eval('z') #=> NameError
> b.eval('x') #=> 2
> b.eval('z') #=> NameError
> binding.eval('x') #=> 2
> binding.eval('z') #=> 4

eval and Binding in ruby

instanceMethodMain isn't called twice.

You can check it by adding

def instanceMethodMain
puts "BEEN HERE"
p "instanceMethodMain"
end

p is called twice with "instanceMethodMain" as parameter.

p p "instanceMethodMain"
#=> "instanceMethodMain"
#=> "instanceMethodMain"

Note that p "string" displays "string" and returns "string", while puts "string" displays string and returns nil :

puts puts "instanceMethodMain"
#=> instanceMethodMain
#=>

How to get `main` object evaluated in Ruby?

To gain access to the "main" object from anywhere, use the TOPLEVEL_BINDING:

class MyShell
def main_object
TOPLEVEL_BINDING.eval('self') #=> "main"
end
end

What's the difference between binding.pry and Pry.start?

Calling Pry.start(binding) from within a console should have a similar effect to calling binding.pry:

rails c
Loading development environment (Rails 5.1.6.2)
[1] pry(main)> class Gnar
[1] pry(main)* def self.gar
[1] pry(main)* @var = "lar!"
[1] pry(main)* Pry.start(binding)
[1] pry(main)* end
[1] pry(main)* end
=> :gar
[2] pry(main)> Gnar.gar

From: /Users/redacted/.gem/gems/pry-0.12.2/lib/pry/pry_instance.rb @ line 388 Pry#evaluate_ruby:

383: def evaluate_ruby(code)
384: inject_sticky_locals!
385: exec_hook :before_eval, code, self
386:
387: result = current_binding.eval(code, Pry.eval_path, Pry.current_line)
=> 388: set_last_result(result, code)
389: ensure
390: update_input_history(code)
391: exec_hook :after_eval, result, self
392: end

[1] pry(#<Pry>)>

A Binding object encapsulates the execution context of your code at a specific location. Binding#pry starts a Pry REPL over a Binding object.

Pry.start just starts a Pry REPL. If you don't pass it a binding, then it's just a Pry REPL without any execution context; the console tells you whether or not it has context by changing from pry(main) (no context) to pry(#<Pry>)> if the execution context of the binding passed to it was a Pry instance:

$ rails c
Loading development environment (Rails 5.1.6.2)
[1] pry(main)> Pry.start(binding)

From: /Users/redacted/.gem/gems/pry-0.12.2/lib/pry/pry_instance.rb @ line 388 Pry#evaluate_ruby:

383: def evaluate_ruby(code)
384: inject_sticky_locals!
385: exec_hook :before_eval, code, self
386:
387: result = current_binding.eval(code, Pry.eval_path, Pry.current_line)
=> 388: set_last_result(result, code)
389: ensure
390: update_input_history(code)
391: exec_hook :after_eval, result, self
392: end

[1] pry(#<Pry>)> exit
=> nil
[2] pry(main)> binding.pry

From: /Users/redacted/.gem/gems/pry-0.12.2/lib/pry/pry_instance.rb @ line 388 Pry#evaluate_ruby:

383: def evaluate_ruby(code)
384: inject_sticky_locals!
385: exec_hook :before_eval, code, self
386:
387: result = current_binding.eval(code, Pry.eval_path, Pry.current_line)
=> 388: set_last_result(result, code)
389: ensure
390: update_input_history(code)
391: exec_hook :after_eval, result, self
392: end

[1] pry(#<Pry>)> exit
=> nil
[3] pry(main)> exit

Similarly, calling binding.pry from within a class method opens up a Pry REPL within the execution context of that class (pry(ClassName)). Calling binding.pry from within an instance method opens up a Pry REPL within the execution context of that instance (pry(#<ClassName>).

rails c
Loading development environment (Rails 5.1.6.2)
[1] pry(main)> class A
[1] pry(main)* def self.b
[1] pry(main)* binding.pry
[1] pry(main)* end
[1] pry(main)*
[1] pry(main)* def c
[1] pry(main)* binding.pry
[1] pry(main)* end
[1] pry(main)* end
=> :c
[2] pry(main)> A.b

From: (pry) @ line 4 A.b:

2: def self.b
3: binding.pry
=> 4: end

[1] pry(A)> exit
=> nil
[3] pry(main)> A.new.c

From: (pry) @ line 8 A#c:

6: def c
7: binding.pry
=> 8: end

[1] pry(#<A>)> exit
=> nil

Hope that helps.

Any way to create a new, empty Binding in Ruby?

An easy way would be to write a method that calls binding and nothing else:

def empty_binding
binding
end

Then:

b = empty_binding
b.local_variables
# [ ]

That binding will still have a self and access to whatever instance variables are available to that self. You could limit that with some chicanery:

module Empty
def self.binding
super
end
end

b = Empty.binding
b.eval('puts local_variables.inspect')
# [ ]
b.eval('puts instance_variables.inspect')
# [ ]
b.eval('puts self.inspect')
# Empty

What works depends on what the goal is. A binding with no local variables is pretty easy, a binding with nothing at all probably isn't possible without hacking Ruby itself (although BasicObject might be useful to get a little closer to empty than a module).

None of these things give you a jail to safely eval inside if that's what you're after.



Related Topics



Leave a reply



Submit