jnv-iruby 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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