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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 79c0b3986af69becb74de866d9027eafb1af2b148056c60963e8c8c6af04ad3d
4
- data.tar.gz: 8478ef6951317b7a33bf7c3b2630291bcd94bb94fcbb2820ef89d08c0addfaec
3
+ metadata.gz: af9c688d8d8f4432cfe214dbc58332a11d57c50e9309b96ba30e7396865a19a7
4
+ data.tar.gz: 7c66b8036e5d34a78055e8ba046501f70dcab1b066bf0836548acc5219bcc5b9
5
5
  SHA512:
6
- metadata.gz: c63cedc392288fba47d26da036ed2ad145acf4493683600cbad71238f66fd61dfeb91cadc1db68ab2211ff3cf6cd69622f17934f9ea0be4e4006a53c2baa0a07
7
- data.tar.gz: ef4a1d7b5bfdd0dc83d5f52b87f8dbc0785f03ccdfdb8d7e1bdfe160e85636fc2165fae7916489c8d22ba082293a64b3e0a466c67e4a4fe3a90726f4d228ffb1
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"/></li>
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"/></label>
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"/> 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>
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"/></label>
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"/></label>
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"/></label>
447
- <label>Name: <input id="album_tracks_attributes_0_name" name="album[tracks_attributes][0][name]" type="text" value="Blue Hawaii"/></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>
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"/></label>
452
- <label>Name: <input id="album_tracks_attributes_1_name" name="album[tracks_attributes][1][name]" type="text" value="Almost Always True"/></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>
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 values it can handle, and set other fields manually
619
- using +set_fields+.
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!(/\s*form-control\s*/,'') if tag.attr[:class]
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
@@ -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
- attribs = obj
62
- options = attr = attr.dup
61
+ opts = attr.dup
62
+ attr = obj
63
+ obj = opts.delete(:obj)
63
64
  else
64
- attribs = attr
65
- options = opts = opts.dup
65
+ opts = opts.dup
66
66
  end
67
67
 
68
- _forme_form_options(obj, attribs, options)
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!(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)
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 tm
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
- unless id = input.opts[:id]
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 SELF_CLOSING.include?(tag.type)
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
@@ -6,7 +6,7 @@ module Forme
6
6
  MAJOR = 2
7
7
 
8
8
  # The minor version of Forme, updated for new feature releases of Forme.
9
- MINOR = 5
9
+ MINOR = 7
10
10
 
11
11
  # The patch version of Forme, updated only for bug fixes from the last
12
12
  # feature release.
data/lib/forme.rb CHANGED
@@ -25,16 +25,10 @@ module Forme
25
25
  rescue LoadError
26
26
  ESCAPE_TABLE = {'&' => '&amp;', '<' => '&lt;', '>' => '&gt;', '"' => '&quot;', "'" => '&#39;'}.freeze
27
27
  ESCAPE_TABLE.each_value(&:freeze)
28
- if RUBY_VERSION >= '1.9'
29
- # Escape the following characters with their HTML/XML
30
- # equivalents.
31
- def self.h(value)
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
@@ -7,7 +7,7 @@ class Roda
7
7
  module RodaPlugins
8
8
  module FormeSet
9
9
  # Require the forme_route_csrf plugin.
10
- def self.load_dependencies(app, _ = nil)
10
+ def self.load_dependencies(app, opts = nil, &_)
11
11
  app.plugin :forme_route_csrf
12
12
  end
13
13
 
@@ -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=>'required'}, '*')] if opts[:label] && opts[:required] && obj.forme_use_required_abbr?
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.5.0
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: 2024-02-13 00:00:00.000000000 Z
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.5.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: []