Hi,
require 'irb'
module IRB
def IRB.start_in_binding(b)
setup(nil)
irb = IRB::Irb.new(WorkSpace.new(b))
@CONF[:MAIN_CONTEXT] = irb.context
catch
![Smile :) :)](data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)
IRB_EXIT) { irb.eval_input }
end
end
I've no idea how this works; it has to rate as cool Ruby, even if it isn't
implemented in Ruby
3) PREFACE
I understood your sentence as "i've no idea how <the code you pasted>
works..." and started explaining how it works and what can be done with it,
translating the concept being explained in the article I linked in my
previous post (code from the inside out [3]).
In the end, just some picoseconds before hitting "SEND", I realized that you
were referring to tryruby.hobix.com.
I'd feel even dumber if I had thrown away the writeup, maybe it could prove
interesting for someone. I also surely made lots of errors and said lots of
imprecise things. Please correct me. TIA
![Smile :) :)](data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)
.
1) WRITEUP
the IRB.start_in_binding method makes you able to start an IRB in whichever
context you want, attaching it to a running ruby program, in the context you
define. let's say, e.g., that you're dissatisfied by using "puts"
or "debugger" to inspect how "things" in your program work, and you'd like to
be *inside* your code, in order to better feel the presence of methods and
objects that you can call and inspect, in some nested place in your code.
e.g. you'd like to write an association extension, but really don't know which
methods can call from that context, or you don't immediately understand in
which scope the association extension code is being called. start_in_binding
to the rescue!
has_many :things do
current_binding = binding # verbose assignment, to make
IRB.start_in_binding(current_binding) # things more clear.
end
when that "has_many" method is run (e.g. by require'ing the model with this
association) another IRB prompt will greet you. welcome inside your program!
with=> Array
and=> true
you can really feel like being an intruder in a foreign land
![Wink ;) ;)](data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)
, you can try
to call things around, or you can define methods and test them *directly*
into the code. So, the ruby code doesn't go from the source file into the
running instance, it goes exactly the opposite way: you write it inside the
running instance, and if it works you copy it into the source file. Pretty
neat
![Smile :) :)](data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)
. The article I linked in my previous post explains this "intruder
pattern", but uses a Tk program as an intrusion point.
if you are unfamiliar with "binding", it is an "execution context", with all
the local variables, methods and 'self' value left in place. Someone can
describe it in more detail than me, or you can just "ri Binding" [1]. You can
pass Binding instances as a second argument to an eval() call, so that the
given code will be evaluated in the passed binding's context.
Kernel#binding returns the current binding.
Some other very neat (hacky?) bit (bunch?) of binding-related code is in
irb/workspace.rb:55
when 3 # binging in function on TOPLEVEL_BINDING(default)
@binding = eval("def irb_binding; binding; end; irb_binding",
TOPLEVEL_BINDING,
__FILE__,
__LINE__ - 3)
here it is defined a method that returns ITS binding, and then it is being
called. that method is executed in TOPLEVEL_BINDING context, and its return
value (a "clean" binding inside Object#irb_binding) is stored in @binding.
@binding is used for further evaluation. that's schweet
![Smile :) :)](data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)
.
other options, always in workspace.rb:
when 0 # binding in proc on TOPLEVEL_BINDING
@binding = eval("proc{binding}.call",
TOPLEVEL_BINDING,
__FILE__,
__LINE__)
here the binding is pulled out from an anonymous lambda call..
the most hacky, imho:
when 1 # binding in loaded file
require "tempfile"
f = Tempfile.open("irb-binding")
f.print <<EOF
$binding = binding
EOF
f.close
load f.path
@binding = $binding
write-some-temp-file-that-stores-the-binding-in-a-global-var-and-pull-out-that-one-using-load
!
sorry for this long blob of things that experienced ruby users know like their
hands, but I went through this stuff some time ago, and while I was
understanding how it works, i learnt all the Binding stuff [2]. it was like
being in a journey through different execution worlds
![Wink ;) ;)](data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)
and I had lot of fun
putting all the pieces together. And I'm sharing my fun and my horrid
english
![Wink ;) ;)](data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)
.
Marcello
2) FOOTNOTES
[1]: i suggest <
http://eigenclass.org/hiki.rb?fastri> if you feel that ri is
too (fscking) slow in doing its job.
[2]: the next step were eigenclasses, which I understood completely only after
reading the RHG <
http://rhg.rubyforge.org>. even that was fun. ruby's magic
dust..
[3]:
http://rubyurl.com/K4P