I wrote a kernel in Ruby that adheres to the IPython messaging protocol. Then I modified IPython, with the help of minrk from #ipython, to instantiate my Ruby Kernel instead of its own Python kernel.
The IPython KernelManager initializes the RubyKernel with popen, and from that point communication occurs over ZeroMQ, exactly as in IPython's kernel.
Once that was done, it's easy to execute ruby against that kernel right in the browser.
In [ ]:
class Neat
def eh?
"hell yes it is."
end
end
Neat.new.eh?
This gives us a very fancy web notebook interface for Ruby. It's a very good tool for programming presentations. Tom Brander's presentation to the BOSS meetup group made me realize exactly how far IPython had come since I last saw it, and I knew I wanted to have it for ruby.
It's basically an in-browser REPL loop, with some extra goodies.
In [ ]:
class ThatsPrettyCool
def how_does_it_work?
puts "I'm glad you asked!"
:see_below
end
end
ThatsPrettyCool.new.how_does_it_work?
Each notebook has its own kernel. When you open a notebook in the web interface, a kernel is started in the background by the IPython webserver. A websocket is then used to connect the frontend directly to the kernel, more or less, and they pass messages back and forth.
All of the messages are in a JSON format. The actual information passed over the wire between the web server and the kernel is a little different, but it basically just includes some session/auth information, and then the components of the message serialized, too be unserialized on the Kernel into the full json message again.
A given JSON message looks like this:
{"header"=>
{"username"=>"username",
"msg_id"=>"38C1E3299BB14F7C99D95504CEAE4856",
"msg_type"=>"execute_request",
"session"=>"BAC5EA31BC6F4C1D888DD5F8C46B6F9D"},
"msg_id"=>"msg_id",
"msg_type"=>"msg_type",
"parent_header"=>{},
"content"=>
{"user_variables"=>[],
"allow_stdin"=>false,
"code"=>
"class ThatsPrettyCool\n def how_does_it_work?\n puts \"I'm glad you asked!\"\n end\nend\n\nThatsPrettyCool.new.how_does_it_work?",
"silent"=>false,
"user_expressions"=>{}},
"buffers"=>[]}