iruby 0.3 → 0.7.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.md +203 -0
  4. data/Gemfile +3 -1
  5. data/LICENSE +1 -1
  6. data/README.md +137 -87
  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 +14 -18
  14. data/lib/iruby.rb +14 -8
  15. data/lib/iruby/backend.rb +38 -10
  16. data/lib/iruby/command.rb +67 -15
  17. data/lib/iruby/display.rb +77 -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.md +299 -0
  22. data/lib/iruby/input/autoload.rb +1 -1
  23. data/lib/iruby/input/builder.rb +4 -4
  24. data/lib/iruby/input/button.rb +2 -2
  25. data/lib/iruby/input/cancel.rb +1 -1
  26. data/lib/iruby/input/checkbox.rb +3 -3
  27. data/lib/iruby/input/date.rb +3 -3
  28. data/lib/iruby/input/field.rb +2 -2
  29. data/lib/iruby/input/file.rb +3 -3
  30. data/lib/iruby/input/form.rb +6 -6
  31. data/lib/iruby/input/label.rb +4 -4
  32. data/lib/iruby/input/multiple.rb +10 -10
  33. data/lib/iruby/input/popup.rb +2 -2
  34. data/lib/iruby/input/radio.rb +6 -6
  35. data/lib/iruby/input/select.rb +8 -8
  36. data/lib/iruby/input/textarea.rb +1 -1
  37. data/lib/iruby/input/widget.rb +2 -2
  38. data/lib/iruby/jupyter.rb +77 -0
  39. data/lib/iruby/kernel.rb +204 -36
  40. data/lib/iruby/ostream.rb +29 -8
  41. data/lib/iruby/session.rb +117 -0
  42. data/lib/iruby/session/cztop.rb +4 -0
  43. data/lib/iruby/session_adapter.rb +72 -0
  44. data/lib/iruby/session_adapter/cztop_adapter.rb +45 -0
  45. data/lib/iruby/session_adapter/ffirzmq_adapter.rb +55 -0
  46. data/lib/iruby/session_adapter/pyzmq_adapter.rb +77 -0
  47. data/lib/iruby/session_adapter/test_adapter.rb +49 -0
  48. data/lib/iruby/utils.rb +13 -2
  49. data/lib/iruby/version.rb +1 -1
  50. data/run-test.sh +12 -0
  51. data/tasks/ci.rake +65 -0
  52. data/test/helper.rb +136 -0
  53. data/test/integration_test.rb +22 -11
  54. data/test/iruby/backend_test.rb +37 -0
  55. data/test/iruby/command_test.rb +207 -0
  56. data/test/iruby/event_manager_test.rb +92 -0
  57. data/test/iruby/jupyter_test.rb +27 -0
  58. data/test/iruby/kernel_test.rb +185 -0
  59. data/test/iruby/mime_test.rb +50 -0
  60. data/test/iruby/multi_logger_test.rb +1 -5
  61. data/test/iruby/session_adapter/cztop_adapter_test.rb +20 -0
  62. data/test/iruby/session_adapter/ffirzmq_adapter_test.rb +20 -0
  63. data/test/iruby/session_adapter/session_adapter_test_base.rb +27 -0
  64. data/test/iruby/session_adapter_test.rb +91 -0
  65. data/test/iruby/session_test.rb +48 -0
  66. data/test/run-test.rb +19 -0
  67. metadata +120 -50
  68. data/.travis.yml +0 -16
  69. data/CHANGES +0 -143
  70. data/CONTRIBUTORS +0 -19
  71. data/lib/iruby/session/rbczmq.rb +0 -68
  72. data/test/test_helper.rb +0 -5
@@ -36,6 +36,10 @@ module IRuby
36
36
  end
37
37
  end
38
38
 
39
+ def description
40
+ 'old-stle session using cztop'
41
+ end
42
+
39
43
  # Build and send a message
40
44
  def send(socket, type, content)
41
45
  idents =
