forme 1.11.0 → 2.1.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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +54 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +235 -209
  5. data/Rakefile +1 -7
  6. data/lib/forme/bs3.rb +18 -4
  7. data/lib/forme/erb.rb +13 -25
  8. data/lib/forme/form.rb +144 -149
  9. data/lib/forme/input.rb +1 -1
  10. data/lib/forme/rails.rb +39 -83
  11. data/lib/forme/raw.rb +2 -2
  12. data/lib/forme/tag.rb +3 -12
  13. data/lib/forme/template.rb +110 -0
  14. data/lib/forme/transformers/formatter.rb +4 -1
  15. data/lib/forme/transformers/helper.rb +0 -1
  16. data/lib/forme/transformers/inputs_wrapper.rb +4 -4
  17. data/lib/forme/version.rb +2 -2
  18. data/lib/forme.rb +15 -2
  19. data/lib/roda/plugins/forme.rb +1 -1
  20. data/lib/roda/plugins/forme_erubi_capture.rb +57 -0
  21. data/lib/roda/plugins/forme_route_csrf.rb +16 -34
  22. data/lib/roda/plugins/forme_set.rb +39 -76
  23. data/lib/sequel/plugins/forme.rb +40 -50
  24. data/lib/sequel/plugins/forme_i18n.rb +3 -1
  25. data/lib/sequel/plugins/forme_set.rb +3 -1
  26. data/spec/all.rb +1 -1
  27. data/spec/bs3_reference_spec.rb +18 -18
  28. data/spec/bs3_sequel_plugin_spec.rb +17 -17
  29. data/spec/bs3_spec.rb +23 -11
  30. data/spec/erb_helper.rb +69 -58
  31. data/spec/erubi_capture_helper.rb +198 -0
  32. data/spec/forme_spec.rb +21 -32
  33. data/spec/rails_integration_spec.rb +39 -25
  34. data/spec/roda_integration_spec.rb +118 -70
  35. data/spec/sequel_helper.rb +0 -1
  36. data/spec/sequel_i18n_helper.rb +1 -1
  37. data/spec/sequel_i18n_plugin_spec.rb +3 -2
  38. data/spec/sequel_plugin_spec.rb +29 -12
  39. data/spec/sequel_set_plugin_spec.rb +9 -2
  40. data/spec/shared_erb_specs.rb +71 -0
  41. data/spec/sinatra_integration_spec.rb +5 -6
  42. data/spec/spec_helper.rb +21 -8
  43. metadata +9 -7
  44. data/lib/forme/erb_form.rb +0 -74
  45. data/lib/forme/sinatra.rb +0 -17
data/lib/forme/rails.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen-string-literal: true
2
2
 
3
- require 'forme'
3
+ require_relative 'template'
4
4
 
5
5
  class ActiveSupport::SafeBuffer
6
6
  include Forme::Raw
@@ -8,104 +8,60 @@ end
8
8
 
9
9
  module Forme
10
10
  module Rails
11
- HIDDEN_TAGS = []
12
-
13
- # Add a hidden tag proc that will be used for all forms created via Forme::Rails::ERB#form.
14
- # The block is yielded the Forme::Tag object for the form tag.
15
- # The block should return either nil if hidden tag should be added, or a Forme::Tag object (or an array of them),
16
- # or a hash with keys specifying the name of the tags and the values specifying the values of the tags .
17
- def self.add_hidden_tag(&block)
18
- HIDDEN_TAGS << block
19
- end
20
-
21
- # Add CSRF token tag by default for POST forms
22
- add_hidden_tag do |tag|
23
- if (form = tag.form) && (template = form.template) && template.protect_against_forgery? && (tag.attr[:method] || tag.attr['method']).to_s.upcase == 'POST'
24
- {template.request_forgery_protection_token=>template.form_authenticity_token}
11
+ class TemplateForm < ::Forme::Template::Form
12
+ %w'inputs tag subform'.each do |meth|
13
+ class_eval(<<-END, __FILE__, __LINE__+1)
14
+ def #{meth}(*)
15
+ if block_given?
16
+ @scope.send(:with_output_buffer){super}
17
+ else
18
+ @scope.raw(super)
19
+ end
20
+ end
21
+ END
25
22
  end
26
- end
27
-
28
- # Subclass used when using Forme/Rails ERB integration,
29
- # handling integration with the view template.
30
- class Form < ::Forme::Form
31
- # The Rails template that created this form.
32
- attr_reader :template
33
23
 
