iruby 0.2.9 → 0.6.1

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 +64 -0
  4. data/Gemfile +3 -1
  5. data/LICENSE +1 -1
  6. data/README.md +120 -92
  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 -18
  14. data/lib/iruby.rb +13 -6
  15. data/lib/iruby/backend.rb +38 -9
  16. data/lib/iruby/command.rb +68 -12
  17. data/lib/iruby/display.rb +81 -41
  18. data/lib/iruby/event_manager.rb +40 -0
  19. data/lib/iruby/formatter.rb +3 -3
  20. data/lib/iruby/input.rb +6 -6
  21. data/lib/iruby/input/README.ipynb +55 -3
  22. data/lib/iruby/input/README.md +299 -0
  23. data/lib/iruby/input/autoload.rb +3 -2
  24. data/lib/iruby/input/builder.rb +4 -4
  25. data/lib/iruby/input/button.rb +2 -2
  26. data/lib/iruby/input/cancel.rb +1 -1
  27. data/lib/iruby/input/checkbox.rb +22 -4
  28. data/lib/iruby/input/date.rb +17 -6
  29. data/lib/iruby/input/field.rb +5 -4
  30. data/lib/iruby/input/file.rb +3 -3
  31. data/lib/iruby/input/form.rb +6 -6
  32. data/lib/iruby/input/label.rb +9 -3
  33. data/lib/iruby/input/multiple.rb +76 -0
  34. data/lib/iruby/input/popup.rb +5 -2
  35. data/lib/iruby/input/radio.rb +18 -6
  36. data/lib/iruby/input/select.rb +26 -12
  37. data/lib/iruby/input/textarea.rb +2 -1
  38. data/lib/iruby/input/widget.rb +2 -2
  39. data/lib/iruby/jupyter.rb +77 -0
  40. data/lib/iruby/kernel.rb +171 -31
  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} +21 -19
  44. data/lib/iruby/session/ffi_rzmq.rb +1 -1
  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 +13 -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 +107 -50
  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,34 +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'
33
- s.add_runtime_dependency 'data_uri', '~> 0.1'
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'
34
29
  end
data/lib/iruby.rb CHANGED
@@ -1,10 +1,13 @@
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'
@@ -13,14 +16,18 @@ require 'iruby/formatter'
13
16
  require 'iruby/utils'
14
17
  require 'iruby/display'
15
18
  require 'iruby/comm'
16
- require 'iruby/session/mixin'
17
19
 
18
- begin
19
- require 'iruby/session/rbczmq'
20
- rescue LoadError
20
+ if ENV.fetch('IRUBY_OLD_SESSION', false)
21
+ require 'iruby/session/mixin'
21
22
  begin
22
23
  require 'iruby/session/ffi_rzmq'
23
24
  rescue LoadError
24
- 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
25
30
  end
31
+ else
32
+ require 'iruby/session'
26
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,44 +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
69
- unless @pry.eval_string.empty?
88
+
89
+ # Pry::Code.complete_expression? return false
90
+ if !@pry.eval_string.empty?
70
91
  syntax_error = @pry.eval_string
71
92
  @pry.reset_eval_string
72
- @pry.evaluate_ruby syntax_error
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)
73
101
  end
102
+
74
103
  raise @pry.last_exception if @pry.last_result_is_exception?
75
104
  @pry.push_initial_binding unless @pry.current_binding # ensure that we have a binding
76
105
  @pry.last_result
@@ -81,7 +110,7 @@ module IRuby
81
110
  end
82
111
 
83
112
  def reset
84
- @pry = Pry.new(output: $stdout, target: TOPLEVEL_BINDING)
113
+ @pry = Pry.new(output: $stdout, target: eval_binding)
85
114
  end
86
115
  end
87
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
@@ -1,17 +1,22 @@
1
1
  module IRuby
2
2
  module Display
3
3
  class << self
4
+ # @private
4
5
  def convert(obj, options)
5
6
  Representation.new(obj, options)
6
7
  end
7
8
 
9
+ # @private
8
10
  def display(obj, options = {})
9
11
  obj = convert(obj, options)
10
12
  options = obj.options
11
13
  obj = obj.object
12
14
 
13
15
  fuzzy_mime = options[:format] # Treated like a fuzzy mime type
14
- raise 'Invalid argument :format' unless !fuzzy_mime || String === fuzzy_mime
16
+ unless !fuzzy_mime || String === fuzzy_mime
17
+ raise 'Invalid argument :format'
18
+ end
19
+
15
20
  if exact_mime = options[:mime]
