forme 1.12.0 → 2.2.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 +4 -4
- data/CHANGELOG +54 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +228 -206
- data/Rakefile +1 -7
- data/lib/forme/bs3.rb +23 -9
- data/lib/forme/erb.rb +13 -25
- data/lib/forme/form.rb +146 -149
- data/lib/forme/input.rb +1 -1
- data/lib/forme/rails.rb +39 -83
- data/lib/forme/raw.rb +2 -2
- data/lib/forme/tag.rb +3 -12
- data/lib/forme/template.rb +110 -0
- data/lib/forme/transformers/error_handler.rb +10 -10
- data/lib/forme/transformers/formatter.rb +32 -34
- data/lib/forme/transformers/helper.rb +0 -1
- data/lib/forme/transformers/inputs_wrapper.rb +4 -4
- data/lib/forme/version.rb +2 -2
- data/lib/forme.rb +13 -2
- data/lib/roda/plugins/forme.rb +1 -1
- data/lib/roda/plugins/forme_erubi_capture.rb +57 -0
- data/lib/roda/plugins/forme_route_csrf.rb +16 -34
- data/lib/roda/plugins/forme_set.rb +39 -76
- data/lib/sequel/plugins/forme.rb +45 -54
- data/lib/sequel/plugins/forme_i18n.rb +3 -1
- data/lib/sequel/plugins/forme_set.rb +2 -4
- data/spec/all.rb +1 -1
- data/spec/bs3_reference_spec.rb +291 -314
- data/spec/bs3_sequel_plugin_spec.rb +155 -155
- data/spec/bs3_spec.rb +247 -206
- data/spec/erb_helper.rb +69 -58
- data/spec/erubi_capture_helper.rb +198 -0
- data/spec/forme_coverage.rb +1 -0
- data/spec/forme_spec.rb +438 -377
- data/spec/rails_integration_spec.rb +21 -11
- data/spec/roda_integration_spec.rb +136 -70
- data/spec/sequel_helper.rb +3 -2
- data/spec/sequel_i18n_helper.rb +1 -1
- data/spec/sequel_i18n_plugin_spec.rb +6 -6
- data/spec/sequel_plugin_spec.rb +262 -150
- data/spec/sequel_set_plugin_spec.rb +9 -3
- data/spec/shared_erb_specs.rb +71 -0
- data/spec/sinatra_integration_spec.rb +31 -6
- data/spec/spec_helper.rb +21 -8
- metadata +8 -6
- data/lib/forme/erb_form.rb +0 -74
- data/lib/forme/sinatra.rb +0 -17
data/README.rdoc
CHANGED
@@ -6,91 +6,75 @@ Forme is a HTML forms library for ruby with the following goals:
|
|
6
6
|
2. Have a simple API
|
7
7
|
3. Support forms both with and without related objects
|
8
8
|
4. Allow compiling down to different types of output
|
9
|
+
5. Integrate easily into web frameworks
|
9
10
|
|
10
11
|
= Introduction
|
11
12
|
|
12
13
|
Forme is designed to make creating HTML forms easier. Flexibility and
|
13
|
-
|
14
|
-
|
15
|
-
to return html strings for widgets, but it could also be used for
|
16
|
-
serializing to other formats, or even as a DSL for a GUI application.
|
14
|
+
ease of use are the primary objectives. Here's a basic example,
|
15
|
+
showing usage without a related object:
|
17
16
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
is that <tt>Forme::Tag</tt> directly represents the underlying html
|
26
|
-
tag, containing a type, optional attributes, and children, while the
|
27
|
-
<tt>Forme::Input</tt> is more abstract and attempts to be user friendly.
|
28
|
-
For example, these both compile by default to the same select tag:
|
17
|
+
Forme.form({:action=>'/foo'}) do |f|
|
18
|
+
f.input(:text, :name=>'bar')
|
19
|
+
f.tag(:fieldset) do
|
20
|
+
f.input(:textarea, :name=>'baz')
|
21
|
+
end
|
22
|
+
f.button('Update')
|
23
|
+
end
|
29
24
|
|
30
|
-
|
31
|
-
# or
|
32
|
-
f.tag(:select, {}, [f.tag(:option, {:value=>1}, ['foo'])])
|
25
|
+
This results in the following HTML:
|
33
26
|
|
34
|
-
|
35
|
-
|
27
|
+
<form action="/foo">
|
28
|
+
<input name="bar" type="text"/>
|
29
|
+
<fieldset>
|
30
|
+
<textarea name="baz"></textarea>
|
31
|
+
</fieldset>
|
32
|
+
<input type="submit" value="Update"/>
|
33
|
+
</form>
|
36
34
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
+Helper+ :: If the <tt>Forme::Input</tt> instance has any help text,
|
42
|
-
adds the help text in a separate tag.
|
43
|
-
+Labeler+ :: If the <tt>Forme::Input</tt> instance has a label,
|
44
|
-
takes the formatted output and labels it.
|
45
|
-
+Wrapper+ :: Takes the output of the formatter, labeler, and
|
46
|
-
error_handler transformers, and wraps it in another tag (or just
|
47
|
-
returns it unmodified).
|
48
|
-
+Serializer+ :: converts a <tt>Forme::Tag</tt> instance into an
|
49
|
-
html string.
|
50
|
-
|
51
|
-
Technically, only the +Serializer+ is necessary. The <tt>Forme::Form#input</tt>
|
52
|
-
and <tt>Forme::Form#tag</tt> methods return +Input+ and +Tag+ objects. These objects
|
53
|
-
both have +to_s+ defined to call the appropriate +Serializer+ with
|
54
|
-
themselves. The +Serializer+ calls the appropriate +Formatter+ if
|
55
|
-
it encounters an +Input+ instance, and attempts to serialize the
|
56
|
-
output of that (which is usually a +Tag+ instance). It is up to
|
57
|
-
the +Formatter+ to call the +Labeler+, +ErrorHandler+, +Helper+,
|
58
|
-
and/or +Wrapper+.
|
59
|
-
|
60
|
-
The <tt>Forme::Form</tt> object takes the 6 transformers as options (:formatter,
|
61
|
-
:labeler, :error_handler, :helper, :wrapper, :inputs_wrapper, and :serializer), all of which
|
62
|
-
should be objects responding to +call+ (so you can use +Proc+s) or be symbols
|
63
|
-
registered with the library using <tt>Forme.register_transformer</tt>:
|
35
|
+
Forme also supports forms that are associated with objects, and
|
36
|
+
has specific support for Sequel::Model objects to allow easily building
|
37
|
+
forms for such objects. The Sequel support handles inputs based on
|
38
|
+
database columns, and automatically handles labels and errors:
|
64
39
|
|
65
|
-
Forme.
|
66
|
-
input
|
40
|
+
Forme.form(Album[1], action: '/foo') do |f|
|
41
|
+
f.input :name
|
42
|
+
f.input :copies_sold
|
67
43
|
end
|
68
44
|
|
69
|
-
|
70
|
-
is a <tt>Forme::Tag</tt> instance, and +input+ is a <tt>Forme::Input</tt>
|
71
|
-
instance. The +Formatter+ and +Serializer+ transformers are the two
|
72
|
-
exceptions, with +Formatter+ being called with just an +input+, and
|
73
|
-
+Serializer+ potentionally being called with any object. The +Serializer+
|
74
|
-
will in general recursively call itself with children of the argument
|
75
|
-
given.
|
76
|
-
|
77
|
-
There is also an +InputsWrapper+ transformer, that is called by
|
78
|
-
<tt>Forme::Form#inputs</tt>. It's used to wrap up a group of
|
79
|
-
related options (in a fieldset by default). It takes +form+
|
80
|
-
(<tt>Forme::Form</tt> instance) and +input_opts+ (+Hash+) arguments.
|
45
|
+
This results in the following HTML:
|
81
46
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
47
|
+
<form action="/foo" method="post">
|
48
|
+
<label>Name:
|
49
|
+
<input id="album_name" name="album[name]" type="text" value="Rising Force"/>
|
50
|
+
</label>
|
51
|
+
<label>Copies Sold:
|
52
|
+
<input id="album_copies_sold" inputmode="numeric" name="album[copies_sold]" pattern="-?[0-9]*" type="text" value="100000"/>
|
53
|
+
</label>
|
54
|
+
</form>
|
86
55
|
|
87
|
-
|
88
|
-
|
56
|
+
In addition to integrating with Sequel, Form also integrates into
|
57
|
+
three separate web frameworks, Roda, Rails, and Sinatra, allowing
|
58
|
+
use of forms inside templates. This is the most common usage of Forme.
|
59
|
+
|
60
|
+
One distinct advantage of Forme over other form libraries is the use
|
61
|
+
of an abstract syntax tree internally, allowing the same form code to
|
62
|
+
compile to different HTML with different options. For example, it
|
63
|
+
allows using the exactly same form code to display a form you can modify
|
64
|
+
as well as a read-only view, just by passing a single option when
|
65
|
+
creating the form. For example, with the first example in this section,
|
66
|
+
if you pass the <tt>formatter: :readonly</tt> option, you get the
|
67
|
+
following HTML instead:
|
68
|
+
|
69
|
+
<form action="/foo">
|
70
|
+
<span class="readonly-text"></span>
|
71
|
+
<fieldset>
|
72
|
+
<div class="readonly-textarea"></div>
|
73
|
+
</fieldset>
|
74
|
+
</form>
|
89
75
|
|
90
|
-
|
91
|
-
|
92
|
-
Forme::Labeler::Explicit.new.call(tag, input)
|
93
|
-
end
|
76
|
+
This allows you to reuse the same form code in multiple contexts,
|
77
|
+
which can save considerable development time.
|
94
78
|
|
95
79
|
= Installation
|
96
80
|
|
@@ -104,9 +88,9 @@ Source :: https://github.com/jeremyevans/forme
|
|
104
88
|
Discussion Forum :: https://github.com/jeremyevans/forme/discussions
|
105
89
|
Bug Tracker :: https://github.com/jeremyevans/forme/issues
|
106
90
|
|
107
|
-
=
|
91
|
+
= Direct Instantiation
|
108
92
|
|
109
|
-
|
93
|
+
While not typically done, you can instantiate Forme::Form objects directly and use them:
|
110
94
|
|
111
95
|
f = Forme::Form.new
|
112
96
|
f.open(:action=>'/foo', :method=>:post) # '<form action="/foo" method="post">'
|
@@ -129,47 +113,11 @@ on the object (or using #[] if the object is a hash).
|
|
129
113
|
f = Forme::Form.new([:foo])
|
130
114
|
f.input(:first) # '<input id="first" name="first" type="text" value="foo"/>'
|
131
115
|
|
132
|
-
= DSL
|
133
|
-
|
134
|
-
Forme comes with a DSL:
|
135
|
-
|
136
|
-
Forme.form(:action=>'/foo') do |f|
|
137
|
-
f.input(:text, :name=>'bar')
|
138
|
-
f.tag(:fieldset) do
|
139
|
-
f.input(:textarea, :name=>'baz')
|
140
|
-
end
|
141
|
-
f.button('Update')
|
142
|
-
end
|
143
|
-
# <form action="/foo">
|
144
|
-
# <input name="bar" type="text"/>
|
145
|
-
# <fieldset>
|
146
|
-
# <textarea name="baz"></textarea>
|
147
|
-
# </fieldset>
|
148
|
-
# <input type="submit" value="Update"/>
|
149
|
-
# </form>
|
150
|
-
|
151
|
-
You can wrap up multiple inputs with the <tt>:inputs</tt> method:
|
152
|
-
|
153
|
-
Forme.form(:action=>'/foo') do |f|
|
154
|
-
f.inputs([[:text, {:name=>'bar'}], [:textarea, {:name=>'baz'}]])
|
155
|
-
end
|
156
|
-
# <form action="/foo">
|
157
|
-
# <fieldset class="inputs">
|
158
|
-
# <input name="bar" type="text"/>
|
159
|
-
# <textarea name="baz"></textarea>
|
160
|
-
# </fieldset>
|
161
|
-
# </form>
|
162
|
-
|
163
|
-
You can even do everything in a single method call:
|
164
|
-
|
165
|
-
Forme.form({:action=>'/foo'},
|
166
|
-
:inputs=>[[:text, {:name=>'bar'}], [:textarea, {:name=>'baz'}]])
|
167
|
-
|
168
116
|
= Forme::Form Creation
|
169
117
|
|
170
|
-
|
171
|
-
|
172
|
-
|
118
|
+
+Forme.form+ takes up to 3 arguments, and yields the Forme::Form
|
119
|
+
object to the block (if given). Here are the argument styles that you can
|
120
|
+
use for +Forme.form+.
|
173
121
|
|
174
122
|
No args :: Creates a +Form+ object with no options and not associated
|
175
123
|
to an +obj+, and with no attributes in the opening tag.
|
@@ -189,21 +137,21 @@ Examples:
|
|
189
137
|
Forme.form
|
190
138
|
|
191
139
|
# 1 hash argument (attributes)
|
192
|
-
Forme.form(:
|
140
|
+
Forme.form(action: '/foo')
|
193
141
|
|
194
142
|
# 1 non-hash argument (a reference object used when building the form)
|
195
143
|
Forme.form(Album[1])
|
196
144
|
|
197
145
|
# 2 hash arguments (attributes, and options)
|
198
|
-
Forme.form({:
|
146
|
+
Forme.form({action: '/foo'}, values: params)
|
199
147
|
|
200
148
|
# 1 non-hash argument, 1-2 hash arguments (ref obj, attributes, options)
|
201
|
-
Forme.form(Album[1], :
|
202
|
-
Forme.form(Album[1], {:
|
149
|
+
Forme.form(Album[1], action: '/foo')
|
150
|
+
Forme.form(Album[1], {action: '/foo'}, values: params)
|
203
151
|
|
204
152
|
If you want a Forme::Form instance where the reference object is a Hash, then you need to pass the hash object using the :obj option:
|
205
153
|
|
206
|
-
Forme.form({:
|
154
|
+
Forme.form({action: '/foo'}, obj: {foo: 'bar'})
|
207
155
|
|
208
156
|
You can also create Forme::Form objects the normal ruby way using Forme::Form#new. The
|
209
157
|
difference between Forme::Form#new and Forme.form is that Forme.form includes the enclosing
|
@@ -214,13 +162,13 @@ a hash of <form> tag attributes, so it has the following API:
|
|
214
162
|
Forme::Form.new
|
215
163
|
|
216
164
|
# 1 hash argument
|
217
|
-
Forme::Form.new(:
|
165
|
+
Forme::Form.new(values: params)
|
218
166
|
|
219
167
|
# 1 non-hash argument
|
220
168
|
Forme::Form.new(Album[1])
|
221
169
|
|
222
170
|
# 1 non-hash argument, 1-2 hash arguments
|
223
|
-
Forme::Form.new(Album[1], :
|
171
|
+
Forme::Form.new(Album[1], values: params)
|
224
172
|
|
225
173
|
= Forme::Form Methods
|
226
174
|
|
@@ -229,9 +177,9 @@ a hash of <form> tag attributes, so it has the following API:
|
|
229
177
|
If you create a Form via Forme::Forme#new, you can use the form method to create a form tag:
|
230
178
|
|
231
179
|
f = Forme::Form.new
|
232
|
-
f.form(:
|
180
|
+
f.form(action: '/foo')
|
233
181
|
|
234
|
-
This is what Forme.form uses internally to create the
|
182
|
+
This is what Forme.form uses internally to create the +<form>+ tag
|
235
183
|
|
236
184
|
== input
|
237
185
|
|
@@ -284,8 +232,8 @@ Which results in a form similar to the following:
|
|
284
232
|
|
285
233
|
== inputs
|
286
234
|
|
287
|
-
This wraps multiple inputs in a tag (it uses the inputs_wrapper transformer discussed below,
|
288
|
-
so it uses a fieldset by default). You can give the inputs to add as an enumerable argument:
|
235
|
+
This wraps multiple inputs in a tag (it uses the +:inputs_wrapper+ transformer discussed below,
|
236
|
+
so it uses a +fieldset+ by default). You can give the inputs to add as an enumerable argument:
|
289
237
|
|
290
238
|
f.inputs([:textarea, [:text, :value=>'a']])
|
291
239
|
# <fieldset>
|
@@ -299,13 +247,13 @@ You can also provide a block:
|
|
299
247
|
f.input(:text, :value=>'a')
|
300
248
|
end
|
301
249
|
|
302
|
-
Any options given are passed to the inputs_wrapper (so you can use options such as
|
303
|
-
to set a legend for the fieldset), and also to the
|
304
|
-
such as
|
250
|
+
Any options given are passed to the +inputs_wrapper+ (so you can use options such as +:legend+
|
251
|
+
to set a legend for the fieldset), and also to the +with_opts+ method (so you can use options
|
252
|
+
such as +:wrapper+ to modify the default wrapper transformer for inputs inside the block).
|
305
253
|
There is also one option specific to the inputs method:
|
306
254
|
|
307
255
|
:nested_inputs_wrapper :: Sets the default inputs_wrapper to use for calls to inputs inside
|
308
|
-
the block. The reason for this option is that
|
256
|
+
the block. The reason for this option is that +:inputs_wrapper+
|
309
257
|
option affects the current call to inputs, so if you want to
|
310
258
|
use a different inputs_wrapper for nested calls, you need this option.
|
311
259
|
|
@@ -323,30 +271,31 @@ It can be called with a string to provide a value for the button:
|
|
323
271
|
|
324
272
|
It can be called with a hash to provide options for the submit input:
|
325
273
|
|
326
|
-
f.button(:
|
274
|
+
f.button(value: 'Search', class: 'btn')
|
327
275
|
# <input class="btn" type="submit" value="Search"/>
|
328
276
|
|
329
277
|
== with_opts
|
330
278
|
|
331
|
-
This requires a block, and modifies the Form's options inside the block,
|
279
|
+
This requires a block, and modifies the Forme::Form's options inside the block,
|
332
280
|
restoring the options when the block returns:
|
333
281
|
|
334
282
|
f.input(:text)
|
335
283
|
# <input type="text"/>
|
336
|
-
|
284
|
+
|
285
|
+
f.with_opts(wrapper: :li) do
|
337
286
|
f.input(:text)
|
338
287
|
end
|
339
288
|
# <li><input type="text"/></li>
|
340
289
|
|
341
|
-
This supports most options you can provide to
|
290
|
+
This supports most options you can provide to Forme::Form, but not all.
|
342
291
|
|
343
292
|
== with_obj
|
344
293
|
|
345
|
-
This uses with_opts to change the Form
|
294
|
+
This uses +with_opts+ to change the Forme::Form object temporarily. It
|
346
295
|
yields the object to the block, and also supports appending to the
|
347
296
|
existing namespaces:
|
348
297
|
|
349
|
-
Forme.form([:foo], :namespace
|
298
|
+
Forme.form([:foo], {action: '/path'}, namespace: 'a') do |f|
|
350
299
|
f.input(:first)
|
351
300
|
# <input id="a_first" name="a[first]" type="text" value="foo"/>
|
352
301
|
f.with_obj(['foobar'], 'b') do |o|
|
@@ -357,11 +306,11 @@ existing namespaces:
|
|
357
306
|
|
358
307
|
== each_obj
|
359
308
|
|
360
|
-
This allows you to provide an object-yielding enumerable.
|
361
|
-
each object in the enumerable. It yields each object as well as the
|
362
|
-
object in the enumerable, and includes the index in the namespace:
|
309
|
+
This allows you to provide an object-yielding enumerable. +each_object+ will call
|
310
|
+
+with_obj+ with each object in the enumerable. It yields each object as well as the
|
311
|
+
index of the object in the enumerable, and includes the index in the namespace:
|
363
312
|
|
364
|
-
objectlist = ['foobar', ['good']]
|
313
|
+
objectlist = [['foobar'], ['good']]
|
365
314
|
Forme.form([:foo], :namespace=>'a') do |f|
|
366
315
|
f.each_obj(objectlist, 'b') do |o, i|
|
367
316
|
f.input(:first, :size=>10+i)
|
@@ -376,7 +325,7 @@ Forme ships with a Sequel plugin (use <tt>Sequel::Model.plugin :forme</tt> to en
|
|
376
325
|
Sequel::Model instances support the +forme_config+ and +forme_input+ methods and return customized inputs.
|
377
326
|
An additional instance method, +forme_namespace+ can optionally be defined to customize how model classnames
|
378
327
|
are transformed into form classes and input IDs and names. This can be useful if your Sequel::Model classes
|
379
|
-
are nested under a parent namespace.
|
328
|
+
are nested under a parent namespace. The default namespace uses Sequel::Model#underscore.
|
380
329
|
|
381
330
|
module Admin
|
382
331
|
class Albums < Sequel::Model
|
@@ -386,27 +335,11 @@ are nested under a parent namespace. It falls back to the behaviour of Sequel::M
|
|
386
335
|
end
|
387
336
|
end
|
388
337
|
|
389
|
-
|
390
|
-
|
391
|
-
The Sequel support handles inputs based on database columns, and automatically handles labels and errors.
|
392
|
-
|
393
|
-
Forme.form(Album[1], :action=>'/foo') do |f|
|
394
|
-
f.input :name
|
395
|
-
f.input :copies_sold
|
396
|
-
end
|
397
|
-
|
398
|
-
This will create a form similar to:
|
399
|
-
|
400
|
-
<form action="/foo" method="post">
|
401
|
-
<label>Name: <input id="album_name" name="album[name]" type="text" value="Rising Force"/></label>
|
402
|
-
<label>Copies Sold: <input id="album_copies_sold" name="album[copies_sold]" type="number" value="100000"/></label>
|
403
|
-
</form>
|
404
|
-
|
405
|
-
The Forme Sequel plugin also integerates with Sequel's validation reflection support with the
|
406
|
-
+validation_class_methods+ plugin that ships with Sequel. It will add +pattern+ and +maxlength+ attributes
|
338
|
+
The Sequel :forme plugin also integerates with Sequel's validation reflection support that comes with the
|
339
|
+
Sequel validation_class_methods plugin. It will add +pattern+ and +maxlength+ attributes
|
407
340
|
based on the format, numericality, and length validations.
|
408
341
|
|
409
|
-
==
|
342
|
+
== Specialized input options for specific column types
|
410
343
|
|
411
344
|
In addition to the default Forme options, the Sequel support includes, for specific column types,
|
412
345
|
these additional options to the #input method:
|
@@ -510,12 +443,12 @@ form similar to:
|
|
510
443
|
|
511
444
|
<input id="album_tracks_attributes_0_id" name="album[tracks_attributes][0][id]" type="hidden" value="1"/>
|
512
445
|
<fieldset class="inputs"><legend>Track #1</legend>
|
513
|
-
<label>Number: <input id="album_tracks_attributes_0_number" name="album[tracks_attributes][0][number]" type="
|
446
|
+
<label>Number: <input id="album_tracks_attributes_0_number" inputmode="numeric" name="album[tracks_attributes][0][number]" pattern="-?[0-9]*" type="text" value="1"/></label>
|
514
447
|
<label>Name: <input id="album_tracks_attributes_0_name" name="album[tracks_attributes][0][name]" type="text" value="Blue Hawaii"/></label>
|
515
448
|
</fieldset>
|
516
449
|
<input id="album_tracks_attributes_1_id" name="album[tracks_attributes][1][id]" type="hidden" value="2"/>
|
517
450
|
<fieldset class="inputs"><legend>Track #2</legend>
|
518
|
-
<label>Number: <input id="album_tracks_attributes_1_number" name="album[tracks_attributes][1][number]" type="
|
451
|
+
<label>Number: <input id="album_tracks_attributes_1_number" inputmode="numeric" name="album[tracks_attributes][1][number]" pattern="-?[0-9]*" type="text" value="2"/></label>
|
519
452
|
<label>Name: <input id="album_tracks_attributes_1_name" name="album[tracks_attributes][1][name]" type="text" value="Almost Always True"/></label>
|
520
453
|
</fieldset>
|
521
454
|
|
@@ -568,7 +501,7 @@ One way to handle the form submission is to use Sequel::Model#set_fields.
|
|
568
501
|
Note that you have to specify the parameter names again as the second argument to
|
569
502
|
set_fields.
|
570
503
|
|
571
|
-
Handling
|
504
|
+
Handling submitted parameters becomes more complex as your forms become more complex.
|
572
505
|
For example, if you are only displaying certain form fields in certain situations:
|
573
506
|
|
574
507
|
album = Album[1]
|
@@ -588,9 +521,9 @@ As you can see, you basically need to recreate the conditionals used when creati
|
|
588
521
|
the form, so that that the processing of the form submission handles only the
|
589
522
|
inputs that were displayed on the form.
|
590
523
|
|
591
|
-
=== forme_set
|
524
|
+
=== Sequel forme_set plugin
|
592
525
|
|
593
|
-
The forme_set plugin is designed to make handling form submissions easier. What it does
|
526
|
+
The Sequel forme_set plugin is designed to make handling form submissions easier. What it does
|
594
527
|
is record the form fields that are used on the object, and then it uses those fields
|
595
528
|
to handle input submitted for the object. For example:
|
596
529
|
|
@@ -669,12 +602,12 @@ this is specific to the web framework you are using, but is it similar to:
|
|
669
602
|
|
670
603
|
forme_set is not perfect, there are ways to use Forme that forme_set will not handle
|
671
604
|
correctly. First, forme_set only works with forms that use model objects, and doesn't
|
672
|
-
handle inputs where the
|
605
|
+
handle inputs where the +:obj+ option is provided to change the input.
|
673
606
|
Additionally, forme_set does not currently handle subform/nested_attributes.
|
674
607
|
|
675
|
-
In cases where forme_set does not handle things correctly, you can use forme_parse
|
676
|
-
which will return metadata that forme_set uses (forme_set calls forme_parse
|
677
|
-
internally). forme_parse returns a hash with the following keys:
|
608
|
+
In cases where forme_set does not handle things correctly, you can use +forme_parse+,
|
609
|
+
which will return metadata that +forme_set+ uses (+forme_set+ calls +forme_parse+
|
610
|
+
internally). +forme_parse+ returns a hash with the following keys:
|
678
611
|
|
679
612
|
:values :: A hash of values that can be used to update the model, suitable for passing
|
680
613
|
to Sequel::Model#set.
|
@@ -682,21 +615,21 @@ internally). forme_parse returns a hash with the following keys:
|
|
682
615
|
check that the submitted values for associated objects match one of the
|
683
616
|
options for the input in the form.
|
684
617
|
|
685
|
-
It is possible to use forme_set for the values it can handle, and set other fields manually
|
686
|
-
using set_fields
|
618
|
+
It is possible to use +forme_set+ for the values it can handle, and set other fields manually
|
619
|
+
using +set_fields+.
|
687
620
|
|
688
|
-
=== forme_set
|
621
|
+
=== Roda forme_set plugin
|
689
622
|
|
690
|
-
The forme_set
|
691
|
-
handling form submissions even easier. This plugin
|
692
|
-
fields were used to build the form, as well as some other metadata. It
|
623
|
+
The Roda forme_set plugin builds on the Sequel forme_set plugin and is designed to make
|
624
|
+
handling form submissions even easier. This plugin adds a hidden form input to store which
|
625
|
+
fields were used to build the form, as well as some other metadata. It adds another hidden
|
693
626
|
form input with an HMAC, so that on submission, if the HMAC matches, you can be sure that an
|
694
627
|
attacker didn't add extra fields.
|
695
628
|
|
696
629
|
There are a couple advantages to this plugin over using just the Sequel forme_set plugin.
|
697
630
|
One is that you do not need to record the form fields when processing the submission of a
|
698
631
|
form, since the information you need is included in the form submission. Another is that
|
699
|
-
calling the forme_set method is simpler, since it can determine the necessary parameters.
|
632
|
+
calling the +forme_set+ method is simpler, since it can determine the necessary parameters.
|
700
633
|
|
701
634
|
While you need code like this when using just the Sequel forme_set plugin:
|
702
635
|
|
@@ -823,20 +756,20 @@ plugin to handle form submissions involving multiple objects, or for the
|
|
823
756
|
objects that were not passed when creating the form.
|
824
757
|
|
825
758
|
Second, the Roda forme_set plugin does not handle cases where the field values
|
826
|
-
are placed outside the
|
759
|
+
are placed outside the form's default namespace. The Sequel forme_set plugin
|
827
760
|
can handle those issues, as long as all values are in the same namespace, since
|
828
761
|
the Sequel forme_set plugin requires you pass in the specific hash to use (the
|
829
|
-
Roda forme_set plugin
|
762
|
+
Roda forme_set plugin uses the form's namespace information and the submitted
|
830
763
|
parameters to determine the hash to use).
|
831
764
|
|
832
765
|
In cases where the Roda forme_set does not handle things correctly, you can use
|
833
766
|
forme_parse, which will return metadata in the same format as the Sequel plugin
|
834
|
-
forme_parse method, with the addition of a
|
767
|
+
forme_parse method, with the addition of a +:form_version+ key in the hash for the
|
835
768
|
form version.
|
836
769
|
|
837
770
|
It is possible to use the Roda forme_set plugin for the submissions it can handle, the
|
838
771
|
Sequel forme_set plugin for the submissions it can handle, and set other fields manually
|
839
|
-
using the Sequel set_fields methods.
|
772
|
+
using the Sequel +set_fields+ methods.
|
840
773
|
|
841
774
|
Note that when using the Roda forme_set plugin with an existing form, you should first
|
842
775
|
enable the Roda plugin without actually using the Roda forme_set method. Do not
|
@@ -854,7 +787,15 @@ forme_i18n :: Handles translations for labels using i18n.
|
|
854
787
|
|
855
788
|
= Roda Support
|
856
789
|
|
857
|
-
Forme ships with
|
790
|
+
Forme ships with multiple Roda plugins
|
791
|
+
|
792
|
+
* forme_set (discussed above)
|
793
|
+
* forme
|
794
|
+
* forme_route_csrf
|
795
|
+
* forme_erubi_capture
|
796
|
+
|
797
|
+
== forme_route_csrf and forme plugins
|
798
|
+
|
858
799
|
For new code, it is recommended to use forme_route_csrf, as that uses Roda's route_csrf
|
859
800
|
plugin, which supports more secure request-specific CSRF tokens. In both cases, usage in ERB
|
860
801
|
templates is the same:
|
@@ -881,14 +822,29 @@ The forme plugin does not require any csrf plugin, but will transparently use
|
|
881
822
|
Rack::Csrf if it is available. If Rack::Csrf is available a CSRF tag if the form's
|
882
823
|
method is +POST+, with no configuration ability.
|
883
824
|
|
884
|
-
|
825
|
+
== forme_erubi_capture plugin
|
826
|
+
|
827
|
+
The forme_erubi_capture plugin builds on the forme_route_csrf plugin, but it supports
|
828
|
+
the erubi/capture_end engine, which allows this syntax:
|
829
|
+
|
830
|
+
<%|= form(@obj, :action=>'/foo') do |f| %>
|
831
|
+
<%= f.input(:field) %>
|
832
|
+
<%|= f.tag(:fieldset) do %>
|
833
|
+
<%= f.input(:field_two) %>
|
834
|
+
<%| end %>
|
835
|
+
<%| end %>
|
836
|
+
|
837
|
+
If you use the forme_erubi_capture plugin, you need to manually set Roda to use the
|
838
|
+
erubi/capture_end engine, which you can do via:
|
839
|
+
|
840
|
+
require 'erubi/capture_end'
|
841
|
+
app.plugin :render, :engine_opts=>{'erb'=>{:engine_class=>Erubi::CaptureEndEngine}}
|
885
842
|
|
886
|
-
|
887
|
-
injecting output (+@_out_buf+ by default).
|
843
|
+
The forme_erubi_capture plugin requires Roda 3.50.0+.
|
888
844
|
|
889
845
|
= Sinatra Support
|
890
846
|
|
891
|
-
Forme ships with a
|
847
|
+
Forme ships with a Sinatra extension that you can get by <tt>require "forme/erb"</tt> and using
|
892
848
|
<tt>including Forme::ERB::Helper</tt>. This is tested to support ERB templates in Sinatra.
|
893
849
|
It allows you to use the following API in your erb templates:
|
894
850
|
|
@@ -899,8 +855,8 @@ It allows you to use the following API in your erb templates:
|
|
899
855
|
<% end %>
|
900
856
|
<% end %>
|
901
857
|
|
902
|
-
In order to this to work transparently, the ERB outvar needs to be <tt>@_out_buf</tt
|
903
|
-
|
858
|
+
In order to this to work transparently, the ERB outvar needs to be <tt>@_out_buf</tt> (this is the
|
859
|
+
default in Sinatra).
|
904
860
|
|
905
861
|
= Rails Support
|
906
862
|
|
@@ -915,7 +871,7 @@ in your Rails forms:
|
|
915
871
|
<% end %>
|
916
872
|
<% end %>
|
917
873
|
|
918
|
-
This has been tested on Rails 3.2-
|
874
|
+
This has been tested on Rails 3.2-7.0.
|
919
875
|
|
920
876
|
= Input Types and Options
|
921
877
|
|
@@ -926,8 +882,8 @@ created via Forme::Form#input:
|
|
926
882
|
|
927
883
|
These options are supported by all of the input types:
|
928
884
|
|
929
|
-
:attr :: The attributes hash to use for the given tag,
|
930
|
-
other options that set attributes.
|
885
|
+
:attr :: The attributes hash to use for the given tag, attributes in this hash
|
886
|
+
take precedence over other options that set attributes.
|
931
887
|
:autofocus :: Set the autofocus attribute if true
|
932
888
|
:class :: A class to use. Unlike other options, this is combined with the
|
933
889
|
classes set in the :attr hash.
|
@@ -956,7 +912,7 @@ These options are supported by all of the input types:
|
|
956
912
|
for textarea tags, or the selected option(s) for select tags.
|
957
913
|
:wrapper :: Set a custom wrapper, overriding the form's default
|
958
914
|
|
959
|
-
== Input
|
915
|
+
== Input Type-Specific Options
|
960
916
|
|
961
917
|
=== :checkbox
|
962
918
|
|
@@ -974,11 +930,11 @@ Creates an input tag with type radio. Options:
|
|
974
930
|
|
975
931
|
=== :date / :datetime
|
976
932
|
|
977
|
-
By default, creates an input tag with type date or datetime. With the :
|
933
|
+
By default, creates an input tag with type date or datetime. With the <tt>as: :select</tt> option,
|
978
934
|
creates multiple select options. Options:
|
979
935
|
|
980
936
|
:as :: When value is :select, uses 3 or 6 select boxes by default.
|
981
|
-
:order :: The order of select boxes when using :
|
937
|
+
:order :: The order of select boxes when using <tt>as: :select</tt>. Entries should be a symbol
|
982
938
|
for the select field and string to use a string
|
983
939
|
(:date default: <tt>[:year, '-', :month, '-', :day]</tt>)
|
984
940
|
(:datetime default: <tt>[:year, '-', :month, '-', :day, ' ', :hour, ':', :minute, ':', :second]</tt>)
|
@@ -1023,7 +979,7 @@ Creates a select tag, containing option tags specified by the :options option.
|
|
1023
979
|
=== :checkboxset
|
1024
980
|
|
1025
981
|
Creates a set of checkbox inputs all using the same name. Supports the same options
|
1026
|
-
as the select type, except that the :multiple option is assumed to be true. Also supports
|
982
|
+
as the :select type, except that the :multiple option is assumed to be true. Also supports
|
1027
983
|
the following options:
|
1028
984
|
|
1029
985
|
:tag_wrapper :: The wrapper transformer for individual tags in the set
|
@@ -1055,6 +1011,10 @@ as text and password, as well as newer HTML5 inputs such as number or email. Opt
|
|
1055
1011
|
These are the options supported by Forme::Form object, mostly used to set
|
1056
1012
|
the defaults for Inputs created via the form:
|
1057
1013
|
|
1014
|
+
:after :: A callable object that is yielded the Form instance after yielding to
|
1015
|
+
the block. Can be used to add hidden inputs to the end of the form.
|
1016
|
+
:before :: A callable object that is yielded the Form instance before yielding to
|
1017
|
+
the block. Can be used to add hidden inputs to the start of the form.
|
1058
1018
|
:config :: The configuration to use, which automatically sets defaults
|
1059
1019
|
for the transformers to use.
|
1060
1020
|
:errors :: A Hash of errors from a previous form submission, used to set
|
@@ -1062,7 +1022,6 @@ the defaults for Inputs created via the form:
|
|
1062
1022
|
:error_handler :: Sets the default error_handler for the form's inputs
|
1063
1023
|
:helper :: Sets the default helper for the form's inputs
|
1064
1024
|
:formatter :: Sets the default formatter for the form's inputs
|
1065
|
-
:hidden_tags :: Sets the hidden tags to automatically add to this form, see below.
|
1066
1025
|
:input_defaults :: Sets the default options for each input type. This should
|
1067
1026
|
be a hash with input type keys, where the values are the
|
1068
1027
|
hash of default options to use for the input type.
|
@@ -1083,17 +1042,7 @@ For forms created by Forme.form, the following options are supported:
|
|
1083
1042
|
to the block.
|
1084
1043
|
:button :: A button to add to the form, after yielding to the block.
|
1085
1044
|
|
1086
|
-
|
1087
|
-
|
1088
|
-
This should be an array, where elements are one of the following types:
|
1089
|
-
|
1090
|
-
String, Array, Forme::Tag :: Added directly as a child of the form tag.
|
1091
|
-
Hash :: Adds a hidden tag for each entry, with keys as the name of the hidden
|
1092
|
-
tag and values as the value of the hidden tag.
|
1093
|
-
Proc :: Will be called with the form tag object, and should return an instance
|
1094
|
-
of one of the handled types (or nil to not add a tag).
|
1095
|
-
|
1096
|
-
= Basic Design
|
1045
|
+
= Internal Architecture
|
1097
1046
|
|
1098
1047
|
Internally, Forme builds an abstract syntax tree of objects that
|
1099
1048
|
represent the form. The abstract syntax tree goes through a
|
@@ -1103,15 +1052,88 @@ strings. Here are the main classes used by the library:
|
|
1103
1052
|
|
1104
1053
|
<tt>Forme::Form</tt> :: main object
|
1105
1054
|
<tt>Forme::Input</tt> :: high level abstract tag (a single +Input+ could represent a select box with a bunch of options)
|
1106
|
-
<tt>Forme::Tag</tt> :: low level abstract tag representing an
|
1055
|
+
<tt>Forme::Tag</tt> :: low level abstract tag representing an HTML tag (there would be a separate +Tag+ for each option in a select box)
|
1056
|
+
|
1057
|
+
The difference between <tt>Forme::Input</tt> and <tt>Forme::Tag</tt>
|
1058
|
+
is that <tt>Forme::Tag</tt> directly represents the underlying HTML
|
1059
|
+
tag, containing a type, optional attributes, and children, while the
|
1060
|
+
<tt>Forme::Input</tt> is more abstract and attempts to be user friendly.
|
1061
|
+
For example, these both compile by default to the same select tag:
|
1062
|
+
|
1063
|
+
f.input(:select, :options=>[['foo', 1]])
|
1064
|
+
# or
|
1065
|
+
f.tag(:select, {}, [f.tag(:option, {:value=>1}, ['foo'])])
|
1107
1066
|
|
1108
1067
|
The group of objects that perform the transformations to
|
1109
1068
|
the abstract syntax trees are known as transformers.
|
1110
1069
|
Transformers use a functional style, and all use a +call+-based
|
1111
1070
|
API, so you can use a +Proc+ for any custom transformer.
|
1071
|
+
The processing of high level <tt>Forme::Input</tt>s into raw HTML
|
1072
|
+
fragments is performed through the following transformers:
|
1073
|
+
|
1074
|
+
+Formatter+ :: converts a <tt>Forme::Input</tt> instance into a
|
1075
|
+
<tt>Forme::Tag</tt> instance (or array of them).
|
1076
|
+
+ErrorHandler+ :: If the <tt>Forme::Input</tt> instance has a error,
|
1077
|
+
takes the formatted tag and marks it as having the error.
|
1078
|
+
+Helper+ :: If the <tt>Forme::Input</tt> instance has any help text,
|
1079
|
+
adds the help text in a separate tag.
|
1080
|
+
+Labeler+ :: If the <tt>Forme::Input</tt> instance has a label,
|
1081
|
+
takes the formatted output and labels it.
|
1082
|
+
+Wrapper+ :: Takes the output of the formatter, labeler, and
|
1083
|
+
error_handler transformers, and wraps it in another tag (or just
|
1084
|
+
returns it unmodified).
|
1085
|
+
+Serializer+ :: converts a <tt>Forme::Tag</tt> instance into an
|
1086
|
+
HTML string.
|
1087
|
+
|
1088
|
+
Technically, only the +Serializer+ is necessary. The Forme::Form#input
|
1089
|
+
and Forme::Form#tag methods internally create +Input+ and
|
1090
|
+
+Tag+ objects. Before returning results, the input or tag is converted
|
1091
|
+
to a string using +to_s+, which calls the appropriate +Serializer+.
|
1092
|
+
The +Serializer+ calls the appropriate +Formatter+ if it encounters an
|
1093
|
+
+Input+ instance, and attempts to serialize the output of that (which is
|
1094
|
+
usually a +Tag+ instance). It is up to the +Formatter+ to call the +Labeler+,
|
1095
|
+
+ErrorHandler+, +Helper+, and/or +Wrapper+.
|
1096
|
+
|
1097
|
+
The Forme::Form object takes the transformers as options (:formatter,
|
1098
|
+
:labeler, :error_handler, :helper, :wrapper, and :serializer), all of which
|
1099
|
+
should be objects responding to +call+ (so you can use <tt>Proc</tt>s) or be symbols
|
1100
|
+
registered with the library using <tt>Forme.register_transformer</tt>:
|
1101
|
+
|
1102
|
+
Forme.register_transformer(:wrapper, :p) do |tag, input|
|
1103
|
+
input.tag(:p, {}, tag)
|
1104
|
+
end
|
1105
|
+
|
1106
|
+
Most transformers are called with two arguments, +tag+ and +input+. +tag+
|
1107
|
+
is a <tt>Forme::Tag</tt> instance, and +input+ is a <tt>Forme::Input</tt>
|
1108
|
+
instance. The +Formatter+ and +Serializer+ transformers are the two
|
1109
|
+
exceptions, with +Formatter+ being called with just an +input+, and
|
1110
|
+
+Serializer+ potentionally being called with any object. The +Serializer+
|
1111
|
+
will in general recursively call itself with children of the argument
|
1112
|
+
given until a string is returned.
|
1113
|
+
|
1114
|
+
There is also an +InputsWrapper+ transformer, that is called by
|
1115
|
+
Forme::Form#inputs. It's used to wrap up a group of
|
1116
|
+
related options (in a +fieldset+ by default). It takes +form+
|
1117
|
+
(Forme::Form instance) and +input_opts+ (+Hash+) arguments.
|
1118
|
+
|
1119
|
+
Most of the transformers can be overridden on a per instance basis by
|
1120
|
+
passing the appropriate option to +input+ or +inputs+:
|
1121
|
+
|
1122
|
+
f.input(:name, :wrapper=>:p)
|
1123
|
+
|
1124
|
+
Existing transformers can be easily extended (ie, to set the class attribute),
|
1125
|
+
by creating your own transformer and then calling the existing transformer.
|
1126
|
+
|
1127
|
+
Forme.register_transformer(:labeler, :explicit) do |tag, input|
|
1128
|
+
input.opts[:label_attr] ||= { :class => 'label' }
|
1129
|
+
Forme::Labeler::Explicit.new.call(tag, input)
|
1130
|
+
end
|
1112
1131
|
|
1113
1132
|
== Transformer Types
|
1114
1133
|
|
1134
|
+
You can override the type of transform for each form or input using the
|
1135
|
+
following options:
|
1136
|
+
|
1115
1137
|
+serializer+ :: tags input/tag, returns string
|
1116
1138
|
+formatter+ :: takes input, returns tag
|
1117
1139
|
+error_handler+ :: takes tag and input, returns version of tag with errors noted
|
@@ -1244,7 +1266,7 @@ You can mark a configuration as the default using:
|
|
1244
1266
|
|
1245
1267
|
=== Bootstrap 3 Support
|
1246
1268
|
|
1247
|
-
Forme ships with support for Bootstrap 3 HTML formatting. This support is shipped in
|
1269
|
+
Forme ships with support for Bootstrap 3-4 HTML formatting. This support is shipped in
|
1248
1270
|
it's own file, so if you don't use it, you don't pay the memory penalty for loading
|
1249
1271
|
it.
|
1250
1272
|
|
@@ -1260,7 +1282,7 @@ All of these have external dependencies:
|
|
1260
1282
|
3. simple_form
|
1261
1283
|
4. padrino-helpers
|
1262
1284
|
|
1263
|
-
Forme's
|
1285
|
+
Forme's API draws a lot of inspiration from both Formtastic and simple_form.
|
1264
1286
|
|
1265
1287
|
= License
|
1266
1288
|
|