forme 1.11.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
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