forme 2.4.1 → 2.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 60794aa5c742e3b40bacd043172a0e201358b944a653f3bd013bf8e6a254154b
4
- data.tar.gz: ae16923653b5bb463f430735a57b1f710a3ccb53420b124f15b6a5e72f290c2e
3
+ metadata.gz: c0078a295325f1880c32a870466a7e280a0cd324e2f8bf91ea81125bf557d225
4
+ data.tar.gz: 120e19dde7e39176a4693a4ab46f7f1c2f4d86265f3de4946af6a1d40f5fd524
5
5
  SHA512:
6
- metadata.gz: b3f915c5dd32938874513739d29a2f6c04365a1f636c2dc814ce2a17de07beb4a9c8000e4f40b112bb8d4198d005552dafcf0cbb957dc0ffa9e11c0b6b820f33
7
- data.tar.gz: 5528741694ca0c36800ce9cacab5a7e764698da5c9c9f1e4bb17bfce9c9a0ba86a354a0e2eca14a9825b5ee792aa7dc5c62eb687b8d5a540fa7b2fea47220617
6
+ metadata.gz: f13bb7fdfaf90be6af18cc67403d4e36f963c6258eeb49bbc4579b356534172fa4d25380f032ef7a80954f532de3a63f67e3ad3a985c0225d4d21db0245fe387
7
+ data.tar.gz: 6ec1d4cbee834565239953a12b826b9b190c4f80901b6dc559954c7414889a1c0691e3a16b25dd5d2176a079ae3826a3b9e13bb6c23e323979046af98288208f
data/CHANGELOG CHANGED
@@ -1,3 +1,21 @@
1
+ === 2.6.0 (2024-06-18)
2
+
3
+ * Add Roda forme_erubi_capture_block plugin to support erubi/capture_block <%= form do %> <% end %> tags (jeremyevans)
4
+
5
+ * Support :hr as select option value to use a hr tag instead of an option tag (jeremyevans)
6
+
7
+ * Support maxlength and minlength options as attributes for textareas (jeremyevans)
8
+
9
+ * Support minlength option as attribute for text inputs (jeremyevans)
10
+
11
+ === 2.5.0 (2024-02-13)
12
+
13
+ * Add hidden inputs to work with formaction/formmethod support in Roda 3.77+ route_csrf plugin (jeremyevans)
14
+
15
+ * Support :formaction option on buttons (jeremyevans)
16
+
17
+ * Support emit: false option for non-rails template forms allowing block based form use without appending to template (jeremyevans)
18
+
1
19
  === 2.4.1 (2023-09-19)
2
20
 
3
21
  * Add dependency on bigdecimal, as bigdecimal is moving from standard library to bundled gem in Ruby 3.4 (jeremyevans)
data/README.rdoc CHANGED
@@ -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
@@ -800,7 +801,7 @@ For new code, it is recommended to use forme_route_csrf, as that uses Roda's rou
800
801
  plugin, which supports more secure request-specific CSRF tokens. In both cases, usage in ERB
801
802
  templates is the same:
802
803
 
803
- <% form(@obj, :action=>'/foo') do |f| %>
804
+ <% form(@obj, action: '/foo') do |f| %>
804
805
  <%= f.input(:field) %>
805
806
  <% f.tag(:fieldset) do %>
806
807
  <%= f.input(:field_two) %>
@@ -810,6 +811,8 @@ templates is the same:
810
811
  The forme_route_csrf plugin's +form+ method supports the following options
811
812
  in addition to the default +Forme.form+ options:
812
813
 
814
+ :emit :: Set to false to not emit implicit tags into template. This should only be
815
+ used if you are not modifying the template inside the block.
813
816
  :csrf :: Set to force whether a CSRF tag should be included. By default, a CSRF
814
817
  tag is included if the form's method is one of the request methods checked
815
818
  by the Roda route_csrf plugin.
