irails 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|