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.
Files changed (72) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ubuntu.yml +62 -0
  3. data/CHANGES +76 -0
  4. data/Gemfile +3 -1
  5. data/LICENSE +1 -1
  6. data/README.md +130 -82
  7. data/Rakefile +36 -10
  8. data/ci/Dockerfile.base.erb +41 -0
  9. data/ci/Dockerfile.main.erb +7 -0
  10. data/ci/requirements.txt +1 -0
  11. data/docker/setup.sh +15 -0
  12. data/docker/test.sh +7 -0
  13. data/iruby.gemspec +13 -17
  14. data/lib/iruby.rb +14 -6
  15. data/lib/iruby/backend.rb +41 -7
  16. data/lib/iruby/command.rb +68 -12
  17. data/lib/iruby/display.rb +80 -39
  18. data/lib/iruby/event_manager.rb +40 -0
  19. data/lib/iruby/formatter.rb +5 -4
  20. data/lib/iruby/input.rb +41 -0
  21. data/lib/iruby/input/README.ipynb +502 -0
  22. data/lib/iruby/input/README.md +299 -0
  23. data/lib/iruby/input/autoload.rb +25 -0
  24. data/lib/iruby/input/builder.rb +67 -0
  25. data/lib/iruby/input/button.rb +47 -0
  26. data/lib/iruby/input/cancel.rb +32 -0
  27. data/lib/iruby/input/checkbox.rb +74 -0
  28. data/lib/iruby/input/date.rb +37 -0
  29. data/lib/iruby/input/field.rb +31 -0
  30. data/lib/iruby/input/file.rb +57 -0
  31. data/lib/iruby/input/form.rb +77 -0
  32. data/lib/iruby/input/label.rb +27 -0
  33. data/lib/iruby/input/multiple.rb +76 -0
  34. data/lib/iruby/input/popup.rb +41 -0
  35. data/lib/iruby/input/radio.rb +59 -0
  36. data/lib/iruby/input/select.rb +59 -0
  37. data/lib/iruby/input/textarea.rb +23 -0
  38. data/lib/iruby/input/widget.rb +34 -0
  39. data/lib/iruby/jupyter.rb +77 -0
  40. data/lib/iruby/kernel.rb +106 -27
  41. data/lib/iruby/ostream.rb +29 -8
  42. data/lib/iruby/session.rb +116 -0
  43. data/lib/iruby/session/{rbczmq.rb → cztop.rb} +25 -13
  44. data/lib/iruby/session/ffi_rzmq.rb +15 -2
  45. data/lib/iruby/session_adapter.rb +72 -0
  46. data/lib/iruby/session_adapter/cztop_adapter.rb +45 -0
  47. data/lib/iruby/session_adapter/ffirzmq_adapter.rb +55 -0
  48. data/lib/iruby/session_adapter/pyzmq_adapter.rb +77 -0
  49. data/lib/iruby/session_adapter/test_adapter.rb +49 -0
  50. data/lib/iruby/utils.rb +5 -2
  51. data/lib/iruby/version.rb +1 -1
  52. data/run-test.sh +12 -0
  53. data/tasks/ci.rake +65 -0
  54. data/test/helper.rb +133 -0
  55. data/test/integration_test.rb +22 -11
  56. data/test/iruby/backend_test.rb +37 -0
  57. data/test/iruby/command_test.rb +207 -0
  58. data/test/iruby/event_manager_test.rb +92 -0
  59. data/test/iruby/jupyter_test.rb +27 -0
  60. data/test/iruby/kernel_test.rb +153 -0
  61. data/test/iruby/mime_test.rb +43 -0
  62. data/test/iruby/multi_logger_test.rb +1 -2
  63. data/test/iruby/session_adapter/cztop_adapter_test.rb +20 -0
  64. data/test/iruby/session_adapter/ffirzmq_adapter_test.rb +20 -0
  65. data/test/iruby/session_adapter/session_adapter_test_base.rb +27 -0
  66. data/test/iruby/session_adapter_test.rb +91 -0
  67. data/test/iruby/session_test.rb +48 -0
  68. data/test/run-test.rb +19 -0
  69. metadata +132 -43
  70. data/.travis.yml +0 -16
  71. data/CONTRIBUTORS +0 -19
  72. 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