@@ -818,10 +821,43 @@ in addition to the default +Forme.form+ options:
818
821
  CSRF token unless the Roda route_csrf plugin has been
819
822
  configured to support non-request specific tokens.
820
823
 
824
+ The <tt>emit: false</tt> option allows you to do:
825
+
826
+ <%= form(@obj, {action: '/foo'}, emit: false) do |f|
827
+ f.input(:field)
828
+ f.tag(:fieldset) do
829
+ f.input(:field_two)
830
+ end
831
+ end %>
832
+
833
+ This is useful if you are calling some method that calls +form+ with a block,
834
+ where the resulting entire Forme::Forme object will be literalized into the
835
+ template. The form will include the CSRF token and forme_set metadata as appropriate.
836
+
821
837
  The forme plugin does not require any csrf plugin, but will transparently use
822
838
  Rack::Csrf if it is available. If Rack::Csrf is available a CSRF tag if the form's
823
839
  method is +POST+, with no configuration ability.
824
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
+
825
861
  == forme_erubi_capture plugin
826
862
 
827
863
  The forme_erubi_capture plugin builds on the forme_route_csrf plugin, but it supports
@@ -856,7 +892,8 @@ It allows you to use the following API in your erb templates:
856
892
  <% end %>
857
893
 
858
894
  In order to this to work transparently, the ERB outvar needs to be <tt>@_out_buf</tt> (this is the
859
- default in Sinatra).
895
+ default in Sinatra). The Sinatra extension also supports the <tt>emit: false</tt> option to not
896
+ directly modify the related template (see example in the Roda section for usage).
860
897
 
861
898
  = Rails Support
862
899
 
@@ -966,6 +1003,8 @@ Creates a select tag, containing option tags specified by the :options option.
966
1003
  array, uses the first entry of the array as the text of the option, and
967
1004
  the last entry of the array as the value of the option. If the last entry
968
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).
969
1008
  :selected :: The value that should be selected. Any options that are equal to
970
1009
  this value (or included in this value if a multiple select box),
971
1010
  are set to selected.
@@ -997,6 +1036,8 @@ Creates a textarea tag. Options:
997
1036
 
998
1037
  :cols :: The number of columns in the text area.
999
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
1000
1041
 
1001
1042
  == all others
1002
1043
 
@@ -1005,6 +1046,7 @@ as text and password, as well as newer HTML5 inputs such as number or email. Opt
1005
1046
 
1006
1047
  :size :: Uses the size attribute on the tag
1007
1048
  :maxlength :: Use the maxlength attribute on the tag
1049
+ :minlength :: Use the minlength attribute on the tag
1008
1050
 
1009
1051
  == Form options
1010
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
@@ -72,9 +72,9 @@ module Forme
72
72
  private
73
73
 
74
74
  def _forme_form(obj, attr, opts, &block)
75
- if block_given?
75
+ if block && opts[:emit] != false
76
76
  erb_form = buffer = offset = nil
77
- block = proc do
77
+ form_block = proc do
78
78
  wrapped_form = erb_form.instance_variable_get(:@form)
79
79
  buffer = wrapped_form.to_s
80
80
  offset = buffer.length
@@ -83,9 +83,9 @@ module Forme
83
83
  offset = buffer.length
84
84
  end
85
85
 
86
- f, attr, block = _forme_wrapped_form_class.form_args(obj, attr, opts, &block)
86
+ f, attr, form_block = _forme_wrapped_form_class.form_args(obj, attr, opts, &form_block)
87
87
  erb_form = _forme_form_class.new(f, self)
88
- erb_form.form(attr, &block)
88
+ erb_form.form(attr, &form_block)
89
89
  erb_form.emit(buffer[offset, buffer.length])
90
90
  else
91
91
  _forme_wrapped_form_class.form(obj, attr, opts, &block)
@@ -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
 
@@ -268,10 +268,16 @@ module Forme
268
268
  ret
269
269
  end
270
270
 