34
- # Set the template object when initializing.
35
- def initialize(*)
36
- super
37
- @template = @opts[:template]
24
+ %w'button input'.each do |meth|
25
+ class_eval(<<-END, __FILE__, __LINE__+1)
26
+ def #{meth}(*)
27
+ @scope.raw(super)
28
+ end
29
+ END
38
30
  end
39
31
 
40
- # Serialize and mark as already escaped the string version of
41
- # the input.
42
32
  def emit(tag)
43
- template.output_buffer << tag.to_s
33
+ @scope.output_buffer << @scope.raw(tag)
44
34
  end
35
+ end
45
36
 
46
- # Capture the inputs into a new output buffer, and return
47
- # the buffer if not given a block
48
- def inputs(*)
37
+ ERB = Template::Helper.clone
38
+ module ERB
39
+ alias _forme form
40
+ remove_method :form
41
+
42
+ def forme(*a, &block)
49
43
  if block_given?
50
- super
51
- else
52
- template.send(:with_output_buffer){super}
53
- end
54
- end
55
-
56
- # If a block is not given, emit the inputs into the current output
57
- # buffer.
58
- def _inputs(inputs=[], opts={}) # :nodoc:
59
- if block_given? && !opts[:subform]
60
- super
44
+ with_output_buffer{_forme(*a, &block)}
61
45
  else
62
- emit(super)
46
+ raw(_forme(*a, &block))
63
47
  end
64
48
  end
65
49
 
66
- # Return a string version of the input that is already marked as safe.
67
- def input(*)
68
- super.to_s
69
- end
70
-
71
- # Return a string version of the button that is already marked as safe.
72
- def button(*)
73
- super.to_s
74
- end
75
-
76
- # Use the template's raw method to mark the given string as html safe.
77
- def raw_output(s)
78
- template.raw(s)
79
- end
50
+ private
80
51
 
81
- # If a block is given, create a new output buffer and make sure all the
82
- # output of the tag goes into that buffer, and return the buffer.
83
- # Otherwise, just return a string version of the tag that is already
84
- # marked as safe.
85
- def tag(type, attr={}, children=[], &block)
86
- if block_given?
87
- template.send(:with_output_buffer){tag_(type, attr, children, &block)}
88
- else
89
- _tag(type, attr, children).to_s
52
+ remove_method :_forme_form_options
53
+ def _forme_form_options(obj, attr, opts)
54
+ if protect_against_forgery?
55
+ opts[:_before_post] = lambda do |form|
56
+ form.tag(:input, :type=>:hidden, :name=>request_forgery_protection_token, :value=>form_authenticity_token)
57
+ end
90
58
  end
91
59
  end
92
-
93
- def tag_(type, attr={}, children=[]) # :nodoc:
94
- tag = _tag(type, attr, children)
95
- emit(serialize_open(tag))
96
- Array(tag.children).each{|c| emit(c)}
97
- yield self if block_given?
98
- emit(serialize_close(tag))
99
- end
100
- end
101
60
 
102
- module ERB
103
- # Create a +Form+ object tied to the current template, and using the standard
104
- # Rails hidden tags.
105
- def forme(obj=nil, attr={}, opts={}, &block)
106
- h = {:template=>self, :hidden_tags=>Forme::Rails::HIDDEN_TAGS}
107
- (obj.is_a?(Hash) ? attr = attr.merge(h) : opts = opts.merge(h))
108
- Form.form(obj, attr, opts, &block)
61
+ remove_method :_forme_form_class
62
+ # The class to use for forms
63
+ def _forme_form_class
64
+ TemplateForm
109
65
  end
110
66
  end
111
67
  end
data/lib/forme/raw.rb CHANGED
@@ -2,12 +2,12 @@
2
2
 
3
3
  module Forme
4
4
  # Empty module for marking objects as "raw", where they will no longer
5
- # html escaped by the default serializer.
5
+ # HTML escaped by the default serializer.
6
6
  module Raw
7
7
  end
8
8
 
9
9
  # A String subclass that includes Raw, which will cause the default
10
- # serializer to no longer html escape the string.
10
+ # serializer to no longer HTML escape the string.
11
11
  class RawString < ::String
12
12
  include Raw
13
13
  end
data/lib/forme/tag.rb CHANGED
@@ -1,8 +1,8 @@
1
1
  # frozen-string-literal: true
2
2
 
3
3
  module Forme
4
- # Low level abstract tag form, where each instance represents a
5
- # html tag with attributes and children.
4
+ # Low level abstract tag form, where each instance represents an
5
+ # HTML tag with attributes and children.
6
6
  class Tag