16
21
  raise 'Invalid argument :mime' unless String === exact_mime
17
22
  raise 'Invalid mime type' unless exact_mime.include?('/')
@@ -27,32 +32,50 @@ module IRuby
27
32
 
28
33
  # As a last resort, interpret string representation of the object
29
34
  # as the given mime type.
30
- data[exact_mime] = protect(exact_mime, obj) if exact_mime && !data.any? {|m,_| exact_mime == m }
35
+ if exact_mime && data.none? { |m, _| exact_mime == m }
36
+ data[exact_mime] = protect(exact_mime, obj)
37
+ end
31
38
 
32
39
  data
33
40
  end
34
41
 
35
- def clear_output(wait=false)
36
- IRuby::Kernel.instance.session.send(:publish, :clear_output, {wait: wait})
42
+ # @private
43
+ def clear_output(wait = false)
44
+ IRuby::Kernel.instance.session.send(:publish, :clear_output, wait: wait)
37
45
  end
38
46
 
39
47
  private
40
48
 
41
49
  def protect(mime, data)
42
- MimeMagic.new(mime).text? ? data.to_s : [data.to_s].pack('m0')
50
+ ascii?(mime) ? data.to_s : [data.to_s].pack('m0')
51
+ end
52
+
53
+ def ascii?(mime)
54
+ case mime
55
+ when "application/javascript"
56
+ # Special case for application/javascript.
57
+ # This needs because mime-types tells us application/javascript a non-text type.
58
+ true
59
+ else
60
+ MIME::Type.new(mime).ascii?
61
+ end
43
62
  end
44
63
 
45
64
  def render(data, obj, exact_mime, fuzzy_mime)
46
65
  # Filter matching renderer by object type
47
- renderer = Registry.renderer.select {|r| r.match?(obj) }
66
+ renderer = Registry.renderer.select { |r| r.match?(obj) }
48
67
 
49
68
  matching_renderer = nil
50
69
 
51
70
  # Find exactly matching display by exact_mime
52
- matching_renderer = renderer.find {|r| exact_mime == r.mime } if exact_mime
71
+ if exact_mime
72
+ matching_renderer = renderer.find { |r| exact_mime == r.mime }
73
+ end
53
74
 
54
75
  # Find fuzzy matching display by fuzzy_mime
55
- matching_renderer ||= renderer.find {|r| r.mime && r.mime.include?(fuzzy_mime) } if fuzzy_mime
76
+ if fuzzy_mime
77
+ matching_renderer ||= renderer.find { |r| r.mime&.include?(fuzzy_mime) }
78
+ end
56
79
 
57
80
  renderer.unshift matching_renderer if matching_renderer
58
81
 
@@ -73,7 +96,8 @@ module IRuby
73
96
  attr_reader :object, :options
74
97
 
75
98
  def initialize(object, options)
76
- @object, @options = object, options
99
+ @object = object
100
+ @options = options
77
101
  end
78
102
 
79
103
  class << self
@@ -91,10 +115,13 @@ module IRuby
91
115
  end
92
116
 
93
117
  class Renderer
94
- attr_reader :match, :mime, :render, :priority
118
+ attr_reader :match, :mime, :priority
95
119
 
96
120
  def initialize(match, mime, render, priority)
97
- @match, @mime, @render, @priority = match, mime, render, priority
121
+ @match = match
122
+ @mime = mime
123
+ @render = render
124
+ @priority = priority
98
125
  end
99
126
 
100
127
  def match?(obj)
@@ -114,7 +141,7 @@ module IRuby
114
141
  @renderer ||= []
115
142
  end
116
143
 
117
- SUPPORTED_MIMES = %w(
144
+ SUPPORTED_MIMES = %w[
118
145
  text/plain
119
146
  text/html
120
147
  text/latex
@@ -122,7 +149,8 @@ module IRuby
122
149
  application/javascript
123
150
  image/png
124
151
  image/jpeg
125
- image/svg+xml)
152
+ image/svg+xml
153
+ ]
126
154
 
127
155
  def match(&block)
128
156
  @match = block
@@ -131,7 +159,7 @@ module IRuby
131
159
  end
132
160
 
133
161
  def respond_to(name)
134
- match {|obj| obj.respond_to?(name) }
162
+ match { |obj| obj.respond_to?(name) }
135
163
  end
136
164
 
137
165
  def type(&block)
@@ -153,7 +181,7 @@ module IRuby
153
181
 
154
182
  def format(mime = nil, &block)