@@ -0,0 +1,72 @@
1
+ module IRuby
2
+ class SessionAdapterNotFound < RuntimeError; end
3
+
4
+ module SessionAdapter
5
+ class BaseAdapter
6
+ def self.available?
7
+ load_requirements
8
+ true
9
+ rescue LoadError
10
+ false
11
+ end
12
+
13
+ def self.load_requirements
14
+ # Do nothing
15
+ end
16
+
17
+ def initialize(config)
18
+ @config = config
19
+ end
20
+
21
+ def name
22
+ self.class.name[/::(\w+)Adapter\Z/, 1].downcase
23
+ end
24
+
25
+ def make_router_socket(protocol, host, port)
26
+ socket, port = make_socket(:ROUTER, protocol, host, port)
27
+ [socket, port]
28
+ end
29
+
30
+ def make_pub_socket(protocol, host, port)
31
+ socket, port = make_socket(:PUB, protocol, host, port)
32
+ [socket, port]
33
+ end
34
+
35
+ def make_rep_socket(protocol, host, port)
36
+ socket, port = make_socket(:REP, protocol, host, port)
37
+ [socket, port]
38
+ end
39
+ end
40
+
41
+ require_relative 'session_adapter/ffirzmq_adapter'
42
+ require_relative 'session_adapter/cztop_adapter'
43
+ require_relative 'session_adapter/pyzmq_adapter'
44
+ require_relative 'session_adapter/test_adapter'
45
+
46
+ def self.select_adapter_class(name=nil)
47
+ classes = {
48
+ 'ffi-rzmq' => SessionAdapter::FfirzmqAdapter,
49
+ 'cztop' => SessionAdapter::CztopAdapter,
50
+ # 'pyzmq' => SessionAdapter::PyzmqAdapter
51
+ 'test' => SessionAdapter::TestAdapter,
52
+ }
53
+ if (name ||= ENV.fetch('IRUBY_SESSION_ADAPTER', nil))
54
+ cls = classes[name]
55
+ unless cls.available?
56
+ if ENV['IRUBY_SESSION_ADAPTER']
57
+ raise SessionAdapterNotFound,
58
+ "Session adapter `#{name}` from IRUBY_SESSION_ADAPTER is unavailable"
59
+ else
60
+ raise SessionAdapterNotFound,
61
+ "Session adapter `#{name}` is unavailable"
62
+ end
63
+ end
64
+ return cls
65
+ end
66
+ classes.each_value do |cls|
67
+ return cls if cls.available?
68
+ end
69
+ raise SessionAdapterNotFound, "No session adapter is available"
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,45 @@
1
+ module IRuby
2
+ module SessionAdapter
3
+ class CztopAdapter < BaseAdapter
4
+ def self.load_requirements
5
+ require 'cztop'
6
+ end
7
+
8
+ def send(sock, data)
9
+ sock << data
10
+ end
11
+
12
+ def recv(sock)
13
+ sock.receive
14
+ end
15
+
16
+ def heartbeat_loop(sock)
17
+ loop do
18
+ message = sock.receive
19
+ sock << message
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def socket_type_class(type_symbol)
26
+ case type_symbol
27
+ when :ROUTER, :PUB, :REP
28
+ CZTop::Socket.const_get(type_symbol)
29
+ else
30
+ if CZTop::Socket.const_defined?(type_symbol)
31
+ raise ArgumentError, "Unsupported ZMQ socket type: #{type_symbol}"
32
+ else
33
+ raise ArgumentError, "Invalid ZMQ socket type: #{type_symbol}"
34
+ end
35
+ end
36
+ end
37
+
38
+ def make_socket(type_symbol, protocol, host, port)
39
+ uri = "#{protocol}://#{host}:#{port}"
40
+ socket_class = socket_type_class(type_symbol)
41
+ socket_class.new(uri)
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,55 @@
1
+ module IRuby
2
+ module SessionAdapter
3
+ class FfirzmqAdapter < BaseAdapter
4
+ def self.load_requirements
5
+ require 'ffi-rzmq'
6
+ end
7
+
8
+ def send(sock, data)
9
+ data.each_with_index do |part, i|
10
+ sock.send_string(part, i == data.size - 1 ? 0 : ZMQ::SNDMORE)
11
+ end
12
+ end
13
+
14
+ def recv(sock)
15
+ msg = []
16
+ while msg.empty? || sock.more_parts?
17
+ begin
18
+ frame = ''
19
+ rc = sock.recv_string(frame)
20
+ ZMQ::Util.error_check('zmq_msg_recv', rc)
21
+ msg << frame
22
+ rescue
23
+ end
24
+ end
25
+ msg
26
+ end
27
+
28
+ def heartbeat_loop(sock)
29
+ @heartbeat_device = ZMQ::Device.new(sock, sock)
30
+ end
31
+
32
+ private
33
+
34
+ def make_socket(type, protocol, host, port)
35
+ case type
36
+ when :ROUTER, :PUB, :REP
37
+ type = ZMQ.const_get(type)
38
+ else
39
+ if ZMQ.const_defined?(type)
40
+ raise ArgumentError, "Unsupported ZMQ socket type: #{type_symbol}"
41
+ else
42
+ raise ArgumentError, "Invalid ZMQ socket type: #{type_symbol}"
43
+ end
44
+ end
45
+ zmq_context.socket(type).tap do |sock|
46
+ sock.bind("#{protocol}://#{host}:#{port}")
47
+ end
48
+ end
49
+
50
+ def zmq_context
51
+ @zmq_context ||= ZMQ::Context.new
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,77 @@
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
@@ -0,0 +1,49 @@
1
+ require 'iruby/session/mixin'
2
+
3
+ module IRuby
4
+ module SessionAdapter
5
+ class TestAdapter < BaseAdapter
6
+ include IRuby::SessionSerialize
7
+
8
+ DummySocket = Struct.new(:type, :protocol, :host, :port)
9
+
10
+ def initialize(config)
11
+ super
12
+
13
+ unless config['key'].empty? || config['signature_scheme'].empty?
14
+ unless config['signature_scheme'] =~ /\Ahmac-/
15
+ raise "Unknown signature_scheme: #{config['signature_scheme']}"
16
+ end
17
+ digest_algorithm = config['signature_scheme'][/\Ahmac-(.*)\Z/, 1]
18
+ @hmac = OpenSSL::HMAC.new(config['key'], OpenSSL::Digest.new(digest_algorithm))
19
+ end
20
+
21
+ @send_callback = nil
22
+ @recv_callback = nil
23
+ end
24
+
25
+ attr_accessor :send_callback, :recv_callback
26
+
27
+ def send(sock, data)
28
+ unless @send_callback.nil?
29
+ @send_callback.call(sock, unserialize(data))
30
+ end
31
+ end
32
+
33
+ def recv(sock)
34
+ unless @recv_callback.nil?
35
+ serialize(@recv_callback.call(sock))
36
+ end
37
+ end
38
+
39
+ def heartbeat_loop(sock)
40
+ end
41
+
42
+ private
43
+
44
+ def make_socket(type, protocol, host, port)
45
+ DummySocket.new(type, protocol, host, port)
46
+ end
47
+ end
48
+ end
49
+ end
data/lib/iruby/utils.rb CHANGED
@@ -4,34 +4,45 @@ module IRuby
4
4
  Display.convert(object, options)