7
7
  # The +Form+ object related to the receiver.
8
8
  attr_reader :form
@@ -23,15 +23,6 @@ module Forme
23
23
  @children = parse_children(children)
24
24
  end
25
25
 
26
- # Adds a child to the array of receiver's children.
27
- def <<(child)
28
- if children
29
- children << child
30
- else
31
- @children = [child]
32
- end
33
- end
34
-
35
26
  # Create a new +Tag+ instance with the given arguments and block
36
27
  # related to the receiver's +form+.
37
28
  def tag(*a, &block)
@@ -40,7 +31,7 @@ module Forme
40
31
 
41
32
  # Return a string containing the serialized content of the receiver.
42
33
  def to_s
43
- form.raw_output(Forme.transform(:serializer, @opts, @form.opts, self))
34
+ Forme.transform(:serializer, @opts, @form.opts, self)
44
35
  end
45
36
 
46
37
  private
@@ -0,0 +1,110 @@
1
+ # frozen-string-literal: true
2
+
3
+ require_relative '../forme'
4
+
5
+ module Forme
6
+ module Template
7
+ class Form
8
+ def initialize(form, scope)
9
+ @form = form
10
+ @scope = scope
11
+ end
12
+
13
+ # Delegate calls by default to the wrapped form
14
+ def method_missing(*a, &block)
15
+ @form.public_send(*a, &block)
16
+ end
17
+
18
+ # If a block is given to inputs, tag, or subform,
19
+ # emit the already generated HTML for the form before yielding
20
+ # to the block, and after returning, emit any HTML generated
21
+ # after returning from the block.
22
+ %w'inputs tag subform'.each do |meth|
23
+ class_eval(<<-END, __FILE__, __LINE__+1)
24
+ def #{meth}(*a, &block)
25
+ return @form.#{meth}(*a) unless block
26
+
27
+ buffer = @form.to_s
28
+ offset = buffer.length
29
+ @form.#{meth}(*a) do
30
+ emit(buffer[offset, buffer.length])
31
+ yield self
32
+ offset = buffer.length
33
+ end
34
+ emit(buffer[offset, buffer.length])
35
+
36
+ nil
37
+ end
38
+ END
39
+ end
40
+
41
+ # Serialize the tag and inject it into the output.
42
+ def emit(tag)
43
+ return unless output = output()
44
+ output << tag
45
+ end
46
+
47
+ private
48
+
49
+ def output
50
+ @scope.instance_variable_get(:@_out_buf)
51
+ end
52
+ end
53
+
54
+ # This is the module used to add the Forme integration
55
+ # to ERB.
56
+ module Helper
57
+ # Create a +Form+ object tied to the current output buffer,
58
+ # using the standard ERB hidden tags.
59
+ def form(obj=nil, attr={}, opts={}, &block)
60
+ if obj.is_a?(Hash)
61
+ attribs = obj
62
+ options = attr = attr.dup
63
+ else
64
+ attribs = attr
65
+ options = opts = opts.dup
66
+ end
67
+
68
+ _forme_form_options(obj, attribs, options)
69
+ _forme_form(obj, attr, opts, &block)
70
+ end
71
+
72
+ private
73
+
74
+ def _forme_form(obj, attr, opts, &block)
75
+ if block_given?
76
+ erb_form = buffer = offset = nil
77
+ block = proc do
78
+ wrapped_form = erb_form.instance_variable_get(:@form)
79
+ buffer = wrapped_form.to_s
80
+ offset = buffer.length
81
+ erb_form.emit(buffer[0, buffer.length])
82
+ yield erb_form
83
+ offset = buffer.length
84
+ end
85
+
86
+ f, attr, block = _forme_wrapped_form_class.form_args(obj, attr, opts, &block)
87
+ erb_form = _forme_form_class.new(f, self)
88
+ erb_form.form(attr, &block)
89
+ erb_form.emit(buffer[offset, buffer.length])
90
+ else
91
+ _forme_wrapped_form_class.form(obj, attr, opts, &block)
92
+ end
93
+ end
94
+
95
+ def _forme_wrapped_form_class
96
+ ::Forme::Form
97
+ end
98
+
99
+ # The class to use for forms
100
+ def _forme_form_class
101
+ Form
102
+ end
103
+
104
+ # The options to use for forms. Any changes should mutate this hash to set options.
105
+ # Does nothing by default.
106
+ def _forme_form_options(obj, attr, opts)
107
+ end
108
+ end
109
+ end
110
+ end
@@ -160,7 +160,10 @@ module Forme
160
160
  @opts[:select_labels] ||= {}