271
+ # Formats a submit input. Respects :formaction option.
272
+ def format_submit
273
+ copy_options_to_attributes([:formaction])
274
+ _format_input(:submit)
275
+ end
276
+
271
277
  # Formats a textarea. Respects the following options:
272
278
  # :value :: Sets value as the child of the textarea.
273
279
  def format_textarea
274
- copy_options_to_attributes([:cols, :rows])
280
+ copy_options_to_attributes([:cols, :rows, :maxlength, :minlength])
275
281
  if val = @attr.delete(:value)
276
282
  tag(:textarea, @attr, [val])
277
283
  else
@@ -446,7 +452,9 @@ module Forme
446
452
 
447
453
  os.map do |x|
448
454
  attr = {}
449
- if tm
455
+ if x == :hr
456
+ next form._tag(:hr, {})
457
+ elsif tm
450
458
  text = x.send(tm)
451
459
  val = x.send(vm) if vm
452
460
  elsif x.is_a?(Array)
@@ -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),
data/lib/forme/version.rb CHANGED
@@ -6,11 +6,11 @@ module Forme
6
6
  MAJOR = 2
7
7
 
8
8
  # The minor version of Forme, updated for new feature releases of Forme.
9
- MINOR = 4
9
+ MINOR = 6
10
10
 
11
11
  # The patch version of Forme, updated only for bug fixes from the last
12
12
  # feature release.
13
- TINY = 1
13
+ TINY = 0
14
14
 
15
15
  # Version constant, use <tt>Forme.version</tt> instead.
16
16
  VERSION = "#{MAJOR}.#{MINOR}.#{TINY}".freeze
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
@@ -33,8 +33,8 @@ class Roda
33
33
  end
34
34
 
35
35
  module InstanceMethods
36
- def form(*)
37
- if block_given?
36
+ def _forme_form(obj, attr, opts, &block)
37
+ if block && opts[:emit] != false
38
38
  capture_erb do
39
39
  super
40
40
  instance_variable_get(render_opts[:template_opts][:outvar])
@@ -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
@@ -36,7 +36,7 @@ class Roda
36
36
  end
37
37
 
38
38
  if apply_csrf
39
- token = if opts.fetch(:use_request_specific_token){use_request_specific_csrf_tokens?}
39
+ token = if use_request_specific_token = opts.fetch(:use_request_specific_token){use_request_specific_csrf_tokens?}
40
40
  csrf_token(csrf_path(attr[:action]), method)
41
41
  else
42
42
  csrf_token
@@ -46,6 +46,23 @@ class Roda
46
46
  opts[:_before] = lambda do |form|
47
47
  form.tag(:input, :type=>:hidden, :name=>csrf_field, :value=>token)
48
48
  end
49
+
50
+ if use_request_specific_token && (formaction_field = csrf_options[:formaction_field])
51
+ formactions = opts[:formactions] = []
52
+ formaction_tokens = opts[:formaction_tokens] = {}
53
+ _after = opts[:_after]
54
+ opts[:formaction_csrfs] = [formaction_field, formaction_tokens]
55
+ formaction_field = csrf_options[:formaction_field]
56
+ opts[:_after] = lambda do |form|
57
+ formactions.each do |action, method|
58
+ path = csrf_path(action)
59
+ fa_token = csrf_token(path, method)
60
+ formaction_tokens[path] = fa_token
61
+ form.tag(:input, :type=>:hidden, :name=>"#{formaction_field}[#{path}]", :value=>fa_token)
62
+ end
63
+ _after.call(form) if _after
64
+ end
65
+ end
49
66
  end
50
67
  end
51
68
  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
 
@@ -102,7 +102,9 @@ class Roda
102
102
  def _forme_form_options(obj, attr, opts)
103
103
  super
104
104
 
105
+ _after = opts[:_after]
105
106
  opts[:_after] = lambda do |form|
107
+ _after.call(form) if _after
106
108
  if (obj = form.opts[:obj]) && obj.respond_to?(:forme_inputs) && (forme_inputs = obj.forme_inputs)