155
183
  renderer << Renderer.new(@match, mime, block, @priority)
156
- renderer.sort_by! {|r| -r.priority }
184
+ renderer.sort_by! { |r| -r.priority }
157
185
 
158
186
  # Decrease priority implicitly for all formats
159
187
  # which are added later for a type.
@@ -169,7 +197,19 @@ module IRuby
169
197
  LaTeX.vector(obj.to_a)
170
198
  end
171
199
 
200
+ type { Numo::NArray }
201
+ format 'text/plain', &:inspect
202
+ format 'text/latex' do |obj|
203
+ obj.ndim == 2 ?
204
+ LaTeX.matrix(obj, obj.shape[0], obj.shape[1]) :
205
+ LaTeX.vector(obj.to_a)
206
+ end
207
+ format 'text/html' do |obj|
208
+ HTML.table(obj.to_a)
209
+ end
210
+
172
211
  type { NArray }
212
+ format 'text/plain', &:inspect
173
213
  format 'text/latex' do |obj|
174
214
  obj.dim == 2 ?
175
215
  LaTeX.matrix(obj.transpose(1, 0), obj.shape[1], obj.shape[0]) :
@@ -231,37 +271,41 @@ module IRuby
231
271
 
232
272
  match do |obj|
233
273
  defined?(Magick::Image) && Magick::Image === obj ||
234
- defined?(MiniMagick::Image) && MiniMagick::Image === obj
274
+ defined?(MiniMagick::Image) && MiniMagick::Image === obj
235
275
  end
236
276
  format 'image' do |obj|
237
277
  format = obj.format || 'PNG'
238
- [format == 'PNG' ? 'image/png' : 'image/jpeg', obj.to_blob {|i| i.format = format }]
278
+ [format == 'PNG' ? 'image/png' : 'image/jpeg', obj.to_blob { |i| i.format = format }]
239
279
  end
240
280
 
241
- type { Gruff::Base }
242
- format 'image/png' do |obj|
243
- obj.to_blob
281
+ match do |obj|
282
+ defined?(Vips::Image) && Vips::Image === obj
283
+ end
284
+ format do |obj|
285
+ # handles Vips::Error, vips_image_get: field "vips-loader" not found
286
+ loader = obj.get('vips-loader') rescue nil
287
+ if loader == 'jpegload'
288
+ ['image/jpeg', obj.write_to_buffer('.jpg')]
289
+ else
290
+ # falls back to png for other/unknown types
291
+ ['image/png', obj.write_to_buffer('.png')]
292
+ end
244
293
  end
245
294
 
295
+ type { Gruff::Base }
296
+ format 'image/png', &:to_blob
297
+
246
298
  respond_to :to_html
247
- format 'text/html' do |obj|
248
- obj.to_html
249
- end
299
+ format 'text/html', &:to_html
250
300
 
251
301
  respond_to :to_latex
252
- format 'text/latex' do |obj|
253
- obj.to_latex
254
- end
302
+ format 'text/latex', &:to_latex
255
303
 
256
304
  respond_to :to_tex
257
- format 'text/latex' do |obj|
258
- obj.to_tex
259
- end
305
+ format 'text/latex', &:to_tex
260
306
 
261
307
  respond_to :to_javascript
262
- format 'text/javascript' do |obj|
263
- obj.to_javascript
264
- end
308
+ format 'text/javascript', &:to_javascript
265
309
 
266
310
  respond_to :to_svg
267
311
  format 'image/svg+xml' do |obj|
@@ -270,21 +314,17 @@ module IRuby
270
314
  end
271
315
 
272
316
  respond_to :to_iruby
273
- format do |obj|
274
- obj.to_iruby
275
- end
317
+ format(&:to_iruby)
276
318
 
277
- match {|obj| obj.respond_to?(:path) && File.readable?(obj.path) }
319
+ match { |obj| obj.respond_to?(:path) && obj.method(:path).arity == 0 && File.readable?(obj.path) }
278
320
  format do |obj|
279
- mime = MimeMagic.by_path(obj.path).to_s
321
+ mime = MIME::Types.of(obj.path).first.to_s
280
322
  [mime, File.read(obj.path)] if SUPPORTED_MIMES.include?(mime)
281
323
  end
282
324
 
283
325
  type { Object }
284
- priority -1000
285
- format 'text/plain' do |obj|
286
- obj.inspect
287
- end
326
+ priority(-1000)
327
+ format 'text/plain', &:inspect
288
328
  end
289
329
  end
290
330
  end