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.
- checksums.yaml +4 -4
- data/CHANGELOG +54 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +235 -209
- data/Rakefile +1 -7
- data/lib/forme/bs3.rb +18 -4
- data/lib/forme/erb.rb +13 -25
- data/lib/forme/form.rb +144 -149
- data/lib/forme/input.rb +1 -1
- data/lib/forme/rails.rb +39 -83
- data/lib/forme/raw.rb +2 -2
- data/lib/forme/tag.rb +3 -12
- data/lib/forme/template.rb +110 -0
- data/lib/forme/transformers/formatter.rb +4 -1
- data/lib/forme/transformers/helper.rb +0 -1
- data/lib/forme/transformers/inputs_wrapper.rb +4 -4
- data/lib/forme/version.rb +2 -2
- data/lib/forme.rb +15 -2
- data/lib/roda/plugins/forme.rb +1 -1
- data/lib/roda/plugins/forme_erubi_capture.rb +57 -0
- data/lib/roda/plugins/forme_route_csrf.rb +16 -34
- data/lib/roda/plugins/forme_set.rb +39 -76
- data/lib/sequel/plugins/forme.rb +40 -50
- data/lib/sequel/plugins/forme_i18n.rb +3 -1
- data/lib/sequel/plugins/forme_set.rb +3 -1
- data/spec/all.rb +1 -1
- data/spec/bs3_reference_spec.rb +18 -18
- data/spec/bs3_sequel_plugin_spec.rb +17 -17
- data/spec/bs3_spec.rb +23 -11
- data/spec/erb_helper.rb +69 -58
- data/spec/erubi_capture_helper.rb +198 -0
- data/spec/forme_spec.rb +21 -32
- data/spec/rails_integration_spec.rb +39 -25
- data/spec/roda_integration_spec.rb +118 -70
- data/spec/sequel_helper.rb +0 -1
- data/spec/sequel_i18n_helper.rb +1 -1
- data/spec/sequel_i18n_plugin_spec.rb +3 -2
- data/spec/sequel_plugin_spec.rb +29 -12
- data/spec/sequel_set_plugin_spec.rb +9 -2
- data/spec/shared_erb_specs.rb +71 -0
- data/spec/sinatra_integration_spec.rb +5 -6
- data/spec/spec_helper.rb +21 -8
- metadata +9 -7
- data/lib/forme/erb_form.rb +0 -74
- 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
|
-
|
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
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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
|
-
|
33
|
+
@scope.output_buffer << @scope.raw(tag)
|
44
34
|
end
|
35
|
+
end
|
45
36
|
|
46
|
-
|
47
|
-
|
48
|
-
|
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
|
-
|
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
|
-
|
46
|
+
raw(_forme(*a, &block))
|
63
47
|
end
|
64
48
|
end
|
65
49
|
|
66
|
-
|
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
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
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
|
-
|
103
|
-
#
|
104
|
-
|
105
|
-
|
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
|
-
#
|
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
|
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
|
5
|
-
#
|
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
|
-
|
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
|
-
|
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
|
@@ -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.
|
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.
|
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.
|
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.
|
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 =
|
6
|
+
MAJOR = 2
|
7
7
|
|
8
8
|
# The minor version of Forme, updated for new feature releases of Forme.
|
9
|
-
MINOR =
|
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
|
-
|
164
|
-
|
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'
|
data/lib/roda/plugins/forme.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
17
|
-
|
18
|
-
|
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
|
-
|
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 =
|
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
|
43
|
-
csrf_token(csrf_path(
|
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
|
-
|
49
|
-
|
50
|
-
|
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
|