forme 2.4.1 → 2.5.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: 60794aa5c742e3b40bacd043172a0e201358b944a653f3bd013bf8e6a254154b
4
- data.tar.gz: ae16923653b5bb463f430735a57b1f710a3ccb53420b124f15b6a5e72f290c2e
3
+ metadata.gz: 79c0b3986af69becb74de866d9027eafb1af2b148056c60963e8c8c6af04ad3d
4
+ data.tar.gz: 8478ef6951317b7a33bf7c3b2630291bcd94bb94fcbb2820ef89d08c0addfaec
5
5
  SHA512:
6
- metadata.gz: b3f915c5dd32938874513739d29a2f6c04365a1f636c2dc814ce2a17de07beb4a9c8000e4f40b112bb8d4198d005552dafcf0cbb957dc0ffa9e11c0b6b820f33
7
- data.tar.gz: 5528741694ca0c36800ce9cacab5a7e764698da5c9c9f1e4bb17bfce9c9a0ba86a354a0e2eca14a9825b5ee792aa7dc5c62eb687b8d5a540fa7b2fea47220617
6
+ metadata.gz: c63cedc392288fba47d26da036ed2ad145acf4493683600cbad71238f66fd61dfeb91cadc1db68ab2211ff3cf6cd69622f17934f9ea0be4e4006a53c2baa0a07
7
+ data.tar.gz: ef4a1d7b5bfdd0dc83d5f52b87f8dbc0785f03ccdfdb8d7e1bdfe160e85636fc2165fae7916489c8d22ba082293a64b3e0a466c67e4a4fe3a90726f4d228ffb1
data/CHANGELOG CHANGED
@@ -1,3 +1,11 @@
1
+ === 2.5.0 (2024-02-13)
2
+
3
+ * Add hidden inputs to work with formaction/formmethod support in Roda 3.77+ route_csrf plugin (jeremyevans)
4
+
5
+ * Support :formaction option on buttons (jeremyevans)
6
+
7
+ * Support emit: false option for non-rails template forms allowing block based form use without appending to template (jeremyevans)
8
+
1
9
  === 2.4.1 (2023-09-19)
2
10
 
3
11
  * Add dependency on bigdecimal, as bigdecimal is moving from standard library to bundled gem in Ruby 3.4 (jeremyevans)
data/README.rdoc CHANGED
@@ -800,7 +800,7 @@ For new code, it is recommended to use forme_route_csrf, as that uses Roda's rou
800
800
  plugin, which supports more secure request-specific CSRF tokens. In both cases, usage in ERB
801
801
  templates is the same:
802
802
 
803
- <% form(@obj, :action=>'/foo') do |f| %>
803
+ <% form(@obj, action: '/foo') do |f| %>
804
804
  <%= f.input(:field) %>
805
805
  <% f.tag(:fieldset) do %>
806
806
  <%= f.input(:field_two) %>
@@ -810,6 +810,8 @@ templates is the same:
810
810
  The forme_route_csrf plugin's +form+ method supports the following options
811
811
  in addition to the default +Forme.form+ options:
812
812
 
813
+ :emit :: Set to false to not emit implicit tags into template. This should only be
814
+ used if you are not modifying the template inside the block.
813
815
  :csrf :: Set to force whether a CSRF tag should be included. By default, a CSRF
814
816
  tag is included if the form's method is one of the request methods checked
815
817
  by the Roda route_csrf plugin.
@@ -818,6 +820,19 @@ in addition to the default +Forme.form+ options:
818
820
  CSRF token unless the Roda route_csrf plugin has been
819
821
  configured to support non-request specific tokens.
820
822
 
823
+ The <tt>emit: false</tt> option allows you to do:
824
+
825
+ <%= form(@obj, {action: '/foo'}, emit: false) do |f|
826
+ f.input(:field)
827
+ f.tag(:fieldset) do
828
+ f.input(:field_two)
829
+ end
830
+ end %>
831
+
832
+ This is useful if you are calling some method that calls +form+ with a block,
833
+ where the resulting entire Forme::Forme object will be literalized into the
834
+ template. The form will include the CSRF token and forme_set metadata as appropriate.
835
+
821
836
  The forme plugin does not require any csrf plugin, but will transparently use
822
837
  Rack::Csrf if it is available. If Rack::Csrf is available a CSRF tag if the form's
823
838
  method is +POST+, with no configuration ability.
@@ -856,7 +871,8 @@ It allows you to use the following API in your erb templates:
856
871
  <% end %>
857
872
 
858
873
  In order to this to work transparently, the ERB outvar needs to be <tt>@_out_buf</tt> (this is the
859
- default in Sinatra).
874
+ default in Sinatra). The Sinatra extension also supports the <tt>emit: false</tt> option to not
875
+ directly modify the related template (see example in the Roda section for usage).
860
876
 
861
877
  = Rails Support
862
878
 
@@ -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)
@@ -268,6 +268,12 @@ 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
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 = 5
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
@@ -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])
@@ -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
@@ -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.5.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-02-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bigdecimal
@@ -239,7 +239,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
239
239
  - !ruby/object:Gem::Version
240
240
  version: '0'
241
241
  requirements: []
242
- rubygems_version: 3.4.10
242
+ rubygems_version: 3.5.3
243
243
  signing_key:
244
244
  specification_version: 4
245
245
  summary: HTML forms library