iruby 0.3 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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/display.rb
CHANGED
@@ -1,17 +1,24 @@
|
|
1
|
+
require "set"
|
2
|
+
|
1
3
|
module IRuby
|
2
4
|
module Display
|
3
5
|
class << self
|
6
|
+
# @private
|
4
7
|
def convert(obj, options)
|
5
8
|
Representation.new(obj, options)
|
6
9
|
end
|
7
10
|
|
11
|
+
# @private
|
8
12
|
def display(obj, options = {})
|
9
13
|
obj = convert(obj, options)
|
10
14
|
options = obj.options
|
11
15
|
obj = obj.object
|
12
16
|
|
13
17
|
fuzzy_mime = options[:format] # Treated like a fuzzy mime type
|
14
|
-
|
18
|
+
unless !fuzzy_mime || String === fuzzy_mime
|
19
|
+
raise 'Invalid argument :format'
|
20
|
+
end
|
21
|
+
|
15
22
|
if exact_mime = options[:mime]
|
16
23
|
raise 'Invalid argument :mime' unless String === exact_mime
|
17
24
|
raise 'Invalid mime type' unless exact_mime.include?('/')
|
@@ -27,32 +34,54 @@ module IRuby
|
|
27
34
|
|
28
35
|
# As a last resort, interpret string representation of the object
|
29
36
|
# as the given mime type.
|
30
|
-
|
37
|
+
if exact_mime && data.none? { |m, _| exact_mime == m }
|
38
|
+
data[exact_mime] = protect(exact_mime, obj)
|
39
|
+
end
|
31
40
|
|
32
41
|
data
|
33
42
|
end
|
34
43
|
|
35
|
-
|
36
|
-
|
44
|
+
# @private
|
45
|
+
def clear_output(wait = false)
|
46
|
+
IRuby::Kernel.instance.session.send(:publish, :clear_output, wait: wait)
|
37
47
|
end
|
38
48
|
|
39
49
|
private
|
40
50
|
|
41
51
|
def protect(mime, data)
|
42
|
-
|
52
|
+
ascii?(mime) ? data.to_s : [data.to_s].pack('m0')
|
53
|
+
end
|
54
|
+
|
55
|
+
# Each of the following mime types must be a text type,
|
56
|
+
# but mime-types library tells us it is a non-text type.
|
57
|
+
FORCE_TEXT_TYPES = Set[
|
58
|
+
"application/javascript",
|
59
|
+
"image/svg+xml"
|
60
|
+
].freeze
|
61
|
+
|
62
|
+
def ascii?(mime)
|
63
|
+
if FORCE_TEXT_TYPES.include?(mime)
|
64
|
+
true
|
65
|
+
else
|
66
|
+
MIME::Type.new(mime).ascii?
|
67
|
+
end
|
43
68
|
end
|
44
69
|
|
45
70
|
def render(data, obj, exact_mime, fuzzy_mime)
|
46
71
|
# Filter matching renderer by object type
|
47
|
-
renderer = Registry.renderer.select {|r| r.match?(obj) }
|
72
|
+
renderer = Registry.renderer.select { |r| r.match?(obj) }
|
48
73
|
|
49
74
|
matching_renderer = nil
|
50
75
|
|
51
76
|
# Find exactly matching display by exact_mime
|
52
|
-
|
77
|
+
if exact_mime
|
78
|
+
matching_renderer = renderer.find { |r| exact_mime == r.mime }
|
79
|
+
end
|
53
80
|
|
54
81
|
# Find fuzzy matching display by fuzzy_mime
|
55
|
-
|
82
|
+
if fuzzy_mime
|
83
|
+
matching_renderer ||= renderer.find { |r| r.mime&.include?(fuzzy_mime) }
|
84
|
+
end
|
56
85
|
|
57
86
|
renderer.unshift matching_renderer if matching_renderer
|
58
87
|
|
@@ -73,7 +102,8 @@ module IRuby
|
|
73
102
|
attr_reader :object, :options
|
74
103
|
|
75
104
|
def initialize(object, options)
|
76
|
-
@object
|
105
|
+
@object = object
|
106
|
+
@options = options
|
77
107
|
end
|
78
108
|
|
79
109
|
class << self
|
@@ -91,10 +121,13 @@ module IRuby
|
|
91
121
|
end
|
92
122
|
|
93
123
|
class Renderer
|
94
|
-
attr_reader :match, :mime, :
|
124
|
+
attr_reader :match, :mime, :priority
|
95
125
|
|
96
126
|
def initialize(match, mime, render, priority)
|
97
|
-
@match
|
127
|
+
@match = match
|
128
|
+
@mime = mime
|
129
|
+
@render = render
|
130
|
+
@priority = priority
|
98
131
|
end
|
99
132
|
|
100
133
|
def match?(obj)
|
@@ -114,7 +147,7 @@ module IRuby
|
|
114
147
|
@renderer ||= []
|
115
148
|
end
|
116
149
|
|
117
|
-
SUPPORTED_MIMES = %w
|
150
|
+
SUPPORTED_MIMES = %w[
|
118
151
|
text/plain
|
119
152
|
text/html
|
120
153
|
text/latex
|
@@ -122,7 +155,8 @@ module IRuby
|
|
122
155
|
application/javascript
|
123
156
|
image/png
|
124
157
|
image/jpeg
|
125
|
-
image/svg+xml
|
158
|
+
image/svg+xml
|
159
|
+
]
|
126
160
|
|
127
161
|
def match(&block)
|
128
162
|
@match = block
|
@@ -131,7 +165,7 @@ module IRuby
|
|
131
165
|
end
|
132
166
|
|
133
167
|
def respond_to(name)
|
134
|
-
match {|obj| obj.respond_to?(name) }
|
168
|
+
match { |obj| obj.respond_to?(name) }
|
135
169
|
end
|
136
170
|
|
137
171
|
def type(&block)
|
@@ -153,7 +187,7 @@ module IRuby
|
|
153
187
|
|
154
188
|
def format(mime = nil, &block)
|
155
189
|
renderer << Renderer.new(@match, mime, block, @priority)
|
156
|
-
renderer.sort_by! {|r| -r.priority }
|
190
|
+
renderer.sort_by! { |r| -r.priority }
|
157
191
|
|
158
192
|
# Decrease priority implicitly for all formats
|
159
193
|
# which are added later for a type.
|
@@ -170,6 +204,7 @@ module IRuby
|
|
170
204
|
end
|
171
205
|
|
172
206
|
type { Numo::NArray }
|
207
|
+
format 'text/plain', &:inspect
|
173
208
|
format 'text/latex' do |obj|
|
174
209
|
obj.ndim == 2 ?
|
175
210
|
LaTeX.matrix(obj, obj.shape[0], obj.shape[1]) :
|
@@ -180,6 +215,7 @@ module IRuby
|
|
180
215
|
end
|
181
216
|
|
182
217
|
type { NArray }
|
218
|
+
format 'text/plain', &:inspect
|
183
219
|
format 'text/latex' do |obj|
|
184
220
|
obj.dim == 2 ?
|
185
221
|
LaTeX.matrix(obj.transpose(1, 0), obj.shape[1], obj.shape[0]) :
|
@@ -241,37 +277,41 @@ module IRuby
|
|
241
277
|
|
242
278
|
match do |obj|
|
243
279
|
defined?(Magick::Image) && Magick::Image === obj ||
|
244
|
-
|
280
|
+
defined?(MiniMagick::Image) && MiniMagick::Image === obj
|
245
281
|
end
|
246
282
|
format 'image' do |obj|
|
247
283
|
format = obj.format || 'PNG'
|
248
|
-
[format == 'PNG' ? 'image/png' : 'image/jpeg', obj.to_blob {|i| i.format = format }]
|
284
|
+
[format == 'PNG' ? 'image/png' : 'image/jpeg', obj.to_blob { |i| i.format = format }]
|
249
285
|
end
|
250
286
|
|
251
|
-
|
252
|
-
|
253
|
-
obj.to_blob
|
287
|
+
match do |obj|
|
288
|
+
defined?(Vips::Image) && Vips::Image === obj
|
254
289
|
end
|
290
|
+
format do |obj|
|
291
|
+
# handles Vips::Error, vips_image_get: field "vips-loader" not found
|
292
|
+
loader = obj.get('vips-loader') rescue nil
|
293
|
+
if loader == 'jpegload'
|
294
|
+
['image/jpeg', obj.write_to_buffer('.jpg')]
|
295
|
+
else
|
296
|
+
# falls back to png for other/unknown types
|
297
|
+
['image/png', obj.write_to_buffer('.png')]
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
type { Gruff::Base }
|
302
|
+
format 'image/png', &:to_blob
|
255
303
|
|
256
304
|
respond_to :to_html
|
257
|
-
format 'text/html'
|
258
|
-
obj.to_html
|
259
|
-
end
|
305
|
+
format 'text/html', &:to_html
|
260
306
|
|
261
307
|
respond_to :to_latex
|
262
|
-
format 'text/latex'
|
263
|
-
obj.to_latex
|
264
|
-
end
|
308
|
+
format 'text/latex', &:to_latex
|
265
309
|
|
266
310
|
respond_to :to_tex
|
267
|
-
format 'text/latex'
|
268
|
-
obj.to_tex
|
269
|
-
end
|
311
|
+
format 'text/latex', &:to_tex
|
270
312
|
|
271
313
|
respond_to :to_javascript
|
272
|
-
format 'text/javascript'
|
273
|
-
obj.to_javascript
|
274
|
-
end
|
314
|
+
format 'text/javascript', &:to_javascript
|
275
315
|
|
276
316
|
respond_to :to_svg
|
277
317
|
format 'image/svg+xml' do |obj|
|
@@ -280,21 +320,17 @@ module IRuby
|
|
280
320
|
end
|
281
321
|
|
282
322
|
respond_to :to_iruby
|
283
|
-
format
|
284
|
-
obj.to_iruby
|
285
|
-
end
|
323
|
+
format(&:to_iruby)
|
286
324
|
|
287
|
-
match {|obj| obj.respond_to?(:path) && File.readable?(obj.path) }
|
325
|
+
match { |obj| obj.respond_to?(:path) && obj.method(:path).arity == 0 && File.readable?(obj.path) }
|
288
326
|
format do |obj|
|
289
|
-
mime =
|
327
|
+
mime = MIME::Types.of(obj.path).first.to_s
|
290
328
|
[mime, File.read(obj.path)] if SUPPORTED_MIMES.include?(mime)
|
291
329
|
end
|
292
330
|
|
293
331
|
type { Object }
|
294
|
-
priority
|
295
|
-
format 'text/plain'
|
296
|
-
obj.inspect
|
297
|
-
end
|
332
|
+
priority(-1000)
|
333
|
+
format 'text/plain', &:inspect
|
298
334
|
end
|
299
335
|
end
|
300
336
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module IRuby
|
2
|
+
class EventManager
|
3
|
+
def initialize(available_events)
|
4
|
+
@available_events = available_events.dup.freeze
|
5
|
+
@callbacks = available_events.map {|n| [n, []] }.to_h
|
6
|
+
end
|
7
|
+
|
8
|
+
attr_reader :available_events
|
9
|
+
|
10
|
+
def register(event, &block)
|
11
|
+
check_available_event(event)
|
12
|
+
@callbacks[event] << block unless block.nil?
|
13
|
+
block
|
14
|
+
end
|
15
|
+
|
16
|
+
def unregister(event, callback)
|
17
|
+
check_available_event(event)
|
18
|
+
val = @callbacks[event].delete(callback)
|
19
|
+
unless val
|
20
|
+
raise ArgumentError,
|
21
|
+
"Given callable object #{callback} is not registered as a #{event} callback"
|
22
|
+
end
|
23
|
+
val
|
24
|
+
end
|
25
|
+
|
26
|
+
def trigger(event, *args, **kwargs)
|
27
|
+
check_available_event(event)
|
28
|
+
@callbacks[event].each do |fn|
|
29
|
+
fn.call(*args, **kwargs)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def check_available_event(event)
|
36
|
+
return if @callbacks.key?(event)
|
37
|
+
raise ArgumentError, "Unknown event name: #{event}", caller
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/iruby/formatter.rb
CHANGED
@@ -78,16 +78,16 @@ module IRuby
|
|
78
78
|
|
79
79
|
if maxcols && keys.size > maxcols
|
80
80
|
keys1 = keys[0...maxcols / 2]
|
81
|
-
keys2 = keys[-maxcols / 2
|
81
|
+
keys2 = keys[-maxcols / 2 + 1..-1]
|
82
82
|
if header
|
83
83
|
header1 = header[0...maxcols / 2]
|
84
|
-
header2 = header[-maxcols / 2
|
84
|
+
header2 = header[-maxcols / 2 + 1..-1]
|
85
85
|
end
|
86
86
|
end
|
87
87
|
|
88
88
|
if maxrows && rows.size > maxrows
|
89
89
|
rows1 = rows[0...maxrows / 2]
|
90
|
-
rows2 = rows[-maxrows / 2
|
90
|
+
rows2 = rows[-maxrows / 2 + 1..-1]
|
91
91
|
end
|
92
92
|
|
93
93
|
table = '<table>'
|
data/lib/iruby/input.rb
CHANGED
@@ -15,21 +15,21 @@ module IRuby
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def form &block
|
18
|
-
builder = Builder.new
|
18
|
+
builder = Builder.new(&block)
|
19
19
|
form = InputForm.new(
|
20
|
-
fields: builder.fields,
|
20
|
+
fields: builder.fields,
|
21
21
|
buttons: builder.buttons
|
22
22
|
)
|
23
23
|
form.widget_display
|
24
24
|
builder.process_result form.submit
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
def popup title='Input', &block
|
28
|
-
builder = Builder.new
|
28
|
+
builder = Builder.new(&block)
|
29
29
|
form = InputForm.new fields: builder.fields
|
30
30
|
popup = Popup.new(
|
31
|
-
title: title,
|
32
|
-
form: form,
|
31
|
+
title: title,
|
32
|
+
form: form,
|
33
33
|
buttons: builder.buttons
|
34
34
|
)
|
35
35
|
popup.widget_display
|
@@ -0,0 +1,299 @@
|
|
1
|
+
|
2
|
+
# IRuby Input
|
3
|
+
|
4
|
+
This README is generated from README.ipynb. Please do not edit this file directly.
|
5
|
+
|
6
|
+
The `IRuby::Input` class makes it easier for IRuby users to get input from users. For example:
|
7
|
+
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
name = IRuby.input 'Enter your name'
|
11
|
+
```
|
12
|
+
|
13
|
+
The following input methods are supported on the `IRuby` module:
|
14
|
+
|
15
|
+
| method | description |
|
16
|
+
| -------- | -------- |
|
17
|
+
| `input(prompt)` | Prompts the user for a line of input |
|
18
|
+
| `password(prompt)` | Prompts the user for a password |
|
19
|
+
| `form(&block)` | Presents a form to the user |
|
20
|
+
| `popup(title,&block)` | Displays a form to the user as a popup |
|
21
|
+
|
22
|
+
## Forms
|
23
|
+
|
24
|
+
Forms are groups of inputs to be collected from the user. For example:
|
25
|
+
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
result = IRuby.form do
|
29
|
+
input :username
|
30
|
+
password :password
|
31
|
+
button
|
32
|
+
end
|
33
|
+
```
|
34
|
+
|
35
|
+
The following methods are available to build forms:
|
36
|
+
|
37
|
+
| method | description |
|
38
|
+
| -------- | -------- |
|
39
|
+
| `input(key=:input)` | Prompts the user for a line of input |
|
40
|
+
| `textarea(key=:textarea),` | Adds a textarea to the form |
|
41
|
+
| `password(key=:password)` | Prompts the user for a password |
|
42
|
+
| `button(key=:done, color: :blue)` | Adds a submit button to the form |
|
43
|
+
| `cancel(prompt='Cancel')` | Adds a cancel button to the form |
|
44
|
+
| `text(string)` | Adds text to the form |
|
45
|
+
| `html(&block)` | Inserts HTML from the given [erector block](https://github.com/erector/erector) |
|
46
|
+
| `file(key=:file)` | Adds a file input to the form |
|
47
|
+
| `date(key=:date)` | Adds a date picker to the form |
|
48
|
+
| `select(*options)` | Adds a dropdown select input to the form |
|
49
|
+
| `radio(*options)` | Adds a radio select input to the form |
|
50
|
+
| `checkbox(*options)` | Adds checkbox inputs to the form |
|
51
|
+
|
52
|
+
## Popups
|
53
|
+
|
54
|
+
Popups are just forms in a bootstrap modal. They are useful when users **Run All** in a notebook with a lot of inputs. The popups always appear in the same spot, so users don't have to scroll down to find the next input.
|
55
|
+
|
56
|
+
Popups accept a `title` argument, for example:
|
57
|
+
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
result = IRuby.popup 'Please enter your name' do
|
61
|
+
input
|
62
|
+
button
|
63
|
+
end
|
64
|
+
```
|
65
|
+
|
66
|
+
## Submit and cancel
|
67
|
+
|
68
|
+
The enter key will submit an input or form and the escape key will cancel it. Canceled inputs are returned as `nil`. Inputs are automatically canceled if destroyed. An input can be destroyed by clearing its cell's output. The `cancel` button will cancel a form and all other buttons will submit it.
|
69
|
+
|
70
|
+
After a form destroyed, the cell's output is cleared. Be careful not to prompt for input in a block that has previous output you would like to keep. Output is cleared to prevent forms from interferring with one another and to ensure that inputs are not inadvertently saved to the notebook.
|
71
|
+
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
result = IRuby.popup 'Confirm' do
|
75
|
+
text 'Are you sure you want to continue?'
|
76
|
+
cancel 'No'
|
77
|
+
button 'Yes'
|
78
|
+
end
|
79
|
+
```
|
80
|
+
|
81
|
+
## Custom keys
|
82
|
+
|
83
|
+
Every widget has an entry in the final results hash. A custom key can be passed as the first parameter to the hash. If no key is provided, the widget name is used as the key. The `cancel` widget has no key; it's first parameter is its label.
|
84
|
+
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
result = IRuby.form do
|
88
|
+
input :username
|
89
|
+
password :password
|
90
|
+
end
|
91
|
+
```
|
92
|
+
|
93
|
+
## Custom labels
|
94
|
+
|
95
|
+
Field labels appear to the left of the field. Button labels appear as the text on the button. `cancel` labels are passed as the first argument. All other widgets' labels are set using the `label` parameter.
|
96
|
+
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
result = IRuby.form do
|
100
|
+
input :name, label: 'Please enter your name'
|
101
|
+
cancel 'None of your business!'
|
102
|
+
button :submit, label: 'All done'
|
103
|
+
end
|
104
|
+
```
|
105
|
+
|
106
|
+
## Defaults
|
107
|
+
|
108
|
+
Most inputs will accept a `default` parameter. If no default is given, the deault is `nil`. Since checkboxes can have multiple values selected, you can pass an array of values. To check everything, pass `true` as the default.
|
109
|
+
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
result = IRuby.form do
|
113
|
+
checkbox :one, 'Fish', 'Cat', 'Dog', default: 'Fish'
|
114
|
+
checkbox :many, 'Fish', 'Cat', 'Dog', default: ['Cat', 'Dog']
|
115
|
+
checkbox :all, 'Fish', 'Cat', 'Dog', default: true
|
116
|
+
button :submit, label: 'All done'
|
117
|
+
end
|
118
|
+
```
|
119
|
+
|
120
|
+
## Dates
|
121
|
+
|
122
|
+
The `date` widget provides a calendar popup and returns a `Time` object. It's default should also be a `Time` object.
|
123
|
+
|
124
|
+
|
125
|
+
```ruby
|
126
|
+
result = IRuby.form do
|
127
|
+
date :birthday
|
128
|
+
date :today, default: Time.now
|
129
|
+
button
|
130
|
+
end
|
131
|
+
```
|
132
|
+
|
133
|
+
## Buttons
|
134
|
+
|
135
|
+
Buttons do not appear in the final hash unless they are clicked. If clicked, their value is `true`. Here are the various colors a button can be:
|
136
|
+
|
137
|
+
|
138
|
+
```ruby
|
139
|
+
result = IRuby.form do
|
140
|
+
IRuby::Input::Button::COLORS.each_key do |color|
|
141
|
+
button color, color: color
|
142
|
+
end
|
143
|
+
end
|
144
|
+
```
|
145
|
+
|
146
|
+
## Textareas
|
147
|
+
|
148
|
+
Textareas are multiline inputs that are convenient for larger inputs. If you need a line return when typing in a textarea, use shift+enter since enter will submit the form.
|
149
|
+
|
150
|
+
|
151
|
+
```ruby
|
152
|
+
result = IRuby.form do
|
153
|
+
text 'Enter email addresses, one per line (use shift+enter for newlines)'
|
154
|
+
textarea :emails
|
155
|
+
end
|
156
|
+
```
|
157
|
+
|
158
|
+
## Text and HTML
|
159
|
+
|
160
|
+
You can insert lines of text or custom html using their respective helpers:
|
161
|
+
|
162
|
+
|
163
|
+
```ruby
|
164
|
+
result = IRuby.form do
|
165
|
+
html { h1 'Choose a Stooge' }
|
166
|
+
text 'Choose your favorite stooge'
|
167
|
+
select :stooge, 'Moe', 'Larry', 'Curly'
|
168
|
+
button
|
169
|
+
end
|
170
|
+
```
|
171
|
+
|
172
|
+
## Dropdowns
|
173
|
+
|
174
|
+
A `select` is a dropdown of options. Use a `multiple` to allow multiple selections. `multiple` widgets accept an additional `size` parameters that determines the number of rows. The default is 4.
|
175
|
+
|
176
|
+
|
177
|
+
```ruby
|
178
|
+
result = IRuby.form do
|
179
|
+
select :stooge, 'Moe', 'Larry', 'Curly'
|
180
|
+
select :stooge, 'Moe', 'Larry', 'Curly', default: 'Moe'
|
181
|
+
multiple :stooges, 'Moe', 'Larry', 'Curly', default: true, size: 3
|
182
|
+
multiple :stooges, 'Moe', 'Larry', 'Curly', default: ['Moe','Curly']
|
183
|
+
button
|
184
|
+
end
|
185
|
+
```
|
186
|
+
|
187
|
+
## Radio selects and checkboxes
|
188
|
+
|
189
|
+
Like selects, radio selects and checkboxes take multiple arguments, each one an option. If the first argument is a symbol, it is used as the key.
|
190
|
+
|
191
|
+
Note that the `checkbox` widget will always return `nil` or an array.
|
192
|
+
|
193
|
+
|
194
|
+
```ruby
|
195
|
+
result = IRuby.form do
|
196
|
+
radio :children, *(0..12), label: 'How many children do you have?'
|
197
|
+
checkbox :gender, 'Male', 'Female', 'Intersex', label: 'Select the genders of your children'
|
198
|
+
button
|
199
|
+
end
|
200
|
+
```
|
201
|
+
|
202
|
+
## Files
|
203
|
+
|
204
|
+
Since file widgets capture the enter key, you should include a button when creating forms that contain only a file input:
|
205
|
+
|
206
|
+
|
207
|
+
```ruby
|
208
|
+
IRuby.form do
|
209
|
+
file :avatar, label: 'Choose an Avatar'
|
210
|
+
button :submit
|
211
|
+
end
|
212
|
+
```
|
213
|
+
|
214
|
+
File widgets return a hash with three keys:
|
215
|
+
|
216
|
+
* `data`: The contents of the file as a string
|
217
|
+
* `content_type`: The type of file, such as `text/plain` or `image/jpeg`
|
218
|
+
* `name`: The name of the uploaded file
|
219
|
+
|
220
|
+
## Example
|
221
|
+
|
222
|
+
Here is an example form that uses every built-in widget.
|
223
|
+
|
224
|
+
|
225
|
+
```ruby
|
226
|
+
result = IRuby.form do
|
227
|
+
html { h1 'The Everything Form' }
|
228
|
+
text 'Marvel at the strange and varied inputs!'
|
229
|
+
date
|
230
|
+
file
|
231
|
+
input :username
|
232
|
+
password
|
233
|
+
textarea
|
234
|
+
radio *(1..10)
|
235
|
+
checkbox 'Fish', 'Cat', 'Dog', label: 'Animals'
|
236
|
+
select :color, *IRuby::Input::Button::COLORS.keys
|
237
|
+
cancel
|
238
|
+
button
|
239
|
+
end
|
240
|
+
```
|
241
|
+
|
242
|
+
## Writing your own widget
|
243
|
+
|
244
|
+
Most form methods are `IRuby::Input::Widget` instances. A `Widget` is an [`Erector::Widget`](https://github.com/erector/erector) with some additional helpers. Here is the `cancel` widget:
|
245
|
+
|
246
|
+
```ruby
|
247
|
+
module IRuby
|
248
|
+
module Input
|
249
|
+
class Cancel < Widget
|
250
|
+
needs :label
|
251
|
+
|
252
|
+
builder :cancel do |label='Cancel'|
|
253
|
+
add_button Cancel.new(label: label)
|
254
|
+
end
|
255
|
+
|
256
|
+
def widget_css
|
257
|
+
".iruby-cancel { margin-left: 5px; }"
|
258
|
+
end
|
259
|
+
|
260
|
+
def widget_js
|
261
|
+
<<-JS
|
262
|
+
$('.iruby-cancel').click(function(){
|
263
|
+
$('#iruby-form').remove();
|
264
|
+
});
|
265
|
+
JS
|
266
|
+
end
|
267
|
+
|
268
|
+
def widget_html
|
269
|
+
button(
|
270
|
+
@label,
|
271
|
+
type: 'button',
|
272
|
+
:'data-dismiss' => 'modal',
|
273
|
+
class: "btn btn-danger pull-right iruby-cancel"
|
274
|
+
)
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
```
|
280
|
+
|
281
|
+
The following methods are available for widgets to use or override:
|
282
|
+
|
283
|
+
| method | description |
|
284
|
+
| -------- | -------- |
|
285
|
+
| `widget_js` | Returns the widget's Javascript |
|
286
|
+
| `widget_css` | Returns the widget's CSS |
|
287
|
+
| `widget_html` | Returns the widget's |
|
288
|
+
| `builder(method,&block)` | Class method to add form building helpers. |
|
289
|
+
|
290
|
+
The following methods are available in the `builder` block:
|
291
|
+
|
292
|
+
| method | description |
|
293
|
+
| -------- | -------- |
|
294
|
+
| `add_field(field)` | Adds a widget to the form's field area |
|
295
|
+
| `add_button(button)` | Adds a button to the form's button area |
|
296
|
+
| `process(key,&block)` | Register a custom processing block for the given key in the results hash |
|
297
|
+
| `unique_key(key)` | Returns a unique key for the given key. Use this to make sure that there are no key collisions in the final results hash. |
|
298
|
+
|
299
|
+
A canceled form always returns `nil`. Otherwise, the form collects any element with a `data-iruby-key` and non-falsey `data-iruby-value` and passes those to the processor proc registered for the key. See the `File` widget for a more involved example of processing results.
|