forme 2.5.0 → 2.7.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 +26 -0
- data/README.rdoc +63 -37
- data/lib/forme/bs3.rb +2 -2
- data/lib/forme/tag.rb +2 -2
- data/lib/forme/template.rb +5 -5
- data/lib/forme/transformers/formatter.rb +9 -7
- data/lib/forme/transformers/labeler.rb +18 -10
- data/lib/forme/transformers/serializer.rb +10 -4
- data/lib/forme/version.rb +1 -1
- data/lib/forme.rb +4 -10
- data/lib/roda/plugins/forme_erubi_capture_block.rb +45 -0
- data/lib/roda/plugins/forme_set.rb +1 -1
- data/lib/sequel/plugins/forme.rb +6 -1
- metadata +5 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: af9c688d8d8f4432cfe214dbc58332a11d57c50e9309b96ba30e7396865a19a7
|
4
|
+
data.tar.gz: 7c66b8036e5d34a78055e8ba046501f70dcab1b066bf0836548acc5219bcc5b9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aa5077f2c26acd92526dc3db78aa45c69c5c7ede218120b4eed39683bf8bf4a351b418bd8f8765fd9612e72d0e8879eab5617c9ac9d193103ff5b2c95afd17a6
|
7
|
+
data.tar.gz: b1999c8bc8ee43a1f3dc1a4601087f067750118cff71b0caf9ba0729e4b7555ffbd3f58ded0bda79f5a53c37a212e79951651e60e9f063c320f6dcda62f67237
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,29 @@
|
|
1
|
+
=== 2.7.0 (2025-09-16)
|
2
|
+
|
3
|
+
* Omit closing / for void element tags (only used previously for xhtml compliance) (jeremyevans)
|
4
|
+
|
5
|
+
* Allow emit: false option to work correctly for non-model template forms (jeremyevans)
|
6
|
+
|
7
|
+
* Support :label_attr as an option-specific option for radioset/checkboxset options (jeremyevans)
|
8
|
+
|
9
|
+
* Allow Tag#initialize to accept a block, and treat it the same as the children argument (jeremyevans)
|
10
|
+
|
11
|
+
* Add Labeler::Explicit#id_for_input private method to allow subclasses to easily get the id (jeremyevans)
|
12
|
+
|
13
|
+
* Support Sequel::Model#forme_required_abbr_title to control the content of the title attribute for abbr tags (jeremyevans)
|
14
|
+
|
15
|
+
* Add Serializer#self_closing_tag? private method for easier overriding in subclasses (jeremyevans)
|
16
|
+
|
17
|
+
=== 2.6.0 (2024-06-18)
|
18
|
+
|
19
|
+
* Add Roda forme_erubi_capture_block plugin to support erubi/capture_block <%= form do %> <% end %> tags (jeremyevans)
|
20
|
+
|
21
|
+
* Support :hr as select option value to use a hr tag instead of an option tag (jeremyevans)
|
22
|
+
|
23
|
+
* Support maxlength and minlength options as attributes for textareas (jeremyevans)
|
24
|
+
|
25
|
+
* Support minlength option as attribute for text inputs (jeremyevans)
|
26
|
+
|
1
27
|
=== 2.5.0 (2024-02-13)
|
2
28
|
|
3
29
|
* Add hidden inputs to work with formaction/formmethod support in Roda 3.77+ route_csrf plugin (jeremyevans)
|
data/README.rdoc
CHANGED
@@ -25,11 +25,11 @@ showing usage without a related object:
|
|
25
25
|
This results in the following HTML:
|
26
26
|
|
27
27
|
<form action="/foo">
|
28
|
-
<input name="bar" type="text"
|
28
|
+
<input name="bar" type="text">
|
29
29
|
<fieldset>
|
30
30
|
<textarea name="baz"></textarea>
|
31
31
|
</fieldset>
|
32
|
-
<input type="submit" value="Update"
|
32
|
+
<input type="submit" value="Update">
|
33
33
|
</form>
|
34
34
|
|
35
35
|
Forme also supports forms that are associated with objects, and
|
@@ -46,10 +46,10 @@ This results in the following HTML:
|
|
46
46
|
|
47
47
|
<form action="/foo" method="post">
|
48
48
|
<label>Name:
|
49
|
-
<input id="album_name" name="album[name]" type="text" value="Rising Force"
|
49
|
+
<input id="album_name" name="album[name]" type="text" value="Rising Force">
|
50
50
|
</label>
|
51
51
|
<label>Copies Sold:
|
52
|
-
<input id="album_copies_sold" inputmode="numeric" name="album[copies_sold]" pattern="-?[0-9]*" type="text" value="100000"
|
52
|
+
<input id="album_copies_sold" inputmode="numeric" name="album[copies_sold]" pattern="-?[0-9]*" type="text" value="100000">
|
53
53
|
</label>
|
54
54
|
</form>
|
55
55
|
|
@@ -95,7 +95,7 @@ While not typically done, you can instantiate Forme::Form objects directly and u
|
|
95
95
|
f = Forme::Form.new
|
96
96
|
f.open(:action=>'/foo', :method=>:post) # '<form action="/foo" method="post">'
|
97
97
|
f.input(:textarea, :value=>'foo', :name=>'bar') # '<textarea name="bar">foo</textarea>'
|
98
|
-
f.input(:text, :value=>'foo', :name=>'bar') # '<input name="bar" type="text" value="foo"
|
98
|
+
f.input(:text, :value=>'foo', :name=>'bar') # '<input name="bar" type="text" value="foo">'
|
99
99
|
f.close # '</form>'
|
100
100
|
|
101
101
|
With an object, <tt>Form#input</tt> calls +forme_input+ on the obj with the form, field, and options, which
|
@@ -104,14 +104,14 @@ should return a <tt>Forme::Input</tt> or <tt>Forme::Tag</tt> instance. Also, in
|
|
104
104
|
the entire form based on the object.
|
105
105
|
|
106
106
|
f = Forme::Form.new(obj)
|
107
|
-
f.input(:field) # '<input id="obj_field" name="obj[field]" type="text" value="foo"
|
107
|
+
f.input(:field) # '<input id="obj_field" name="obj[field]" type="text" value="foo">'
|
108
108
|
|
109
109
|
If the object doesn't respond to +forme_input+, it falls back to creating text fields
|
110
110
|
with the name and id set to the field name and the value set by calling the given method
|
111
111
|
on the object (or using #[] if the object is a hash).
|
112
112
|
|
113
113
|
f = Forme::Form.new([:foo])
|
114
|
-
f.input(:first) # '<input id="first" name="first" type="text" value="foo"
|
114
|
+
f.input(:first) # '<input id="first" name="first" type="text" value="foo">'
|
115
115
|
|
116
116
|
= Forme::Form Creation
|
117
117
|
|
@@ -187,25 +187,25 @@ This adds an input to the form. If the form has an associated object, and that
|
|
187
187
|
object responds to +forme_input+, calls forme_input with the argument and options:
|
188
188
|
|
189
189
|
f = Forme::Form.new(obj)
|
190
|
-
f.input(:field) # '<input id="obj_field" name="obj[field]" type="text" value="foo"
|
190
|
+
f.input(:field) # '<input id="obj_field" name="obj[field]" type="text" value="foo">'
|
191
191
|
|
192
192
|
If the form has an associated object, and that object does not respond to +forme_input+,
|
193
193
|
calls the method on the object (or uses [] if the object is a hash), and uses the result
|
194
194
|
as the value for a text input:
|
195
195
|
|
196
196
|
f = Forme::Form.new([:foo])
|
197
|
-
f.input(:first) # '<input id="first" name="first" type="text" value="foo"
|
197
|
+
f.input(:first) # '<input id="first" name="first" type="text" value="foo">'
|
198
198
|
|
199
199
|
If the object does not respond to +forme_input+, you can change the type of the input
|
200
200
|
via the +:type+ option:
|
201
201
|
|
202
202
|
f = Forme::Form.new(obj)
|
203
|
-
f.input(:field, :type=>:email) # '<input id="obj_field" name="obj[field]" type="email" value="foo"
|
203
|
+
f.input(:field, :type=>:email) # '<input id="obj_field" name="obj[field]" type="email" value="foo">'
|
204
204
|
|
205
205
|
If the form does not have an associated object, the first argument is used as the input type:
|
206
206
|
|
207
207
|
f = Forme::Form.new
|
208
|
-
f.input(:text) # '<input type="text"
|
208
|
+
f.input(:text) # '<input type="text" >'
|
209
209
|
|
210
210
|
The second argument is an options hash. See below for the supported input types and options.
|
211
211
|
|
@@ -226,7 +226,7 @@ Which results in a form similar to the following:
|
|
226
226
|
|
227
227
|
<form>
|
228
228
|
<span class="foo">
|
229
|
-
<input type="text"
|
229
|
+
<input type="text">
|
230
230
|
</span>
|
231
231
|
</form>
|
232
232
|
|
@@ -238,7 +238,7 @@ so it uses a +fieldset+ by default). You can give the inputs to add as an enumer
|
|
238
238
|
f.inputs([:textarea, [:text, :value=>'a']])
|
239
239
|
# <fieldset>
|
240
240
|
# <textarea></textarea>
|
241
|
-
# <input type="text" value="a"
|
241
|
+
# <input type="text" value="a">
|
242
242
|
# </fieldset>
|
243
243
|
|
244
244
|
You can also provide a block:
|
@@ -262,17 +262,17 @@ There is also one option specific to the inputs method:
|
|
262
262
|
This adds a submit input to the form:
|
263
263
|
|
264
264
|
f.button
|
265
|
-
# <input type="submit"
|
265
|
+
# <input type="submit">
|
266
266
|
|
267
267
|
It can be called with a string to provide a value for the button:
|
268
268
|
|
269
269
|
f.button('Search')
|
270
|
-
# <input type="submit" value="Search"
|
270
|
+
# <input type="submit" value="Search">
|
271
271
|
|
272
272
|
It can be called with a hash to provide options for the submit input:
|
273
273
|
|
274
274
|
f.button(value: 'Search', class: 'btn')
|
275
|
-
# <input class="btn" type="submit" value="Search"
|
275
|
+
# <input class="btn" type="submit" value="Search">
|
276
276
|
|
277
277
|
== with_opts
|
278
278
|
|
@@ -280,12 +280,12 @@ This requires a block, and modifies the Forme::Form's options inside the block,
|
|
280
280
|
restoring the options when the block returns:
|
281
281
|
|
282
282
|
f.input(:text)
|
283
|
-
# <input type="text"
|
283
|
+
# <input type="text">
|
284
284
|
|
285
285
|
f.with_opts(wrapper: :li) do
|
286
286
|
f.input(:text)
|
287
287
|
end
|
288
|
-
# <li><input type="text"
|
288
|
+
# <li><input type="text"></li>
|
289
289
|
|
290
290
|
This supports most options you can provide to Forme::Form, but not all.
|
291
291
|
|
@@ -297,10 +297,10 @@ existing namespaces:
|
|
297
297
|
|
298
298
|
Forme.form([:foo], {action: '/path'}, namespace: 'a') do |f|
|
299
299
|
f.input(:first)
|
300
|
-
# <input id="a_first" name="a[first]" type="text" value="foo"
|
300
|
+
# <input id="a_first" name="a[first]" type="text" value="foo">
|
301
301
|
f.with_obj(['foobar'], 'b') do |o|
|
302
302
|
f.input(:first, :size=>o.first.size)
|
303
|
-
# <input id="a_b_first" name="a[b][first]" size="6" type="text" value="foobar"
|
303
|
+
# <input id="a_b_first" name="a[b][first]" size="6" type="text" value="foobar">
|
304
304
|
end
|
305
305
|
end
|
306
306
|
|
@@ -315,8 +315,8 @@ index of the object in the enumerable, and includes the index in the namespace:
|
|
315
315
|
f.each_obj(objectlist, 'b') do |o, i|
|
316
316
|
f.input(:first, :size=>10+i)
|
317
317
|
end
|
318
|
-
# <input id="a_b_0_first" name="a[b][0][first]" size="10" type="text" value="foobar"
|
319
|
-
# <input id="a_b_1_first" name="a[b][1][first]" size="11" type="text" value="good"
|
318
|
+
# <input id="a_b_0_first" name="a[b][0][first]" size="10" type="text" value="foobar">
|
319
|
+
# <input id="a_b_1_first" name="a[b][1][first]" size="11" type="text" value="good">
|
320
320
|
end
|
321
321
|
|
322
322
|
= Sequel Support
|
@@ -375,16 +375,16 @@ to the current object.
|
|
375
375
|
This will create a form similar to:
|
376
376
|
|
377
377
|
<form action="/foo" method="post">
|
378
|
-
<label>Name: <input id="album_name" name="album[name]" type="text" value="Blue Hawaii"
|
378
|
+
<label>Name: <input id="album_name" name="album[name]" type="text" value="Blue Hawaii"></label>
|
379
379
|
<label>Artist: <select id="album_artist_id" name="album[artist_id]">
|
380
380
|
<option selected="selected" value="1">Elvis Presley</option>
|
381
381
|
<option value="2">The Beatles</option>
|
382
382
|
<option value="3">The Monkeys</option>
|
383
383
|
</select></label>
|
384
384
|
<span class="label">Tags:
|
385
|
-
<label><input checked="checked" id="album_tag_pks_1" name="album[tag_pks][]" type="checkbox" value="1"
|
386
|
-
<label><input id="album_tag_pks_2" name="album[tag_pks][]" type="checkbox" value="2"
|
387
|
-
<label><input id="album_tag_pks_3" name="album[tag_pks][]" type="checkbox" value="3"
|
385
|
+
<label><input checked="checked" id="album_tag_pks_1" name="album[tag_pks][]" type="checkbox" value="1"> Rock and Roll</label>
|
386
|
+
<label><input id="album_tag_pks_2" name="album[tag_pks][]" type="checkbox" value="2"> Blues</label>
|
387
|
+
<label><input id="album_tag_pks_3" name="album[tag_pks][]" type="checkbox" value="3"> Country</label>
|
388
388
|
</span>
|
389
389
|
</form>
|
390
390
|
|
@@ -434,22 +434,22 @@ form similar to:
|
|
434
434
|
|
435
435
|
<form action="/foo" method="post">
|
436
436
|
|
437
|
-
<label>Name: <input id="album_name" name="album[name]" type="text" value="Blue Hawaii"
|
437
|
+
<label>Name: <input id="album_name" name="album[name]" type="text" value="Blue Hawaii"></label>
|
438
438
|
|
439
|
-
<input id="album_artist_attributes_id" name="album[artist_attributes][id]" type="hidden" value="1"
|
439
|
+
<input id="album_artist_attributes_id" name="album[artist_attributes][id]" type="hidden" value="1">
|
440
440
|
<fieldset class="inputs"><legend>Artist</legend>
|
441
|
-
<label>Name: <input id="album_artist_attributes_name" name="album[artist_attributes][name]" type="text" value="Elvis Presley"
|
441
|
+
<label>Name: <input id="album_artist_attributes_name" name="album[artist_attributes][name]" type="text" value="Elvis Presley"></label>
|
442
442
|
</fieldset>
|
443
443
|
|
444
|
-
<input id="album_tracks_attributes_0_id" name="album[tracks_attributes][0][id]" type="hidden" value="1"
|
444
|
+
<input id="album_tracks_attributes_0_id" name="album[tracks_attributes][0][id]" type="hidden" value="1">
|
445
445
|
<fieldset class="inputs"><legend>Track #1</legend>
|
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"
|
447
|
-
<label>Name: <input id="album_tracks_attributes_0_name" name="album[tracks_attributes][0][name]" type="text" value="Blue Hawaii"
|
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>
|
447
|
+
<label>Name: <input id="album_tracks_attributes_0_name" name="album[tracks_attributes][0][name]" type="text" value="Blue Hawaii"></label>
|
448
448
|
</fieldset>
|
449
|
-
<input id="album_tracks_attributes_1_id" name="album[tracks_attributes][1][id]" type="hidden" value="2"
|
449
|
+
<input id="album_tracks_attributes_1_id" name="album[tracks_attributes][1][id]" type="hidden" value="2">
|
450
450
|
<fieldset class="inputs"><legend>Track #2</legend>
|
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"
|
452
|
-
<label>Name: <input id="album_tracks_attributes_1_name" name="album[tracks_attributes][1][name]" type="text" value="Almost Always True"
|
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>
|
452
|
+
<label>Name: <input id="album_tracks_attributes_1_name" name="album[tracks_attributes][1][name]" type="text" value="Almost Always True"></label>
|
453
453
|
</fieldset>
|
454
454
|
|
455
455
|
</form>
|
@@ -615,8 +615,8 @@ internally). +forme_parse+ returns a hash with the following keys:
|
|
615
615
|
check that the submitted values for associated objects match one of the
|
616
616
|
options for the input in the form.
|
617
617
|
|
618
|
-
It is possible to use +forme_set+ for the
|
619
|
-
|
618
|
+
It is possible to use +forme_set+ for the forms it can handle, and use +forme_parse+ and
|
619
|
+
+set_fields+ for other forms.
|
620
620
|
|
621
621
|
=== Roda forme_set plugin
|
622
622
|
|
@@ -792,6 +792,7 @@ Forme ships with multiple Roda plugins
|
|
792
792
|
* forme_set (discussed above)
|
793
793
|
* forme
|
794
794
|
* forme_route_csrf
|
795
|
+
* forme_erubi_capture_block
|
795
796
|
* forme_erubi_capture
|
796
797
|
|
797
798
|
== forme_route_csrf and forme plugins
|
@@ -837,6 +838,26 @@ The forme plugin does not require any csrf plugin, but will transparently use
|
|
837
838
|
Rack::Csrf if it is available. If Rack::Csrf is available a CSRF tag if the form's
|
838
839
|
method is +POST+, with no configuration ability.
|
839
840
|
|
841
|
+
== forme_erubi_capture_block plugin
|
842
|
+
|
843
|
+
The forme_erubi_capture_block plugin builds on the forme_route_csrf plugin, but it supports
|
844
|
+
the erubi/capture_block engine, which allows this syntax:
|
845
|
+
|
846
|
+
<%= form(@obj, :action=>'/foo') do |f| %>
|
847
|
+
<%= f.input(:field) %>
|
848
|
+
<%= f.tag(:fieldset) do %>
|
849
|
+
<%= f.input(:field_two) %>
|
850
|
+
<% end %>
|
851
|
+
<% end %>
|
852
|
+
|
853
|
+
If you use the forme_erubi_capture)block plugin, you need to manually set Roda to use the
|
854
|
+
erubi/capture_block engine, which you can do via:
|
855
|
+
|
856
|
+
require 'erubi/capture_block'
|
857
|
+
app.plugin :render, :engine_opts=>{'erb'=>{:engine_class=>Erubi::CaptureBlockEngine}}
|
858
|
+
|
859
|
+
The forme_erubi_capture plugin requires Erubi 1.13.0+.
|
860
|
+
|
840
861
|
== forme_erubi_capture plugin
|
841
862
|
|
842
863
|
The forme_erubi_capture plugin builds on the forme_route_csrf plugin, but it supports
|
@@ -982,6 +1003,8 @@ Creates a select tag, containing option tags specified by the :options option.
|
|
982
1003
|
array, uses the first entry of the array as the text of the option, and
|
983
1004
|
the last entry of the array as the value of the option. If the last entry
|
984
1005
|
of the array is a hash, uses the hash as the attributes for the option.
|
1006
|
+
If the option value is +:hr+, uses an hr tag (allowed in recent versions of
|
1007
|
+
the HTML standard).
|
985
1008
|
:selected :: The value that should be selected. Any options that are equal to
|
986
1009
|
this value (or included in this value if a multiple select box),
|
987
1010
|
are set to selected.
|
@@ -1013,6 +1036,8 @@ Creates a textarea tag. Options:
|
|
1013
1036
|
|
1014
1037
|
:cols :: The number of columns in the text area.
|
1015
1038
|
:rows :: The number of rows in the text area.
|
1039
|
+
:maxlength :: Use the maxlength attribute on the tag
|
1040
|
+
:minlength :: Use the minlength attribute on the tag
|
1016
1041
|
|
1017
1042
|
== all others
|
1018
1043
|
|
@@ -1021,6 +1046,7 @@ as text and password, as well as newer HTML5 inputs such as number or email. Opt
|
|
1021
1046
|
|
1022
1047
|
:size :: Uses the size attribute on the tag
|
1023
1048
|
:maxlength :: Use the maxlength attribute on the tag
|
1049
|
+
:minlength :: Use the minlength attribute on the tag
|
1024
1050
|
|
1025
1051
|
== Form options
|
1026
1052
|
|
data/lib/forme/bs3.rb
CHANGED
@@ -356,7 +356,7 @@ module Forme
|
|
356
356
|
case tag.attr[:type].to_sym
|
357
357
|
when :checkbox, :radio, :hidden
|
358
358
|
# .form-control class causes rendering problems, so remove if found
|
359
|
-
tag.attr[:class].gsub
|
359
|
+
tag.attr[:class] = tag.attr[:class].gsub(/\s*form-control\s*/,'') if tag.attr[:class]
|
360
360
|
tag.attr[:class] = nil if tag.attr[:class] && tag.attr[:class].empty?
|
361
361
|
|
362
362
|
when :file
|
@@ -380,7 +380,7 @@ module Forme
|
|
380
380
|
tag.attr[:class] = "form-control #{klass.gsub(/\s*form-control\s*/,'')}".strip
|
381
381
|
end
|
382
382
|
|
383
|
-
return "<#{tag.type}#{attr_html(tag.attr)}
|
383
|
+
return "<#{tag.type}#{attr_html(tag.attr)}>"
|
384
384
|
|
385
385
|
when :textarea, :select
|
386
386
|
klass = tag.attr[:class] ? "form-control #{tag.attr[:class].to_s}" : ''
|
data/lib/forme/tag.rb
CHANGED
@@ -18,9 +18,9 @@ module Forme
|
|
18
18
|
attr_reader :children
|
19
19
|
|
20
20
|
# Set the +form+, +type+, +attr+, and +children+.
|
21
|
-
def initialize(form, type, attr={}, children=nil)
|
21
|
+
def initialize(form, type, attr={}, children=nil, &block)
|
22
22
|
@form, @type, @attr = form, type, (attr||{})
|
23
|
-
@children = parse_children(children)
|
23
|
+
@children = parse_children(children||block)
|
24
24
|
end
|
25
25
|
|
26
26
|
# Create a new +Tag+ instance with the given arguments and block
|
data/lib/forme/template.rb
CHANGED
@@ -58,14 +58,14 @@ module Forme
|
|
58
58
|
# using the standard ERB hidden tags.
|
59
59
|
def form(obj=nil, attr={}, opts={}, &block)
|
60
60
|
if obj.is_a?(Hash)
|
61
|
-
|
62
|
-
|
61
|
+
opts = attr.dup
|
62
|
+
attr = obj
|
63
|
+
obj = opts.delete(:obj)
|
63
64
|
else
|
64
|
-
|
65
|
-
options = opts = opts.dup
|
65
|
+
opts = opts.dup
|
66
66
|
end
|
67
67
|
|
68
|
-
_forme_form_options(obj,
|
68
|
+
_forme_form_options(obj, attr, opts)
|
69
69
|
_forme_form(obj, attr, opts, &block)
|
70
70
|
end
|
71
71
|
|
@@ -178,7 +178,7 @@ module Forme
|
|
178
178
|
# with the type attribute set to input.
|
179
179
|
def _format_input(type)
|
180
180
|
@attr[:type] = type
|
181
|
-
copy_options_to_attributes([:size, :maxlength])
|
181
|
+
copy_options_to_attributes([:size, :maxlength, :minlength])
|
182
182
|
tag(:input)
|
183
183
|
end
|
184
184
|
|
@@ -237,9 +237,9 @@ module Forme
|
|
237
237
|
last_input = nil
|
238
238
|
ret = process_select_optgroups(:_format_set_optgroup) do |label, value, sel, attrs|
|
239
239
|
value = label if value.nil?
|
240
|
-
label_attr = {:class=>:option}
|
241
|
-
label_attr.merge
|
242
|
-
r_opts = attrs.merge(tag_attrs).merge(:label=>label||value, :label_attr=>label_attr, :wrapper=>tag_wrapper, :labeler=>tag_labeler, :error=>nil, :error_attr=>nil)
|
240
|
+
label_attr = attrs[:label_attr] || {:class=>:option}
|
241
|
+
label_attr = label_attr.merge(tag_label_attr) if tag_label_attr
|
242
|
+
r_opts = attrs.merge(tag_attrs).merge!(:label=>label||value, :label_attr=>label_attr, :wrapper=>tag_wrapper, :labeler=>tag_labeler, :error=>nil, :error_attr=>nil, :parent=>self)
|
243
243
|
if r_opts[:value].nil?
|
244
244
|
r_opts[:value] = value unless value.nil?
|
245
245
|
end
|
@@ -277,7 +277,7 @@ module Forme
|
|
277
277
|
# Formats a textarea. Respects the following options:
|
278
278
|
# :value :: Sets value as the child of the textarea.
|
279
279
|
def format_textarea
|
280
|
-
copy_options_to_attributes([:cols, :rows])
|
280
|
+
copy_options_to_attributes([:cols, :rows, :maxlength, :minlength])
|
281
281
|
if val = @attr.delete(:value)
|
282
282
|
tag(:textarea, @attr, [val])
|
283
283
|
else
|
@@ -452,7 +452,9 @@ module Forme
|
|
452
452
|
|
453
453
|
os.map do |x|
|
454
454
|
attr = {}
|
455
|
-
if
|
455
|
+
if x == :hr
|
456
|
+
next form._tag(:hr, {})
|
457
|
+
elsif tm
|
456
458
|
text = x.send(tm)
|
457
459
|
val = x.send(vm) if vm
|
458
460
|
elsif x.is_a?(Array)
|
@@ -577,7 +579,7 @@ module Forme
|
|
577
579
|
when String
|
578
580
|
text = text.gsub(/\A[\r\n]+|[\r\n]+\z/, '').split(/(?:\r?\n)(?:\r?\n)+/).map do |t|
|
579
581
|
t = Forme.h(t)
|
580
|
-
t.gsub!(/\r?\n/, "<br
|
582
|
+
t.gsub!(/\r?\n/, "<br>")
|
581
583
|
tag(:p, {}, Forme.raw(t))
|
582
584
|
end
|
583
585
|
end
|
@@ -50,16 +50,7 @@ module Forme
|
|
50
50
|
# :label_for option is used, the label created will not be
|
51
51
|
# associated with an input.
|
52
52
|
def call(tag, input)
|
53
|
-
|
54
|
-
if key = input.opts[:key]
|
55
|
-
namespaces = input.form_opts[:namespace]
|
56
|
-
id = "#{namespaces.join('_')}#{'_' unless namespaces.empty?}#{key}"
|
57
|
-
if key_id = input.opts[:key_id]
|
58
|
-
id += "_#{key_id.to_s}"
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
53
|
+
id = id_for_input(input)
|
63
54
|
label_attr = input.opts[:label_attr]
|
64
55
|
label_attr = label_attr ? label_attr.dup : {}
|
65
56
|
label_attr[:for] ||= input.opts.fetch(:label_for, id)
|
@@ -76,6 +67,23 @@ module Forme
|
|
76
67
|
|
77
68
|
t
|
78
69
|
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
# Determine the appropriate id for the input, to use in the for attribute
|
74
|
+
def id_for_input(input)
|
75
|
+
unless id = input.opts[:id]
|
76
|
+
if key = input.opts[:key]
|
77
|
+
namespaces = input.form_opts[:namespace]
|
78
|
+
id = "#{namespaces.join('_')}#{'_' unless namespaces.empty?}#{key}"
|
79
|
+
if key_id = input.opts[:key_id]
|
80
|
+
id += "_#{key_id.to_s}"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
id
|
86
|
+
end
|
79
87
|
end
|
80
88
|
|
81
89
|
class Labeler::Span
|
@@ -9,7 +9,7 @@ module Forme
|
|
9
9
|
Forme.register_transformer(:serializer, :default, new)
|
10
10
|
|
11
11
|
# Which tags are self closing (such tags ignore children).
|
12
|
-
SELF_CLOSING = [:img, :input]
|
12
|
+
SELF_CLOSING = [:img, :input, :hr]
|
13
13
|
|
14
14
|
# Serialize the tag object to an html string. Supports +Tag+ instances,
|
15
15
|
# +Input+ instances (recursing into +call+ with the result of formatting the input),
|
@@ -19,8 +19,8 @@ module Forme
|
|
19
19
|
def call(tag)
|
20
20
|
case tag
|
21
21
|
when Tag
|
22
|
-
if
|
23
|
-
"<#{tag.type}#{attr_html(tag.attr)}
|
22
|
+
if self_closing_tag?(tag.type)
|
23
|
+
"<#{tag.type}#{attr_html(tag.attr)}>"
|
24
24
|
else
|
25
25
|
"#{serialize_open(tag)}#{call(tag.children)}#{serialize_close(tag)}"
|
26
26
|
end
|
@@ -79,6 +79,12 @@ module Forme
|
|
79
79
|
attr = attr.to_a.reject{|k,v| v.nil?}
|
80
80
|
" #{attr.map{|k, v| "#{k}=\"#{attr_value(v)}\""}.sort.join(' ')}" unless attr.empty?
|
81
81
|
end
|
82
|
+
|
83
|
+
# Return whether the tag is self closing. this can be overridden in subclasses
|
84
|
+
# to make additional types self closing.
|
85
|
+
def self_closing_tag?(type)
|
86
|
+
SELF_CLOSING.include?(type)
|
87
|
+
end
|
82
88
|
end
|
83
89
|
|
84
90
|
# Overrides formatting of dates and times to use an American format without
|
@@ -94,7 +100,7 @@ module Forme
|
|
94
100
|
attr.delete(:type)
|
95
101
|
attr.delete('type')
|
96
102
|
attr['type'] = 'text'
|
97
|
-
"<#{tag.type}#{attr_html(attr)}
|
103
|
+
"<#{tag.type}#{attr_html(attr)}>"
|
98
104
|
else
|
99
105
|
super
|
100
106
|
end
|
data/lib/forme/version.rb
CHANGED
data/lib/forme.rb
CHANGED
@@ -25,16 +25,10 @@ module Forme
|
|
25
25
|
rescue LoadError
|
26
26
|
ESCAPE_TABLE = {'&' => '&', '<' => '<', '>' => '>', '"' => '"', "'" => '''}.freeze
|
27
27
|
ESCAPE_TABLE.each_value(&:freeze)
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
value.to_s.gsub(/[&<>"']/, ESCAPE_TABLE)
|
33
|
-
end
|
34
|
-
else
|
35
|
-
def self.h(value)
|
36
|
-
value.to_s.gsub(/[&<>"']/){|s| ESCAPE_TABLE[s]}
|
37
|
-
end
|
28
|
+
# Escape the following characters with their HTML/XML
|
29
|
+
# equivalents.
|
30
|
+
def self.h(value)
|
31
|
+
value.to_s.gsub(/[&<>"']/, ESCAPE_TABLE)
|
38
32
|
end
|
39
33
|
end
|
40
34
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
require_relative '../../forme/template'
|
4
|
+
|
5
|
+
class Roda
|
6
|
+
module RodaPlugins
|
7
|
+
module FormeErubiCaptureBlock
|
8
|
+
def self.load_dependencies(app)
|
9
|
+
app.plugin :forme_route_csrf
|
10
|
+
end
|
11
|
+
|
12
|
+
class Form < ::Forme::Template::Form
|
13
|
+
%w'inputs tag subform'.each do |meth|
|
14
|
+
class_eval(<<-END, __FILE__, __LINE__+1)
|
15
|
+
def #{meth}(*)
|
16
|
+
if block_given?
|
17
|
+
@scope.instance_variable_get(@scope.render_opts[:template_opts][:outvar]).capture{super}
|
18
|
+
else
|
19
|
+
super
|
20
|
+
end
|
21
|
+
end
|
22
|
+
END
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
module InstanceMethods
|
27
|
+
def _forme_form(obj, attr, opts, &block)
|
28
|
+
if block && opts[:emit] != false
|
29
|
+
instance_variable_get(render_opts[:template_opts][:outvar]).capture{super}
|
30
|
+
else
|
31
|
+
super
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def _forme_form_class
|
38
|
+
Form
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
register_plugin(:forme_erubi_capture_block, FormeErubiCaptureBlock)
|
44
|
+
end
|
45
|
+
end
|
data/lib/sequel/plugins/forme.rb
CHANGED
@@ -223,7 +223,7 @@ module Sequel # :nodoc:
|
|
223
223
|
# is required.
|
224
224
|
def handle_label(f)
|
225
225
|
opts[:label] = humanize(field) unless opts.has_key?(:label)
|
226
|
-
opts[:label] = [opts[:label], form._tag(:abbr, {:title=>
|
226
|
+
opts[:label] = [opts[:label], form._tag(:abbr, {:title=>obj.forme_required_abbr_title}, '*')] if opts[:label] && opts[:required] && obj.forme_use_required_abbr?
|
227
227
|
end
|
228
228
|
|
229
229
|
# Update the attributes and options for any recognized validations
|
@@ -508,6 +508,11 @@ module Sequel # :nodoc:
|
|
508
508
|
'post'
|
509
509
|
end
|
510
510
|
|
511
|
+
# The content of the title attribute of the abbr tag included in labels that are required.
|
512
|
+
def forme_required_abbr_title
|
513
|
+
'required'
|
514
|
+
end
|
515
|
+
|
511
516
|
# Whether to set an abbr tag in labels for required inputs.
|
512
517
|
def forme_use_required_abbr?
|
513
518
|
true
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: forme
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremy Evans
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: bigdecimal
|
@@ -176,9 +175,9 @@ email: code@jeremyevans.net
|
|
176
175
|
executables: []
|
177
176
|
extensions: []
|
178
177
|
extra_rdoc_files:
|
179
|
-
- README.rdoc
|
180
178
|
- CHANGELOG
|
181
179
|
- MIT-LICENSE
|
180
|
+
- README.rdoc
|
182
181
|
files:
|
183
182
|
- CHANGELOG
|
184
183
|
- MIT-LICENSE
|
@@ -203,6 +202,7 @@ files:
|
|
203
202
|
- lib/forme/version.rb
|
204
203
|
- lib/roda/plugins/forme.rb
|
205
204
|
- lib/roda/plugins/forme_erubi_capture.rb
|
205
|
+
- lib/roda/plugins/forme_erubi_capture_block.rb
|
206
206
|
- lib/roda/plugins/forme_route_csrf.rb
|
207
207
|
- lib/roda/plugins/forme_set.rb
|
208
208
|
- lib/sequel/plugins/forme.rb
|
@@ -217,7 +217,6 @@ metadata:
|
|
217
217
|
documentation_uri: http://forme.jeremyevans.net
|
218
218
|
mailing_list_uri: https://github.com/jeremyevans/forme/discussions
|
219
219
|
source_code_uri: https://github.com/jeremyevans/forme
|
220
|
-
post_install_message:
|
221
220
|
rdoc_options:
|
222
221
|
- "--quiet"
|
223
222
|
- "--line-numbers"
|
@@ -239,8 +238,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
239
238
|
- !ruby/object:Gem::Version
|
240
239
|
version: '0'
|
241
240
|
requirements: []
|
242
|
-
rubygems_version: 3.
|
243
|
-
signing_key:
|
241
|
+
rubygems_version: 3.6.9
|
244
242
|
specification_version: 4
|
245
243
|
summary: HTML forms library
|
246
244
|
test_files: []
|