diff --git a/CHANGELOG b/CHANGELOG index 1ec7c23..5a2648b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,7 @@ +=== master + +* Support emit: false option for non-rails template forms allowing block based form use without appending to template (jeremyevans) + === 2.4.1 (2023-09-19) * Add dependency on bigdecimal, as bigdecimal is moving from standard library to bundled gem in Ruby 3.4 (jeremyevans) diff --git a/README.rdoc b/README.rdoc index b2d2dd9..2b15dbc 100644 --- a/README.rdoc +++ b/README.rdoc @@ -800,7 +800,7 @@ For new code, it is recommended to use forme_route_csrf, as that uses Roda's rou plugin, which supports more secure request-specific CSRF tokens. In both cases, usage in ERB templates is the same: - <% form(@obj, :action=>'/foo') do |f| %> + <% form(@obj, action: '/foo') do |f| %> <%= f.input(:field) %> <% f.tag(:fieldset) do %> <%= f.input(:field_two) %> @@ -810,6 +810,8 @@ templates is the same: The forme_route_csrf plugin's +form+ method supports the following options in addition to the default +Forme.form+ options: +:emit :: Set to false to not emit implicit tags into template. This should only be + used if you are not modifying the template inside the block. :csrf :: Set to force whether a CSRF tag should be included. By default, a CSRF tag is included if the form's method is one of the request methods checked by the Roda route_csrf plugin. @@ -818,6 +820,19 @@ in addition to the default +Forme.form+ options: CSRF token unless the Roda route_csrf plugin has been configured to support non-request specific tokens. +The emit: false option allows you to do: + + <%= form(@obj, {action: '/foo'}, emit: false) do |f| + f.input(:field) + f.tag(:fieldset) do + f.input(:field_two) + end + end %> + +This is useful if you are calling some method that calls +form+ with a block, +where the resulting entire Forme::Forme object will be literalized into the +template. The form will include the CSRF token and forme_set metadata as appropriate. + The forme plugin does not require any csrf plugin, but will transparently use Rack::Csrf if it is available. If Rack::Csrf is available a CSRF tag if the form's method is +POST+, with no configuration ability. @@ -856,7 +871,8 @@ It allows you to use the following API in your erb templates: <% end %> In order to this to work transparently, the ERB outvar needs to be @_out_buf (this is the -default in Sinatra). +default in Sinatra). The Sinatra extension also supports the emit: false option to not +directly modify the related template (see example in the Roda section for usage). = Rails Support diff --git a/lib/forme/template.rb b/lib/forme/template.rb index 28c2643..d8746c2 100644 --- a/lib/forme/template.rb +++ b/lib/forme/template.rb @@ -22,7 +22,7 @@ def method_missing(*a, &block) %w'inputs tag subform'.each do |meth| class_eval(<<-END, __FILE__, __LINE__+1) def #{meth}(*a, &block) - return @form.#{meth}(*a) unless block + return @form.#{meth}(*a) if !block || @form.opts[:emit] == false buffer = @form.to_s offset = buffer.length @@ -40,6 +40,7 @@ def #{meth}(*a, &block) # Serialize the tag and inject it into the output. def emit(tag) + return if @form.opts[:emit] == false return unless output = output() output << tag end @@ -72,7 +73,7 @@ def form(obj=nil, attr={}, opts={}, &block) private def _forme_form(obj, attr, opts, &block) - if block_given? + if block_given? && opts[:emit] != false erb_form = buffer = offset = nil block = proc do wrapped_form = erb_form.instance_variable_get(:@form) diff --git a/lib/roda/plugins/forme_erubi_capture.rb b/lib/roda/plugins/forme_erubi_capture.rb index fae98fd..b9e65d7 100644 --- a/lib/roda/plugins/forme_erubi_capture.rb +++ b/lib/roda/plugins/forme_erubi_capture.rb @@ -15,7 +15,7 @@ class Form < ::Forme::Template::Form %w'inputs tag subform'.each do |meth| class_eval(<<-END, __FILE__, __LINE__+1) def #{meth}(*) - if block_given? + if block_given? && @form.opts[:emit] != false @scope.capture_erb do super @scope.instance_variable_get(@scope.render_opts[:template_opts][:outvar]) @@ -33,8 +33,8 @@ def emit(tag) end module InstanceMethods - def form(*) - if block_given? + def form(obj=nil, attr={}, opts={}, &block) + if block && (obj.is_a?(Hash) ? attr : opts)[:emit] != false capture_erb do super instance_variable_get(render_opts[:template_opts][:outvar]) diff --git a/spec/erb_helper.rb b/spec/erb_helper.rb index f1dec3a..d5db60a 100644 --- a/spec/erb_helper.rb +++ b/spec/erb_helper.rb @@ -28,6 +28,18 @@ END end + r.get 'no_emit' do + erb <'/baz'}, :emit=>false) do |f| + f.tag(:p, {}, 'FBB') + f.tag(:div) do + f.input(:first) + f.input(:last) + end +end %> +END + end + r.get 'nest' do erb <'/baz') do |f| %> diff --git a/spec/erubi_capture_helper.rb b/spec/erubi_capture_helper.rb index ae0b807..68271bf 100644 --- a/spec/erubi_capture_helper.rb +++ b/spec/erubi_capture_helper.rb @@ -28,6 +28,18 @@ END end + r.get 'no_emit' do + erb <'/baz'}, :emit=>false) do |f| + f.tag(:p, {}, 'FBB') + f.tag(:div) do + f.input(:first) + f.input(:last) + end +end %> +END + end + r.get 'nest' do erb <'/baz') do |f| %> diff --git a/spec/roda_integration_spec.rb b/spec/roda_integration_spec.rb index 0256c70..1d93283 100644 --- a/spec/roda_integration_spec.rb +++ b/spec/roda_integration_spec.rb @@ -231,6 +231,23 @@ def _forme_set(meth, obj, orig_hash, *form_args, &block) body.sub(%r{}, '').must_equal '0
1
Foo
Name
2
3' end + it "should have subform work correctly when using emit: false form option" do + @app.route do |r| + @album = Album.load(:name=>'N', :copies_sold=>2, :id=>1) + @album.associations[:artist] = Artist.load(:name=>'A', :id=>2) + erb <'/baz'}, :button=>'Sub', :emit=>false) do |f| + f.subform(:artist, :inputs=>[:name], :legend=>'Foo', :grid=>true, :labels=>%w'Name') +end %> +3 +END + end + + body = @app.call('REQUEST_METHOD'=>'GET')[2].join.gsub("\n", ' ').gsub(/ +/, ' ').chomp(' ') + body.sub(%r{}, '').must_equal '0
Foo
Name
3' + end + it "#forme_set should include HMAC values if form includes inputs for obj" do h = forme_set(@ab, :name=>'Foo') proc{forme_call(h)}.must_raise Roda::RodaPlugins::FormeSet::Error diff --git a/spec/shared_erb_specs.rb b/spec/shared_erb_specs.rb index 1676ed2..632641a 100644 --- a/spec/shared_erb_specs.rb +++ b/spec/shared_erb_specs.rb @@ -19,6 +19,10 @@ def o.puts(*) end sin_get('/inputs_block_wrapper').must_equal '
FBB
' end + it "#form should handle emit: false option with self-contained blocks to be usable" do + sin_get('/no_emit').must_equal '

FBB

' + end + it "#form should add start and end tags and yield Forme::Form instance" do sin_get('/nest').must_equal '

FBB

' end