161
161
  order.map do |x|
162
162
  next x if x.is_a?(String)
163
- opts = @opts.merge(:label=>@opts[:select_labels][x], :wrapper=>nil, :error=>nil, :name=>"#{name}[#{x}]", :value=>values[x], :options=>ops[x].map{|y| [sprintf(format, y), y]})
163
+ options = ops[x].map do |value, text|
164
+ [text || sprintf(format, value), value]
165
+ end
166
+ opts = @opts.merge(:label=>@opts[:select_labels][x], :wrapper=>nil, :error=>nil, :name=>"#{name}[#{x}]", :value=>values[x], :options=>options)
164
167
  opts[:id] = if first_input
165
168
  first_input = false
166
169
  id
@@ -16,4 +16,3 @@ module Forme
16
16
  end
17
17
  end
18
18
  end
19
-
@@ -15,7 +15,7 @@ module Forme
15
15
  Forme.attr_classes(attr, 'inputs')
16
16
  if legend = opts[:legend]
17
17
  form.tag(:fieldset, attr) do
18
- form.emit(form.tag(:legend, opts[:legend_attr], legend))
18
+ form.tag(:legend, opts[:legend_attr], legend)
19
19
  yield
20
20
  end
21
21
  else
@@ -32,7 +32,7 @@ module Forme
32
32
 
33
33
  # Wrap the inputs in a <fieldset> and a <ol> tag.
34
34
  def call(form, opts)
35
- super(form, opts){form.tag_(:ol){yield}}
35
+ super(form, opts){form.tag(:ol){yield}}
36
36
  end
37
37
  end
38
38
 
@@ -83,12 +83,12 @@ module Forme
83
83
  attr = opts[:attr] ? opts[:attr].dup : {}
84
84
  form.tag(:table, attr) do
85
85
  if legend = opts[:legend]
86
- form.emit(form.tag(:caption, opts[:legend_attr], legend))
86
+ form.tag(:caption, opts[:legend_attr], legend)
87
87
  end
88
88
 
89
89
  if (labels = opts[:labels]) && !labels.empty?
90
90
  form.tag(:thead) do
91
- form.emit(form.tag(:tr, {}, labels.map{|l| form._tag(:th, {}, l)}))
91
+ form.tag(:tr, {}, labels.map{|l| form._tag(:th, {}, l)})
92
92
  end
93
93
  end
94
94
 
data/lib/forme/version.rb CHANGED
@@ -3,10 +3,10 @@
3
3
  module Forme
4
4
  # The major version of Forme, updated only for major changes that are
5
5
  # likely to require modification to apps using Forme.
6
- MAJOR = 1
6
+ MAJOR = 2
7
7
 
8
8
  # The minor version of Forme, updated for new feature releases of Forme.
9
- MINOR = 11
9
+ MINOR = 1
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
@@ -10,6 +10,7 @@ module Forme
10
10
 
11
11
  begin
12
12
  require 'cgi/escape'
13
+ # :nocov:
13
14
  unless CGI.respond_to?(:escapeHTML) # work around for JRuby 9.1
14
15
  CGI = Object.new
15
16
  CGI.extend(defined?(::CGI::Escape) ? ::CGI::Escape : ::CGI::Util)
@@ -32,6 +33,7 @@ module Forme
32
33
  end
33
34
  end
34
35
  end
36
+ # :nocov:
35
37
 
36
38
  @default_add_blank_prompt = nil
37
39
  @default_config = :default
@@ -160,5 +162,16 @@ module Forme
160
162
  end
161
163
  end
162
164
 
163
- %w'form input tag raw version'.each{|f| require File.expand_path("../forme/#{f}", __FILE__)}
164
- %w'error_handler formatter helper inputs_wrapper labeler serializer wrapper'.each{|f| require File.expand_path("../forme/transformers/#{f}", __FILE__)}
165
+ require_relative 'forme/form'
166
+ require_relative 'forme/input'
167
+ require_relative 'forme/tag'
168
+ require_relative 'forme/raw'
169
+ require_relative 'forme/version'
170
+
171
+ require_relative 'forme/transformers/error_handler'
172
+ require_relative 'forme/transformers/formatter'
173
+ require_relative 'forme/transformers/helper'
174
+ require_relative 'forme/transformers/inputs_wrapper'
175
+ require_relative 'forme/transformers/labeler'
176
+ require_relative 'forme/transformers/serializer'
177
+ require_relative 'forme/transformers/wrapper'
@@ -1,6 +1,6 @@
1
1
  # frozen-string-literal: true
