iruby 0.7.0 → 0.7.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 14aea673b75a6fe8884eff08c52b8831bbcec141df2525ed4c0a877738654271
4
- data.tar.gz: 89e5e091e67b7ef85f98ca0cb9ebe65cef0e32aacd75351c81b0c287666939c0
3
+ metadata.gz: 8e37561d8cd48b57f5ad431cfdf984306e638a262113f2cb741cf3a7d7a8e2e1
4
+ data.tar.gz: b1cd1da09bd9a6158749225e0097f92442ec5d600ab6f40a6189b50d0f2a4bb6
5
5
  SHA512:
6
- metadata.gz: 53b824e32c52cbf2893b24310ba4d3c7caa52ec872f36f57644a95bdd139232b4b5e032f161fb86baf94d011964caf9d739a4795af6805fd174360ed866477fa
7
- data.tar.gz: ee663bca3c4a67730d73e709c24d827af73e7c58734afe4dffc22c685c909462e9c9113f9548a8f1c8b5ce33b0b94338386b24c07978db547fab1caa21828ea7
6
+ metadata.gz: 6f70bdf2fa9da79a2e072f8a27ae729f5656a02a1d34a14bdd01287a16bfe257732875fc7a76db3f498cfd25cf49f4a9aa5ce2c82040ec90082bfca5e5d2f4e2
7
+ data.tar.gz: 789fe4b197775ef1245e96db8ac039a0df166898652c689ce2f66a7eb25db99a5f4806bccb7b76cc52cbdc7c4e6a3749eebda903b586b170842c9630f50af082
@@ -0,0 +1,124 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - master
7
+ pull_request:
8
+ types:
9
+ - opened
10
+ - synchronize
11
+ - reopened
12
+
13
+ jobs:
14
+ ubuntu:
15
+ name: Ubuntu
16
+ runs-on: ${{ matrix.os }}
17
+
18
+ strategy:
19
+ fail-fast: false
20
+ matrix:
21
+ os:
22
+ - ubuntu-latest
23
+ ruby:
24
+ - "3.0"
25
+ - "2.7"
26
+ - "2.6"
27
+ - "2.5"
28
+ - "2.4"
29
+ - "2.3"
30
+ - debug
31
+
32
+ steps:
33
+ - uses: actions/checkout@v2
34
+ with:
35
+ fetch-depth: 1
36
+
37
+ - uses: ruby/setup-ruby@v1
38
+ with:
39
+ ruby-version: ${{ matrix.ruby }}
40
+
41
+ - run: rake build
42
+
43
+ - name: Install irb for old Ruby
44
+ if: |
45
+ matrix.ruby == '2.5' ||
46
+ matrix.ruby == '2.4' ||
47
+ matrix.ruby == '2.3'
48
+ run: |
49
+ cat <<GEMFILE > Gemfile.irb
50
+ source 'https://rubygems.org'
51
+ gem 'irb'
52
+ GEMFILE
53
+ BUNDLE_GEMFILE=Gemfile.irb bundle install --jobs 4 --retry 3
54
+
55
+ - run: gem install pkg/*.gem
56
+
57
+ - run: ruby -r iruby -e "p IRuby::SessionAdapter.select_adapter_class"
58
+ env:
59
+ IRUBY_SESSION_ADAPTER: ffi-rzmq
60
+
61
+ - name: Install requirements on ubuntu
62
+ run: |
63
+ sudo apt update
64
+ sudo apt install -y --no-install-recommends \
65
+ libczmq-dev \
66
+ python3 \
67
+ python3-pip \
68
+ python3-setuptools
69
+ sudo pip3 install wheel
70
+ sudo pip3 install -r ci/requirements.txt
71
+
72
+ - run: bundle install --jobs 4 --retry 3
73
+
74
+ - name: Run tests
75
+ env:
76
+ PYTHON: python3
77
+ ADAPTERS: cztop ffi-rzmq
78
+ run: |
79
+ for adapter in $ADAPTERS; do
80
+ export IRUBY_TEST_SESSION_ADAPTER_NAME=$adapter
81
+ bundle exec rake test TESTOPTS=-v
82
+ done
83
+
84
+ windows:
85
+ name: Windows
86
+ runs-on: windows-latest
87
+
88
+ steps:
89
+ - uses: actions/checkout@v2
90
+ with:
91
+ fetch-depth: 1
92
+
93
+ - uses: ruby/setup-ruby@v1
94
+ with:
95
+ ruby-version: "3.0"
96
+
97
+ - run: rake build
98
+
99
+ - run: gem install pkg/*.gem
100
+
101
+ - run: ruby -r iruby -e "p IRuby::SessionAdapter.select_adapter_class"
102
+ env:
103
+ IRUBY_SESSION_ADAPTER: ffi-rzmq
104
+
105
+ macos:
106
+ name: macOS
107
+ runs-on: macos-latest
108
+
109
+ steps:
110
+ - uses: actions/checkout@v2
111
+ with:
112
+ fetch-depth: 1
113
+
114
+ - uses: ruby/setup-ruby@v1
115
+ with:
116
+ ruby-version: "3.0"
117
+
118
+ - run: rake build
119
+
120
+ - run: gem install pkg/*.gem
121
+
122
+ - run: ruby -r iruby -e "p IRuby::SessionAdapter.select_adapter_class"
123
+ env:
124
+ IRUBY_SESSION_ADAPTER: ffi-rzmq
data/CHANGES.md CHANGED
@@ -1,3 +1,35 @@
1
+ # 0.7.4 (2021-08-16)
2
+
3
+ ## Enhancements
4
+
5
+ * Install zeromq library automatically https://github.com/SciRuby/iruby/pull/307, https://github.com/SciRuby/iruby/pull/308 (@mrkn, @kou)
6
+ * Remove pyzmq session adapter (@mrkn)
7
+ * Make cztop session adapter deprecated (@mrkn)
8
+
9
+ # 0.7.3 (2021-07-08)
10
+
11
+ ## Bug Fixes
12
+
13
+ * Do not call default renderers when to_iruby_mimebundle is available (@mrkn)
14
+
15
+ # 0.7.2 (2021-06-23)
16
+
17
+ ## Bug Fixes
18
+
19
+ * Fix IRuby.table for Ruby >= 2.7 https://github.com/SciRuby/iruby/pull/305 (@topofocus)
20
+ * Fix PlainBackend to include modules https://github.com/SciRuby/iruby/issues/303 (@UliKuch, @mrkn)
21
+
22
+ # 0.7.1 (2021-06-21)
23
+
24
+ ## Enhancements
25
+
26
+ * Add support of `to_iruby_mimebundle` format method https://github.com/SciRuby/iruby/pull/304 (@mrkn)
27
+
28
+ ## Bug Fixes
29
+
30
+ * Prevent unintentional display the result of Session#send (@mrkn)
31
+ * Update display formatter for Gruff::Base to prevent warning (@mrkn)
32
+
1
33
  # 0.7.0 (2021-05-28)
2
34
 
3
35
  ## Enhancements
@@ -7,9 +39,14 @@
7
39
 
8
40
  ## Bug Fixes
9
41
 
10
- * Follow the messages and hooks orders during execute_request processing (@mrkn)
11
42
  * Fix the handling of image/svg+xml https://github.com/SciRuby/iruby/pull/300, https://github.com/SciRuby/iruby/pull/301 (@kojix2)
12
43
 
44
+ # 0.6.1 (2021-05-26)
45
+
46
+ ## Bug Fixes
47
+
48
+ * Follow the messages and hooks orders during execute_request processing (@mrkn)
49
+
13
50
  # 0.6.0 (2021-05-25)
14
51
 
15
52
  ## Bug Fixes
data/README.md CHANGED
@@ -21,67 +21,53 @@ https://mybinder.org/v2/gh/RubyData/binder/master?filepath=../lab
21
21
  ### Requirements
22
22
 
23
23
  * [Jupyter](https://jupyter.org)
24
- * One of the following is required
25
- * [ffi-rzmq](https://github.com/chuckremes/ffi-rzmq) and [libzmq](https://github.com/zeromq/libzmq)
26
- * [CZTop](https://gitlab.com/paddor/cztop) and [CZMQ](https://github.com/zeromq/czmq)
27
24
 
28
- If both ffi-rzmq and cztop are installed, ffi-rzmq is used. If you prefer cztop, set the following environment variable.
25
+ The following requirements are automatically installed.
29
26
 
30
- ```sh
31
- export IRUBY_SESSION_ADAPTER="cztop"
32
- ```
27
+ * [ffi-rzmq](https://github.com/chuckremes/ffi-rzmq)
28
+ * [libzmq](https://github.com/zeromq/libzmq)
29
+
30
+ The following dependencies are optional.
33
31
 
34
32
  * [Pry][Pry], if you want to use [Pry][Pry] instead of IRB for the code execution backend
35
- * If you want to install the development version of IRuby from the source code, try [specific_install](https://github.com/rdp/specific_install).
36
33
 
37
- ```
38
- gem specific_install https://github.com/SciRuby/iruby
39
- ```
40
34
 
41
- ### Ubuntu
35
+ ### Installing Jupyter Notebook and/or JupyterLab
42
36
 
43
- Install Jupyter.
37
+ See the official document to know how to install Jupyter Notebook and/or JupyterLab.
38
+
39
+ * https://jupyter.readthedocs.io/en/latest/install/notebook-classic.html
40
+ * https://jupyter.readthedocs.io/en/latest/install.html
41
+
42
+ ### Ubuntu
44
43
 
45
44
  #### Ubuntu 17+
46
45
 
47
46
  ```shell
48
47
  sudo apt install libtool libffi-dev ruby ruby-dev make
49
- sudo apt install libzmq3-dev libczmq-dev
50
48
 
51
- gem install ffi-rzmq
52
- gem install iruby --pre
49
+ gem install --user-install iruby
53
50
  iruby register --force
54
51
  ```
55
52
 
56
53
  #### Ubuntu 16
57
54
 
58
- CZTop requires CZMQ >= 4.0.0 and ZMQ >= 4.2.0. The official packages for Ubuntu 16.04 don't satisfy these version requrements, so you need to install from source.
55
+ The latest IRuby requires Ruby >= 2.4 while Ubuntu's official Ruby package is version 2.3.
56
+ So you need to install Ruby >= 2.4 by yourself before preparing IRuby.
57
+ We recommend to use rbenv.
59
58
 
60
59
  ```shell
61
60
  sudo apt install libtool libffi-dev ruby ruby-dev make
62
- sudo apt install git libzmq-dev autoconf pkg-config
63
- git clone https://github.com/zeromq/czmq
64
- cd czmq
65
- ./autogen.sh && ./configure && sudo make && sudo make install
66
-
67
- gem install cztop
68
- gem install iruby --pre
61
+ gem install --user-install iruby
69
62
  iruby register --force
70
63
  ```
71
64
 
72
65
  ### Windows
73
66
 
74
- Install git and Jupyter.
75
67
  [DevKit](https://rubyinstaller.org/add-ons/devkit.html) is necessary for building RubyGems with native C-based extensions.
76
68
 
77
- Install ZeroMQ.
78
- ```shell
79
- pacman -S mingw64/mingw-w64-x86_64-zeromq
80
- ```
81
-
82
69
  ```shell
83
- gem install ffi-rzmq
84
- gem install iruby --pre
70
+ gem install iruby
85
71
  iruby register --force
86
72
  ```
87
73
 
@@ -93,17 +79,7 @@ Install Jupyter.
93
79
  #### Homebrew
94
80
 
95
81
  ```shell
96
- brew install automake gmp libtool wget
97
- brew install zeromq --HEAD
98
- brew install czmq --HEAD
99
- ```
100
-
101
- ```shell
102
- # export LIBZMQ_PATH=$(brew --prefix zeromq)/lib
103
- # export LIBCZMQ_PATH=$(brew --prefix czmq)/lib
104
- # gem install cztop
105
- gem install ffi-rzmq
106
- gem install iruby --pre
82
+ gem install iruby
107
83
  iruby register --force
108
84
  ```
109
85
 
@@ -113,8 +89,8 @@ If you are using macports, run the following commands.
113
89
 
114
90
  ```shell
115
91
  port install libtool autoconf automake autogen
116
- gem install ffi-rzmq
117
92
  gem install iruby
93
+ iruby register --force
118
94
  ```
119
95
 
120
96
  ### Docker
@@ -123,7 +99,7 @@ Try [RubyData Docker Stacks](https://github.com/RubyData/docker-stacks).
123
99
  Running jupyter notebook:
124
100
 
125
101
  ```shell
126
- docker run -p 8888:8888 rubydata/datascience-notebook
102
+ docker run --rm -it -p 8888:8888 rubydata/datascience-notebook
127
103
  ```
128
104
 
129
105
  ### Installation for JRuby
@@ -131,7 +107,6 @@ docker run -p 8888:8888 rubydata/datascience-notebook
131
107
  You can use Java classes in your IRuby notebook.
132
108
 
133
109
  * JRuby version >= 9.0.4.0
134
- * cztop gem
135
110
  * iruby gem
136
111
 
137
112
  After installation, make sure that your `env` is set up to use jruby.
@@ -148,6 +123,29 @@ If you have already used IRuby with a different version, you need to generate a
148
123
  $ iruby register --force
149
124
  ```
150
125
 
126
+ ### Install the development version of IRuby
127
+
128
+ **Be careful to use the development version because it is usually unstable.**
129
+
130
+ If you want to install the development version of IRuby from the source code, try [specific_install](https://github.com/rdp/specific_install).
131
+
132
+ ```
133
+ gem specific_install https://github.com/SciRuby/iruby
134
+ ```
135
+
136
+ ### Note for using with CZTop and CZMQ
137
+
138
+ [CZTop](https://gitlab.com/paddor/cztop) adapter has been deprecated since IRuby version 0.7.4.
139
+ It will be removed after several versions.
140
+
141
+ If you want to use IRuby with CZTop, you need to install it and [CZMQ](https://github.com/zeromq/czmq).
142
+
143
+ If both ffi-rzmq and cztop are installed, ffi-rzmq is used. If you prefer cztop, set the following environment variable.
144
+
145
+ ```sh
146
+ export IRUBY_SESSION_ADAPTER="cztop"
147
+ ```
148
+
151
149
  ## Backends
152
150
 
153
151
  There are two backends: PlainBackend and PryBackend.
data/ext/Rakefile ADDED
@@ -0,0 +1,19 @@
1
+ task default: :all
2
+
3
+ task all: [:ensure_zeromq]
4
+
5
+ task :ensure_zeromq do
6
+ begin
7
+ require 'ffi-rzmq'
8
+ rescue LoadError
9
+ require 'native-package-installer'
10
+ unless NativePackageInstaller.install(arch_linux: 'zeromq',
11
+ debian: 'libzmq3-dev',
12
+ freebsd: 'libzmq4',
13
+ homebrew: 'zmq',
14
+ macports: 'zmq',
15
+ redhat: 'zeromq-devel')
16
+ raise 'Failed to install ZeroMQ'
17
+ end
18
+ end
19
+ end
data/iruby.gemspec CHANGED
@@ -14,6 +14,7 @@ Gem::Specification.new do |s|
14
14
  s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
15
15
  s.test_files = s.files.grep(%r{^test/})
16
16
  s.require_paths = %w[lib]
17
+ s.extensions = %w[ext/Rakefile]
17
18
 
18
19
  s.required_ruby_version = '>= 2.3.0'
19
20
 
@@ -22,9 +23,12 @@ Gem::Specification.new do |s|
22
23
  s.add_dependency 'irb'
23
24
  s.add_dependency 'mime-types', '>= 3.3.1'
24
25
  s.add_dependency 'multi_json', '~> 1.11'
26
+ s.add_dependency 'native-package-installer'
25
27
 
26
28
  s.add_development_dependency 'pycall', '>= 1.2.1'
27
29
  s.add_development_dependency 'rake'
28
30
  s.add_development_dependency 'test-unit'
29
31
  s.add_development_dependency 'test-unit-rr'
32
+
33
+ s.metadata['msys2_mingw_dependencies'] = 'zeromq'
30
34
  end
data/lib/iruby/backend.rb CHANGED
@@ -40,6 +40,7 @@ module IRuby
40
40
  require 'irb/completion'
41
41
  IRB.setup(nil)
42
42
  @main = TOPLEVEL_BINDING.eval("self").dup
43
+ init_main_object(@main)
43
44
  @workspace = IRB::WorkSpace.new(@main)
44
45
  @irb = IRB::Irb.new(@workspace)
45
46
  @eval_path = @irb.context.irb_path
@@ -58,6 +59,16 @@ module IRuby
58
59
  def complete(code)
59
60
  IRB::InputCompletor::CompletionProc.call(code)
60
61
  end
62
+
63
+ private
64
+
65
+ def init_main_object(main)
66
+ wrapper_module = Module.new
67
+ main.extend(wrapper_module)
68
+ main.define_singleton_method(:include) do |*args|
69
+ wrapper_module.include(*args)
70
+ end
71
+ end
61
72
  end
62
73
 
63
74
  class PryBackend
data/lib/iruby/display.rb CHANGED
@@ -2,6 +2,22 @@ require "set"
2
2
 
3
3
  module IRuby
4
4
  module Display
5
+ DEFAULT_MIME_TYPE_FORMAT_METHODS = {
6
+ "text/html" => :to_html,
7
+ "text/markdown" => :to_markdown,
8
+ "image/svg+xml" => :to_svg,
9
+ "image/png" => :to_png,
10
+ "appliation/pdf" => :to_pdf,
11
+ "image/jpeg" => :to_jpeg,
12
+ "text/latex" => [:to_latex, :to_tex],
13
+ # NOTE: Do not include the entry of "application/json" because
14
+ # all objects can respond to `to_json` due to json library
15
+ # "application/json" => :to_json,
16
+ "application/javascript" => :to_javascript,
17
+ nil => :to_iruby,
18
+ "text/plain" => :inspect
19
+ }.freeze
20
+
5
21
  class << self
6
22
  # @private
7
23
  def convert(obj, options)
@@ -24,17 +40,51 @@ module IRuby
24
40
  raise 'Invalid mime type' unless exact_mime.include?('/')
25
41
  end
26
42
 
27
- data = {}
28
-
29
- # Render additional representation
30
- render(data, obj, exact_mime, fuzzy_mime)
31
-
32
- # IPython always requires a text representation
33
- render(data, obj, 'text/plain', nil) unless data['text/plain']
43
+ data = if obj.respond_to?(:to_iruby_mimebundle)
44
+ render_mimebundle(obj, exact_mime, fuzzy_mime)
45
+ else
46
+ {}
47
+ end
48
+
49
+ # Render by additional formatters
50
+ render_by_registry(data, obj, exact_mime, fuzzy_mime)
51
+
52
+ # Render by to_xxx methods
53
+ default_renderers = if obj.respond_to?(:to_iruby_mimebundle)
54
+ # Do not use Hash#slice for Ruby < 2.5
55
+ {"text/plain" => DEFAULT_MIME_TYPE_FORMAT_METHODS["text/plain"]}
56
+ else
57
+ DEFAULT_MIME_TYPE_FORMAT_METHODS
58
+ end
59
+ default_renderers.each do |mime, methods|
60
+ next if mime.nil? && !data.empty? # for to_iruby
61
+
62
+ next if mime && data.key?(mime) # do not overwrite
63
+
64
+ method = Array(methods).find {|m| obj.respond_to?(m) }
65
+ next if method.nil?
66
+
67
+ result = obj.send(method)
68
+ case mime
69
+ when nil # to_iruby
70
+ case result
71
+ when nil
72
+ # do nothing
73
+ next
74
+ when Array
75
+ mime, result = result
76
+ else
77
+ warn(("Ignore the result of to_iruby method of %p because " +
78
+ "it does not return a pair of mime-type and formatted representation") % obj)
79
+ next
80
+ end
81
+ end
82
+ data[mime] = result
83
+ end
34
84
 
35
85
  # As a last resort, interpret string representation of the object
36
86
  # as the given mime type.
37
- if exact_mime && data.none? { |m, _| exact_mime == m }
87
+ if exact_mime && !data.key?(exact_mime)
38
88
  data[exact_mime] = protect(exact_mime, obj)
39
89
  end
40
90
 
@@ -67,7 +117,19 @@ module IRuby
67
117
  end
68
118
  end
69
119
 
70
- def render(data, obj, exact_mime, fuzzy_mime)
120
+ private def render_mimebundle(obj, exact_mime, fuzzy_mime)
121
+ data = {}
122
+ include_mime = [exact_mime].compact
123
+ formats, metadata = obj.to_iruby_mimebundle(include: include_mime)
124
+ formats.each do |mime, value|
125
+ if fuzzy_mime.nil? || mime.include?(fuzzy_mime)
126
+ data[mime] = value
127
+ end
128
+ end
129
+ data
130
+ end
131
+
132
+ private def render_by_registry(data, obj, exact_mime, fuzzy_mime)
71
133
  # Filter matching renderer by object type
72
134
  renderer = Registry.renderer.select { |r| r.match?(obj) }
73
135
 
@@ -88,6 +150,8 @@ module IRuby
88
150
  # Return first render result which has the right mime type
89
151
  renderer.each do |r|
90
152
  mime, result = r.render(obj)
153
+ next if data.key?(mime)
154
+
91
155
  if mime && result && (!exact_mime || exact_mime == mime) && (!fuzzy_mime || mime.include?(fuzzy_mime))
92
156
  data[mime] = protect(mime, result)
93
157
  break
@@ -98,6 +162,19 @@ module IRuby
98
162
  end
99
163
  end
100
164
 
165
+ private def render_by_to_iruby(data, obj)
166
+ if obj.respond_to?(:to_iruby)
167
+ result = obj.to_iruby
168
+ mime, rep = case result
169
+ when Array
170
+ result
171
+ else
172
+ [nil, result]
173
+ end
174
+ data[mime] = rep
175
+ end
176
+ end
177
+
101
178
  class Representation
102
179
  attr_reader :object, :options
103
180
 
@@ -120,6 +197,60 @@ module IRuby
120
197
  end
121
198
  end
122
199
 
200
+ class FormatMatcher
201
+ def initialize(&block)
202
+ @block = block
203
+ end
204
+
205
+ def call(obj)
206
+ @block.(obj)
207
+ end
208
+
209
+ def inspect
210
+ "#{self.class.name}[%p]" % @block
211
+ end
212
+ end
213
+
214
+ class RespondToFormatMatcher < FormatMatcher
215
+ def initialize(name)
216
+ super() {|obj| obj.respond_to?(name) }
217
+ @name = name
218
+ end
219
+
220
+ attr_reader :name
221
+
222
+ def inspect
223
+ "#{self.class.name}[respond_to?(%p)]" % name
224
+ end
225
+ end
226
+
227
+ class TypeFormatMatcher < FormatMatcher
228
+ def initialize(class_block)
229
+ super() do |obj|
230
+ begin
231
+ self.klass === obj
232
+ # We have to rescue all exceptions since constant autoloading could fail with a different error
233
+ rescue Exception
234
+ false
235
+ end
236
+ end
237
+ @class_block = class_block
238
+ end
239
+
240
+ def klass
241
+ @class_block.()
242
+ end
243
+
244
+ def inspect
245
+ klass = begin
246
+ @class_block.()
247
+ rescue Exception
248
+ @class_block
249
+ end
250
+ "#{self.class.name}[%p]" % klass
251
+ end
252
+ end
253
+
123
254
  class Renderer
124
255
  attr_reader :match, :mime, :priority
125
256
 
@@ -147,37 +278,22 @@ module IRuby
147
278
  @renderer ||= []
148
279
  end
149
280
 
150
- SUPPORTED_MIMES = %w[
151
- text/plain
152
- text/html
153
- text/latex
154
- application/json
155
- application/javascript
156
- image/png
157
- image/jpeg
158
- image/svg+xml
159
- ]
160
-
161
281
  def match(&block)
162
- @match = block
282
+ @match = FormatMatcher.new(&block)
163
283
  priority 0
164
284
  nil
165
285
  end
166
286
 
167
287
  def respond_to(name)
168
- match { |obj| obj.respond_to?(name) }
288
+ @match = RespondToFormatMatcher.new(name)
289
+ priority 0
290
+ nil
169
291
  end
170
292
 
171
293
  def type(&block)
172
- match do |obj|
173
- begin
174
- block.call === obj
175
- # We have to rescue all exceptions since constant autoloading could fail with a different error
176
- rescue Exception
177
- rescue #NameError
178
- false
179
- end
180
- end
294
+ @match = TypeFormatMatcher.new(block)
295
+ priority 0
296
+ nil
181
297
  end
182
298
 
183
299
  def priority(p)
@@ -275,13 +391,24 @@ module IRuby
275
391
  end
276
392
  end
277
393
 
394
+ format_magick_image = ->(obj) do
395
+ format = obj.format || 'PNG'
396
+ [
397
+ format == 'PNG' ? 'image/png' : 'image/jpeg',
398
+ obj.to_blob {|i| i.format = format }
399
+ ]
400
+ end
401
+
278
402
  match do |obj|
279
403
  defined?(Magick::Image) && Magick::Image === obj ||
280
404
  defined?(MiniMagick::Image) && MiniMagick::Image === obj
281
405
  end
406
+ format 'image', &format_magick_image
407
+
408
+ type { Gruff::Base }
282
409
  format 'image' do |obj|
283
- format = obj.format || 'PNG'
284
- [format == 'PNG' ? 'image/png' : 'image/jpeg', obj.to_blob { |i| i.format = format }]
410
+ image = obj.to_image
411
+ format_magick_image.(obj.to_image)
285
412
  end
286
413
 
287
414
  match do |obj|
@@ -298,39 +425,19 @@ module IRuby
298
425
  end
299
426
  end
300
427
 
301
- type { Gruff::Base }
302
- format 'image/png', &:to_blob
303
-
304
- respond_to :to_html
305
- format 'text/html', &:to_html
306
-
307
- respond_to :to_latex
308
- format 'text/latex', &:to_latex
309
-
310
- respond_to :to_tex
311
- format 'text/latex', &:to_tex
312
-
313
- respond_to :to_javascript
314
- format 'text/javascript', &:to_javascript
315
-
316
- respond_to :to_svg
428
+ type { Rubyvis::Mark }
317
429
  format 'image/svg+xml' do |obj|
318
- obj.render if defined?(Rubyvis) && Rubyvis::Mark === obj
430
+ obj.render
319
431
  obj.to_svg
320
432
  end
321
433
 
322
- respond_to :to_iruby
323
- format(&:to_iruby)
324
-
325
434
  match { |obj| obj.respond_to?(:path) && obj.method(:path).arity == 0 && File.readable?(obj.path) }
326
435
  format do |obj|
327
436
  mime = MIME::Types.of(obj.path).first.to_s
328
- [mime, File.read(obj.path)] if SUPPORTED_MIMES.include?(mime)
437
+ if mime && DEFAULT_MIME_TYPE_FORMAT_METHODS.key?(mime)
438
+ [mime, File.read(obj.path)]
439
+ end
329
440
  end
330
-
331
- type { Object }
332
- priority(-1000)
333
- format 'text/plain', &:inspect
334
441
  end
335
442
  end
336
443
  end
@@ -23,7 +23,7 @@ module IRuby
23
23
  idents, msg_list = frames[0..i-1], frames[i+1..-1]
24
24
 
25
25
  minlen = 5
26
- raise 'malformed message, must have at least #{minlen} elements' unless msg_list.length >= minlen
26
+ raise "malformed message, must have at least #{minlen} elements" unless msg_list.length >= minlen
27
27
  s, header, parent_header, metadata, content, buffers = *msg_list
28
28
  raise 'Invalid signature' unless s == sign(msg_list[1..-1])
29
29
  {
@@ -40,14 +40,12 @@ module IRuby
40
40
 
41
41
  require_relative 'session_adapter/ffirzmq_adapter'
42
42
  require_relative 'session_adapter/cztop_adapter'
43
- require_relative 'session_adapter/pyzmq_adapter'
44
43
  require_relative 'session_adapter/test_adapter'
45
44
 
46
45
  def self.select_adapter_class(name=nil)
47
46
  classes = {
48
47
  'ffi-rzmq' => SessionAdapter::FfirzmqAdapter,
49
48
  'cztop' => SessionAdapter::CztopAdapter,
50
- # 'pyzmq' => SessionAdapter::PyzmqAdapter
51
49
  'test' => SessionAdapter::TestAdapter,
52
50
  }
53
51
  if (name ||= ENV.fetch('IRUBY_SESSION_ADAPTER', nil))
@@ -61,6 +59,9 @@ module IRuby
61
59
  "Session adapter `#{name}` is unavailable"
62
60
  end
63
61
  end
62
+ if name == 'cztop'
63
+ warn "WARNING: cztop was deprecated and will be removed; Use ffi-rzmq, instead."
64
+ end
64
65
  return cls
65
66
  end
66
67
  classes.each_value do |cls|
data/lib/iruby/utils.rb CHANGED
@@ -9,6 +9,9 @@ module IRuby
9
9
  Kernel.instance.session.send(:publish, :display_data,
10
10
  data: Display.display(obj, options),
11
11
  metadata: {}) unless obj.nil?
12
+ # The next `nil` is necessary to prevent unintentional displaying
13
+ # the result of Session#send
14
+ nil
12
15
  end
13
16
 
14
17
  # Clear the output area
@@ -18,7 +21,7 @@ module IRuby
18
21
 
19
22
  # Format the given object into HTML table
20
23
  def table(s, **options)
21
- html(HTML.table(s, options))
24
+ html(HTML.table(s, **options))
22
25
  end
23
26
 
24
27
  # Treat the given string as LaTeX text
data/lib/iruby/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module IRuby
2
- VERSION = '0.7.0'
2
+ VERSION = '0.7.4'
3
3
  end
@@ -8,6 +8,12 @@ module IRubyTest
8
8
  assert_equal 3, @plainbackend.eval('1+2', false)
9
9
  end
10
10
 
11
+ def test_include_module
12
+ assert_nothing_raised do
13
+ @plainbackend.eval("include Math, Comparable", false)
14
+ end
15
+ end
16
+
11
17
  def test_complete_req
12
18
  assert_includes @plainbackend.complete('req'), 'require'
13
19
  end
@@ -26,6 +32,12 @@ module IRubyTest
26
32
  assert_equal 3, @prybackend.eval('1+2', false)
27
33
  end
28
34
 
35
+ def test_include_module
36
+ assert_nothing_raised do
37
+ @prybackend.eval("include Math, Comparable", false)
38
+ end
39
+ end
40
+
29
41
  def test_complete_req
30
42
  assert_includes @prybackend.complete('req'), 'require'
31
43
  end
@@ -0,0 +1,185 @@
1
+ module IRubyTest
2
+ class DisplayTest < TestBase
3
+ def setup
4
+ @object = Object.new
5
+ @object.instance_variable_set(:@to_html_called, false)
6
+ @object.instance_variable_set(:@to_markdown_called, false)
7
+ @object.instance_variable_set(:@to_iruby_called, false)
8
+ @object.instance_variable_set(:@to_iruby_mimebundle_called, false)
9
+
10
+ class << @object
11
+ attr_reader :to_html_called
12
+ attr_reader :to_markdown_called
13
+ attr_reader :to_iruby_called
14
+ attr_reader :to_iruby_mimebundle_called
15
+
16
+ def html
17
+ "<b>html</b>"
18
+ end
19
+
20
+ def markdown
21
+ "*markdown*"
22
+ end
23
+
24
+ def inspect
25
+ "!!! inspect !!!"
26
+ end
27
+ end
28
+ end
29
+
30
+ def define_to_html
31
+ class << @object
32
+ def to_html
33
+ @to_html_called = true
34
+ html
35
+ end
36
+ end
37
+ end
38
+
39
+ def define_to_markdown
40
+ class << @object
41
+ def to_markdown
42
+ @to_markdown_called = true
43
+ markdown
44
+ end
45
+ end
46
+ end
47
+
48
+ def define_to_iruby
49
+ class << @object
50
+ def to_iruby
51
+ @to_iruby_called = true
52
+ ["text/html", "<b>to_iruby</b>"]
53
+ end
54
+ end
55
+ end
56
+
57
+ def define_to_iruby_mimebundle
58
+ class << @object
59
+ def to_iruby_mimebundle(include: [])
60
+ @to_iruby_mimebundle_called = true
61
+ mimes = if include.empty?
62
+ ["text/markdown", "application/json"]
63
+ else
64
+ include
65
+ end
66
+ formats = mimes.map { |mime|
67
+ result = case mime
68
+ when "text/markdown"
69
+ "**markdown**"
70
+ when "application/json"
71
+ %Q[{"mimebundle": "json"}]
72
+ end
73
+ [mime, result]
74
+ }.to_h
75
+ metadata = {}
76
+ return formats, metadata
77
+ end
78
+ end
79
+ end
80
+
81
+ def assert_iruby_display(expected)
82
+ assert_equal(expected,
83
+ {
84
+ result: IRuby::Display.display(@object),
85
+ to_html_called: @object.to_html_called,
86
+ to_markdown_called: @object.to_markdown_called,
87
+ to_iruby_called: @object.to_iruby_called,
88
+ to_iruby_mimebundle_called: @object.to_iruby_mimebundle_called
89
+ })
90
+ end
91
+
92
+ sub_test_case("the object cannot handle all the mime types") do
93
+ def test_display
94
+ assert_iruby_display({
95
+ result: {"text/plain" => "!!! inspect !!!"},
96
+ to_html_called: false,
97
+ to_markdown_called: false,
98
+ to_iruby_called: false,
99
+ to_iruby_mimebundle_called: false
100
+ })
101
+ end
102
+ end
103
+
104
+ sub_test_case("the object can respond to to_iruby") do
105
+ def setup
106
+ super
107
+ define_to_iruby
108
+ end
109
+
110
+ def test_display
111
+ assert_iruby_display({
112
+ result: {
113
+ "text/html" => "<b>to_iruby</b>",
114
+ "text/plain" => "!!! inspect !!!"
115
+ },
116
+ to_html_called: false,
117
+ to_markdown_called: false,
118
+ to_iruby_called: true,
119
+ to_iruby_mimebundle_called: false
120
+ })
121
+ end
122
+
123
+ sub_test_case("the object can respond to to_markdown") do
124
+ def setup
125
+ super
126
+ define_to_markdown
127
+ end
128
+
129
+ def test_display
130
+ assert_iruby_display({
131
+ result: {
132
+ "text/markdown" => "*markdown*",
133
+ "text/plain" => "!!! inspect !!!"
134
+ },
135
+ to_html_called: false,
136
+ to_markdown_called: true,
137
+ to_iruby_called: false,
138
+ to_iruby_mimebundle_called: false
139
+ })
140
+ end
141
+ end
142
+
143
+ sub_test_case("the object can respond to to_html") do
144
+ def setup
145
+ super
146
+ define_to_html
147
+ end
148
+
149
+ def test_display
150
+ assert_iruby_display({
151
+ result: {
152
+ "text/html" => "<b>html</b>",
153
+ "text/plain" => "!!! inspect !!!"
154
+ },
155
+ to_html_called: true,
156
+ to_markdown_called: false,
157
+ to_iruby_called: false,
158
+ to_iruby_mimebundle_called: false
159
+ })
160
+ end
161
+
162
+ sub_test_case("the object can respond to to_iruby_mimebundle") do
163
+ def setup
164
+ super
165
+ define_to_iruby_mimebundle
166
+ end
167
+
168
+ def test_display
169
+ assert_iruby_display({
170
+ result: {
171
+ "text/markdown" => "**markdown**",
172
+ "application/json" => %Q[{"mimebundle": "json"}],
173
+ "text/plain" => "!!! inspect !!!"
174
+ },
175
+ to_html_called: false,
176
+ to_markdown_called: false,
177
+ to_iruby_called: false,
178
+ to_iruby_mimebundle_called: true
179
+ })
180
+ end
181
+ end
182
+ end
183
+ end
184
+ end
185
+ end
@@ -14,7 +14,6 @@ module IRubyTest
14
14
  assert_rr do
15
15
  stub(IRuby::SessionAdapter::CztopAdapter).available? { true }
16
16
  stub(IRuby::SessionAdapter::FfirzmqAdapter).available? { false }
17
- stub(IRuby::SessionAdapter::PyzmqAdapter).available? { false }
18
17
 
19
18
  cls = IRuby::SessionAdapter.select_adapter_class
20
19
  assert_equal IRuby::SessionAdapter::CztopAdapter, cls
@@ -25,25 +24,12 @@ module IRubyTest
25
24
  assert_rr do
26
25
  stub(IRuby::SessionAdapter::FfirzmqAdapter).available? { true }
27
26
  stub(IRuby::SessionAdapter::CztopAdapter).available? { false }
28
- stub(IRuby::SessionAdapter::PyzmqAdapter).available? { false }
29
27
 
30
28
  cls = IRuby::SessionAdapter.select_adapter_class
31
29
  assert_equal IRuby::SessionAdapter::FfirzmqAdapter, cls
32
30
  end
33
31
  end
34
32
 
35
- def test_select_adapter_class_with_pyzmq
36
- omit("pyzmq adapter is disabled")
37
- assert_rr do
38
- stub(IRuby::SessionAdapter::PyzmqAdapter).available? { true }
39
- stub(IRuby::SessionAdapter::FfirzmqAdapter).available? { false }
40
- stub(IRuby::SessionAdapter::CztopAdapter).available? { false }
41
-
42
- cls = IRuby::SessionAdapter.select_adapter_class
43
- assert_equal IRuby::SessionAdapter::PyzmqAdapter, cls
44
- end
45
- end
46
-
47
33
  def test_select_adapter_class_with_env
48
34
  with_env('IRUBY_SESSION_ADAPTER' => 'cztop') do
49
35
  assert_rr do
@@ -72,20 +58,6 @@ module IRubyTest
72
58
  end
73
59
  end
74
60
  end
75
-
76
- with_env('IRUBY_SESSION_ADAPTER' => 'pyzmq') do
77
- # pyzmq adapter is disabled
78
- #
79
- # IRuby::SessionAdapter::PyzmqAdapter.stub :available?, true do
80
- # assert_equal IRuby::SessionAdapter::PyzmqAdapter, IRuby::SessionAdapter.select_adapter_class
81
- # end
82
- #
83
- # IRuby::SessionAdapter::PyzmqAdapter.stub :available?, false do
84
- # assert_raises IRuby::SessionAdapterNotFound do
85
- # IRuby::SessionAdapter.select_adapter_class
86
- # end
87
- # end
88
- end
89
61
  end
90
62
  end
91
63
  end
@@ -22,9 +22,6 @@ module IRubyTest
22
22
  IRuby::SessionAdapter::CztopAdapter
23
23
  when 'ffi-rzmq'
24
24
  IRuby::SessionAdapter::FfirzmqAdapter
25
- when 'pyzmq'
26
- omit("pyzmq adapter is disabled")
27
- # IRuby::SessionAdapter::PyzmqAdapter
28
25
  else
29
26
  flunk "Unknown session adapter: #{adapter_name.inspect}"
30
27
  end
@@ -37,7 +34,6 @@ module IRubyTest
37
34
  assert_rr do
38
35
  stub(IRuby::SessionAdapter::CztopAdapter).available? { false }
39
36
  stub(IRuby::SessionAdapter::FfirzmqAdapter).available? { false }
40
- stub(IRuby::SessionAdapter::PyzmqAdapter).available? { false }
41
37
  stub(IRuby::SessionAdapter::TestAdapter).available? { false }
42
38
  assert_raises IRuby::SessionAdapterNotFound do
43
39
  IRuby::Session.new(@session_config)
@@ -0,0 +1,25 @@
1
+ module IRubyTest
2
+ class UtilsTest < TestBase
3
+ sub_test_case("IRuby.table") do
4
+ def setup
5
+ @data = {
6
+ X: [ 1, 2, 3 ],
7
+ Y: [ 4, 5, 6 ]
8
+ }
9
+ end
10
+ sub_test_case("without header: option") do
11
+ def test_table
12
+ result = IRuby.table(@data)
13
+ assert_include(result.object, "<th>X</th>")
14
+ end
15
+ end
16
+
17
+ sub_test_case("with header: false") do
18
+ def test_table
19
+ result = IRuby.table(@data, header: false)
20
+ assert_not_include(result.object, "<th>X</th>")
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.7.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Mendler
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2021-05-28 00:00:00.000000000 Z
12
+ date: 2021-08-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: data_uri
@@ -81,6 +81,20 @@ dependencies:
81
81
  - - "~>"
82
82
  - !ruby/object:Gem::Version
83
83
  version: '1.11'
84
+ - !ruby/object:Gem::Dependency
85
+ name: native-package-installer
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ type: :runtime
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
84
98
  - !ruby/object:Gem::Dependency
85
99
  name: pycall
86
100
  requirement: !ruby/object:Gem::Requirement
@@ -142,10 +156,11 @@ email:
142
156
  - mail@daniel-mendler.de
143
157
  executables:
144
158
  - iruby
145
- extensions: []
159
+ extensions:
160
+ - ext/Rakefile
146
161
  extra_rdoc_files: []
147
162
  files:
148
- - ".github/workflows/ubuntu.yml"
163
+ - ".github/workflows/ci.yml"
149
164
  - ".gitignore"
150
165
  - CHANGES.md
151
166
  - Gemfile
@@ -158,6 +173,7 @@ files:
158
173
  - ci/requirements.txt
159
174
  - docker/setup.sh
160
175
  - docker/test.sh
176
+ - ext/Rakefile
161
177
  - iruby.gemspec
162
178
  - lib/iruby.rb
163
179
  - lib/iruby/assets/kernel.css
@@ -200,7 +216,6 @@ files:
200
216
  - lib/iruby/session_adapter.rb
201
217
  - lib/iruby/session_adapter/cztop_adapter.rb
202
218
  - lib/iruby/session_adapter/ffirzmq_adapter.rb
203
- - lib/iruby/session_adapter/pyzmq_adapter.rb
204
219
  - lib/iruby/session_adapter/test_adapter.rb
205
220
  - lib/iruby/utils.rb
206
221
  - lib/iruby/version.rb
@@ -213,6 +228,7 @@ files:
213
228
  - test/integration_test.rb
214
229
  - test/iruby/backend_test.rb
215
230
  - test/iruby/command_test.rb
231
+ - test/iruby/display_test.rb
216
232
  - test/iruby/event_manager_test.rb
217
233
  - test/iruby/jupyter_test.rb
218
234
  - test/iruby/kernel_test.rb
@@ -223,11 +239,13 @@ files:
223
239
  - test/iruby/session_adapter/session_adapter_test_base.rb
224
240
  - test/iruby/session_adapter_test.rb
225
241
  - test/iruby/session_test.rb
242
+ - test/iruby/utils_test.rb
226
243
  - test/run-test.rb
227
244
  homepage: https://github.com/SciRuby/iruby
228
245
  licenses:
229
246
  - MIT
230
- metadata: {}
247
+ metadata:
248
+ msys2_mingw_dependencies: zeromq
231
249
  post_install_message:
232
250
  rdoc_options: []
233
251
  require_paths:
@@ -243,7 +261,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
243
261
  - !ruby/object:Gem::Version
244
262
  version: '0'
245
263
  requirements: []
246
- rubygems_version: 3.3.0.dev
264
+ rubygems_version: 3.2.23
247
265
  signing_key:
248
266
  specification_version: 4
249
267
  summary: Ruby Kernel for Jupyter
@@ -252,6 +270,7 @@ test_files:
252
270
  - test/integration_test.rb
253
271
  - test/iruby/backend_test.rb
254
272
  - test/iruby/command_test.rb
273
+ - test/iruby/display_test.rb
255
274
  - test/iruby/event_manager_test.rb
256
275
  - test/iruby/jupyter_test.rb
257
276
  - test/iruby/kernel_test.rb
@@ -262,4 +281,5 @@ test_files:
262
281
  - test/iruby/session_adapter/session_adapter_test_base.rb
263
282
  - test/iruby/session_adapter_test.rb
264
283
  - test/iruby/session_test.rb
284
+ - test/iruby/utils_test.rb
265
285
  - test/run-test.rb
@@ -1,62 +0,0 @@
1
- name: CI
2
-
3
- on:
4
- - push
5
- - pull_request
6
-
7
- jobs:
8
- test:
9
- name: Test
10
- runs-on: ${{ matrix.os }}
11
-
12
- strategy:
13
- fail-fast: false
14
- matrix:
15
- os:
16
- - ubuntu-20.04
17
- - ubuntu-latest
18
- ruby:
19
- - 3.0
20
- - 2.7
21
- - 2.6
22
- - 2.5
23
- - 2.4
24
- - 2.3
25
- - debug
26
-
27
- steps:
28
- - uses: actions/checkout@v2
29
- with:
30
- fetch-depth: 1
31
-
32
- - uses: ruby/setup-ruby@v1
33
- with:
34
- ruby-version: ${{ matrix.ruby }}
35
-
36
- - name: Install requirements on ubuntu
37
- run: |
38
- sudo apt install -y --no-install-recommends \
39
- libczmq-dev \
40
- python3 \
41
- python3-pip \
42
- python3-setuptools
43
- sudo pip3 install wheel
44
- sudo pip3 install -r ci/requirements.txt
45
-
46
- - run: gem install bundler
47
-
48
- - run: bundle install --jobs 4 --retry 3
49
-
50
- - name: Run tests
51
- env:
52
- PYTHON: python3
53
- ADAPTERS: cztop ffi-rzmq pyzmq
54
- run: |
55
- for adapter in $ADAPTERS; do
56
- export IRUBY_TEST_SESSION_ADAPTER_NAME=$adapter
57
- bundle exec rake test TESTOPTS=-v
58
- done
59
-
60
- - run: rake build
61
-
62
- - run: gem install pkg/*.gem
@@ -1,77 +0,0 @@
1
- module IRuby
2
- module SessionAdapter
3
- class PyzmqAdapter < BaseAdapter
4
-
5
- class << self
6
- def load_requirements
7
- require 'pycall'
8
- import_pyzmq
9
- end
10
-
11
- def import_pyzmq
12
- @zmq = PyCall.import_module('zmq')
13
- rescue PyCall::PyError => error
14
- raise LoadError, error.message
15
- end
16
-
17
- attr_reader :zmq
18
- end
19
-
20
- def make_router_socket(protocol, host, port)
21
- make_socket(:ROUTER, protocol, host, port)
22
- end
23
-
24
- def make_pub_socket(protocol, host, port)
25
- make_socket(:PUB, protocol, host, port)
26
- end
27
-
28
- def heartbeat_loop(sock)
29
- PyCall.sys.path.append(File.expand_path('../pyzmq', __FILE__))
30
- heartbeat = PyCall.import_module('iruby.heartbeat')
31
- @heartbeat_thread = heartbeat.Heartbeat.new(sock)
32
- @heartbeat_thread.start
33
- end
34
-
35
- private
36
-
37
- def socket_type(type_symbol)
38
- case type_symbol
39
- when :ROUTER, :PUB, :REP
40
- zmq[type_symbol]
41
- else
42
- raise ArgumentError, "Unknown ZMQ socket type: #{type_symbol}"
43
- end
44
- end
45
-
46
- def make_socket(type_symbol, protocol, host, port)
47
- type = socket_type(type_symbol)
48
- sock = zmq_context.socket(type)
49
- bind_socket(sock, protocol, host, port)
50
- sock
51
- end
52
-
53
- def bind_socket(sock, protocol, host, port)
54
- iface = "#{protocol}://#{host}"
55
- case protocol
56
- when 'tcp'
57
- if port <= 0
58
- port = sock.bind_to_random_port(iface)
59
- else
60
- sock.bind("#{iface}:#{port}")
61
- end
62
- else
63
- raise ArgumentError, "Unsupported protocol: #{protocol}"
64
- end
65
- [sock, port]
66
- end
67
-
68
- def zmq_context
69
- zmq.Context.instance
70
- end
71
-
72
- def zmq
73
- self.class.zmq
74
- end
75
- end
76
- end
77
- end