iruby 0.2.7 → 0.5.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 (69) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ubuntu.yml +62 -0
  3. data/CHANGES +62 -0
  4. data/Gemfile +3 -1
  5. data/LICENSE +1 -1
  6. data/README.md +148 -27
  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 +19 -3
  15. data/lib/iruby/backend.rb +22 -2
  16. data/lib/iruby/command.rb +76 -13
  17. data/lib/iruby/display.rb +69 -39
  18. data/lib/iruby/formatter.rb +5 -4
  19. data/lib/iruby/input.rb +41 -0
  20. data/lib/iruby/input/README.ipynb +502 -0
  21. data/lib/iruby/input/README.md +299 -0
  22. data/lib/iruby/input/autoload.rb +25 -0
  23. data/lib/iruby/input/builder.rb +67 -0
  24. data/lib/iruby/input/button.rb +47 -0
  25. data/lib/iruby/input/cancel.rb +32 -0
  26. data/lib/iruby/input/checkbox.rb +74 -0
  27. data/lib/iruby/input/date.rb +37 -0
  28. data/lib/iruby/input/field.rb +31 -0
  29. data/lib/iruby/input/file.rb +57 -0
  30. data/lib/iruby/input/form.rb +77 -0
  31. data/lib/iruby/input/label.rb +27 -0
  32. data/lib/iruby/input/multiple.rb +76 -0
  33. data/lib/iruby/input/popup.rb +41 -0
  34. data/lib/iruby/input/radio.rb +59 -0
  35. data/lib/iruby/input/select.rb +59 -0
  36. data/lib/iruby/input/textarea.rb +23 -0
  37. data/lib/iruby/input/widget.rb +34 -0
  38. data/lib/iruby/jupyter.rb +77 -0
  39. data/lib/iruby/kernel.rb +67 -22
  40. data/lib/iruby/ostream.rb +24 -8
  41. data/lib/iruby/session.rb +85 -67
  42. data/lib/iruby/session/cztop.rb +70 -0
  43. data/lib/iruby/session/ffi_rzmq.rb +87 -0
  44. data/lib/iruby/session/mixin.rb +47 -0
  45. data/lib/iruby/session_adapter.rb +66 -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/utils.rb +5 -2
  50. data/lib/iruby/version.rb +1 -1
  51. data/run-test.sh +12 -0
  52. data/tasks/ci.rake +65 -0
  53. data/test/helper.rb +90 -0
  54. data/test/integration_test.rb +22 -11
  55. data/test/iruby/backend_test.rb +37 -0
  56. data/test/iruby/command_test.rb +207 -0
  57. data/test/iruby/jupyter_test.rb +27 -0
  58. data/test/iruby/mime_test.rb +32 -0
  59. data/test/iruby/multi_logger_test.rb +1 -2
  60. data/test/iruby/session_adapter/cztop_adapter_test.rb +20 -0
  61. data/test/iruby/session_adapter/ffirzmq_adapter_test.rb +20 -0
  62. data/test/iruby/session_adapter/session_adapter_test_base.rb +27 -0
  63. data/test/iruby/session_adapter_test.rb +91 -0
  64. data/test/iruby/session_test.rb +47 -0
  65. data/test/run-test.rb +18 -0
  66. metadata +130 -46
  67. data/.travis.yml +0 -16
  68. data/CONTRIBUTORS +0 -19
  69. data/test/test_helper.rb +0 -5
