jnv-iruby 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7e6455a899a0f5681f903057acdb3d9bbb6eaa26
4
+ data.tar.gz: dc4375cd4ca2bddc059dcc414f92dcc7895c2f54
5
+ SHA512:
6
+ metadata.gz: 67542fdf88d6c60c9d450f17ffcc4ed9ffa5a101b148117139cc85def8f8e5a0d570dacdd99b80d96a47892a0ce672381559f5e4709a772e7c7466fa4f3a926d
7
+ data.tar.gz: 7eee6d884ef082dc39340e6d4cf4581ab1ddcdbad323ad5b580f05fcb2f8145e2558e4c96358a98aa0c3ee2b0d3876992479f808d40318bb1cdfeac956f6ec33
@@ -0,0 +1,18 @@
1
+ .ipynb_checkpoints/
2
+ *.gem
3
+ *.rbc
4
+ .bundle
5
+ .config
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in iruby.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Damián Silvani
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,70 @@
1
+ # IRuby
2
+
3
+ This is a Ruby kernel for IPython.
4
+
5
+ It adds a special `iruby_profile` command for staging some customization
6
+ that enables the Ruby kernel by default, and sets syntax-highlighting in the notebook
7
+ to Ruby mode.
8
+
9
+ ### Usage
10
+
11
+ Clone this repository and run `bin/iruby_profile` to create the profile, then
12
+ use IPython as usual:
13
+
14
+ ```bash
15
+ git clone git://github.com/minrk/iruby
16
+ cd iruby
17
+ # build and install IRuby
18
+ gem build iruby.gemspec
19
+ $ gem install iruby-*.gem
20
+ # Create an IPython profile with default config
21
+ $ iruby_profile --create
22
+ $ ipython notebook --profile=ruby
23
+ ```
24
+
25
+
26
+ ## Background
27
+
28
+ ### Building an in-browser REPL for Ruby (IRuby)
29
+
30
+ Hey, I'm Josh Adams. I'm a partner and CTO at isotope|eleven. We alo host
31
+ Birmingham, AL's Open Source Software meetup - BOSS.
32
+
33
+ At one of these sessions in early 2012, Tom Brander did a presentation and used
34
+ IPython in his browser to manage it (there was much code and it was executed
35
+ live). This was the first time I'd seen IPython in the browser where it
36
+ actually worked like it was supposed to, and I was extremely impressed.
37
+
38
+ If you've not seen IPython, it looks like this <* Insert Screenshot Here *> in
39
+ its web-browser mode. It also manages a lot of console-basd REPLs.
40
+
41
+ Anyway, it has notebooks that you can save out to execute later, and you can
42
+ pass them around as little code snippets for other people to check out. It's
43
+ very impressive.
44
+
45
+ But I'm primarily a Rubyist, and I'm happy that way :) I couldn't sit by while
46
+ Python had this awesome tool that we lacked. I looked around for a bit, and
47
+ there was nothing like IPython in our ecosystem. There were, however, quite a
48
+ few people asking about it. So I figured I'd do something about it.
49
+
50
+ #### The Architecture
51
+
52
+ So the IPython guys did a great job explaining their core architecture, both in
53
+ words and in pared-down code, in a blog post they wrote concerning it. In
54
+ general, it works like this <* Diagram *>
55
+
56
+ There's a kernel that runs in the background and gets connected to by a
57
+ frontend. They communicate using zeromq, and they send json formatted messages
58
+ back and forth. These messages are in a very well defined structure. Anyway,
59
+ this way the frontend of the repl is disconnected from the environment that's
60
+ running it.
61
+
62
+ So the code repository they linked to in their blog post included the kernel and
63
+ the frontend as small-ish python files - around 300 and 200 lines respectively.
64
+ We had a hack weekend at isotope|eleven where myself and Robby Clements got
65
+ together and (when we weren't playing Counterstrike1.6) did the closest thing to
66
+ a straight port that we could swing. Within about 2 hours of work, we had a
67
+ working proof of concept that was primarily a 1 to 1 port.
68
+
69
+ The next move was to build the web frontend. This just consists of a websocket
70
+ server and a fairly basic frontend webpage.
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,117 @@
1
+ {
2
+ "metadata": {
3
+ "name": "Ruby Notebook"
4
+ },
5
+ "nbformat": 3,
6
+ "nbformat_minor": 0,
7
+ "worksheets": [
8
+ {
9
+ "cells": [
10
+ {
11
+ "cell_type": "markdown",
12
+ "metadata": {},
13
+ "source": [
14
+ "# This is an IPython notebook, backed by a ruby kernel.\n",
15
+ "\n",
16
+ "I wrote a kernel in Ruby that adheres to the [IPython](http://ipython.org/) messaging protocol. Then I modified IPython, with the help of minrk from #ipython, to instantiate my Ruby Kernel instead of its own Python kernel.\n",
17
+ "\n",
18
+ "The IPython KernelManager initializes the RubyKernel with popen, and from that point communication occurs over ZeroMQ, exactly as in IPython's kernel.\n",
19
+ "\n",
20
+ "Once that was done, it's easy to execute ruby against that kernel right in the browser."
21
+ ]
22
+ },
23
+ {
24
+ "cell_type": "code",
25
+ "collapsed": false,
26
+ "input": [
27
+ "class Neat\n",
28
+ " def eh?\n",
29
+ " \"hell yes it is.\"\n",
30
+ " end\n",
31
+ "end\n",
32
+ "\n",
33
+ "Neat.new.eh?"
34
+ ],
35
+ "language": "ruby",
36
+ "metadata": {},
37
+ "outputs": []
38
+ },
39
+ {
40
+ "cell_type": "markdown",
41
+ "metadata": {},
42
+ "source": [
43
+ "## What does this give you?\n",
44
+ "\n",
45
+ "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.\n",
46
+ "\n",
47
+ "It's basically an in-browser REPL loop, with some extra goodies."
48
+ ]
49
+ },
50
+ {
51
+ "cell_type": "code",
52
+ "collapsed": false,
53
+ "input": [
54
+ "class ThatsPrettyCool\n",
55
+ " def how_does_it_work?\n",
56
+ " puts \"I'm glad you asked!\"\n",
57
+ " :see_below\n",
58
+ " end\n",
59
+ "end\n",
60
+ "\n",
61
+ "ThatsPrettyCool.new.how_does_it_work?"
62
+ ],
63
+ "language": "ruby",
64
+ "metadata": {},
65
+ "outputs": []
66
+ },
67
+ {
68
+ "cell_type": "markdown",
69
+ "metadata": {},
70
+ "source": [
71
+ "### Here's how it works\n",
72
+ "\n",
73
+ "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.\n",
74
+ "\n",
75
+ "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.\n",
76
+ "\n",
77
+ "### What does a message look like?\n",
78
+ "\n",
79
+ "A given JSON message looks like this:\n",
80
+ "\n",
81
+ " {\"header\"=>\n",
82
+ " {\"username\"=>\"username\",\n",
83
+ " \"msg_id\"=>\"38C1E3299BB14F7C99D95504CEAE4856\",\n",
84
+ " \"msg_type\"=>\"execute_request\",\n",
85
+ " \"session\"=>\"BAC5EA31BC6F4C1D888DD5F8C46B6F9D\"},\n",
86
+ " \"msg_id\"=>\"msg_id\",\n",
87
+ " \"msg_type\"=>\"msg_type\",\n",
88
+ " \"parent_header\"=>{},\n",
89
+ " \"content\"=>\n",
90
+ " {\"user_variables\"=>[],\n",
91
+ " \"allow_stdin\"=>false,\n",
92
+ " \"code\"=>\n",
93
+ " \"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?\",\n",
94
+ " \"silent\"=>false,\n",
95
+ " \"user_expressions\"=>{}},\n",
96
+ " \"buffers\"=>[]}"
97
+ ]
98
+ },
99
+ {
100
+ "cell_type": "markdown",
101
+ "metadata": {},
102
+ "source": [
103
+ "### What remains to be done for this to be 'complete'?\n",
104
+ "\n",
105
+ "- Right now, I'm not passing back the 'return value of the last expression' like you'd expect a proper REPL to do. I'm not exactly sure how to get the analog of python's sys.displayhook working, but I welcome anyone to teach me how it's done :) I can probably figure it out from the pry source.\n",
106
+ "- backtraces are a little bit wonky right now and don't give you the proper backtrace into the executed code, but rather into the Kernel's source where the eval loop runs...not very helpful.\n",
107
+ "- There aren't any tests.\n",
108
+ "- It would be nice to support the fancy IPython graph and html-table gooies, I'll look into that.\n",
109
+ "- IPython supports tab-completion in the browser REPL, but my kernel doesn't know how to respond to those messages.\n",
110
+ "- There are some other message types I've not yet implemented, but I don't really see the problem at present :)"
111
+ ]
112
+ }
113
+ ],
114
+ "metadata": {}
115
+ }
116
+ ]
117
+ }
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ USAGE = <<-EOS
4
+ This script is not intended to be used manually.
5
+
6
+ To start an IPython Notebook server with IRuby, first create a profile with
7
+ `iruby_profile`, then use `ipython notebook` to start the server:
8
+
9
+ $ iruby_profile --create
10
+ $ ipython notebook --profile=ruby
11
+
12
+ Read more at http://ipython.org/ipython-doc/dev/interactive/notebook.html
13
+
14
+ EOS
15
+
16
+ def start_kernel!(config_path, boot_file=nil)
17
+ #ENV["BUNDLE_GEM"] = "/home/munshkr/overol-parsers/Gemfile"
18
+ #ENV["APP_ROOT"] = "/home/munshkr/overol-parsers"
19
+
20
+ require boot_file if boot_file
21
+ require 'iruby/kernel'
22
+
23
+ configfile = File.read(config_path)
24
+ config = JSON.parse(configfile)
25
+
26
+ c = ZMQ::Context.new
27
+
28
+ shell_port = config['shell_port']
29
+ pub_port = config['iopub_port']
30
+ hb_port = config['hb_port']
31
+
32
+ ip = '127.0.0.1'
33
+ connection = ('tcp://%s' % ip) + ':%i'
34
+ shell_conn = connection % shell_port
35
+ pub_conn = connection % pub_port
36
+ hb_conn = connection % hb_port
37
+
38
+ $stdout.puts "Starting the kernel..."
39
+ $stdout.puts "On:",shell_conn, pub_conn, hb_conn
40
+
41
+ session = IRuby::Session.new('kernel')
42
+
43
+ reply_socket = c.socket(ZMQ::XREP)
44
+ reply_socket.bind(shell_conn)
45
+
46
+ pub_socket = c.socket(ZMQ::PUB)
47
+ pub_socket.bind(pub_conn)
48
+
49
+ hb_thread = Thread.new do
50
+ hb_socket = c.socket(ZMQ::REP)
51
+ hb_socket.bind(hb_conn)
52
+ ZMQ::Device.new(ZMQ::FORWARDER, hb_socket, hb_socket)
53
+ end
54
+
55
+ stdout = IRuby::OutStream.new(session, pub_socket, 'stdout')
56
+ #stderr = OutStream.new(session, pub_socket, 'stderr')
57
+ old_stdout = STDOUT
58
+ $stdout = stdout
59
+ #$stderr = stderr
60
+
61
+
62
+ kernel = IRuby::Kernel.new(session, reply_socket, pub_socket)
63
+ display_hook = IRuby::DisplayHook.new(kernel, session, pub_socket)
64
+ $displayhook = display_hook
65
+
66
+ # For debugging convenience, put sleep and a string in the namespace, so we
67
+ # have them every time we start.
68
+ #kernel.user_ns['sleep'] = sleep
69
+ #kernel.user_ns['s'] = 'Test string'
70
+
71
+ old_stdout.puts "Use Ctrl-\\ (NOT Ctrl-C!) to terminate."
72
+ kernel.start()
73
+ end
74
+
75
+
76
+ if ARGV.size == 0 || ARGV.size > 2
77
+ puts USAGE
78
+ exit(1)
79
+ end
80
+ config_path, boot_file = ARGV
81
+
82
+ if !File.exists?(config_path)
83
+ puts "Config file '#{config_path}' does not exist"
84
+ exit(2)
85
+ end
86
+
87
+ if boot_file && !File.exists?(boot_file)
88
+ puts "File '#{boot_file}' does not exist"
89
+ exit(3)
90
+ end
91
+
92
+ start_kernel!(config_path, boot_file)
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env ruby
2
+ require 'trollop'
3
+
4
+ opts = Trollop::options do
5
+ banner <<-EOS
6
+ `iruby_profile` creates an IPython profile already customized for using the
7
+ IRuby kernel. You can specify a custom name with --name, the real name will
8
+ have 'iruby_' prepended.
9
+
10
+ Examples:
11
+ $ iruby_profile --create
12
+ # creates profile at $(ipython locate profile ruby)
13
+
14
+ $ iruby_profile --create --name='special'
15
+ # creates profile at $(ipython locate profile special)
16
+
17
+ To use the profile, start IPython as usual:
18
+ $ ipython notebook --profile='ruby'
19
+
20
+ Options:
21
+ EOS
22
+
23
+ opt :create, 'Create a profile', default: false
24
+ opt :name, 'Profile name', type: :string, default: 'ruby'
25
+ end
26
+
27
+ if not opts[:create_given]
28
+ puts "Try --help first."
29
+ exit(1)
30
+ end
31
+
32
+ if opts[:create]
33
+ require 'iruby/profile'
34
+
35
+ p = IRuby::Profile.new(opts[:name])
36
+ p.create!
37
+ end
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'iruby/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "jnv-iruby"
8
+ spec.version = IRuby::VERSION
9
+ spec.authors = ["Damián Silvani", "Min RK", "martin sarsale", "Josh Adams"]
10
+ spec.email = ["benjaminrk@gmail.com"]
11
+ spec.description = %q{Ruby Kernel for IPython}
12
+ spec.summary = %q{A Ruby kernel for IPython frontends (notebook console, etc.)}
13
+ spec.homepage = "https://github.com/minrk/iruby"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+
24
+ spec.add_runtime_dependency "bond"
25
+ spec.add_runtime_dependency "ffi-rzmq"
26
+ spec.add_runtime_dependency "json"
27
+ spec.add_runtime_dependency "term-ansicolor"
28
+ spec.add_runtime_dependency "trollop"
29
+ spec.add_runtime_dependency "uuid"
30
+ spec.add_runtime_dependency "gruff"
31
+ end
@@ -0,0 +1,4 @@
1
+ require 'iruby/version'
2
+
3
+ module IRuby
4
+ end
@@ -0,0 +1,33 @@
1
+ require 'iruby/message'
2
+
3
+ module IRuby
4
+ class DisplayHook
5
+ def initialize kernel, session, pub_socket
6
+ @kernel = kernel
7
+ @session = session
8
+ @pub_socket = pub_socket
9
+ @parent_header = {}
10
+ end
11
+
12
+ def display(obj)
13
+ if obj.nil?
14
+ return
15
+ end
16
+ # STDERR.puts @kernel.user_ns
17
+ # @user_ns._ = obj
18
+ # STDERR.puts "displayhook call:"
19
+ # STDERR.puts @parent_header.inspect
20
+ #@pub_socket.send(msg.to_json)
21
+ data = {}
22
+ output = obj
23
+ data['text/plain'] = output
24
+ data[obj.mime] = output
25
+ content = {data: data, metadata: {}, execution_count: @kernel.execution_count}
26
+ @session.send(@pub_socket, 'pyout', content, @parent_header)
27
+ end
28
+
29
+ def set_parent parent
30
+ @parent_header = Message.extract_header(parent)
31
+ end
32
+ end
33
+ end