forme 1.12.0 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|