107
109
  columns = []
108
110
  valid_values = {}
@@ -129,6 +131,9 @@ class Roda
129
131
  data['columns'] = columns
130
132
  data['namespaces'] = form.opts[:namespace]
131
133
  data['csrf'] = form.opts[:csrf]
134
+ if (formactions = form.opts[:formaction_csrfs]) && !formactions[1].empty?
135
+ data['formaction_csrfs'] = formactions
136
+ end
132
137
  data['valid_values'] = valid_values unless valid_values.empty?
133
138
  data['form_version'] = form.opts[:form_version] if form.opts[:form_version]
134
139
 
@@ -154,8 +159,16 @@ class Roda
154
159
 
155
160
  data = JSON.parse(data)
156
161
  csrf_field, hmac_csrf_value = data['csrf']
162
+ formaction_csrf_field, formaction_values = data['formaction_csrfs']
163
+
157
164
  if csrf_field
158
- csrf_value = params[csrf_field].to_s
165
+ formaction_params = params[formaction_csrf_field]
166
+ if formaction_csrf_field && (formaction_params = params[formaction_csrf_field]).is_a?(Hash) && (csrf_value = formaction_params[request.path])
167
+ hmac_csrf_value = formaction_values[request.path]
168
+ else
169
+ csrf_value = params[csrf_field].to_s
170
+ end
171
+
159
172
  hmac_csrf_value = hmac_csrf_value.to_s
160
173
  unless Rack::Utils.secure_compare(csrf_value.ljust(hmac_csrf_value.length), hmac_csrf_value) && csrf_value.length == hmac_csrf_value.length
161
174
  return _forme_parse_error(:csrf_mismatch, obj)
@@ -17,6 +17,22 @@ module Sequel # :nodoc:
17
17
  # that use a <tt>Sequel::Model</tt> instance as the form's
18
18
  # +obj+.
19
19
  module SequelForm
20
+ # If the form has the :formactions option and the button has the
21
+ # formaction option or attribute, append the action and method
22
+ # for this button to the formactions. This is used by the
23
+ # Roda forme_route_csrf and forme_set plugins so that formaction
24
+ # can work as expected.
25
+ def button(opts={})
26
+ if opts.is_a?(Hash) && (formactions = self.opts[:formactions]) &&
27
+ (formaction = opts[:formaction] || ((attr = opts[:attr]) && (attr[:formaction] || attr['formaction'])))
28
+ formmethod = opts[:formmethod] ||
29
+ ((attr = opts[:attr]) && (attr[:formmethod] || attr['formmethod'])) ||
30
+ ((attr = form_tag_attributes) && (attr[:method] || attr['method']))
31
+ formactions << [formaction, formmethod]
32
+ end
33
+ super
34
+ end
35
+
20
36
  # Use the post method by default for Sequel forms, unless
21
37
  # overridden with the :method attribute.
22
38
  def form(attr={}, &block)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: forme
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.4.1
4
+ version: 2.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-09-19 00:00:00.000000000 Z
11
+ date: 2024-06-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bigdecimal
@@ -203,6 +203,7 @@ files:
203
203
  - lib/forme/version.rb
204
204
  - lib/roda/plugins/forme.rb
205
205
  - lib/roda/plugins/forme_erubi_capture.rb
206
+ - lib/roda/plugins/forme_erubi_capture_block.rb
206
207
  - lib/roda/plugins/forme_route_csrf.rb
207
208
  - lib/roda/plugins/forme_set.rb
208
209
  - lib/sequel/plugins/forme.rb
@@ -239,7 +240,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
239
240
  - !ruby/object:Gem::Version
240
241
  version: '0'
241
242
  requirements: []
242
- rubygems_version: 3.4.10
243
+ rubygems_version: 3.5.9
243
244
  signing_key:
244
245
  specification_version: 4
245
246
  summary: HTML forms library