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