irails 0.1.0
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.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/.travis.yml +16 -0
- data/CONTRIBUTORS +19 -0
- data/Gemfile +15 -0
- data/LICENSE +22 -0
- data/README.md +46 -0
- data/Rakefile +15 -0
- data/bin/irails +5 -0
- data/irails.gemspec +34 -0
- data/lib/irails/assets/kernel.css +1 -0
- data/lib/irails/assets/kernel.js +15 -0
- data/lib/irails/assets/logo-32x32.png +0 -0
- data/lib/irails/assets/logo-64x64.png +0 -0
- data/lib/irails/backend.rb +82 -0
- data/lib/irails/comm.rb +46 -0
- data/lib/irails/command.rb +136 -0
- data/lib/irails/display.rb +286 -0
- data/lib/irails/formatter.rb +146 -0
- data/lib/irails/kernel.rb +165 -0
- data/lib/irails/logger.rb +19 -0
- data/lib/irails/ostream.rb +46 -0
- data/lib/irails/session.rb +103 -0
- data/lib/irails/utils.rb +41 -0
- data/lib/irails/version.rb +3 -0
- data/lib/irails.rb +16 -0
- data/logo/logo-32x32.png +0 -0
- data/logo/logo-64x64.png +0 -0
- data/logo/ruby.svg +948 -0
- data/test/integration_test.rb +41 -0
- data/test/irails/multi_logger_test.rb +15 -0
- data/test/test_helper.rb +5 -0
- metadata +189 -0
@@ -0,0 +1,286 @@
|
|
1
|
+
module IRails
|
2
|
+
module Display
|
3
|
+
class << self
|
4
|
+
def convert(obj, options)
|
5
|
+
Representation.new(obj, options)
|
6
|
+
end
|
7
|
+
|
8
|
+
def display(obj, options = {})
|
9
|
+
obj = convert(obj, options)
|
10
|
+
options = obj.options
|
11
|
+
obj = obj.object
|
12
|
+
|
13
|
+
fuzzy_mime = options[:format] # Treated like a fuzzy mime type
|
14
|
+
raise 'Invalid argument :format' unless !fuzzy_mime || String === fuzzy_mime
|
15
|
+
if exact_mime = options[:mime]
|
16
|
+
raise 'Invalid argument :mime' unless String === exact_mime
|
17
|
+
raise 'Invalid mime type' unless exact_mime.include?('/')
|
18
|
+
end
|
19
|
+
|
20
|
+
data = {}
|
21
|
+
|
22
|
+
# Render additional representation
|
23
|
+
render(data, obj, exact_mime, fuzzy_mime)
|
24
|
+
|
25
|
+
# IPython always requires a text representation
|
26
|
+
render(data, obj, 'text/plain', nil) unless data['text/plain']
|
27
|
+
|
28
|
+
# As a last resort, interpret string representation of the object
|
29
|
+
# as the given mime type.
|
30
|
+
data[exact_mime] = protect(exact_mime, obj) if exact_mime && !data.any? {|m,_| exact_mime == m }
|
31
|
+
|
32
|
+
data
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def protect(mime, data)
|
38
|
+
MimeMagic.new(mime).text? ? data.to_s : [data.to_s].pack('m0')
|
39
|
+
end
|
40
|
+
|
41
|
+
def render(data, obj, exact_mime, fuzzy_mime)
|
42
|
+
# Filter matching renderer by object type
|
43
|
+
renderer = Registry.renderer.select {|r| r.match?(obj) }
|
44
|
+
|
45
|
+
matching_renderer = nil
|
46
|
+
|
47
|
+
# Find exactly matching display by exact_mime
|
48
|
+
matching_renderer = renderer.find {|r| exact_mime == r.mime } if exact_mime
|
49
|
+
|
50
|
+
# Find fuzzy matching display by fuzzy_mime
|
51
|
+
matching_renderer ||= renderer.find {|r| r.mime && r.mime.include?(fuzzy_mime) } if fuzzy_mime
|
52
|
+
|
53
|
+
renderer.unshift matching_renderer if matching_renderer
|
54
|
+
|
55
|
+
# Return first render result which has the right mime type
|
56
|
+
renderer.each do |r|
|
57
|
+
mime, result = r.render(obj)
|
58
|
+
if mime && result && (!exact_mime || exact_mime == mime) && (!fuzzy_mime || mime.include?(fuzzy_mime))
|
59
|
+
data[mime] = protect(mime, result)
|
60
|
+
break
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
nil
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class Representation
|
69
|
+
attr_reader :object, :options
|
70
|
+
|
71
|
+
def initialize(object, options)
|
72
|
+
@object, @options = object, options
|
73
|
+
end
|
74
|
+
|
75
|
+
class << self
|
76
|
+
alias old_new new
|
77
|
+
|
78
|
+
def new(obj, options)
|
79
|
+
options = { format: options } if String === options
|
80
|
+
if Representation === obj
|
81
|
+
options = obj.options.merge(options)
|
82
|
+
obj = obj.object
|
83
|
+
end
|
84
|
+
old_new(obj, options)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
class Renderer
|
90
|
+
attr_reader :match, :mime, :render, :priority
|
91
|
+
|
92
|
+
def initialize(match, mime, render, priority)
|
93
|
+
@match, @mime, @render, @priority = match, mime, render, priority
|
94
|
+
end
|
95
|
+
|
96
|
+
def match?(obj)
|
97
|
+
@match.call(obj)
|
98
|
+
end
|
99
|
+
|
100
|
+
def render(obj)
|
101
|
+
result = @render.call(obj)
|
102
|
+
Array === result ? result : [@mime, result]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
module Registry
|
107
|
+
extend self
|
108
|
+
|
109
|
+
def renderer
|
110
|
+
@renderer ||= []
|
111
|
+
end
|
112
|
+
|
113
|
+
SUPPORTED_MIMES = %w(
|
114
|
+
text/plain
|
115
|
+
text/html
|
116
|
+
text/latex
|
117
|
+
application/json
|
118
|
+
application/javascript
|
119
|
+
image/png
|
120
|
+
image/jpeg
|
121
|
+
image/svg+xml)
|
122
|
+
|
123
|
+
def match(&block)
|
124
|
+
@match = block
|
125
|
+
priority 0
|
126
|
+
nil
|
127
|
+
end
|
128
|
+
|
129
|
+
def respond_to(name)
|
130
|
+
match {|obj| obj.respond_to?(name) }
|
131
|
+
end
|
132
|
+
|
133
|
+
def type(&block)
|
134
|
+
match do |obj|
|
135
|
+
begin
|
136
|
+
block.call === obj
|
137
|
+
# We have to rescue all exceptions since constant autoloading could fail with a different error
|
138
|
+
rescue Exception
|
139
|
+
rescue #NameError
|
140
|
+
false
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def priority(p)
|
146
|
+
@priority = p
|
147
|
+
nil
|
148
|
+
end
|
149
|
+
|
150
|
+
def format(mime = nil, &block)
|
151
|
+
renderer << Renderer.new(@match, mime, block, @priority)
|
152
|
+
renderer.sort_by! {|r| -r.priority }
|
153
|
+
|
154
|
+
# Decrease priority implicitly for all formats
|
155
|
+
# which are added later for a type.
|
156
|
+
# Overwrite with the `priority` method!
|
157
|
+
@priority -= 1
|
158
|
+
nil
|
159
|
+
end
|
160
|
+
|
161
|
+
type { NMatrix }
|
162
|
+
format 'text/latex' do |obj|
|
163
|
+
obj.dim == 2 ?
|
164
|
+
LaTeX.matrix(obj, obj.shape[0], obj.shape[1]) :
|
165
|
+
LaTeX.vector(obj.to_a)
|
166
|
+
end
|
167
|
+
|
168
|
+
type { NArray }
|
169
|
+
format 'text/latex' do |obj|
|
170
|
+
obj.dim == 2 ?
|
171
|
+
LaTeX.matrix(obj.transpose(1, 0), obj.shape[1], obj.shape[0]) :
|
172
|
+
LaTeX.vector(obj.to_a)
|
173
|
+
end
|
174
|
+
format 'text/html' do |obj|
|
175
|
+
HTML.table(obj.to_a)
|
176
|
+
end
|
177
|
+
|
178
|
+
type { Matrix }
|
179
|
+
format 'text/latex' do |obj|
|
180
|
+
LaTeX.matrix(obj, obj.row_size, obj.column_size)
|
181
|
+
end
|
182
|
+
format 'text/html' do |obj|
|
183
|
+
HTML.table(obj.to_a)
|
184
|
+
end
|
185
|
+
|
186
|
+
type { GSL::Matrix }
|
187
|
+
format 'text/latex' do |obj|
|
188
|
+
LaTeX.matrix(obj, obj.size1, obj.size2)
|
189
|
+
end
|
190
|
+
format 'text/html' do |obj|
|
191
|
+
HTML.table(obj.to_a)
|
192
|
+
end
|
193
|
+
|
194
|
+
type { GSL::Vector }
|
195
|
+
format 'text/latex' do |obj|
|
196
|
+
LaTeX.vector(obj.to_a)
|
197
|
+
end
|
198
|
+
format 'text/html' do |obj|
|
199
|
+
HTML.table(obj.to_a)
|
200
|
+
end
|
201
|
+
|
202
|
+
type { GSL::Complex }
|
203
|
+
format 'text/latex' do |obj|
|
204
|
+
"$#{obj.re}+#{obj.im}\\imath$"
|
205
|
+
end
|
206
|
+
|
207
|
+
type { Complex }
|
208
|
+
format 'text/latex' do |obj|
|
209
|
+
"$#{obj.real}+#{obj.imag}\\imath$"
|
210
|
+
end
|
211
|
+
|
212
|
+
type { Gnuplot::Plot }
|
213
|
+
format 'image/svg+xml' do |obj|
|
214
|
+
Tempfile.open('plot') do |f|
|
215
|
+
terminal = obj['terminal'].to_s.split(' ')
|
216
|
+
terminal[0] = 'svg'
|
217
|
+
terminal << 'enhanced' unless terminal.include?('noenhanced')
|
218
|
+
obj.terminal terminal.join(' ')
|
219
|
+
obj.output f.path
|
220
|
+
Gnuplot.open do |io|
|
221
|
+
io << obj.to_gplot
|
222
|
+
io << obj.store_datasets
|
223
|
+
end
|
224
|
+
File.read(f.path)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
match do |obj|
|
229
|
+
defined?(Magick::Image) && Magick::Image === obj ||
|
230
|
+
defined?(MiniMagick::Image) && MiniMagick::Image === obj
|
231
|
+
end
|
232
|
+
format 'image' do |obj|
|
233
|
+
format = obj.format || 'PNG'
|
234
|
+
[format == 'PNG' ? 'image/png' : 'image/jpeg', obj.to_blob {|i| i.format = format }]
|
235
|
+
end
|
236
|
+
|
237
|
+
type { Gruff::Base }
|
238
|
+
format 'image/png' do |obj|
|
239
|
+
obj.to_blob
|
240
|
+
end
|
241
|
+
|
242
|
+
respond_to :to_html
|
243
|
+
format 'text/html' do |obj|
|
244
|
+
obj.to_html
|
245
|
+
end
|
246
|
+
|
247
|
+
respond_to :to_latex
|
248
|
+
format 'text/latex' do |obj|
|
249
|
+
obj.to_latex
|
250
|
+
end
|
251
|
+
|
252
|
+
respond_to :to_tex
|
253
|
+
format 'text/latex' do |obj|
|
254
|
+
obj.to_tex
|
255
|
+
end
|
256
|
+
|
257
|
+
respond_to :to_javascript
|
258
|
+
format 'text/javascript' do |obj|
|
259
|
+
obj.to_javascript
|
260
|
+
end
|
261
|
+
|
262
|
+
respond_to :to_svg
|
263
|
+
format 'image/svg+xml' do |obj|
|
264
|
+
obj.render if defined?(Rubyvis) && Rubyvis::Mark === obj
|
265
|
+
obj.to_svg
|
266
|
+
end
|
267
|
+
|
268
|
+
respond_to :to_irails
|
269
|
+
format do |obj|
|
270
|
+
obj.to_irails
|
271
|
+
end
|
272
|
+
|
273
|
+
match {|obj| obj.respond_to?(:path) && File.readable?(obj.path) }
|
274
|
+
format do |obj|
|
275
|
+
mime = MimeMagic.by_path(obj.path).to_s
|
276
|
+
[mime, File.read(obj.path)] if SUPPORTED_MIMES.include?(mime)
|
277
|
+
end
|
278
|
+
|
279
|
+
type { Object }
|
280
|
+
priority -1000
|
281
|
+
format 'text/plain' do |obj|
|
282
|
+
obj.inspect
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
module IRails
|
2
|
+
module LaTeX
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def vector(v)
|
6
|
+
x = 'c' * v.size
|
7
|
+
y = v.map(&:to_s).join(' & ')
|
8
|
+
"$$\\left(\\begin{array}{#{x}} #{y} \\end{array}\\right)$$"
|
9
|
+
end
|
10
|
+
|
11
|
+
def matrix(m, row_count, column_count)
|
12
|
+
s = "$$\\left(\\begin{array}{#{'c' * column_count}}\n"
|
13
|
+
(0...row_count).each do |i|
|
14
|
+
s << ' ' << m[i,0].to_s
|
15
|
+
(1...column_count).each do |j|
|
16
|
+
s << '&' << m[i,j].to_s
|
17
|
+
end
|
18
|
+
s << "\\\\\n"
|
19
|
+
end
|
20
|
+
s << "\\end{array}\\right)$$"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
module HTML
|
25
|
+
extend self
|
26
|
+
|
27
|
+
def table(obj, maxrows: 15, maxcols: 15, **options)
|
28
|
+
raise ArgumentError, 'Invalid :maxrows' if maxrows && maxrows < 3
|
29
|
+
raise ArgumentError, 'Invalid :maxcols' if maxcols && maxcols < 3
|
30
|
+
|
31
|
+
return obj unless obj.respond_to?(:each)
|
32
|
+
|
33
|
+
rows = []
|
34
|
+
|
35
|
+
if obj.respond_to?(:keys)
|
36
|
+
# Hash of Arrays
|
37
|
+
header = obj.keys
|
38
|
+
keys = (0...obj.keys.size).to_a
|
39
|
+
cols = obj.values.map {|x| [x].flatten(1) }
|
40
|
+
num_rows = cols.map(&:size).max
|
41
|
+
rows = []
|
42
|
+
(0...num_rows).each do |i|
|
43
|
+
rows << []
|
44
|
+
(0...cols.size).each do |j|
|
45
|
+
rows[i][j] = cols[j][i]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
else
|
49
|
+
keys = nil
|
50
|
+
array_size = 0
|
51
|
+
|
52
|
+
obj.each do |row|
|
53
|
+
if row.respond_to?(:keys)
|
54
|
+
# Array of Hashes
|
55
|
+
keys ||= Set.new
|
56
|
+
keys.merge(row.keys)
|
57
|
+
elsif row.respond_to?(:map)
|
58
|
+
# Array of Arrays
|
59
|
+
array_size = row.size if array_size < row.size
|
60
|
+
end
|
61
|
+
rows << row
|
62
|
+
end
|
63
|
+
|
64
|
+
if header = keys
|
65
|
+
keys.merge(0...array_size)
|
66
|
+
else
|
67
|
+
keys = 0...array_size
|
68
|
+
end
|
69
|
+
keys = keys.to_a
|
70
|
+
end
|
71
|
+
|
72
|
+
header ||= keys if options[:header]
|
73
|
+
|
74
|
+
rows1, rows2 = rows, nil
|
75
|
+
keys1, keys2 = keys, nil
|
76
|
+
header1, header2 = header, nil
|
77
|
+
|
78
|
+
if maxcols && keys.size > maxcols
|
79
|
+
keys1 = keys[0...maxcols / 2]
|
80
|
+
keys2 = keys[-maxcols / 2...-1]
|
81
|
+
if header
|
82
|
+
header1 = header[0...maxcols / 2]
|
83
|
+
header2 = header[-maxcols / 2...-1]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
if maxrows && rows.size > maxrows
|
88
|
+
rows1 = rows[0...maxrows / 2]
|
89
|
+
rows2 = rows[-maxrows / 2...-1]
|
90
|
+
end
|
91
|
+
|
92
|
+
table = '<table>'
|
93
|
+
|
94
|
+
if header1 && options[:header] != false
|
95
|
+
table << '<tr>' << header1.map {|k| "<th>#{cell k}</th>" }.join
|
96
|
+
table << "<th>…</th>" << header2.map {|k| "<th>#{cell k}</th>" }.join if keys2
|
97
|
+
table << '</tr>'
|
98
|
+
end
|
99
|
+
|
100
|
+
row_block(table, rows1, keys1, keys2)
|
101
|
+
|
102
|
+
if rows2
|
103
|
+
table << "<tr><td#{keys1.size > 1 ? " colspan='#{keys1.size}'" : ''}>⋮</td>"
|
104
|
+
table << "<td>⋱</td><td#{keys2.size > 1 ? " colspan='#{keys2.size}'" : ''}>⋮</td>" if keys2
|
105
|
+
table << '</tr>'
|
106
|
+
|
107
|
+
row_block(table, rows2, keys1, keys2)
|
108
|
+
end
|
109
|
+
|
110
|
+
table << '</table>'
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
def cell(obj)
|
116
|
+
obj.respond_to?(:to_html) ? obj.to_html : obj
|
117
|
+
end
|
118
|
+
|
119
|
+
def elem(row, k)
|
120
|
+
cell((row[k] rescue nil))
|
121
|
+
end
|
122
|
+
|
123
|
+
def row_block(table, rows, keys1, keys2)
|
124
|
+
cols = keys1.size
|
125
|
+
cols += keys2.size + 1 if keys2
|
126
|
+
rows.each_with_index do |row, i|
|
127
|
+
table << '<tr>'
|
128
|
+
if row.respond_to?(:map)
|
129
|
+
row_html = keys1.map {|k| "<td>#{elem row, k}</td>" }.join
|
130
|
+
if keys2
|
131
|
+
row_html << "<td#{rows.size > 1 ? " rowspan='#{rows.size}'" : ''}>…</td>" if i == 0
|
132
|
+
row_html << keys2.map {|k| "<td>#{elem row, k}</td>" }.join
|
133
|
+
end
|
134
|
+
if row_html.empty?
|
135
|
+
table << "<td#{cols > 1 ? " colspan='#{cols}'" : ''}></td>"
|
136
|
+
else
|
137
|
+
table << row_html
|
138
|
+
end
|
139
|
+
else
|
140
|
+
table << "<td#{cols > 1 ? " colspan='#{cols}'" : ''}>#{cell row}</td>"
|
141
|
+
end
|
142
|
+
table << '</tr>'
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
module IRails
|
2
|
+
class Kernel
|
3
|
+
RED = "\e[31m"
|
4
|
+
WHITE = "\e[37m"
|
5
|
+
RESET = "\e[0m"
|
6
|
+
|
7
|
+
class<< self
|
8
|
+
attr_accessor :instance
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :session
|
12
|
+
|
13
|
+
def initialize(config_file)
|
14
|
+
@config = MultiJson.load(File.read(config_file))
|
15
|
+
IRails.logger.debug("IRails kernel start with config #{@config}")
|
16
|
+
Kernel.instance = self
|
17
|
+
|
18
|
+
@session = Session.new(@config)
|
19
|
+
$stdout = OStream.new(@session, :stdout)
|
20
|
+
$stderr = OStream.new(@session, :stderr)
|
21
|
+
|
22
|
+
@execution_count = 0
|
23
|
+
@backend = create_backend
|
24
|
+
@running = true
|
25
|
+
end
|
26
|
+
|
27
|
+
def create_backend
|
28
|
+
PryBackend.new
|
29
|
+
rescue Exception => e
|
30
|
+
IRails.logger.warn "Could not load PryBackend: #{e.message}\n#{e.backtrace.join("\n")}" unless LoadError === e
|
31
|
+
PlainBackend.new
|
32
|
+
end
|
33
|
+
|
34
|
+
def run
|
35
|
+
send_status :starting
|
36
|
+
while @running
|
37
|
+
dispatch
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def dispatch
|
42
|
+
msg = @session.recv(:reply)
|
43
|
+
type = msg[:header]['msg_type']
|
44
|
+
raise "Unknown message type: #{msg.inspect}" unless type =~ /comm_|_request\Z/ && respond_to?(type)
|
45
|
+
begin
|
46
|
+
send_status :busy
|
47
|
+
send(type, msg)
|
48
|
+
ensure
|
49
|
+
send_status :idle
|
50
|
+
end
|
51
|
+
rescue Exception => e
|
52
|
+
IRails.logger.debug "Kernel error: #{e.message}\n#{e.backtrace.join("\n")}"
|
53
|
+
@session.send(:publish, :error, error_message(e))
|
54
|
+
end
|
55
|
+
|
56
|
+
def kernel_info_request(msg)
|
57
|
+
@session.send(:reply, :kernel_info_reply,
|
58
|
+
protocol_version: '5.0',
|
59
|
+
implementation: 'irails',
|
60
|
+
banner: "IRails #{IRails::VERSION}",
|
61
|
+
implementation_version: IRails::VERSION,
|
62
|
+
language_info: {
|
63
|
+
name: 'ruby',
|
64
|
+
version: RUBY_VERSION,
|
65
|
+
mimetype: 'application/x-ruby',
|
66
|
+
file_extension: '.rb'
|
67
|
+
})
|
68
|
+
end
|
69
|
+
|
70
|
+
def send_status(status)
|
71
|
+
@session.send(:publish, :status, execution_state: status)
|
72
|
+
end
|
73
|
+
|
74
|
+
def execute_request(msg)
|
75
|
+
code = msg[:content]['code']
|
76
|
+
@execution_count += 1 if msg[:content]['store_history']
|
77
|
+
@session.send(:publish, :execute_input, code: code, execution_count: @execution_count)
|
78
|
+
|
79
|
+
content = {
|
80
|
+
status: :ok,
|
81
|
+
payload: [],
|
82
|
+
user_expressions: {},
|
83
|
+
execution_count: @execution_count
|
84
|
+
}
|
85
|
+
result = nil
|
86
|
+
begin
|
87
|
+
result = @backend.eval(code, msg[:content]['store_history'])
|
88
|
+
rescue SystemExit
|
89
|
+
content[:payload] << { source: :ask_exit }
|
90
|
+
rescue Exception => e
|
91
|
+
content = error_message(e)
|
92
|
+
@session.send(:publish, :error, content)
|
93
|
+
end
|
94
|
+
@session.send(:reply, :execute_reply, content)
|
95
|
+
@session.send(:publish, :execute_result,
|
96
|
+
data: Display.display(result),
|
97
|
+
metadata: {},
|
98
|
+
execution_count: @execution_count) unless result.nil? || msg[:content]['silent']
|
99
|
+
end
|
100
|
+
|
101
|
+
def error_message(e)
|
102
|
+
{ status: :error,
|
103
|
+
ename: e.class.to_s,
|
104
|
+
evalue: e.message,
|
105
|
+
traceback: ["#{RED}#{e.class}#{RESET}: #{e.message}", *e.backtrace.map { |l| "#{WHITE}#{l}#{RESET}" }],
|
106
|
+
execution_count: @execution_count }
|
107
|
+
end
|
108
|
+
|
109
|
+
def complete_request(msg)
|
110
|
+
# HACK for #26, only complete last line
|
111
|
+
code = msg[:content]['code']
|
112
|
+
if start = code.rindex("\n")
|
113
|
+
code = code[start+1..-1]
|
114
|
+
start += 1
|
115
|
+
end
|
116
|
+
@session.send(:reply, :complete_reply,
|
117
|
+
matches: @backend.complete(code),
|
118
|
+
status: :ok,
|
119
|
+
cursor_start: start.to_i,
|
120
|
+
cursor_end: msg[:content]['cursor_pos'])
|
121
|
+
end
|
122
|
+
|
123
|
+
def connect_request(msg)
|
124
|
+
@session.send(:reply, :connect_reply, Hash[%w(shell_port iopub_port stdin_port hb_port).map {|k| [k, @config[k]] }])
|
125
|
+
end
|
126
|
+
|
127
|
+
def shutdown_request(msg)
|
128
|
+
@session.send(:reply, :shutdown_reply, msg[:content])
|
129
|
+
@running = false
|
130
|
+
end
|
131
|
+
|
132
|
+
def history_request(msg)
|
133
|
+
# we will just send back empty history for now, pending clarification
|
134
|
+
# as requested in ipython/ipython#3806
|
135
|
+
@session.send(:reply, :history_reply, history: [])
|
136
|
+
end
|
137
|
+
|
138
|
+
def inspect_request(msg)
|
139
|
+
result = @backend.eval(msg[:content]['code'])
|
140
|
+
@session.send(:reply, :inspect_reply,
|
141
|
+
status: :ok,
|
142
|
+
data: Display.display(result),
|
143
|
+
metadata: {})
|
144
|
+
rescue Exception => e
|
145
|
+
IRails.logger.warn "Inspection error: #{e.message}\n#{e.backtrace.join("\n")}"
|
146
|
+
@session.send(:reply, :inspect_reply, status: :error)
|
147
|
+
end
|
148
|
+
|
149
|
+
def comm_open(msg)
|
150
|
+
comm_id = msg[:content]['comm_id']
|
151
|
+
target_name = msg[:content]['target_name']
|
152
|
+
Comm.comm[comm_id] = Comm.target[target_name].new(target_name, comm_id)
|
153
|
+
end
|
154
|
+
|
155
|
+
def comm_msg(msg)
|
156
|
+
Comm.comm[msg[:content]['comm_id']].handle_msg(msg[:content]['data'])
|
157
|
+
end
|
158
|
+
|
159
|
+
def comm_close(msg)
|
160
|
+
comm_id = msg[:content]['comm_id']
|
161
|
+
Comm.comm[comm_id].handle_close(msg[:content]['data'])
|
162
|
+
Comm.comm.delete(comm_id)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module IRails
|
4
|
+
class << self
|
5
|
+
attr_accessor :logger
|
6
|
+
end
|
7
|
+
|
8
|
+
class MultiLogger < BasicObject
|
9
|
+
attr_reader :loggers
|
10
|
+
|
11
|
+
def initialize(*loggers)
|
12
|
+
@loggers = loggers
|
13
|
+
end
|
14
|
+
|
15
|
+
def method_missing(name, *args, &b)
|
16
|
+
@loggers.map {|x| x.respond_to?(name) && x.public_send(name, *args, &b) }.any?
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module IRails
|
2
|
+
# IO-like object that publishes to 0MQ socket.
|
3
|
+
class OStream
|
4
|
+
attr_accessor :sync
|
5
|
+
|
6
|
+
def initialize(session, name)
|
7
|
+
@session, @name = session, name
|
8
|
+
end
|
9
|
+
|
10
|
+
def close
|
11
|
+
@session = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def flush
|
15
|
+
end
|
16
|
+
|
17
|
+
def isatty
|
18
|
+
false
|
19
|
+
end
|
20
|
+
alias_method :tty?, :isatty
|
21
|
+
|
22
|
+
def read(*args)
|
23
|
+
raise IOError, 'not opened for reading'
|
24
|
+
end
|
25
|
+
alias_method :next, :read
|
26
|
+
alias_method :readline, :read
|
27
|
+
|
28
|
+
def write(s)
|
29
|
+
raise 'I/O operation on closed file' unless @session
|
30
|
+
@session.send(:publish, :stream, name: @name, text: s.to_s)
|
31
|
+
nil
|
32
|
+
end
|
33
|
+
alias_method :<<, :write
|
34
|
+
alias_method :print, :write
|
35
|
+
|
36
|
+
def puts(*lines)
|
37
|
+
lines = [''] if lines.empty?
|
38
|
+
lines.each { |s| write("#{s}\n")}
|
39
|
+
nil
|
40
|
+
end
|
41
|
+
|
42
|
+
def writelines(lines)
|
43
|
+
lines.each { |s| write(s) }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|