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.
- checksums.yaml +5 -5
- data/.github/workflows/ubuntu.yml +62 -0
- data/CHANGES.md +203 -0
- data/Gemfile +3 -1
- data/LICENSE +1 -1
- data/README.md +137 -87
- data/Rakefile +36 -10
- data/ci/Dockerfile.base.erb +41 -0
- data/ci/Dockerfile.main.erb +7 -0
- data/ci/requirements.txt +1 -0
- data/docker/setup.sh +15 -0
- data/docker/test.sh +7 -0
- data/iruby.gemspec +14 -18
- data/lib/iruby.rb +14 -8
- data/lib/iruby/backend.rb +38 -10
- data/lib/iruby/command.rb +67 -15
- data/lib/iruby/display.rb +77 -41
- data/lib/iruby/event_manager.rb +40 -0
- data/lib/iruby/formatter.rb +3 -3
- data/lib/iruby/input.rb +6 -6
- data/lib/iruby/input/README.md +299 -0
- data/lib/iruby/input/autoload.rb +1 -1
- data/lib/iruby/input/builder.rb +4 -4
- data/lib/iruby/input/button.rb +2 -2
- data/lib/iruby/input/cancel.rb +1 -1
- data/lib/iruby/input/checkbox.rb +3 -3
- data/lib/iruby/input/date.rb +3 -3
- data/lib/iruby/input/field.rb +2 -2
- data/lib/iruby/input/file.rb +3 -3
- data/lib/iruby/input/form.rb +6 -6
- data/lib/iruby/input/label.rb +4 -4
- data/lib/iruby/input/multiple.rb +10 -10
- data/lib/iruby/input/popup.rb +2 -2
- data/lib/iruby/input/radio.rb +6 -6
- data/lib/iruby/input/select.rb +8 -8
- data/lib/iruby/input/textarea.rb +1 -1
- data/lib/iruby/input/widget.rb +2 -2
- data/lib/iruby/jupyter.rb +77 -0
- data/lib/iruby/kernel.rb +204 -36
- data/lib/iruby/ostream.rb +29 -8
- data/lib/iruby/session.rb +117 -0
- data/lib/iruby/session/cztop.rb +4 -0
- data/lib/iruby/session_adapter.rb +72 -0
- data/lib/iruby/session_adapter/cztop_adapter.rb +45 -0
- data/lib/iruby/session_adapter/ffirzmq_adapter.rb +55 -0
- data/lib/iruby/session_adapter/pyzmq_adapter.rb +77 -0
- data/lib/iruby/session_adapter/test_adapter.rb +49 -0
- data/lib/iruby/utils.rb +13 -2
- data/lib/iruby/version.rb +1 -1
- data/run-test.sh +12 -0
- data/tasks/ci.rake +65 -0
- data/test/helper.rb +136 -0
- data/test/integration_test.rb +22 -11
- data/test/iruby/backend_test.rb +37 -0
- data/test/iruby/command_test.rb +207 -0
- data/test/iruby/event_manager_test.rb +92 -0
- data/test/iruby/jupyter_test.rb +27 -0
- data/test/iruby/kernel_test.rb +185 -0
- data/test/iruby/mime_test.rb +50 -0
- data/test/iruby/multi_logger_test.rb +1 -5
- data/test/iruby/session_adapter/cztop_adapter_test.rb +20 -0
- data/test/iruby/session_adapter/ffirzmq_adapter_test.rb +20 -0
- data/test/iruby/session_adapter/session_adapter_test_base.rb +27 -0
- data/test/iruby/session_adapter_test.rb +91 -0
- data/test/iruby/session_test.rb +48 -0
- data/test/run-test.rb +19 -0
- metadata +120 -50
- data/.travis.yml +0 -16
- data/CHANGES +0 -143
- data/CONTRIBUTORS +0 -19
- data/lib/iruby/session/rbczmq.rb +0 -68
- data/test/test_helper.rb +0 -5
data/lib/iruby/session/cztop.rb
CHANGED
@@ -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
data/run-test.sh
ADDED
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
|