@@ -0,0 +1,74 @@
1
+ module IRuby
2
+ module Input
3
+ class Checkbox < Label
4
+ needs :options, :default
5
+
6
+ builder :checkbox do |*args, **params|
7
+ key = :checkbox
8
+ key, *args = args if args.first.is_a? Symbol
9
+
10
+ params[:key] = unique_key(key)
11
+ params[:options] = args
12
+
13
+ params[:default] = case params[:default]
14
+ when false, nil
15
+ []
16
+ when true
17
+ [*params[:options]]
18
+ else
19
+ [*params[:default]]
20
+ end
21
+
22
+ add_field Checkbox.new(**params)
23
+ end
24
+
25
+ def widget_css
26
+ <<-CSS
27
+ .iruby-checkbox.form-control { display: inline-table; }
28
+ .iruby-checkbox .checkbox-inline { margin: 0 15px 0 0; }
29
+ CSS
30
+ end
31
+
32
+ def widget_js
33
+ <<-JS
34
+ $('.iruby-checkbox input').change(function(){
35
+ var parent = $(this).closest('.iruby-checkbox');
36
+ $(parent).data('iruby-value', []);
37
+
38
+ $(parent).find(':checked').each(function(){
39
+ $(parent).data('iruby-value').push($(this).val());
40
+ });
41
+
42
+ if ($(parent).data('iruby-value').length == 0) {
43
+ $(parent).data('iruby-value', null);
44
+ }
45
+ });
46
+
47
+ $('.iruby-checkbox input').trigger('change');
48
+ JS
49
+ end
50
+
51
+ def widget_html
52
+ params = {
53
+ :'data-iruby-key' => @key,
54
+ class: 'iruby-checkbox form-control'
55
+ }
56
+ widget_label do
57
+ div **params do
58
+ @options.each do |option|
59
+ label class: 'checkbox-inline' do
60
+ input(
61
+ name: @key,
62
+ value: option,
63
+ type: 'checkbox',
64
+ checked: @default.include?(option)
65
+ )
66
+ text option
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,37 @@
1
+ module IRuby
2
+ module Input
3
+ class Date < Field
4
+ needs js_class: 'iruby-date', icon: '📅'
5
+
6
+ builder :date do |key='date', **params|
7
+ params[:default] ||= false
8
+ params[:key] = unique_key key
9
+
10
+ if params[:default].is_a? Time
11
+ params[:default] = params[:default].strftime('%m/%d/%Y')
12
+ end
13
+
14
+ add_field Date.new(**params)
15
+
16
+ process params[:key] do |result,key,value|
17
+ result[key.to_sym] = Time.strptime(value,'%m/%d/%Y')
18
+ end
19
+ end
20
+
21
+ def widget_css
22
+ '#ui-datepicker-div { z-index: 2000 !important; }'
23
+ end
24
+
25
+ def widget_js
26
+ <<-JS
27
+ $('.iruby-date').datepicker({
28
+ dateFormat: 'mm/dd/yy',
29
+ onClose: function(date) {
30
+ $(this).data('iruby-value', date);
31
+ }
32
+ });
33
+ JS
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,31 @@
1
+ module IRuby
2
+ module Input
3
+ class Field < Label
4
+ needs default: nil, type: 'text', js_class: 'iruby-field'
5
+
6
+ builder :input do |key='input', **params|
7
+ params[:key] = unique_key key
8
+ add_field Field.new(**params)
9
+ end
10
+
11
+ def widget_js
12
+ <<-JS
13
+ $('.iruby-field').keyup(function() {
14
+ $(this).data('iruby-value', $(this).val());
15
+ });
16
+ JS
17
+ end
18
+
19
+ def widget_html
20
+ widget_label do
21
+ input(
22
+ type: @type,
23
+ :'data-iruby-key' => @key,
24
+ class: "form-control #{@js_class}",
25
+ value: @default
26
+ )
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,57 @@
1
+ require 'data_uri'
2
+
3
+ module IRuby
4
+ module Input
5
+ class File < Label
6
+ builder :file do |key='file', **params|
7
+ key = unique_key key
8
+ add_field File.new(key: key, **params)
9
+
10
+ # tell the builder to process files differently
11
+ process key do |result,key,value|
12
+ uri = URI::Data.new value['data']
13
+
14
+ # get rid of Chrome's silly path
15
+ name = value['name'].sub('C:\\fakepath\\','')
16
+
17
+ result[key.to_sym] = {
18
+ name: name,
19
+ data: uri.data,
20
+ content_type: uri.content_type
21
+ }
22
+ end
23
+ end
24
+
25
+ def widget_js
26
+ <<-JS
27
+ $('.iruby-file').change(function() {
28
+ var input = $(this);
29
+
30
+ $.grep($(this).prop('files'), function(file) {
31
+ var reader = new FileReader();
32
+
33
+ reader.addEventListener("load", function(event) {
34
+ input.data('iruby-value', {
35
+ name: input.val(),
36
+ data: event.target.result
37
+ });
38
+ });
39
+
40
+ reader.readAsDataURL(file);
41
+ });
42
+ });
43
+ JS
44
+ end
45
+
46
+ def widget_html
47
+ widget_label do
48
+ input(
49
+ type: 'file',
50
+ :'data-iruby-key' => @key,
51
+ class: 'form-control iruby-file'
52
+ )
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,77 @@
1
+ require 'securerandom'
2
+
3
+ module IRuby
4
+ module Input
5
+ class InputForm < Widget
6
+ needs :fields, buttons: []
7
+
8
+ def widget_js
9
+ javascript = <<-JS
10
+ var remove = function () {
11
+ Jupyter.notebook.kernel.send_input_reply(
12
+ JSON.stringify({
13
+ '#{@id = SecureRandom.uuid}': null
14
+ })
15
+ );
16
+ };
17
+
18
+ $("#iruby-form").on("remove", remove);
19
+
20
+ $('#iruby-form').submit(function() {
21
+ var result = {};
22
+ $(this).off('remove', remove);
23
+
24
+ $('[data-iruby-key]').each(function() {
25
+ if ($(this).data('iruby-key')) {
26
+ var value = $(this).data('iruby-value');
27
+ if (value) {
28
+ result[$(this).data('iruby-key')] = value;
29
+ }
30
+ }
31
+ });
32
+
33
+ Jupyter.notebook.kernel.send_input_reply(
34
+ JSON.stringify({'#{@id}': result})
35
+ );
36
+
37
+ $(this).remove();
38
+ return false;
39
+ });
40
+
41
+ $('#iruby-form').keydown(function(event) {
42
+ if (event.keyCode == 13 && !event.shiftKey) {
43
+ $('#iruby-form').submit();
44
+ } else if (event.keyCode == 27) {
45
+ $('#iruby-form').remove();
46
+ }
47
+ });
48
+ JS
49
+
50
+ widget_join :widget_js, javascript, *@fields, *@buttons
51
+ end
52
+
53
+ def widget_css
54
+ spacing = '#iruby-form > * { margin-bottom: 5px; }'
55
+ widget_join :widget_css, spacing, *@fields, *@buttons
56
+ end
57
+
58
+ def widget_html
59
+ form id: 'iruby-form', class: 'col-md-12' do
60
+ @fields.each {|field| widget field}
61
+ end
62
+ @buttons.each {|button| widget button}
63
+ end
64
+
65
+ def submit
66
+ result = MultiJson.load(Kernel.instance.session.recv_input)
67
+
68
+ unless result.has_key? @id
69
+ submit
70
+ else
71
+ Display.clear_output
72
+ result[@id]
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,27 @@
1
+ module IRuby
2
+ module Input
3
+ class Label < Widget
4
+ needs label: nil, icon: nil
5
+
6
+ def widget_label
7
+ div class: 'iruby-label input-group' do
8
+ span class: 'input-group-addon' do
9
+ text @label || to_label(@key)
10
+ end
11
+
12
+ yield
13
+
14
+ if @icon
15
+ span @icon, class: "input-group-addon"
16
+ end
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def to_label label
23
+ label.to_s.tr('_',' ').capitalize
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,76 @@
1
+ module IRuby
2
+ module Input
3
+ class Multiple < Label
4
+ needs :options, :default, size: nil
5
+
6
+ builder :multiple do |*args, **params|
7
+ key = :multiple
8
+ key, *args = args if args.first.is_a? Symbol
9
+
10
+ params[:key] = unique_key(key)
11
+ params[:options] = args
12
+
13
+ params[:default] = case params[:default]
14
+ when false, nil
15
+ []
16
+ when true
17
+ [*params[:options]]
18
+ else
19
+ [*params[:default]]
20
+ end
21
+
22
+ add_field Multiple.new(**params)
23
+ end
24
+
25
+ def widget_css
26
+ <<-CSS
27
+ .iruby-multiple {
28
+ display: table;
29
+ min-width: 25%;
30
+ }
31
+ .form-control.iruby-multiple-container {
32
+ display: table;
33
+ }
34
+ CSS
35
+ end
36
+
37
+ def widget_js
38
+ <<-JS
39
+ $('.iruby-multiple').change(function(){
40
+ var multiple = $(this);
41
+ multiple.data('iruby-value', []);
42
+
43
+ multiple.find(':selected').each(function(){
44
+ multiple.data('iruby-value').push($(this).val());
45
+ });
46
+
47
+ if (multiple.data('iruby-value').length == 0) {
48
+ multiple.data('iruby-value', null);
49
+ }
50
+ });
51
+
52
+ $('.iruby-multiple').trigger('change');
53
+ JS
54
+ end
55
+
56
+ def widget_html
57
+ widget_label do
58
+ div class: 'form-control iruby-multiple-container' do
59
+ params = {
60
+ size: @size,
61
+ multiple: true,
62
+ class: 'iruby-multiple',
63
+ :'data-iruby-key' => @key
64
+ }
65
+
66
+ select **params do
67
+ @options.each do |o|
68
+ option o, selected: @default.include?(o)
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,41 @@
1
+ module IRuby
2
+ module Input
3
+ class Popup < Widget
4
+ needs :title, :form, buttons: []
5
+
6
+ def widget_css
7
+ style = '.modal-body { overflow: auto; }'
8
+ widget_join :widget_css, style, @form, *@buttons
9
+ end
10
+
11
+ def widget_js
12
+ js = <<-JS
13
+ require(['base/js/dialog'], function(dialog) {
14
+ var popup = dialog.modal({
15
+ title: '#{@title.gsub("'"){"\\'"}}',
16
+ body: '#{@form.to_html}',
17
+ destroy: true,
18
+ sanitize: false,
19
+ keyboard_manager: Jupyter.notebook.keyboard_manager,
20
+ open: function() {
21
+ #{widget_join :widget_js, @form, *@buttons}
22
+
23
+ var popup = $(this);
24
+
25
+ $('#iruby-form').submit(function() {
26
+ popup.modal('hide');
27
+ });
28
+
29
+ Jupyter.notebook.keyboard_manager.disable();
30
+ }
31
+ });
32
+
33
+ popup.find('.modal-footer').each(function(e) {
34
+ $(this).append('#{@buttons.map(&:to_html).join}');
35
+ });
36
+ });
37
+ JS
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,59 @@
1
+ module IRuby
2
+ module Input
3
+ class Radio < Label
4
+ needs :options, :default
5
+
6
+ builder :radio do |*args, **params|
7
+ key = :radio
8
+ key, *args = args if args.first.is_a? Symbol
9
+
10
+ params[:key] = unique_key(key)
11
+ params[:options] = args
12
+ params[:default] ||= false
13
+ add_field Radio.new(**params)
14
+ end
15
+
16
+ def widget_css
17
+ <<-CSS
18
+ .iruby-radio.form-control { display: inline-table; }
19
+ .iruby-radio .radio-inline { margin: 0 15px 0 0; }
20
+ CSS
21
+ end
22
+
23
+ def widget_js
24
+ <<-JS
25
+ $('.iruby-radio input').change(function(){
26
+ var parent = $(this).closest('.iruby-radio');
27
+ $(parent).data('iruby-value',
28
+ $(parent).find(':checked').val()
29
+ );
30
+ });
31
+ $('.iruby-radio input').trigger('change');
32
+ JS
33
+ end
34
+
35
+ def widget_html
36
+ params = {
37
+ :'data-iruby-key' => @key,
38
+ :'data-iruby-value' => @options.first,
39
+ class: 'iruby-radio form-control'
40
+ }
41
+ widget_label do
42
+ div **params do
43
+ @options.each do |option|
44
+ label class: 'radio-inline' do
45
+ input(
46
+ name: @key,
47
+ value: option,
48
+ type: 'radio',
49
+ checked: @default == option
50
+ )
51
+ text option
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end