+ )
@@ -0,0 +1,7 @@
1
+ FROM iruby-test-base:ruby-<%= ruby_version %>
2
+
3
+ RUN gem install cztop
4
+ RUN mkdir -p /iruby
5
+ ADD . /iruby
6
+ WORKDIR /iruby
7
+ RUN bundle install
@@ -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
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+
3
+ set -ex
4
+
5
+ cd /tmp/iruby
6
+ bundle install --with test --without plot
7
+ bundle exec rake test
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/IPython'
12
- s.description = 'A Ruby kernel for Jupyter/IPython frontends (e.g. notebook). Try it at try.jupyter.org.'
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(lib)
16
+ s.require_paths = %w[lib]
20
17
 
21
- m = "Consider installing the optional dependencies to get additional functionality:\n"
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.required_ruby_version = '>= 2.1.0'
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 'rake', '~> 10.4'
28
- s.add_development_dependency 'minitest', '~> 5.6'
29
-
30
- s.add_runtime_dependency 'bond', '~> 0.5'
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 'mimemagic'
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
- begin
18
- require 'iruby/session/rbczmq'
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
- STDERR.puts "You should install rbczmq or ffi_rzmq before running iruby notebook. See README."
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 = TOPLEVEL_BINDING
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 'bond'
40
- Bond.start(debug: true)
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
- TOPLEVEL_BINDING.eval(code)
54
+ @irb.context.evaluate(code, 0)
55
+ @irb.context.last_value
45
56
  end
46
57
 
47
58
  def complete(code)
48
- Bond.agent.call(code, code)
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: TOPLEVEL_BINDING)
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 = ENV['IPYTHONDIR'] || '~/.ipython'
10
- @args.each do |arg|
11
- ipython_dir = $1 if arg =~ /\A--ipython-dir=(.*)\Z/
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
- if registered_iruby_path && !@args.include?('--force')
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}\n#{e.backtrace.join("\n")}" }
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.dependencies.any? {|s| s.name == 'iruby' }
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
- raise 'Invalid argument :format' unless !fuzzy_mime || String === fuzzy_mime
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
- data[exact_mime] = protect(exact_mime, obj) if exact_mime && !data.any? {|m,_| exact_mime == m }
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
- MimeMagic.new(mime).text? ? data.to_s : [data.to_s].pack('m0')
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
- matching_renderer = renderer.find {|r| exact_mime == r.mime } if exact_mime
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
- matching_renderer ||= renderer.find {|r| r.mime && r.mime.include?(fuzzy_mime) } if fuzzy_mime
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, @options = object, options
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, :render, :priority
115
+ attr_reader :match, :mime, :priority
91
116
 
92
117
  def initialize(match, mime, render, priority)
93
- @match, @mime, @render, @priority = match, mime, render, priority
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
- defined?(MiniMagick::Image) && MiniMagick::Image === obj
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
- type { Gruff::Base }
238
- format 'image/png' do |obj|
239
- obj.to_blob
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' do |obj|
244
- obj.to_html
245
- end
296
+ format 'text/html', &:to_html
246
297
 
247
298
  respond_to :to_latex
248
- format 'text/latex' do |obj|
249
- obj.to_latex
250
- end
299
+ format 'text/latex', &:to_latex
251
300
 
252
301
  respond_to :to_tex
253
- format 'text/latex' do |obj|
254
- obj.to_tex
255
- end
302
+ format 'text/latex', &:to_tex
256
303
 
257
304
  respond_to :to_javascript
258
- format 'text/javascript' do |obj|
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 do |obj|
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 = MimeMagic.by_path(obj.path).to_s
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 -1000
281
- format 'text/plain' do |obj|
282
- obj.inspect
283
- end
323
+ priority(-1000)
324
+ format 'text/plain', &:inspect
284
325
  end
285
326
  end
286
327
  end