iruby 0.2.8 → 0.2.9
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 +4 -4
- data/CHANGES +17 -0
- data/README.md +29 -9
- data/iruby.gemspec +1 -0
- data/lib/iruby.rb +1 -0
- data/lib/iruby/backend.rb +5 -0
- data/lib/iruby/display.rb +4 -0
- data/lib/iruby/formatter.rb +2 -1
- data/lib/iruby/input.rb +41 -0
- data/lib/iruby/input/README.ipynb +450 -0
- data/lib/iruby/input/autoload.rb +24 -0
- data/lib/iruby/input/builder.rb +67 -0
- data/lib/iruby/input/button.rb +47 -0
- data/lib/iruby/input/cancel.rb +32 -0
- data/lib/iruby/input/checkbox.rb +56 -0
- data/lib/iruby/input/date.rb +26 -0
- data/lib/iruby/input/field.rb +30 -0
- data/lib/iruby/input/file.rb +57 -0
- data/lib/iruby/input/form.rb +77 -0
- data/lib/iruby/input/label.rb +21 -0
- data/lib/iruby/input/popup.rb +38 -0
- data/lib/iruby/input/radio.rb +47 -0
- data/lib/iruby/input/select.rb +45 -0
- data/lib/iruby/input/textarea.rb +22 -0
- data/lib/iruby/input/widget.rb +34 -0
- data/lib/iruby/session/ffi_rzmq.rb +14 -1
- data/lib/iruby/session/rbczmq.rb +11 -1
- data/lib/iruby/version.rb +1 -1
- metadata +35 -3
@@ -0,0 +1,24 @@
|
|
1
|
+
begin
|
2
|
+
require 'erector'
|
3
|
+
rescue LoadError
|
4
|
+
raise LoadError, <<-ERROR.gsub(/\s+/,' ')
|
5
|
+
IRuby::Input requires the erector gem.
|
6
|
+
`gem install erector` or add `gem 'erector'`
|
7
|
+
it to your Gemfile to continue.
|
8
|
+
ERROR
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'iruby/input/builder'
|
12
|
+
require 'iruby/input/widget'
|
13
|
+
require 'iruby/input/form'
|
14
|
+
require 'iruby/input/label'
|
15
|
+
require 'iruby/input/field'
|
16
|
+
require 'iruby/input/popup'
|
17
|
+
require 'iruby/input/button'
|
18
|
+
require 'iruby/input/cancel'
|
19
|
+
require 'iruby/input/file'
|
20
|
+
require 'iruby/input/select'
|
21
|
+
require 'iruby/input/checkbox'
|
22
|
+
require 'iruby/input/radio'
|
23
|
+
require 'iruby/input/textarea'
|
24
|
+
require 'iruby/input/date'
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module IRuby
|
2
|
+
module Input
|
3
|
+
class Builder
|
4
|
+
attr_reader :fields, :buttons
|
5
|
+
|
6
|
+
def initialize &block
|
7
|
+
@processors = {}
|
8
|
+
@fields, @buttons = [], []
|
9
|
+
instance_eval &block
|
10
|
+
end
|
11
|
+
|
12
|
+
def add_field field
|
13
|
+
@fields << field
|
14
|
+
end
|
15
|
+
|
16
|
+
def add_button button
|
17
|
+
# see bit.ly/1Tsv6x4
|
18
|
+
@buttons.unshift button
|
19
|
+
end
|
20
|
+
|
21
|
+
def html &block
|
22
|
+
add_field Class.new(Widget) {
|
23
|
+
define_method(:widget_html) { instance_eval &block }
|
24
|
+
}.new
|
25
|
+
end
|
26
|
+
|
27
|
+
def text string
|
28
|
+
html { label string }
|
29
|
+
end
|
30
|
+
|
31
|
+
def password key='password', **params
|
32
|
+
input key, **params.merge(type: 'password')
|
33
|
+
end
|
34
|
+
|
35
|
+
def process_result result
|
36
|
+
unless result.nil?
|
37
|
+
result.each_with_object({}) do |(k,v),h|
|
38
|
+
if @processors.has_key? k
|
39
|
+
@processors[k].call h, k, v
|
40
|
+
else
|
41
|
+
h[k.to_sym] = v
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def process key, &block
|
50
|
+
@processors[key.to_s] = block
|
51
|
+
end
|
52
|
+
|
53
|
+
def unique_key key
|
54
|
+
@keys ||= []
|
55
|
+
|
56
|
+
if @keys.include? key
|
57
|
+
(2..Float::INFINITY).each do |i|
|
58
|
+
test = "#{key}#{i}"
|
59
|
+
break key = test unless @keys.include? test
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
@keys << key; key
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module IRuby
|
2
|
+
module Input
|
3
|
+
# extend the label class for the to_label helper
|
4
|
+
class Button < Label
|
5
|
+
needs color: :blue, js_class: 'iruby-button'
|
6
|
+
|
7
|
+
COLORS = {
|
8
|
+
blue: 'primary',
|
9
|
+
gray: 'secondary',
|
10
|
+
green: 'success',
|
11
|
+
aqua: 'info',
|
12
|
+
orange: 'warning',
|
13
|
+
red: 'danger',
|
14
|
+
none: 'link'
|
15
|
+
}
|
16
|
+
|
17
|
+
COLORS.default = 'primary'
|
18
|
+
|
19
|
+
builder :button do |key='done', **params|
|
20
|
+
params[:key] = unique_key(key)
|
21
|
+
add_button Button.new(**params)
|
22
|
+
end
|
23
|
+
|
24
|
+
def widget_css
|
25
|
+
".#{@js_class} { margin-left: 5px; }"
|
26
|
+
end
|
27
|
+
|
28
|
+
def widget_js
|
29
|
+
<<-JS
|
30
|
+
$('.iruby-button').click(function(){
|
31
|
+
$(this).data('iruby-value', true);
|
32
|
+
$('#iruby-form').submit();
|
33
|
+
});
|
34
|
+
JS
|
35
|
+
end
|
36
|
+
|
37
|
+
def widget_html
|
38
|
+
button(
|
39
|
+
@label || to_label(@key),
|
40
|
+
type: 'button',
|
41
|
+
:'data-iruby-key' => @key,
|
42
|
+
class: "btn btn-#{COLORS[@color]} pull-right #{@js_class}"
|
43
|
+
)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module IRuby
|
2
|
+
module Input
|
3
|
+
class Cancel < Widget
|
4
|
+
needs :label
|
5
|
+
|
6
|
+
builder :cancel do |label='Cancel'|
|
7
|
+
add_button Cancel.new(label: label)
|
8
|
+
end
|
9
|
+
|
10
|
+
def widget_css
|
11
|
+
".iruby-cancel { margin-left: 5px; }"
|
12
|
+
end
|
13
|
+
|
14
|
+
def widget_js
|
15
|
+
<<-JS
|
16
|
+
$('.iruby-cancel').click(function(){
|
17
|
+
$('#iruby-form').remove();
|
18
|
+
});
|
19
|
+
JS
|
20
|
+
end
|
21
|
+
|
22
|
+
def widget_html
|
23
|
+
button(
|
24
|
+
@label,
|
25
|
+
type: 'button',
|
26
|
+
:'data-dismiss' => 'modal',
|
27
|
+
class: "btn btn-danger pull-right iruby-cancel"
|
28
|
+
)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module IRuby
|
2
|
+
module Input
|
3
|
+
class Checkbox < Label
|
4
|
+
needs :options
|
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
|
+
add_field Checkbox.new(**params)
|
13
|
+
end
|
14
|
+
|
15
|
+
def widget_css
|
16
|
+
'.iruby-checkbox-container { margin-left: 10px; }'
|
17
|
+
end
|
18
|
+
|
19
|
+
def widget_js
|
20
|
+
<<-JS
|
21
|
+
$('.iruby-checkbox input').change(function(){
|
22
|
+
var parent = $(this).closest('.iruby-checkbox');
|
23
|
+
$(parent).data('iruby-value', []);
|
24
|
+
|
25
|
+
$(parent).find(':checked').each(function(){
|
26
|
+
$(parent).data('iruby-value').push($(this).val());
|
27
|
+
});
|
28
|
+
|
29
|
+
if ($(parent).data('iruby-value').length == 0) {
|
30
|
+
$(parent).data('iruby-value', null);
|
31
|
+
}
|
32
|
+
});
|
33
|
+
JS
|
34
|
+
end
|
35
|
+
|
36
|
+
def widget_html
|
37
|
+
params = {
|
38
|
+
:'data-iruby-key' => @key,
|
39
|
+
class: 'iruby-checkbox form-control'
|
40
|
+
}
|
41
|
+
widget_label do
|
42
|
+
div **params do
|
43
|
+
@options.each do |option|
|
44
|
+
label class: 'checkbox-inline' do
|
45
|
+
input(
|
46
|
+
name: @key, value: option, type: 'checkbox'
|
47
|
+
)
|
48
|
+
text option
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module IRuby
|
2
|
+
module Input
|
3
|
+
class Date < Field
|
4
|
+
needs js_class: 'iruby-date'
|
5
|
+
|
6
|
+
builder :date do |key='date', **params|
|
7
|
+
params[:key] = unique_key key
|
8
|
+
add_field Date.new(**params)
|
9
|
+
end
|
10
|
+
|
11
|
+
def widget_css
|
12
|
+
'#ui-datepicker-div { z-index: 2000; }'
|
13
|
+
end
|
14
|
+
|
15
|
+
def widget_js
|
16
|
+
<<-JS
|
17
|
+
$('.iruby-date').datepicker({
|
18
|
+
onClose: function(date) {
|
19
|
+
$('.iruby-date').data('iruby-value', date);
|
20
|
+
}
|
21
|
+
});
|
22
|
+
JS
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module IRuby
|
2
|
+
module Input
|
3
|
+
class Field < Label
|
4
|
+
needs :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
|
+
)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
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,21 @@
|
|
1
|
+
module IRuby
|
2
|
+
module Input
|
3
|
+
class Label < Widget
|
4
|
+
needs label: nil
|
5
|
+
|
6
|
+
def widget_label
|
7
|
+
label = @label || to_label(@key)
|
8
|
+
div class: 'iruby-label input-group' do
|
9
|
+
span label, class: 'input-group-addon'
|
10
|
+
yield
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def to_label label
|
17
|
+
label.to_s.tr('_',' ').capitalize
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|