5
5
  end
6
6
 
7
+ # Display the object
7
8
  def display(obj, options = {})
8
9
  Kernel.instance.session.send(:publish, :display_data,
9
10
  data: Display.display(obj, options),
10
- metadata: {},
11
- source: 'ruby') unless obj.nil?
11
+ metadata: {}) unless obj.nil?
12
12
  end
13
13
 
14
+ # Clear the output area
15
+ def clear_output(wait=false)
16
+ Display.clear_output(wait)
17
+ end
18
+
19
+ # Format the given object into HTML table
14
20
  def table(s, **options)
15
21
  html(HTML.table(s, options))
16
22
  end
17
23
 
24
+ # Treat the given string as LaTeX text
18
25
  def latex(s)
19
26
  convert(s, mime: 'text/latex')
20
27
  end
21
28
  alias tex latex
22
29
 
30
+ # Format the given string of TeX equation into LaTeX text
23
31
  def math(s)
24
32
  convert("$$#{s}$$", mime: 'text/latex')
25
33
  end
26
34
 
35
+ # Treat the given string as HTML
27
36
  def html(s)
28
37
  convert(s, mime: 'text/html')
29
38
  end
30
39
 
40
+ # Treat the given string as JavaScript code
31
41
  def javascript(s)
32
42
  convert(s, mime: 'application/javascript')
