iruby 0.2.8 → 0.6.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 +5 -5
- data/.github/workflows/ubuntu.yml +62 -0
- data/CHANGES +76 -0
- data/Gemfile +3 -1
- data/LICENSE +1 -1
- data/README.md +130 -82
- data/Rakefile +36 -10
- data/ci/Dockerfile.base.erb +41 -0
- data/ci/Dockerfile.main.erb +7 -0
- data/ci/requirements.txt +1 -0
- data/docker/setup.sh +15 -0
- data/docker/test.sh +7 -0
- data/iruby.gemspec +13 -17
- data/lib/iruby.rb +14 -6
- data/lib/iruby/backend.rb +41 -7
- data/lib/iruby/command.rb +68 -12
- data/lib/iruby/display.rb +80 -39
- data/lib/iruby/event_manager.rb +40 -0
- data/lib/iruby/formatter.rb +5 -4
- data/lib/iruby/input.rb +41 -0
- data/lib/iruby/input/README.ipynb +502 -0
- data/lib/iruby/input/README.md +299 -0
- data/lib/iruby/input/autoload.rb +25 -0
- data/lib/iruby/input/builder.rb +67 -0
- data/lib/iruby/input/button.rb +47 -0
- data/lib/iruby/input/cancel.rb +32 -0
- data/lib/iruby/input/checkbox.rb +74 -0
- data/lib/iruby/input/date.rb +37 -0
- data/lib/iruby/input/field.rb +31 -0
- data/lib/iruby/input/file.rb +57 -0
- data/lib/iruby/input/form.rb +77 -0
- data/lib/iruby/input/label.rb +27 -0
- data/lib/iruby/input/multiple.rb +76 -0
- data/lib/iruby/input/popup.rb +41 -0
- data/lib/iruby/input/radio.rb +59 -0
- data/lib/iruby/input/select.rb +59 -0
- data/lib/iruby/input/textarea.rb +23 -0
- data/lib/iruby/input/widget.rb +34 -0
- data/lib/iruby/jupyter.rb +77 -0
- data/lib/iruby/kernel.rb +106 -27
- data/lib/iruby/ostream.rb +29 -8
- data/lib/iruby/session.rb +116 -0
- data/lib/iruby/session/{rbczmq.rb → cztop.rb} +25 -13
- data/lib/iruby/session/ffi_rzmq.rb +15 -2
- data/lib/iruby/session_adapter.rb +72 -0
- data/lib/iruby/session_adapter/cztop_adapter.rb +45 -0
- data/lib/iruby/session_adapter/ffirzmq_adapter.rb +55 -0
- data/lib/iruby/session_adapter/pyzmq_adapter.rb +77 -0
- data/lib/iruby/session_adapter/test_adapter.rb +49 -0
- data/lib/iruby/utils.rb +5 -2
- data/lib/iruby/version.rb +1 -1
- data/run-test.sh +12 -0
- data/tasks/ci.rake +65 -0
- data/test/helper.rb +133 -0
- data/test/integration_test.rb +22 -11
- data/test/iruby/backend_test.rb +37 -0
- data/test/iruby/command_test.rb +207 -0
- data/test/iruby/event_manager_test.rb +92 -0
- data/test/iruby/jupyter_test.rb +27 -0
- data/test/iruby/kernel_test.rb +153 -0
- data/test/iruby/mime_test.rb +43 -0
- data/test/iruby/multi_logger_test.rb +1 -2
- data/test/iruby/session_adapter/cztop_adapter_test.rb +20 -0
- data/test/iruby/session_adapter/ffirzmq_adapter_test.rb +20 -0
- data/test/iruby/session_adapter/session_adapter_test_base.rb +27 -0
- data/test/iruby/session_adapter_test.rb +91 -0
- data/test/iruby/session_test.rb +48 -0
- data/test/run-test.rb +19 -0
- metadata +132 -43
- data/.travis.yml +0 -16
- data/CONTRIBUTORS +0 -19
- data/test/test_helper.rb +0 -5
@@ -0,0 +1,41 @@
|
|
1
|
+
FROM rubylang/ruby:<%= ruby_version %>-bionic
|
2
|
+
|
3
|
+
ADD ci/requirements.txt /tmp
|
4
|
+
|
5
|
+
RUN apt-get update \
|
6
|
+
&& apt-get install -y --no-install-recommends \
|
7
|
+
libczmq-dev \
|
8
|
+
python3 \
|
9
|
+
python3-pip \
|
10
|
+
python3-setuptools \
|
11
|
+
libpython3.6 \
|
12
|
+
&& pip3 install wheel \
|
13
|
+
&& pip3 install -r /tmp/requirements.txt \
|
14
|
+
&& rm -f /tmp/requirements.txt
|
15
|
+
|
16
|
+
# ZeroMQ version 4.1.6 and CZMQ version 3.0.2 for rbczmq
|
17
|
+
RUN apt-get update \
|
18
|
+
&& apt-get install -y --no-install-recommends \
|
19
|
+
build-essential \
|
20
|
+
file \
|
21
|
+
wget \
|
22
|
+
&& cd /tmp \
|
23
|
+
&& wget https://github.com/zeromq/zeromq4-1/releases/download/v4.1.6/zeromq-4.1.6.tar.gz \
|
24
|
+
&& wget https://archive.org/download/zeromq_czmq_3.0.2/czmq-3.0.2.tar.gz \
|
25
|
+
&& tar xf zeromq-4.1.6.tar.gz \
|
26
|
+
&& tar xf czmq-3.0.2.tar.gz \
|
27
|
+
&& \
|
28
|
+
( \
|
29
|
+
cd zeromq-4.1.6 \
|
30
|
+
&& ./configure \
|
31
|
+
&& make install \
|
32
|
+
) \
|
33
|
+
&& \
|
34
|
+
( \
|
35
|
+
cd czmq-3.0.2 \
|
36
|
+
&& wget -O 1.patch https://github.com/zeromq/czmq/commit/2594d406d8ec6f54e54d7570d7febba10a6906b2.diff \
|
37
|
+
&& wget -O 2.patch https://github.com/zeromq/czmq/commit/b651cb479235751b22b8f9a822a2fc6bc1be01ab.diff \
|
38
|
+
&& cat *.patch | patch -p1 \
|
39
|
+
&& ./configure \
|
40
|
+
&& make install \
|
41
|
+
)
|
data/ci/requirements.txt
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
jupyter-console>=6.0.0
|
data/docker/setup.sh
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
set -ex
|
4
|
+
|
5
|
+
apt-get update
|
6
|
+
apt-get install -y --no-install-recommends \
|
7
|
+
libczmq-dev \
|
8
|
+
python3 \
|
9
|
+
python3-pip \
|
10
|
+
python3-setuptools \
|
11
|
+
python3-wheel
|
12
|
+
|
13
|
+
cd /tmp/iruby
|
14
|
+
bundle install --with test --without plot
|
15
|
+
pip3 install jupyter
|
data/docker/test.sh
ADDED
data/iruby.gemspec
CHANGED
@@ -1,33 +1,29 @@
|
|
1
|
-
# coding: utf-8
|
2
1
|
require_relative 'lib/iruby/version'
|
3
|
-
require 'date'
|
4
2
|
|
5
3
|
Gem::Specification.new do |s|
|
6
4
|
s.name = 'iruby'
|
7
|
-
s.date = Date.today.to_s
|
8
5
|
s.version = IRuby::VERSION
|
9
6
|
s.authors = ['Daniel Mendler', 'The SciRuby developers']
|
10
7
|
s.email = ['mail@daniel-mendler.de']
|
11
|
-
s.summary = 'Ruby Kernel for Jupyter
|
12
|
-
s.description = 'A Ruby kernel for Jupyter
|
8
|
+
s.summary = 'Ruby Kernel for Jupyter'
|
9
|
+
s.description = 'A Ruby kernel for Jupyter environment. Try it at try.jupyter.org.'
|
13
10
|
s.homepage = 'https://github.com/SciRuby/iruby'
|
14
11
|
s.license = 'MIT'
|
15
12
|
|
16
|
-
s.files = `git ls-files`.split(
|
13
|
+
s.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
17
14
|
s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
15
|
s.test_files = s.files.grep(%r{^test/})
|
19
|
-
s.require_paths = %w
|
16
|
+
s.require_paths = %w[lib]
|
20
17
|
|
21
|
-
|
22
|
-
File.read('Gemfile').scan(/gem\s+'(.*?)'/) { m << " * #{$1}\n" }
|
23
|
-
s.post_install_message = m << "\n"
|
18
|
+
s.required_ruby_version = '>= 2.3.0'
|
24
19
|
|
25
|
-
s.
|
20
|
+
s.add_dependency 'data_uri', '~> 0.1'
|
21
|
+
s.add_dependency 'ffi-rzmq'
|
22
|
+
s.add_dependency 'mime-types', '>= 3.3.1'
|
23
|
+
s.add_dependency 'multi_json', '~> 1.11'
|
26
24
|
|
27
|
-
s.add_development_dependency '
|
28
|
-
s.add_development_dependency '
|
29
|
-
|
30
|
-
s.
|
31
|
-
s.add_runtime_dependency 'multi_json', '~> 1.11'
|
32
|
-
s.add_runtime_dependency 'mimemagic', '~> 0.3'
|
25
|
+
s.add_development_dependency 'pycall', '>= 1.2.1'
|
26
|
+
s.add_development_dependency 'rake'
|
27
|
+
s.add_development_dependency 'test-unit'
|
28
|
+
s.add_development_dependency 'test-unit-rr'
|
33
29
|
end
|
data/lib/iruby.rb
CHANGED
@@ -1,25 +1,33 @@
|
|
1
|
-
require '
|
1
|
+
require 'mime/types'
|
2
2
|
require 'multi_json'
|
3
3
|
require 'securerandom'
|
4
4
|
require 'openssl'
|
5
5
|
require 'tempfile'
|
6
6
|
require 'set'
|
7
|
+
|
7
8
|
require 'iruby/version'
|
9
|
+
require 'iruby/jupyter'
|
10
|
+
require 'iruby/event_manager'
|
8
11
|
require 'iruby/kernel'
|
9
12
|
require 'iruby/backend'
|
10
13
|
require 'iruby/ostream'
|
14
|
+
require 'iruby/input'
|
11
15
|
require 'iruby/formatter'
|
12
16
|
require 'iruby/utils'
|
13
17
|
require 'iruby/display'
|
14
18
|
require 'iruby/comm'
|
15
|
-
require 'iruby/session/mixin'
|
16
19
|
|
17
|
-
|
18
|
-
require 'iruby/session/
|
19
|
-
rescue LoadError
|
20
|
+
if ENV.fetch('IRUBY_OLD_SESSION', false)
|
21
|
+
require 'iruby/session/mixin'
|
20
22
|
begin
|
21
23
|
require 'iruby/session/ffi_rzmq'
|
22
24
|
rescue LoadError
|
23
|
-
|
25
|
+
begin
|
26
|
+
require 'iruby/session/cztop'
|
27
|
+
rescue LoadError
|
28
|
+
STDERR.puts "Please install ffi-rzmq or cztop before running iruby. See README."
|
29
|
+
end
|
24
30
|
end
|
31
|
+
else
|
32
|
+
require 'iruby/session'
|
25
33
|
end
|
data/lib/iruby/backend.rb
CHANGED
@@ -1,10 +1,9 @@
|
|
1
1
|
module IRuby
|
2
2
|
In, Out = [nil], [nil]
|
3
|
-
::In, ::Out = In, Out
|
4
3
|
|
5
4
|
module History
|
6
5
|
def eval(code, store_history)
|
7
|
-
b =
|
6
|
+
b = eval_binding
|
8
7
|
|
9
8
|
b.local_variable_set(:_ih, In) unless b.local_variable_defined?(:_ih)
|
10
9
|
b.local_variable_set(:_oh, Out) unless b.local_variable_defined?(:_oh)
|
@@ -33,39 +32,74 @@ module IRuby
|
|
33
32
|
end
|
34
33
|
|
35
34
|
class PlainBackend
|
35
|
+
attr_reader :eval_path
|
36
36
|
prepend History
|
37
37
|
|
38
38
|
def initialize
|
39
|
-
require '
|
40
|
-
|
39
|
+
require 'irb'
|
40
|
+
require 'irb/completion'
|
41
|
+
IRB.setup(nil)
|
42
|
+
@main = TOPLEVEL_BINDING.eval("self").dup
|
43
|
+
@workspace = IRB::WorkSpace.new(@main)
|
44
|
+
@irb = IRB::Irb.new(@workspace)
|
45
|
+
@eval_path = @irb.context.irb_path
|
46
|
+
IRB.conf[:MAIN_CONTEXT] = @irb.context
|
47
|
+
end
|
48
|
+
|
49
|
+
def eval_binding
|
50
|
+
@workspace.binding
|
41
51
|
end
|
42
52
|
|
43
53
|
def eval(code, store_history)
|
44
|
-
|
54
|
+
@irb.context.evaluate(code, 0)
|
55
|
+
@irb.context.last_value
|
45
56
|
end
|
46
57
|
|
47
58
|
def complete(code)
|
48
|
-
|
59
|
+
IRB::InputCompletor::CompletionProc.call(code)
|
49
60
|
end
|
50
61
|
end
|
51
62
|
|
52
63
|
class PryBackend
|
64
|
+
attr_reader :eval_path
|
53
65
|
prepend History
|
54
66
|
|
55
67
|
def initialize
|
56
68
|
require 'pry'
|
69
|
+
Pry.memory_size = 3
|
57
70
|
Pry.pager = false # Don't use the pager
|
58
71
|
Pry.print = proc {|output, value|} # No result printing
|
59
72
|
Pry.exception_handler = proc {|output, exception, _| }
|
73
|
+
@eval_path = Pry.eval_path
|
60
74
|
reset
|
61
75
|
end
|
62
76
|
|
77
|
+
def eval_binding
|
78
|
+
TOPLEVEL_BINDING
|
79
|
+
end
|
80
|
+
|
63
81
|
def eval(code, store_history)
|
82
|
+
Pry.current_line = 1
|
64
83
|
@pry.last_result = nil
|
65
84
|
unless @pry.eval(code)
|
66
85
|
reset
|
67
86
|
raise SystemExit
|
68
87
|
end
|
88
|
+
|
89
|
+
# Pry::Code.complete_expression? return false
|
90
|
+
if !@pry.eval_string.empty?
|
91
|
+
syntax_error = @pry.eval_string
|
92
|
+
@pry.reset_eval_string
|
93
|
+
@pry.evaluate_ruby(syntax_error)
|
94
|
+
|
95
|
+
# Pry::Code.complete_expression? raise SyntaxError
|
96
|
+
# evaluate again for current line number
|
97
|
+
elsif @pry.last_result_is_exception? &&
|
98
|
+
@pry.last_exception.is_a?(SyntaxError) &&
|
99
|
+
@pry.last_exception.is_a?(Pry::UserError)
|
100
|
+
@pry.evaluate_ruby(code)
|
101
|
+
end
|
102
|
+
|
69
103
|
raise @pry.last_exception if @pry.last_result_is_exception?
|
70
104
|
@pry.push_initial_binding unless @pry.current_binding # ensure that we have a binding
|
71
105
|
@pry.last_result
|
@@ -76,7 +110,7 @@ module IRuby
|
|
76
110
|
end
|
77
111
|
|
78
112
|
def reset
|
79
|
-
@pry = Pry.new(output: $stdout, target:
|
113
|
+
@pry = Pry.new(output: $stdout, target: eval_binding)
|
80
114
|
end
|
81
115
|
end
|
82
116
|
end
|
data/lib/iruby/command.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'iruby/jupyter'
|
2
|
+
|
1
3
|
require 'fileutils'
|
2
4
|
require 'multi_json'
|
3
5
|
|
@@ -6,15 +8,18 @@ module IRuby
|
|
6
8
|
def initialize(args)
|
7
9
|
@args = args
|
8
10
|
|
9
|
-
ipython_dir =
|
10
|
-
@
|
11
|
-
|
12
|
-
end
|
13
|
-
@kernel_dir = File.join(File.expand_path(ipython_dir), 'kernels', 'ruby')
|
14
|
-
@kernel_file = File.join(@kernel_dir, 'kernel.json')
|
11
|
+
@ipython_dir = File.expand_path("~/.ipython")
|
12
|
+
@kernel_dir = resolve_kernelspec_dir.freeze
|
13
|
+
@kernel_file = File.join(@kernel_dir, 'kernel.json').freeze
|
15
14
|
@iruby_path = File.expand_path $0
|
16
15
|
end
|
17
16
|
|
17
|
+
attr_reader :ipython_dir, :kernel_dir, :kernel_file
|
18
|
+
|
19
|
+
def ipython_kernel_dir
|
20
|
+
File.join(File.expand_path(@ipython_dir), 'kernels', 'ruby')
|
21
|
+
end
|
22
|
+
|
18
23
|
def run
|
19
24
|
case @args.first
|
20
25
|
when 'version', '-v', '--version'
|
@@ -23,11 +28,12 @@ module IRuby
|
|
23
28
|
when 'help', '-h', '--help'
|
24
29
|
print_help
|
25
30
|
when 'register'
|
26
|
-
|
31
|
+
force_p = @args.include?('--force')
|
32
|
+
if registered_iruby_path && !force_p
|
27
33
|
STDERR.puts "#{@kernel_file} already exists!\nUse --force to force a register."
|
28
34
|
exit 1
|
29
35
|
end
|
30
|
-
register_kernel
|
36
|
+
register_kernel(force_p)
|
31
37
|
when 'unregister'
|
32
38
|
unregister_kernel
|
33
39
|
when 'kernel'
|
@@ -39,6 +45,38 @@ module IRuby
|
|
39
45
|
|
40
46
|
private
|
41
47
|
|
48
|
+
def resolve_kernelspec_dir
|
49
|
+
if ENV.has_key?('JUPYTER_DATA_DIR')
|
50
|
+
if ENV.has_key?('IPYTHONDIR')
|
51
|
+
warn 'both JUPYTER_DATA_DIR and IPYTHONDIR are supplied; IPYTHONDIR is ignored.'
|
52
|
+
end
|
53
|
+
if @args.find {|x| /\A--ipython-dir=/ =~ x }
|
54
|
+
warn 'both JUPYTER_DATA_DIR and --ipython-dir are supplied; --ipython-dir is ignored.'
|
55
|
+
end
|
56
|
+
jupyter_data_dir = ENV['JUPYTER_DATA_DIR']
|
57
|
+
return File.join(jupyter_data_dir, 'kernels', 'ruby')
|
58
|
+
end
|
59
|
+
|
60
|
+
if ENV.has_key?('IPYTHONDIR')
|
61
|
+
warn 'IPYTHONDIR is deprecated. Use JUPYTER_DATA_DIR instead.'
|
62
|
+
ipython_dir = ENV['IPYTHONDIR']
|
63
|
+
end
|
64
|
+
|
65
|
+
@args.each do |arg|
|
66
|
+
next unless /\A--ipython-dir=(.*)\Z/ =~ arg
|
67
|
+
ipython_dir = Regexp.last_match[1]
|
68
|
+
warn '--ipython-dir is deprecated. Use JUPYTER_DATA_DIR environment variable instead.'
|
69
|
+
break
|
70
|
+
end
|
71
|
+
|
72
|
+
if ipython_dir
|
73
|
+
@ipython_dir = ipython_dir
|
74
|
+
ipython_kernel_dir
|
75
|
+
else
|
76
|
+
File.join(Jupyter.kernelspec_dir, 'ruby')
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
42
80
|
def print_help
|
43
81
|
puts %{
|
44
82
|
Usage:
|
@@ -56,6 +94,8 @@ Try `ipython help` for more information.
|
|
56
94
|
def run_kernel
|
57
95
|
require 'iruby/logger'
|
58
96
|
IRuby.logger = MultiLogger.new(*Logger.new(STDOUT))
|
97
|
+
STDOUT.sync = true # FIXME: This can make the integration test.
|
98
|
+
|
59
99
|
@args.reject! {|arg| arg =~ /\A--log=(.*)\Z/ && IRuby.logger.loggers << Logger.new($1) }
|
60
100
|
IRuby.logger.level = @args.delete('--debug') ? Logger::DEBUG : Logger::INFO
|
61
101
|
|
@@ -64,7 +104,7 @@ Try `ipython help` for more information.
|
|
64
104
|
Dir.chdir(working_dir) if working_dir
|
65
105
|
|
66
106
|
require boot_file if boot_file
|
67
|
-
check_bundler {|e| IRuby.logger.warn "Could not load bundler: #{e.message}
|
107
|
+
check_bundler {|e| IRuby.logger.warn "Could not load bundler: #{e.message}" }
|
68
108
|
|
69
109
|
require 'iruby'
|
70
110
|
Kernel.new(config_file).run
|
@@ -95,7 +135,7 @@ Try `ipython help` for more information.
|
|
95
135
|
end
|
96
136
|
|
97
137
|
def check_registered_kernel
|
98
|
-
if kernel = registered_iruby_path
|
138
|
+
if (kernel = registered_iruby_path)
|
99
139
|
STDERR.puts "#{@iruby_path} differs from registered path #{registered_iruby_path}.
|
100
140
|
This might not work. Run 'iruby register --force' to fix it." if @iruby_path != kernel
|
101
141
|
else
|
@@ -106,14 +146,19 @@ This might not work. Run 'iruby register --force' to fix it." if @iruby_path !=
|
|
106
146
|
def check_bundler
|
107
147
|
require 'bundler'
|
108
148
|
raise %q{iruby is missing from Gemfile. This might not work.
|
109
|
-
Add `gem 'iruby'` to your Gemfile to fix it.} unless Bundler.definition.
|
149
|
+
Add `gem 'iruby'` to your Gemfile to fix it.} unless Bundler.definition.specs.any? {|s| s.name == 'iruby' }
|
110
150
|
Bundler.setup
|
111
151
|
rescue LoadError
|
112
152
|
rescue Exception => e
|
113
153
|
yield(e)
|
114
154
|
end
|
115
155
|
|
116
|
-
def register_kernel
|
156
|
+
def register_kernel(force_p=false)
|
157
|
+
if force_p
|
158
|
+
unregister_kernel_in_ipython_dir
|
159
|
+
else
|
160
|
+
return unless check_existing_kernel_in_ipython_dir
|
161
|
+
end
|
117
162
|
FileUtils.mkpath(@kernel_dir)
|
118
163
|
unless RUBY_PLATFORM =~ /mswin(?!ce)|mingw|cygwin/
|
119
164
|
File.write(@kernel_file, MultiJson.dump(argv: [ @iruby_path, 'kernel', '{connection_file}' ],
|
@@ -127,6 +172,13 @@ Add `gem 'iruby'` to your Gemfile to fix it.} unless Bundler.definition.dependen
|
|
127
172
|
FileUtils.copy(Dir[File.join(__dir__, 'assets', '*')], @kernel_dir) rescue nil
|
128
173
|
end
|
129
174
|
|
175
|
+
def check_existing_kernel_in_ipython_dir
|
176
|
+
return true unless File.file?(File.join(ipython_kernel_dir, 'kernel.json'))
|
177
|
+
warn "IRuby kernel file already exists in the deprecated IPython's data directory."
|
178
|
+
warn "Using --force, you can replace the old kernel file with the new one in Jupyter's data directory."
|
179
|
+
false
|
180
|
+
end
|
181
|
+
|
130
182
|
def registered_iruby_path
|
131
183
|
File.exist?(@kernel_file) && MultiJson.load(File.read(@kernel_file))['argv'].first
|
132
184
|
end
|
@@ -134,5 +186,9 @@ Add `gem 'iruby'` to your Gemfile to fix it.} unless Bundler.definition.dependen
|
|
134
186
|
def unregister_kernel
|
135
187
|
FileUtils.rm_rf(@kernel_dir)
|
136
188
|
end
|
189
|
+
|
190
|
+
def unregister_kernel_in_ipython_dir
|
191
|
+
FileUtils.rm_rf(ipython_kernel_dir)
|
192
|
+
end
|
137
193
|
end
|
138
194
|
end
|
data/lib/iruby/display.rb
CHANGED
@@ -11,7 +11,10 @@ module IRuby
|
|
11
11
|
obj = obj.object
|
12
12
|
|
13
13
|
fuzzy_mime = options[:format] # Treated like a fuzzy mime type
|
14
|
-
|
14
|
+
unless !fuzzy_mime || String === fuzzy_mime
|
15
|
+
raise 'Invalid argument :format'
|
16
|
+
end
|
17
|
+
|
15
18
|
if exact_mime = options[:mime]
|
16
19
|
raise 'Invalid argument :mime' unless String === exact_mime
|
17
20
|
raise 'Invalid mime type' unless exact_mime.include?('/')
|
@@ -27,28 +30,49 @@ module IRuby
|
|
27
30
|
|
28
31
|
# As a last resort, interpret string representation of the object
|
29
32
|
# as the given mime type.
|
30
|
-
|
33
|
+
if exact_mime && data.none? { |m, _| exact_mime == m }
|
34
|
+
data[exact_mime] = protect(exact_mime, obj)
|
35
|
+
end
|
31
36
|
|
32
37
|
data
|
33
38
|
end
|
34
39
|
|
40
|
+
def clear_output(wait = false)
|
41
|
+
IRuby::Kernel.instance.session.send(:publish, :clear_output, wait: wait)
|
42
|
+
end
|
43
|
+
|
35
44
|
private
|
36
45
|
|
37
46
|
def protect(mime, data)
|
38
|
-
|
47
|
+
ascii?(mime) ? data.to_s : [data.to_s].pack('m0')
|
48
|
+
end
|
49
|
+
|
50
|
+
def ascii?(mime)
|
51
|
+
case mime
|
52
|
+
when "application/javascript"
|
53
|
+
# Special case for application/javascript.
|
54
|
+
# This needs because mime-types tells us application/javascript a non-text type.
|
55
|
+
true
|
56
|
+
else
|
57
|
+
MIME::Type.new(mime).ascii?
|
58
|
+
end
|
39
59
|
end
|
40
60
|
|
41
61
|
def render(data, obj, exact_mime, fuzzy_mime)
|
42
62
|
# Filter matching renderer by object type
|
43
|
-
renderer = Registry.renderer.select {|r| r.match?(obj) }
|
63
|
+
renderer = Registry.renderer.select { |r| r.match?(obj) }
|
44
64
|
|
45
65
|
matching_renderer = nil
|
46
66
|
|
47
67
|
# Find exactly matching display by exact_mime
|
48
|
-
|
68
|
+
if exact_mime
|
69
|
+
matching_renderer = renderer.find { |r| exact_mime == r.mime }
|
70
|
+
end
|
49
71
|
|
50
72
|
# Find fuzzy matching display by fuzzy_mime
|
51
|
-
|
73
|
+
if fuzzy_mime
|
74
|
+
matching_renderer ||= renderer.find { |r| r.mime&.include?(fuzzy_mime) }
|
75
|
+
end
|
52
76
|
|
53
77
|
renderer.unshift matching_renderer if matching_renderer
|
54
78
|
|
@@ -69,7 +93,8 @@ module IRuby
|
|
69
93
|
attr_reader :object, :options
|
70
94
|
|
71
95
|
def initialize(object, options)
|
72
|
-
@object
|
96
|
+
@object = object
|
97
|
+
@options = options
|
73
98
|
end
|
74
99
|
|
75
100
|
class << self
|
@@ -87,10 +112,13 @@ module IRuby
|
|
87
112
|
end
|
88
113
|
|
89
114
|
class Renderer
|
90
|
-
attr_reader :match, :mime, :
|
115
|
+
attr_reader :match, :mime, :priority
|
91
116
|
|
92
117
|
def initialize(match, mime, render, priority)
|
93
|
-
@match
|
118
|
+
@match = match
|
119
|
+
@mime = mime
|
120
|
+
@render = render
|
121
|
+
@priority = priority
|
94
122
|
end
|
95
123
|
|
96
124
|
def match?(obj)
|
@@ -110,7 +138,7 @@ module IRuby
|
|
110
138
|
@renderer ||= []
|
111
139
|
end
|
112
140
|
|
113
|
-
SUPPORTED_MIMES = %w
|
141
|
+
SUPPORTED_MIMES = %w[
|
114
142
|
text/plain
|
115
143
|
text/html
|
116
144
|
text/latex
|
@@ -118,7 +146,8 @@ module IRuby
|
|
118
146
|
application/javascript
|
119
147
|
image/png
|
120
148
|
image/jpeg
|
121
|
-
image/svg+xml
|
149
|
+
image/svg+xml
|
150
|
+
]
|
122
151
|
|
123
152
|
def match(&block)
|
124
153
|
@match = block
|
@@ -127,7 +156,7 @@ module IRuby
|
|
127
156
|
end
|
128
157
|
|
129
158
|
def respond_to(name)
|
130
|
-
match {|obj| obj.respond_to?(name) }
|
159
|
+
match { |obj| obj.respond_to?(name) }
|
131
160
|
end
|
132
161
|
|
133
162
|
def type(&block)
|
@@ -149,7 +178,7 @@ module IRuby
|
|
149
178
|
|
150
179
|
def format(mime = nil, &block)
|
151
180
|
renderer << Renderer.new(@match, mime, block, @priority)
|
152
|
-
renderer.sort_by! {|r| -r.priority }
|
181
|
+
renderer.sort_by! { |r| -r.priority }
|
153
182
|
|
154
183
|
# Decrease priority implicitly for all formats
|
155
184
|
# which are added later for a type.
|
@@ -165,7 +194,19 @@ module IRuby
|
|
165
194
|
LaTeX.vector(obj.to_a)
|
166
195
|
end
|
167
196
|
|
197
|
+
type { Numo::NArray }
|
198
|
+
format 'text/plain', &:inspect
|
199
|
+
format 'text/latex' do |obj|
|
200
|
+
obj.ndim == 2 ?
|
201
|
+
LaTeX.matrix(obj, obj.shape[0], obj.shape[1]) :
|
202
|
+
LaTeX.vector(obj.to_a)
|
203
|
+
end
|
204
|
+
format 'text/html' do |obj|
|
205
|
+
HTML.table(obj.to_a)
|
206
|
+
end
|
207
|
+
|
168
208
|
type { NArray }
|
209
|
+
format 'text/plain', &:inspect
|
169
210
|
format 'text/latex' do |obj|
|
170
211
|
obj.dim == 2 ?
|
171
212
|
LaTeX.matrix(obj.transpose(1, 0), obj.shape[1], obj.shape[0]) :
|
@@ -227,37 +268,41 @@ module IRuby
|
|
227
268
|
|
228
269
|
match do |obj|
|
229
270
|
defined?(Magick::Image) && Magick::Image === obj ||
|
230
|
-
|
271
|
+
defined?(MiniMagick::Image) && MiniMagick::Image === obj
|
231
272
|
end
|
232
273
|
format 'image' do |obj|
|
233
274
|
format = obj.format || 'PNG'
|
234
|
-
[format == 'PNG' ? 'image/png' : 'image/jpeg', obj.to_blob {|i| i.format = format }]
|
275
|
+
[format == 'PNG' ? 'image/png' : 'image/jpeg', obj.to_blob { |i| i.format = format }]
|
235
276
|
end
|
236
277
|
|
237
|
-
|
238
|
-
|
239
|
-
|
278
|
+
match do |obj|
|
279
|
+
defined?(Vips::Image) && Vips::Image === obj
|
280
|
+
end
|
281
|
+
format do |obj|
|
282
|
+
# handles Vips::Error, vips_image_get: field "vips-loader" not found
|
283
|
+
loader = obj.get('vips-loader') rescue nil
|
284
|
+
if loader == 'jpegload'
|
285
|
+
['image/jpeg', obj.write_to_buffer('.jpg')]
|
286
|
+
else
|
287
|
+
# falls back to png for other/unknown types
|
288
|
+
['image/png', obj.write_to_buffer('.png')]
|
289
|
+
end
|
240
290
|
end
|
241
291
|
|
292
|
+
type { Gruff::Base }
|
293
|
+
format 'image/png', &:to_blob
|
294
|
+
|
242
295
|
respond_to :to_html
|
243
|
-
format 'text/html'
|
244
|
-
obj.to_html
|
245
|
-
end
|
296
|
+
format 'text/html', &:to_html
|
246
297
|
|
247
298
|
respond_to :to_latex
|
248
|
-
format 'text/latex'
|
249
|
-
obj.to_latex
|
250
|
-
end
|
299
|
+
format 'text/latex', &:to_latex
|
251
300
|
|
252
301
|
respond_to :to_tex
|
253
|
-
format 'text/latex'
|
254
|
-
obj.to_tex
|
255
|
-
end
|
302
|
+
format 'text/latex', &:to_tex
|
256
303
|
|
257
304
|
respond_to :to_javascript
|
258
|
-
format 'text/javascript'
|
259
|
-
obj.to_javascript
|
260
|
-
end
|
305
|
+
format 'text/javascript', &:to_javascript
|
261
306
|
|
262
307
|
respond_to :to_svg
|
263
308
|
format 'image/svg+xml' do |obj|
|
@@ -266,21 +311,17 @@ module IRuby
|
|
266
311
|
end
|
267
312
|
|
268
313
|
respond_to :to_iruby
|
269
|
-
format
|
270
|
-
obj.to_iruby
|
271
|
-
end
|
314
|
+
format(&:to_iruby)
|
272
315
|
|
273
|
-
match {|obj| obj.respond_to?(:path) && File.readable?(obj.path) }
|
316
|
+
match { |obj| obj.respond_to?(:path) && obj.method(:path).arity == 0 && File.readable?(obj.path) }
|
274
317
|
format do |obj|
|
275
|
-
mime =
|
318
|
+
mime = MIME::Types.of(obj.path).first.to_s
|
276
319
|
[mime, File.read(obj.path)] if SUPPORTED_MIMES.include?(mime)
|
277
320
|
end
|
278
321
|
|
279
322
|
type { Object }
|
280
|
-
priority
|
281
|
-
format 'text/plain'
|
282
|
-
obj.inspect
|
283
|
-
end
|
323
|
+
priority(-1000)
|
324
|
+
format 'text/plain', &:inspect
|
284
325
|
end
|
285
326
|
end
|
286
327
|
end
|