forme 1.9.0 → 2.0.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 +70 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +396 -202
- data/lib/forme/bs3.rb +19 -5
- data/lib/forme/erb.rb +18 -16
- data/lib/forme/form.rb +151 -118
- data/lib/forme/input.rb +1 -1
- data/lib/forme/rails.rb +41 -72
- data/lib/forme/raw.rb +2 -2
- data/lib/forme/sinatra.rb +6 -2
- data/lib/forme/tag.rb +3 -12
- data/lib/forme/template.rb +118 -0
- data/lib/forme/transformers/error_handler.rb +46 -1
- data/lib/forme/transformers/formatter.rb +36 -35
- data/lib/forme/transformers/helper.rb +0 -1
- data/lib/forme/transformers/inputs_wrapper.rb +6 -6
- data/lib/forme/transformers/labeler.rb +19 -0
- data/lib/forme/transformers/wrapper.rb +1 -1
- data/lib/forme/version.rb +2 -2
- data/lib/forme.rb +15 -2
- data/lib/roda/plugins/forme.rb +1 -1
- data/lib/roda/plugins/forme_erubi_capture.rb +62 -0
- data/lib/roda/plugins/forme_route_csrf.rb +16 -20
- data/lib/roda/plugins/forme_set.rb +177 -0
- data/lib/sequel/plugins/forme.rb +42 -55
- data/lib/sequel/plugins/forme_i18n.rb +3 -1
- data/lib/sequel/plugins/forme_set.rb +50 -28
- data/spec/all.rb +1 -1
- data/spec/bs3_reference_spec.rb +18 -18
- data/spec/bs3_sequel_plugin_spec.rb +7 -7
- data/spec/bs3_spec.rb +23 -11
- data/spec/erb_helper.rb +73 -58
- data/spec/erubi_capture_helper.rb +202 -0
- data/spec/forme_spec.rb +80 -29
- data/spec/rails_integration_spec.rb +47 -24
- data/spec/roda_integration_spec.rb +459 -48
- data/spec/sequel_helper.rb +0 -1
- data/spec/sequel_i18n_helper.rb +1 -1
- data/spec/sequel_i18n_plugin_spec.rb +3 -2
- data/spec/sequel_plugin_spec.rb +25 -8
- data/spec/sequel_set_plugin_spec.rb +10 -3
- data/spec/shared_erb_specs.rb +75 -0
- data/spec/sinatra_integration_spec.rb +5 -6
- data/spec/spec_helper.rb +23 -5
- metadata +30 -8
- data/lib/forme/erb_form.rb +0 -74
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
|
-
|
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
|
23
24
|
|
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:
|
25
|
+
This results in the following HTML:
|
29
26
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
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.
|
81
|
-
|
82
|
-
Most of the transformers can be overridden on a per instance basis by
|
83
|
-
passing the appropriate option to +input+ or +inputs+:
|
45
|
+
This results in the following HTML:
|
84
46
|
|
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" name="album[copies_sold]" type="number" 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 context,
|
77
|
+
which can save considerable development time.
|
94
78
|
|
95
79
|
= Installation
|
96
80
|
|
@@ -101,13 +85,12 @@ by creating your own transformer and then calling the existing transformer.
|
|
101
85
|
Demo Site :: http://forme-demo.jeremyevans.net
|
102
86
|
RDoc :: http://forme.jeremyevans.net
|
103
87
|
Source :: https://github.com/jeremyevans/forme
|
104
|
-
|
105
|
-
Google Group :: https://groups.google.com/forum/#!forum/ruby-forme
|
88
|
+
Discussion Forum :: https://github.com/jeremyevans/forme/discussions
|
106
89
|
Bug Tracker :: https://github.com/jeremyevans/forme/issues
|
107
90
|
|
108
|
-
=
|
91
|
+
= Direct Instantiation
|
109
92
|
|
110
|
-
|
93
|
+
While not typically done, you can instantiate Forme::Form objects directly and use them:
|
111
94
|
|
112
95
|
f = Forme::Form.new
|
113
96
|
f.open(:action=>'/foo', :method=>:post) # '<form action="/foo" method="post">'
|
@@ -130,47 +113,11 @@ on the object (or using #[] if the object is a hash).
|
|
130
113
|
f = Forme::Form.new([:foo])
|
131
114
|
f.input(:first) # '<input id="first" name="first" type="text" value="foo"/>'
|
132
115
|
|
133
|
-
= DSL
|
134
|
-
|
135
|
-
Forme comes with a DSL:
|
136
|
-
|
137
|
-
Forme.form(:action=>'/foo') do |f|
|
138
|
-
f.input(:text, :name=>'bar')
|
139
|
-
f.tag(:fieldset) do
|
140
|
-
f.input(:textarea, :name=>'baz')
|
141
|
-
end
|
142
|
-
f.button('Update')
|
143
|
-
end
|
144
|
-
# <form action="/foo">
|
145
|
-
# <input name="bar" type="text"/>
|
146
|
-
# <fieldset>
|
147
|
-
# <textarea name="baz"></textarea>
|
148
|
-
# </fieldset>
|
149
|
-
# <input type="submit" value="Update"/>
|
150
|
-
# </form>
|
151
|
-
|
152
|
-
You can wrap up multiple inputs with the <tt>:inputs</tt> method:
|
153
|
-
|
154
|
-
Forme.form(:action=>'/foo') do |f|
|
155
|
-
f.inputs([[:text, {:name=>'bar'}], [:textarea, {:name=>'baz'}]])
|
156
|
-
end
|
157
|
-
# <form action="/foo">
|
158
|
-
# <fieldset class="inputs">
|
159
|
-
# <input name="bar" type="text"/>
|
160
|
-
# <textarea name="baz"></textarea>
|
161
|
-
# </fieldset>
|
162
|
-
# </form>
|
163
|
-
|
164
|
-
You can even do everything in a single method call:
|
165
|
-
|
166
|
-
Forme.form({:action=>'/foo'},
|
167
|
-
:inputs=>[[:text, {:name=>'bar'}], [:textarea, {:name=>'baz'}]])
|
168
|
-
|
169
116
|
= Forme::Form Creation
|
170
117
|
|
171
|
-
|
172
|
-
|
173
|
-
|
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+.
|
174
121
|
|
175
122
|
No args :: Creates a +Form+ object with no options and not associated
|
176
123
|
to an +obj+, and with no attributes in the opening tag.
|
@@ -190,21 +137,21 @@ Examples:
|
|
190
137
|
Forme.form
|
191
138
|
|
192
139
|
# 1 hash argument (attributes)
|
193
|
-
Forme.form(:
|
140
|
+
Forme.form(action: '/foo')
|
194
141
|
|
195
142
|
# 1 non-hash argument (a reference object used when building the form)
|
196
143
|
Forme.form(Album[1])
|
197
144
|
|
198
145
|
# 2 hash arguments (attributes, and options)
|
199
|
-
Forme.form({:
|
146
|
+
Forme.form({action: '/foo'}, values: params)
|
200
147
|
|
201
148
|
# 1 non-hash argument, 1-2 hash arguments (ref obj, attributes, options)
|
202
|
-
Forme.form(Album[1], :
|
203
|
-
Forme.form(Album[1], {:
|
149
|
+
Forme.form(Album[1], action: '/foo')
|
150
|
+
Forme.form(Album[1], {action: '/foo'}, values: params)
|
204
151
|
|
205
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:
|
206
153
|
|
207
|
-
Forme.form({:
|
154
|
+
Forme.form({action: '/foo'}, obj: {foo: 'bar'})
|
208
155
|
|
209
156
|
You can also create Forme::Form objects the normal ruby way using Forme::Form#new. The
|
210
157
|
difference between Forme::Form#new and Forme.form is that Forme.form includes the enclosing
|
@@ -215,13 +162,13 @@ a hash of <form> tag attributes, so it has the following API:
|
|
215
162
|
Forme::Form.new
|
216
163
|
|
217
164
|
# 1 hash argument
|
218
|
-
Forme::Form.new(:
|
165
|
+
Forme::Form.new(values: params)
|
219
166
|
|
220
167
|
# 1 non-hash argument
|
221
168
|
Forme::Form.new(Album[1])
|
222
169
|
|
223
170
|
# 1 non-hash argument, 1-2 hash arguments
|
224
|
-
Forme::Form.new(Album[1], :
|
171
|
+
Forme::Form.new(Album[1], values: params)
|
225
172
|
|
226
173
|
= Forme::Form Methods
|
227
174
|
|
@@ -230,9 +177,9 @@ a hash of <form> tag attributes, so it has the following API:
|
|
230
177
|
If you create a Form via Forme::Forme#new, you can use the form method to create a form tag:
|
231
178
|
|
232
179
|
f = Forme::Form.new
|
233
|
-
f.form(:
|
180
|
+
f.form(action: '/foo')
|
234
181
|
|
235
|
-
This is what Forme.form uses internally to create the
|
182
|
+
This is what Forme.form uses internally to create the +<form>+ tag
|
236
183
|
|
237
184
|
== input
|
238
185
|
|
@@ -285,8 +232,8 @@ Which results in a form similar to the following:
|
|
285
232
|
|
286
233
|
== inputs
|
287
234
|
|
288
|
-
This wraps multiple inputs in a tag (it uses the inputs_wrapper transformer discussed below,
|
289
|
-
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:
|
290
237
|
|
291
238
|
f.inputs([:textarea, [:text, :value=>'a']])
|
292
239
|
# <fieldset>
|
@@ -300,13 +247,13 @@ You can also provide a block:
|
|
300
247
|
f.input(:text, :value=>'a')
|
301
248
|
end
|
302
249
|
|
303
|
-
Any options given are passed to the inputs_wrapper (so you can use options such as
|
304
|
-
to set a legend for the fieldset), and also to the
|
305
|
-
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).
|
306
253
|
There is also one option specific to the inputs method:
|
307
254
|
|
308
255
|
:nested_inputs_wrapper :: Sets the default inputs_wrapper to use for calls to inputs inside
|
309
|
-
the block. The reason for this option is that
|
256
|
+
the block. The reason for this option is that +:inputs_wrapper+
|
310
257
|
option affects the current call to inputs, so if you want to
|
311
258
|
use a different inputs_wrapper for nested calls, you need this option.
|
312
259
|
|
@@ -324,30 +271,31 @@ It can be called with a string to provide a value for the button:
|
|
324
271
|
|
325
272
|
It can be called with a hash to provide options for the submit input:
|
326
273
|
|
327
|
-
f.button(:
|
274
|
+
f.button(value: 'Search', class: 'btn')
|
328
275
|
# <input class="btn" type="submit" value="Search"/>
|
329
276
|
|
330
277
|
== with_opts
|
331
278
|
|
332
|
-
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,
|
333
280
|
restoring the options when the block returns:
|
334
281
|
|
335
282
|
f.input(:text)
|
336
283
|
# <input type="text"/>
|
337
|
-
|
284
|
+
|
285
|
+
f.with_opts(wrapper: :li) do
|
338
286
|
f.input(:text)
|
339
287
|
end
|
340
288
|
# <li><input type="text"/></li>
|
341
289
|
|
342
|
-
This supports most options you can provide to
|
290
|
+
This supports most options you can provide to Forme::Form, but not all.
|
343
291
|
|
344
292
|
== with_obj
|
345
293
|
|
346
|
-
This uses with_opts to change the Form
|
294
|
+
This uses +with_opts+ to change the Forme::Form object temporarily. It
|
347
295
|
yields the object to the block, and also supports appending to the
|
348
296
|
existing namespaces:
|
349
297
|
|
350
|
-
Forme.form([:foo], :namespace
|
298
|
+
Forme.form([:foo], {action: '/path'}, namespace: 'a') do |f|
|
351
299
|
f.input(:first)
|
352
300
|
# <input id="a_first" name="a[first]" type="text" value="foo"/>
|
353
301
|
f.with_obj(['foobar'], 'b') do |o|
|
@@ -358,11 +306,11 @@ existing namespaces:
|
|
358
306
|
|
359
307
|
== each_obj
|
360
308
|
|
361
|
-
This allows you to provide an object-yielding enumerable.
|
362
|
-
each object in the enumerable. It yields each object as well as the
|
363
|
-
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:
|
364
312
|
|
365
|
-
objectlist = ['foobar', ['good']]
|
313
|
+
objectlist = [['foobar'], ['good']]
|
366
314
|
Forme.form([:foo], :namespace=>'a') do |f|
|
367
315
|
f.each_obj(objectlist, 'b') do |o, i|
|
368
316
|
f.input(:first, :size=>10+i)
|
@@ -377,7 +325,7 @@ Forme ships with a Sequel plugin (use <tt>Sequel::Model.plugin :forme</tt> to en
|
|
377
325
|
Sequel::Model instances support the +forme_config+ and +forme_input+ methods and return customized inputs.
|
378
326
|
An additional instance method, +forme_namespace+ can optionally be defined to customize how model classnames
|
379
327
|
are transformed into form classes and input IDs and names. This can be useful if your Sequel::Model classes
|
380
|
-
are nested under a parent namespace.
|
328
|
+
are nested under a parent namespace. The default namespace uses Sequel::Model#underscore.
|
381
329
|
|
382
330
|
module Admin
|
383
331
|
class Albums < Sequel::Model
|
@@ -387,27 +335,11 @@ are nested under a parent namespace. It falls back to the behaviour of Sequel::M
|
|
387
335
|
end
|
388
336
|
end
|
389
337
|
|
390
|
-
|
391
|
-
|
392
|
-
The Sequel support handles inputs based on database columns, and automatically handles labels and errors.
|
393
|
-
|
394
|
-
Forme.form(Album[1], :action=>'/foo') do |f|
|
395
|
-
f.input :name
|
396
|
-
f.input :copies_sold
|
397
|
-
end
|
398
|
-
|
399
|
-
This will create a form similar to:
|
400
|
-
|
401
|
-
<form action="/foo" method="post">
|
402
|
-
<label>Name: <input id="album_name" name="album[name]" type="text" value="Rising Force"/></label>
|
403
|
-
<label>Copies Sold: <input id="album_copies_sold" name="album[copies_sold]" type="number" value="100000"/></label>
|
404
|
-
</form>
|
405
|
-
|
406
|
-
The Forme Sequel plugin also integerates with Sequel's validation reflection support with the
|
407
|
-
+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
|
408
340
|
based on the format, numericality, and length validations.
|
409
341
|
|
410
|
-
==
|
342
|
+
== Specialized input options for specific column types
|
411
343
|
|
412
344
|
In addition to the default Forme options, the Sequel support includes, for specific column types,
|
413
345
|
these additional options to the #input method:
|
@@ -569,7 +501,7 @@ One way to handle the form submission is to use Sequel::Model#set_fields.
|
|
569
501
|
Note that you have to specify the parameter names again as the second argument to
|
570
502
|
set_fields.
|
571
503
|
|
572
|
-
Handling
|
504
|
+
Handling submitted parameters becomes more complex as your forms become more complex.
|
573
505
|
For example, if you are only displaying certain form fields in certain situations:
|
574
506
|
|
575
507
|
album = Album[1]
|
@@ -589,9 +521,9 @@ As you can see, you basically need to recreate the conditionals used when creati
|
|
589
521
|
the form, so that that the processing of the form submission handles only the
|
590
522
|
inputs that were displayed on the form.
|
591
523
|
|
592
|
-
=== forme_set plugin
|
524
|
+
=== Sequel forme_set plugin
|
593
525
|
|
594
|
-
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
|
595
527
|
is record the form fields that are used on the object, and then it uses those fields
|
596
528
|
to handle input submitted for the object. For example:
|
597
529
|
|
@@ -670,12 +602,12 @@ this is specific to the web framework you are using, but is it similar to:
|
|
670
602
|
|
671
603
|
forme_set is not perfect, there are ways to use Forme that forme_set will not handle
|
672
604
|
correctly. First, forme_set only works with forms that use model objects, and doesn't
|
673
|
-
handle inputs where the
|
605
|
+
handle inputs where the +:obj+ option is provided to change the input.
|
674
606
|
Additionally, forme_set does not currently handle subform/nested_attributes.
|
675
607
|
|
676
|
-
In cases where forme_set does not handle things correctly, you can use forme_parse
|
677
|
-
which will return metadata that forme_set uses (forme_set calls forme_parse
|
678
|
-
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:
|
679
611
|
|
680
612
|
:values :: A hash of values that can be used to update the model, suitable for passing
|
681
613
|
to Sequel::Model#set.
|
@@ -683,8 +615,168 @@ internally). forme_parse returns a hash with the following keys:
|
|
683
615
|
check that the submitted values for associated objects match one of the
|
684
616
|
options for the input in the form.
|
685
617
|
|
686
|
-
It is possible to use forme_set for the values it can handle, and set other fields manually
|
687
|
-
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+.
|
620
|
+
|
621
|
+
=== Roda forme_set plugin
|
622
|
+
|
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
|
626
|
+
form input with an HMAC, so that on submission, if the HMAC matches, you can be sure that an
|
627
|
+
attacker didn't add extra fields.
|
628
|
+
|
629
|
+
There are a couple advantages to this plugin over using just the Sequel forme_set plugin.
|
630
|
+
One is that you do not need to record the form fields when processing the submission of a
|
631
|
+
form, since the information you need is included in the form submission. Another is that
|
632
|
+
calling the +forme_set+ method is simpler, since it can determine the necessary parameters.
|
633
|
+
|
634
|
+
While you need code like this when using just the Sequel forme_set plugin:
|
635
|
+
|
636
|
+
album = Album[1]
|
637
|
+
Forme.form(album, :action=>'/foo') do |f|
|
638
|
+
f.input :name
|
639
|
+
f.input :copies_sold if album.released?
|
640
|
+
end
|
641
|
+
album.forme_set(params['album'])
|
642
|
+
|
643
|
+
when you also use the Roda forme_set plugin, you can simplify it to:
|
644
|
+
|
645
|
+
album = Album[1]
|
646
|
+
forme_set(album)
|
647
|
+
|
648
|
+
==== Validations
|
649
|
+
|
650
|
+
The Roda forme_set plugin supports and uses the same validations as the Sequel forme_set
|
651
|
+
plugin. However, the Roda plugin is more accurate because it uses the options that were
|
652
|
+
present on the form when it was originally built, instead of the options that would be
|
653
|
+
present on the form when the form was submitted. However, note that that can be a
|
654
|
+
negative if you are dynamically adding values to both the database and the form between
|
655
|
+
when the form was built and when it was submitted.
|
656
|
+
|
657
|
+
==== Usage
|
658
|
+
|
659
|
+
Because the Roda forme_set plugin includes the metadata needed to process the form in form
|
660
|
+
submissions, you don't need to rearrange code to use it, or rerender templates.
|
661
|
+
You can do:
|
662
|
+
|
663
|
+
album = Album[1]
|
664
|
+
forme_set(album)
|
665
|
+
|
666
|
+
And the method will update the +album+ object using the appropriate form values.
|
667
|
+
|
668
|
+
Note that using the Roda forme_set plugin requires you set a secret for the HMAC. It
|
669
|
+
is important that you keep this value secret, because if an attacker has access to this,
|
670
|
+
they would be able to set arbitrary attributes for model objects. In your Roda class,
|
671
|
+
you can load the plugin via:
|
672
|
+
|
673
|
+
plugin :forme_set, :secret => ENV["APP_FORME_HMAC_SECRET"]
|
674
|
+
|
675
|
+
By default, invalid form submissions will raise an exception. If you want to change
|
676
|
+
that behavior (i.e. to display a nice error page), pass a block when loading the plugin:
|
677
|
+
|
678
|
+
plugin :forme_set do |error_type, obj|
|
679
|
+
# ...
|
680
|
+
end
|
681
|
+
|
682
|
+
The block arguments will be a symbol for the type of error (:missing_data, :missing_hmac,
|
683
|
+
:hmac_mismatch, :csrf_mismatch, or :missing_namespace) and the object passed to +forme_set+.
|
684
|
+
This block should raise or halt. If it does not, the default behavior of raising an
|
685
|
+
exception will be taken.
|
686
|
+
|
687
|
+
=== Form Versions
|
688
|
+
|
689
|
+
The Roda forme_set plugin supports form versions. This allows you to gracefully handle
|
690
|
+
changes to forms, processing submissions of the form generated before the change (if
|
691
|
+
possible) as well as the processing submissions of the form generated after the change.
|
692
|
+
|
693
|
+
For example, maybe you have an existing form with just an input for the name:
|
694
|
+
|
695
|
+
form(album) do |f|
|
696
|
+
f.input(:name)
|
697
|
+
end
|
698
|
+
|
699
|
+
Then later, you want to add an input for the number of copies sold:
|
700
|
+
|
701
|
+
form(album) do |f|
|
702
|
+
f.input(:name)
|
703
|
+
f.input(:copies_sold)
|
704
|
+
end
|
705
|
+
|
706
|
+
Using the Roda forme_set plugin, submissions of the old form would only set the
|
707
|
+
name field, it wouldn't set the copies_sold field, since when the form was created,
|
708
|
+
only the name field was used.
|
709
|
+
|
710
|
+
You can handle this case be versioning the form when making changes to it:
|
711
|
+
|
712
|
+
form(album, {}, :form_version=>1) do |f|
|
713
|
+
f.input(:name)
|
714
|
+
f.input(:copies_sold)
|
715
|
+
end
|
716
|
+
|
717
|
+
When you are processing the form submission with forme_set, you pass a block, which
|
718
|
+
will be yielded the version for the form (nil if no version was set):
|
719
|
+
|
720
|
+
forme_set(album) do |version|
|
721
|
+
if version == nil
|
722
|
+
album.copies_sold = 0
|
723
|
+
end
|
724
|
+
end
|
725
|
+
|
726
|
+
The block is also yielded the object passed for forme_set, useful if you don't keep
|
727
|
+
a reference to it:
|
728
|
+
|
729
|
+
album = forme_set(Album.new) do |version, obj|
|
730
|
+
if version == nil
|
731
|
+
obj.copies_sold = 0
|
732
|
+
end
|
733
|
+
end
|
734
|
+
|
735
|
+
You only need to support old versions of the form for as long as their could be
|
736
|
+
active sessions that could use the old versions of the form. As long you as
|
737
|
+
are expiring sessions to prevent session fixation, you can remove the version
|
738
|
+
handling after the expiration period has passed since the change to the form
|
739
|
+
was made.
|
740
|
+
|
741
|
+
Note that this issue with handling changes to forms is not specific to the Roda
|
742
|
+
forme_set plugin, it affects pretty much all form submissions. The Roda forme_set
|
743
|
+
plugin just makes this issue easier to handle.
|
744
|
+
|
745
|
+
==== Caveats
|
746
|
+
|
747
|
+
The Roda forme_set plugin has basically the same caveats as Sequel forme_set plugin.
|
748
|
+
Additionally, it has a couple other restrictions that the Sequel forme_set plugin
|
749
|
+
does not have.
|
750
|
+
|
751
|
+
First, the Roda forme_set plugin only handles a single object in forms,
|
752
|
+
which must be provided when creating the form. It does not handle multiple
|
753
|
+
objects in the same form, and ignores any fields set for an object different
|
754
|
+
from the one passed when creating the form. You can use the Sequel forme_set
|
755
|
+
plugin to handle form submissions involving multiple objects, or for the
|
756
|
+
objects that were not passed when creating the form.
|
757
|
+
|
758
|
+
Second, the Roda forme_set plugin does not handle cases where the field values
|
759
|
+
are placed outside the form's default namespace. The Sequel forme_set plugin
|
760
|
+
can handle those issues, as long as all values are in the same namespace, since
|
761
|
+
the Sequel forme_set plugin requires you pass in the specific hash to use (the
|
762
|
+
Roda forme_set plugin uses the form's namespace information and the submitted
|
763
|
+
parameters to determine the hash to use).
|
764
|
+
|
765
|
+
In cases where the Roda forme_set does not handle things correctly, you can use
|
766
|
+
forme_parse, which will return metadata in the same format as the Sequel plugin
|
767
|
+
forme_parse method, with the addition of a +:form_version+ key in the hash for the
|
768
|
+
form version.
|
769
|
+
|
770
|
+
It is possible to use the Roda forme_set plugin for the submissions it can handle, the
|
771
|
+
Sequel forme_set plugin for the submissions it can handle, and set other fields manually
|
772
|
+
using the Sequel +set_fields+ methods.
|
773
|
+
|
774
|
+
Note that when using the Roda forme_set plugin with an existing form, you should first
|
775
|
+
enable the Roda plugin without actually using the Roda forme_set method. Do not
|
776
|
+
start using the Roda forme_set method until all currently valid sessions were
|
777
|
+
established after the Roda forme_set plugin was enabled. Otherwise, sessions that
|
778
|
+
access the form before the Roda forme_set plugin was enabled will not work if they
|
779
|
+
submit the form after the Roda forme_set plugin is enabled.
|
688
780
|
|
689
781
|
== Other Sequel Plugins
|
690
782
|
|
@@ -695,9 +787,17 @@ forme_i18n :: Handles translations for labels using i18n.
|
|
695
787
|
|
696
788
|
= Roda Support
|
697
789
|
|
698
|
-
Forme ships with
|
699
|
-
|
700
|
-
|
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
|
+
|
799
|
+
For new code, it is recommended to use forme_route_csrf, as that uses Roda's route_csrf
|
800
|
+
plugin, which supports more secure request-specific CSRF tokens. In both cases, usage in ERB
|
701
801
|
templates is the same:
|
702
802
|
|
703
803
|
<% form(@obj, :action=>'/foo') do |f| %>
|
@@ -722,14 +822,29 @@ The forme plugin does not require any csrf plugin, but will transparently use
|
|
722
822
|
Rack::Csrf if it is available. If Rack::Csrf is available a CSRF tag if the form's
|
723
823
|
method is +POST+, with no configuration ability.
|
724
824
|
|
725
|
-
|
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:
|
726
839
|
|
727
|
-
|
728
|
-
|
840
|
+
require 'erubi/capture_end'
|
841
|
+
app.plugin :render, :engine_opts=>{'erb'=>{:engine_class=>Erubi::CaptureEndEngine}}
|
842
|
+
|
843
|
+
The forme_erubi_capture plugin requires Roda 3.50.0+.
|
729
844
|
|
730
845
|
= Sinatra Support
|
731
846
|
|
732
|
-
Forme ships with a
|
847
|
+
Forme ships with a Sinatra extension that you can get by <tt>require "forme/erb"</tt> and using
|
733
848
|
<tt>including Forme::ERB::Helper</tt>. This is tested to support ERB templates in Sinatra.
|
734
849
|
It allows you to use the following API in your erb templates:
|
735
850
|
|
@@ -740,8 +855,8 @@ It allows you to use the following API in your erb templates:
|
|
740
855
|
<% end %>
|
741
856
|
<% end %>
|
742
857
|
|
743
|
-
In order to this to work transparently, the ERB outvar needs to be <tt>@_out_buf</tt
|
744
|
-
|
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).
|
745
860
|
|
746
861
|
= Rails Support
|
747
862
|
|
@@ -756,7 +871,7 @@ in your Rails forms:
|
|
756
871
|
<% end %>
|
757
872
|
<% end %>
|
758
873
|
|
759
|
-
This has been tested on Rails 3.2-
|
874
|
+
This has been tested on Rails 3.2-6.1.
|
760
875
|
|
761
876
|
= Input Types and Options
|
762
877
|
|
@@ -767,8 +882,8 @@ created via Forme::Form#input:
|
|
767
882
|
|
768
883
|
These options are supported by all of the input types:
|
769
884
|
|
770
|
-
:attr :: The attributes hash to use for the given tag,
|
771
|
-
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.
|
772
887
|
:autofocus :: Set the autofocus attribute if true
|
773
888
|
:class :: A class to use. Unlike other options, this is combined with the
|
774
889
|
classes set in the :attr hash.
|
@@ -780,6 +895,7 @@ These options are supported by all of the input types:
|
|
780
895
|
:disabled :: Set the disabled attribute if true
|
781
896
|
:error :: Set an error message, invoking the error_handler
|
782
897
|
:error_handler :: Set a custom error_handler, overriding the form's default
|
898
|
+
:help :: Set help text to use, invoking the helper
|
783
899
|
:helper :: Set a custom helper, overriding the form's default
|
784
900
|
:id :: The id attribute to use
|
785
901
|
:key :: The base to use for the name and id attributes, based on the current
|
@@ -796,7 +912,7 @@ These options are supported by all of the input types:
|
|
796
912
|
for textarea tags, or the selected option(s) for select tags.
|
797
913
|
:wrapper :: Set a custom wrapper, overriding the form's default
|
798
914
|
|
799
|
-
== Input
|
915
|
+
== Input Type-Specific Options
|
800
916
|
|
801
917
|
=== :checkbox
|
802
918
|
|
@@ -814,16 +930,21 @@ Creates an input tag with type radio. Options:
|
|
814
930
|
|
815
931
|
=== :date / :datetime
|
816
932
|
|
817
|
-
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,
|
818
934
|
creates multiple select options. Options:
|
819
935
|
|
820
936
|
:as :: When value is :select, uses 3 or 6 select boxes by default.
|
821
|
-
: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
|
822
938
|
for the select field and string to use a string
|
823
939
|
(:date default: <tt>[:year, '-', :month, '-', :day]</tt>)
|
824
940
|
(:datetime default: <tt>[:year, '-', :month, '-', :day, ' ', :hour, ':', :minute, ':', :second]</tt>)
|
941
|
+
:select_labels :: The labels to use for the select boxes. Should be a hash keyed by the
|
942
|
+
symbol used (e.g. <tt>{:month=>'Month'}</tt>). By default, no labels are used.
|
825
943
|
:select_options :: The options to use for the select boxes. Should be a hash keyed by the
|
826
|
-
symbol used in order (e.g. <tt>{:year=>1970..2020}</tt>)
|
944
|
+
symbol used in order (e.g. <tt>{:year=>1970..2020}</tt>). The values
|
945
|
+
can be a number used as both the value and the text of the option or
|
946
|
+
an array with two elements, the first of which is the value for the option
|
947
|
+
and the second of which is the text for the option.
|
827
948
|
|
828
949
|
=== :select
|
829
950
|
|
@@ -843,7 +964,8 @@ Creates a select tag, containing option tags specified by the :options option.
|
|
843
964
|
:options :: An enumerable of options used for creating option tags.
|
844
965
|
If the :text_method and :value_method are not given and the entry is an
|
845
966
|
array, uses the first entry of the array as the text of the option, and
|
846
|
-
the
|
967
|
+
the last entry of the array as the value of the option. If the last entry
|
968
|
+
of the array is a hash, uses the hash as the attributes for the option.
|
847
969
|
:selected :: The value that should be selected. Any options that are equal to
|
848
970
|
this value (or included in this value if a multiple select box),
|
849
971
|
are set to selected.
|
@@ -857,11 +979,12 @@ Creates a select tag, containing option tags specified by the :options option.
|
|
857
979
|
=== :checkboxset
|
858
980
|
|
859
981
|
Creates a set of checkbox inputs all using the same name. Supports the same options
|
860
|
-
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
|
861
983
|
the following options:
|
862
984
|
|
863
985
|
:tag_wrapper :: The wrapper transformer for individual tags in the set
|
864
986
|
:tag_labeler :: The labeler transformer for individual tags in the set
|
987
|
+
:tag_label_attr :: The attributes to use for labels for individual tags in the set
|
865
988
|
|
866
989
|
=== :radioset
|
867
990
|
|
@@ -888,6 +1011,10 @@ as text and password, as well as newer HTML5 inputs such as number or email. Opt
|
|
888
1011
|
These are the options supported by Forme::Form object, mostly used to set
|
889
1012
|
the defaults for Inputs created via the form:
|
890
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.
|
891
1018
|
:config :: The configuration to use, which automatically sets defaults
|
892
1019
|
for the transformers to use.
|
893
1020
|
:errors :: A Hash of errors from a previous form submission, used to set
|
@@ -895,7 +1022,6 @@ the defaults for Inputs created via the form:
|
|
895
1022
|
:error_handler :: Sets the default error_handler for the form's inputs
|
896
1023
|
:helper :: Sets the default helper for the form's inputs
|
897
1024
|
:formatter :: Sets the default formatter for the form's inputs
|
898
|
-
:hidden_tags :: Sets the hidden tags to automatically add to this form, see below.
|
899
1025
|
:input_defaults :: Sets the default options for each input type. This should
|
900
1026
|
be a hash with input type keys, where the values are the
|
901
1027
|
hash of default options to use for the input type.
|
@@ -916,17 +1042,7 @@ For forms created by Forme.form, the following options are supported:
|
|
916
1042
|
to the block.
|
917
1043
|
:button :: A button to add to the form, after yielding to the block.
|
918
1044
|
|
919
|
-
|
920
|
-
|
921
|
-
This should be an array, where elements are one of the following types:
|
922
|
-
|
923
|
-
String, Array, Forme::Tag :: Added directly as a child of the form tag.
|
924
|
-
Hash :: Adds a hidden tag for each entry, with keys as the name of the hidden
|
925
|
-
tag and values as the value of the hidden tag.
|
926
|
-
Proc :: Will be called with the form tag object, and should return an instance
|
927
|
-
of one of the handled types (or nil to not add a tag).
|
928
|
-
|
929
|
-
= Basic Design
|
1045
|
+
= Internal Architecture
|
930
1046
|
|
931
1047
|
Internally, Forme builds an abstract syntax tree of objects that
|
932
1048
|
represent the form. The abstract syntax tree goes through a
|
@@ -936,15 +1052,88 @@ strings. Here are the main classes used by the library:
|
|
936
1052
|
|
937
1053
|
<tt>Forme::Form</tt> :: main object
|
938
1054
|
<tt>Forme::Input</tt> :: high level abstract tag (a single +Input+ could represent a select box with a bunch of options)
|
939
|
-
<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'])])
|
940
1066
|
|
941
1067
|
The group of objects that perform the transformations to
|
942
1068
|
the abstract syntax trees are known as transformers.
|
943
1069
|
Transformers use a functional style, and all use a +call+-based
|
944
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 +Proc+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
|
945
1131
|
|
946
1132
|
== Transformer Types
|
947
1133
|
|
1134
|
+
You can override the type of transform for each form or input using the
|
1135
|
+
following options:
|
1136
|
+
|
948
1137
|
+serializer+ :: tags input/tag, returns string
|
949
1138
|
+formatter+ :: takes input, returns tag
|
950
1139
|
+error_handler+ :: takes tag and input, returns version of tag with errors noted
|
@@ -980,7 +1169,9 @@ Forme ships with a bunch of built-in transformers that you can use:
|
|
980
1169
|
|
981
1170
|
=== +error_handler+
|
982
1171
|
|
1172
|
+
:after_legend :: designed for usage with :legend labeler, putting error message after legend, adding error for first input in the set
|
983
1173
|
:default :: modifies tag to add an error class and adds a span with the error message
|
1174
|
+
:set :: default error_handler for checkboxset and radioset inputs, that adds an error to the last input in the set
|
984
1175
|
|
985
1176
|
This supports the following options:
|
986
1177
|
|
@@ -998,8 +1189,10 @@ This supports the following options:
|
|
998
1189
|
|
999
1190
|
:default :: uses implicit labels, where the tag is a child of the label tag
|
1000
1191
|
:explicit :: uses explicit labels with the for attribute, where tag is a sibling of the label tag
|
1192
|
+
:legend :: adds a legend before the tags, mostly useful for accessible checkboxset and radioset inputs
|
1193
|
+
:span :: default labeler for checkboxset and radioset inputs that adds a span before the tags
|
1001
1194
|
|
1002
|
-
|
1195
|
+
The :default and :explicit labelers respect the following options:
|
1003
1196
|
|
1004
1197
|
:label_position :: Can be set to :before or :after to place the label before or after the the input.
|
1005
1198
|
:label_attr :: A hash of attributes to use for the label tag
|
@@ -1008,6 +1201,7 @@ Both of these respect the following options:
|
|
1008
1201
|
|
1009
1202
|
:default :: returns tag without wrapping
|
1010
1203
|
:div :: wraps tag in div tag
|
1204
|
+
:fieldset :: wraps tags in a fieldset, mostly useful for accessible checkboxset and radioset inputs
|
1011
1205
|
:fieldset_ol :: same as :li, but also sets +inputs_wrapper+ to :fieldset_ol
|
1012
1206
|
:li :: wraps tag in li tag
|
1013
1207
|
:ol :: same as :li, but also sets +inputs_wrapper+ to :ol
|
@@ -1072,7 +1266,7 @@ You can mark a configuration as the default using:
|
|
1072
1266
|
|
1073
1267
|
=== Bootstrap 3 Support
|
1074
1268
|
|
1075
|
-
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
|
1076
1270
|
it's own file, so if you don't use it, you don't pay the memory penalty for loading
|
1077
1271
|
it.
|
1078
1272
|
|
@@ -1088,7 +1282,7 @@ All of these have external dependencies:
|
|
1088
1282
|
3. simple_form
|
1089
1283
|
4. padrino-helpers
|
1090
1284
|
|
1091
|
-
Forme's
|
1285
|
+
Forme's API draws a lot of inspiration from both Formtastic and simple_form.
|
1092
1286
|
|
1093
1287
|
= License
|
1094
1288
|
|