2
2
 
3
- require 'forme/erb'
3
+ require_relative '../../forme/erb'
4
4
 
5
5
  class Roda
6
6
  module RodaPlugins
@@ -0,0 +1,57 @@
1
+ # frozen-string-literal: true
2
+
3
+ require_relative '../../forme/template'
4
+
5
+ class Roda
6
+ module RodaPlugins
7
+ module FormeErubiCapture
8
+ def self.load_dependencies(app)
9
+ app.plugin :forme_route_csrf
10
+ app.plugin :capture_erb
11
+ app.plugin :inject_erb
12
+ end
13
+
14
+ class Form < ::Forme::Template::Form
15
+ %w'inputs tag subform'.each do |meth|
16
+ class_eval(<<-END, __FILE__, __LINE__+1)
17
+ def #{meth}(*)
18
+ if block_given?
19
+ @scope.capture_erb do
20
+ super
21
+ @scope.instance_variable_get(@scope.render_opts[:template_opts][:outvar])
22
+ end
23
+ else
24
+ super
25
+ end
26
+ end
27
+ END
28
+ end
29
+
30
+ def emit(tag)
31
+ @scope.inject_erb(tag)
32
+ end
33
+ end
34
+
35
+ module InstanceMethods
36
+ def form(*)
37
+ if block_given?
38
+ capture_erb do
39
+ super
40
+ instance_variable_get(render_opts[:template_opts][:outvar])
41
+ end
42
+ else
43
+ super
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ def _forme_form_class
50
+ Form
51
+ end
52
+ end
53
+ end
54
+
55
+ register_plugin(:forme_erubi_capture, FormeErubiCapture)
56
+ end
57
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen-string-literal: true
2
2
 
3
- require 'forme/erb_form'
3
+ require_relative '../../forme/template'
4
4
 
5
5
  class Roda
6
6
  module RodaPlugins
@@ -13,21 +13,18 @@ class Roda
13
13
  end
14
14
 
15
15
  module InstanceMethods
16
- # Create a +Form+ object tied to the current output buffer,
17
- # using the standard ERB hidden tags.
18
- def form(obj=nil, attr={}, opts={}, &block)
19
- if obj.is_a?(Hash)
20
- attribs = obj
21
- options = attr = attr.dup
22
- else
23
- attribs = attr
24
- options = opts = opts.dup
25
- end
16
+ include ::Forme::Template::Helper
17
+
18
+ private
26
19
 
27
- apply_csrf = options[:csrf]
20
+ # Add the csrf and hidden tags options if needed.
21
+ def _forme_form_options(obj, attr, opts)
22
+ super
23
+
24
+ apply_csrf = opts[:csrf]
28
25
 
29
26
  if apply_csrf || apply_csrf.nil?
30
- unless method = attribs[:method] || attribs['method']
27
+ unless method = attr[:method] || attr['method']
31
28
  if obj && !obj.is_a?(Hash) && obj.respond_to?(:forme_default_request_method)
32
29
  method = obj.forme_default_request_method
33
30
  end
@@ -39,32 +36,17 @@ class Roda
39
36
  end
40
37
 
41
38
  if apply_csrf
42
- token = if options.fetch(:use_request_specific_token){use_request_specific_csrf_tokens?}
43
- csrf_token(csrf_path(attribs[:action]), method)
39
+ token = if opts.fetch(:use_request_specific_token){use_request_specific_csrf_tokens?}
40
+ csrf_token(csrf_path(attr[:action]), method)
44
41
  else
45
42
  csrf_token
46
43
  end
47
44
 
48
- options[:csrf] = [csrf_field, token]
49
- options[:hidden_tags] ||= []
50
- options[:hidden_tags] += [{csrf_field=>token}]
45
+ opts[:csrf] = [csrf_field, token]
46
+ opts[:_before] = lambda do |form|
47
+ form.tag(:input, :type=>:hidden, :name=>csrf_field, :value=>token)
48
+ end
51
49
  end
52
-
53
- options[:output] = @_out_buf if block
54
- _forme_form_options(options)
55
- _forme_form_class.form(obj, attr, opts, &block)
56
- end
57
-
58
- private
59
-
60
- # The class to use for forms
61
- def _forme_form_class
62
- ::Forme::ERB::Form
63
- end
64
-
65
- # The options to use for forms. Any changes should mutate this hash to set options.
66
- def _forme_form_options(options)
67
- options
68
50
  end
69
51
  end
70
52
  end