33
43
  end
34
44
 
45
+ # Treat the given string as SVG text
35
46
  def svg(s)
36
47
  convert(s, mime: 'image/svg+xml')
37
48
  end
data/lib/iruby/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module IRuby
2
- VERSION = '0.3'
2
+ VERSION = '0.7.0'
3
3
  end
data/run-test.sh ADDED
@@ -0,0 +1,12 @@
1
+ #! /bin/bash
2
+
3
+ set -ex
4
+
5
+ export PYTHON=python3
6
+
7
+ ADAPTERS="cztop ffi-rzmq pyzmq"
8
+
9
+ for adapter in $ADAPTERS; do
10
+ export IRUBY_TEST_SESSION_ADAPTER_NAME=$adapter
11
+ bundle exec rake test TESTOPTS=-v
12
+ done
data/tasks/ci.rake ADDED
@@ -0,0 +1,65 @@
1
+ namespace :ci do
2
+ namespace :docker do
3
+ def ruby_version
4
+ @ruby_version ||= ENV['ruby_version']
5
+ end
6
+
7
+ def ruby_image_name
8
+ @ruby_image_name ||= "rubylang/ruby:#{ruby_version}-bionic"
9
+ end
10
+
11
+ def iruby_test_base_image_name
12
+ @iruby_test_base_image_name ||= "iruby-test-base:ruby-#{ruby_version}"
13
+ end
14
+
15
+ def iruby_test_image_name
16
+ @docker_image_name ||= begin
17
+ "sciruby/iruby-test:ruby-#{ruby_version}"
18
+ end
19
+ end
20
+
21
+ def docker_image_found?(image_name)
22
+ image_id = `docker images -q #{image_name}`.chomp
23
+ image_id.length > 0
24
+ end
25
+
26
+ directory 'tmp'
27
+
28
+ desc "Build iruby-test-base docker image"
29
+ task :build_test_base_image => 'tmp' do
30
+ unless docker_image_found?(iruby_test_base_image_name)
31
+ require 'erb'
32
+ dockerfile_content = ERB.new(File.read('ci/Dockerfile.base.erb')).result(binding)
33
+ File.write('tmp/Dockerfile', dockerfile_content)
34
+ sh 'docker', 'build', '-t', iruby_test_base_image_name, '-f', 'tmp/Dockerfile', '.'
35
+ end
36
+ end
37
+
38
+ desc "Pull docker image of ruby"
39
+ task :pull_ruby_image do
40
+ sh 'docker', 'pull', ruby_image_name
41
+ end
42
+
43
+ desc "Build iruby-test docker image"
44
+ task :build_test_image => 'tmp' do
45
+ require 'erb'
46
+ dockerfile_content = ERB.new(File.read('ci/Dockerfile.main.erb')).result(binding)
47
+ File.write('tmp/Dockerfile', dockerfile_content)
48
+ sh 'docker', 'build', '-t', iruby_test_image_name, '-f', 'tmp/Dockerfile', '.'
49
+ end
50
+
51
+ desc 'before_install script for CI with Docker'
52
+ task :before_install => :pull_ruby_image
53
+ task :before_install => :build_test_base_image
54
+
55
+ desc 'install script for CI with Docker'
56
+ task :install => :build_test_image
57
+
58
+ desc 'main script for CI with Docker'
59
+ task :script do
60
+ volumes = ['-v', "#{Dir.pwd}:/iruby"] if ENV['attach_pwd']
61
+ sh 'docker', 'run', '--rm', '-it', *volumes,
62
+ iruby_test_image_name, 'bash', 'run-test.sh'
63
+ end
64
+ end
65
+ end