iruby 0.2.8 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ubuntu.yml +62 -0
  3. data/CHANGES +76 -0
  4. data/Gemfile +3 -1
  5. data/LICENSE +1 -1
  6. data/README.md +130 -82
  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 -17
  14. data/lib/iruby.rb +14 -6
  15. data/lib/iruby/backend.rb +41 -7
  16. data/lib/iruby/command.rb +68 -12
  17. data/lib/iruby/display.rb +80 -39
  18. data/lib/iruby/event_manager.rb +40 -0
  19. data/lib/iruby/formatter.rb +5 -4
  20. data/lib/iruby/input.rb +41 -0
  21. data/lib/iruby/input/README.ipynb +502 -0
  22. data/lib/iruby/input/README.md +299 -0
  23. data/lib/iruby/input/autoload.rb +25 -0
  24. data/lib/iruby/input/builder.rb +67 -0
  25. data/lib/iruby/input/button.rb +47 -0
  26. data/lib/iruby/input/cancel.rb +32 -0
  27. data/lib/iruby/input/checkbox.rb +74 -0
  28. data/lib/iruby/input/date.rb +37 -0
  29. data/lib/iruby/input/field.rb +31 -0
  30. data/lib/iruby/input/file.rb +57 -0
  31. data/lib/iruby/input/form.rb +77 -0
  32. data/lib/iruby/input/label.rb +27 -0
  33. data/lib/iruby/input/multiple.rb +76 -0
  34. data/lib/iruby/input/popup.rb +41 -0
  35. data/lib/iruby/input/radio.rb +59 -0
  36. data/lib/iruby/input/select.rb +59 -0
  37. data/lib/iruby/input/textarea.rb +23 -0
  38. data/lib/iruby/input/widget.rb +34 -0
  39. data/lib/iruby/jupyter.rb +77 -0
  40. data/lib/iruby/kernel.rb +106 -27
  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} +25 -13
  44. data/lib/iruby/session/ffi_rzmq.rb +15 -2
  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 +5 -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 +132 -43
  70. data/.travis.yml +0 -16
  71. data/CONTRIBUTORS +0 -19
  72. 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