forme 1.12.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +54 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +228 -206
  5. data/Rakefile +1 -7
  6. data/lib/forme/bs3.rb +23 -9
  7. data/lib/forme/erb.rb +13 -25
  8. data/lib/forme/form.rb +146 -149
  9. data/lib/forme/input.rb +1 -1
  10. data/lib/forme/rails.rb +39 -83
  11. data/lib/forme/raw.rb +2 -2
  12. data/lib/forme/tag.rb +3 -12
  13. data/lib/forme/template.rb +110 -0
  14. data/lib/forme/transformers/error_handler.rb +10 -10
  15. data/lib/forme/transformers/formatter.rb +32 -34
  16. data/lib/forme/transformers/helper.rb +0 -1
  17. data/lib/forme/transformers/inputs_wrapper.rb +4 -4
  18. data/lib/forme/version.rb +2 -2
  19. data/lib/forme.rb +13 -2
  20. data/lib/roda/plugins/forme.rb +1 -1
  21. data/lib/roda/plugins/forme_erubi_capture.rb +57 -0
  22. data/lib/roda/plugins/forme_route_csrf.rb +16 -34
  23. data/lib/roda/plugins/forme_set.rb +39 -76
  24. data/lib/sequel/plugins/forme.rb +45 -54
  25. data/lib/sequel/plugins/forme_i18n.rb +3 -1
  26. data/lib/sequel/plugins/forme_set.rb +2 -4
  27. data/spec/all.rb +1 -1
  28. data/spec/bs3_reference_spec.rb +291 -314
  29. data/spec/bs3_sequel_plugin_spec.rb +155 -155
  30. data/spec/bs3_spec.rb +247 -206
  31. data/spec/erb_helper.rb +69 -58
  32. data/spec/erubi_capture_helper.rb +198 -0
  33. data/spec/forme_coverage.rb +1 -0
  34. data/spec/forme_spec.rb +438 -377
  35. data/spec/rails_integration_spec.rb +21 -11
  36. data/spec/roda_integration_spec.rb +136 -70
  37. data/spec/sequel_helper.rb +3 -2
  38. data/spec/sequel_i18n_helper.rb +1 -1
  39. data/spec/sequel_i18n_plugin_spec.rb +6 -6
  40. data/spec/sequel_plugin_spec.rb +262 -150
  41. data/spec/sequel_set_plugin_spec.rb +9 -3
  42. data/spec/shared_erb_specs.rb +71 -0
  43. data/spec/sinatra_integration_spec.rb +31 -6
  44. data/spec/spec_helper.rb +21 -8
  45. metadata +8 -6
  46. data/lib/forme/erb_form.rb +0 -74
  47. 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
- simplicity are primary objectives. The basic usage involves creating
14
- a <tt>Forme::Form</tt> instance, and calling +input+ and +tag+ methods
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
- In order to be flexible, Forme stores tags in abstract form until
19
- output is requested. There are two separate abstract <i>forms</i> that Forme
20
- uses. One is <tt>Forme::Input</tt>, and the other is <tt>Forme::Tag</tt>.
21
- <tt>Forme::Input</tt> is a high level abstract form, while <tt>Forme::Tag</tt>
22
- is a low level abstract form.
23
-
24
- The difference between <tt>Forme::Input</tt> and <tt>Forme::Tag</tt>
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
- f.input(:select, :options=>[['foo', 1]])
31
- # or
32
- f.tag(:select, {}, [f.tag(:option, {:value=>1}, ['foo'])])
25
+ This results in the following HTML:
33
26
 
34
- The processing of high level <tt>Forme::Input</tt>s into raw html
35
- data is broken down to the following steps (called transformers):
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
- +Formatter+ :: converts a <tt>Forme::Input</tt> instance into a
38
- <tt>Forme::Tag</tt> instance (or array of them).
39
- +ErrorHandler+ :: If the <tt>Forme::Input</tt> instance has a error,
40
- takes the formatted tag and marks it as having the error.
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.register_transformer(:wrapper, :p) do |tag, input|
66
- input.tag(:p, {}, tag)
40
+ Forme.form(Album[1], action: '/foo') do |f|
41
+ f.input :name
42
+ f.input :copies_sold
67
43
  end
68
44
 
69
- Most transformers are called with two arguments, +tag+ and +input+. +tag+
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
- Most of the transformers can be overridden on a per instance basis by
83
- passing the appropriate option to +input+ or +inputs+:
84
-
85
- f.input(:name, :wrapper=>:p)
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
- Existing transformers can be easily extended (ie, to set the class attribute),
88
- by creating your own transformer and then calling the existing transformer.
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
- Forme.register_transformer(:labeler, :explicit) do |tag, input|
91
- input.opts[:label_attr] ||= { :class => 'label' }
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
- = Basic Usage
91
+ = Direct Instantiation
108
92
 
109
- Without an object, Forme is a simple form builder:
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
- As shown above, the general way to create Forme::Form instances is via the Forme.form method.
171
- This method takes up to 3 arguments, and yields the Forme::Form object to the block (if given).
172
- Here are the argument styles that you can use for Forme.form.
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(:action=>'/foo')
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({:action=>'/foo'}, :values=>params)
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], :action=>'/foo')
202
- Forme.form(Album[1], {:action=>'/foo'}, :values=>params)
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({:action=>'/foo'}, :obj=>{:foo=>'bar'})
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(:values=>params)
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], :values=>params)
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(:action=>'/foo')
180
+ f.form(action: '/foo')
233
181
 
234
- This is what Forme.form uses internally to create the <form> tag
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 :legend
303
- to set a legend for the fieldset), and also to the the with_opts (so you can use options
304
- such as :wrapper to modify the default wrapper transformer for inputs inside the block).
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 :inputs_wrapper
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(:value=>'Search', :class=>'btn')
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
- f.with_opts(:wrapper=>:li) do
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 the Form, but not all.
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's object temporarily, but it
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=>'a') do |f|
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. Forme will call with_obj with
361
- each object in the enumerable. It yields each object as well as the index of 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. It falls back to the behaviour of Sequel::Model#underscore.
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
- == Basics
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
- == specialized input options
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="number" value="1"/></label>
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="number" value="2"/></label>
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 Submitted parameters becomes more complex as your forms become more complex.
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 Sequel plugin
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 :obj option is provided to change the input.
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 Roda plugin
621
+ === Roda forme_set plugin
689
622
 
690
- The forme_set Roda plugin builds on the forme_set Sequel plugin and is designed to make
691
- handling form submissions even easier. This plugin uses a hidden form input to store which
692
- fields were used to build the form, as well as some other metadata. It uses another hidden
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 forms default namespace. The Sequel forme_set plugin
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 use the form's namespace information and the submitted
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 :form_version key in the hash for the
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 three Roda plugins: forme_set (discussed above), forme, and forme_route_csrf.
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
- Both plugins also support the following option:
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
- :output :: The object that the form output should be injected into when
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 ERB extension that you can get by <tt>require "forme/erb"</tt> and using
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>. If that
903
- is not the case, use the :output option to +form+ to specify the outvar.
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-5.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, takes precedence over
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 Types
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 :as=>:select option,
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 :as=>:select. Entries should be a symbol
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
- === :hidden_tags
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 html tag (there would be a separate +Tag+ for each option in a select box)
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 DSL draws a lot of inspiration from both Formtastic and simple_form.
1285
+ Forme's API draws a lot of inspiration from both Formtastic and simple_form.
1264
1286
 
1265
1287
  = License
1266
1288