forme 1.12.0 → 2.0.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 +34 -0
- data/README.rdoc +226 -204
- data/lib/forme/bs3.rb +18 -4
- data/lib/forme/erb.rb +18 -16
- data/lib/forme/form.rb +150 -117
- data/lib/forme/input.rb +1 -1
- data/lib/forme/rails.rb +41 -72
- data/lib/forme/raw.rb +2 -2
- data/lib/forme/sinatra.rb +6 -2
- data/lib/forme/tag.rb +3 -12
- data/lib/forme/template.rb +118 -0
- 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 +13 -2
- data/lib/roda/plugins/forme.rb +1 -1
- data/lib/roda/plugins/forme_erubi_capture.rb +62 -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 +37 -51
- data/lib/sequel/plugins/forme_i18n.rb +3 -1
- data/spec/all.rb +1 -1
- data/spec/bs3_reference_spec.rb +18 -18
- data/spec/bs3_sequel_plugin_spec.rb +7 -7
- data/spec/bs3_spec.rb +23 -11
- data/spec/erb_helper.rb +73 -58
- data/spec/erubi_capture_helper.rb +202 -0
- data/spec/forme_spec.rb +30 -15
- data/spec/rails_integration_spec.rb +17 -11
- data/spec/roda_integration_spec.rb +123 -68
- data/spec/sequel_helper.rb +0 -1
- data/spec/sequel_i18n_helper.rb +1 -1
- data/spec/sequel_i18n_plugin_spec.rb +2 -2
- data/spec/sequel_plugin_spec.rb +11 -2
- data/spec/sequel_set_plugin_spec.rb +3 -3
- data/spec/shared_erb_specs.rb +75 -0
- data/spec/sinatra_integration_spec.rb +5 -6
- data/spec/spec_helper.rb +21 -3
- metadata +8 -5
- data/lib/forme/erb_form.rb +0 -74
@@ -0,0 +1,202 @@
|
|
1
|
+
require_relative 'shared_erb_specs'
|
2
|
+
|
3
|
+
ERUBI_CAPTURE_BLOCK = lambda do |r|
|
4
|
+
r.get '' do
|
5
|
+
erb <<END
|
6
|
+
<%|= form([:foo, :bar], :action=>'/baz') do |f| %>
|
7
|
+
<p>FBB</p>
|
8
|
+
<%= f.input(:first) %>
|
9
|
+
<%= f.input(:last) %>
|
10
|
+
<%= f.button('Save') %>
|
11
|
+
<%| end %>
|
12
|
+
END
|
13
|
+
end
|
14
|
+
|
15
|
+
r.get 'inputs_block' do
|
16
|
+
erb <<END
|
17
|
+
<%|= form([:foo, :bar], :action=>'/baz') do |f| %><%|= f.inputs(:legend=>'FBB') do %>
|
18
|
+
<%= f.input(:last) %>
|
19
|
+
<%| end %><%| end %>
|
20
|
+
END
|
21
|
+
end
|
22
|
+
|
23
|
+
r.get 'inputs_block_wrapper' do
|
24
|
+
erb <<END
|
25
|
+
<%|= form([:foo, :bar], {:action=>'/baz'}, :inputs_wrapper=>:fieldset_ol) do |f| %><%|= f.inputs(:legend=>'FBB') do %>
|
26
|
+
<%= f.input(:last) %>
|
27
|
+
<%| end %><%| end %>
|
28
|
+
END
|
29
|
+
end
|
30
|
+
|
31
|
+
r.get 'nest' do
|
32
|
+
erb <<END
|
33
|
+
<%|= form([:foo, :bar], :action=>'/baz') do |f| %>
|
34
|
+
<%= f.tag(:p, {}, 'FBB') %>
|
35
|
+
<%|= f.tag(:div) do %>
|
36
|
+
<%= f.input(:first) %>
|
37
|
+
<%= f.input(:last) %>
|
38
|
+
<%| end %>
|
39
|
+
|
40
|
+
<%| end %>
|
41
|
+
END
|
42
|
+
end
|
43
|
+
|
44
|
+
r.get 'nest_sep' do
|
45
|
+
@nest = <<END
|
46
|
+
n1
|
47
|
+
<%|= f.tag(:div) do %>
|
48
|
+
n2
|
49
|
+
<%= f.input(:first) %>
|
50
|
+
<%= f.input(:last) %>
|
51
|
+
n3
|
52
|
+
<%| end %>
|
53
|
+
n4
|
54
|
+
<%= f.inputs([:first, :last], :legend=>'Foo') %>
|
55
|
+
n5
|
56
|
+
END
|
57
|
+
erb <<END
|
58
|
+
0
|
59
|
+
<%|= form([:foo, :bar], :action=>'/baz') do |f| %>
|
60
|
+
1
|
61
|
+
<%= f.tag(:p, {}, 'FBB') %>
|
62
|
+
2
|
63
|
+
<%= erb(@nest, :locals=>{:f=>f}) %>
|
64
|
+
3
|
65
|
+
<%| end %>
|
66
|
+
4
|
67
|
+
END
|
68
|
+
end
|
69
|
+
|
70
|
+
r.get 'nest_seq_simple' do
|
71
|
+
@album = Album.load(:name=>'N', :copies_sold=>2, :id=>1)
|
72
|
+
@album.associations[:artist] = Artist.load(:name=>'A', :id=>2)
|
73
|
+
@nest = <<END
|
74
|
+
n1
|
75
|
+
<%|= f.subform(:artist) do %>
|
76
|
+
n2
|
77
|
+
<%= f.input(:name2) %>
|
78
|
+
n3
|
79
|
+
<%| end %>
|
80
|
+
n4
|
81
|
+
END
|
82
|
+
erb <<END
|
83
|
+
0
|
84
|
+
<%|= form(@album, :action=>'/baz') do |f| %>
|
85
|
+
1
|
86
|
+
<%= erb(@nest, :locals=>{:f=>f}) %>
|
87
|
+
3
|
88
|
+
<%| end %>
|
89
|
+
4
|
90
|
+
END
|
91
|
+
end
|
92
|
+
|
93
|
+
r.get 'nest_seq' do
|
94
|
+
@album = Album.load(:name=>'N', :copies_sold=>2, :id=>1)
|
95
|
+
@album.associations[:artist] = Artist.load(:name=>'A', :id=>2)
|
96
|
+
@nest = <<END
|
97
|
+
n1
|
98
|
+
<%|= f.subform(:artist) do %>
|
99
|
+
n2
|
100
|
+
<%= f.input(:name2) %>
|
101
|
+
n3
|
102
|
+
<%| end %>
|
103
|
+
n4
|
104
|
+
<%= f.subform(:artist, :inputs=>[:name3], :legend=>'Bar') %>
|
105
|
+
n5
|
106
|
+
END
|
107
|
+
erb <<END
|
108
|
+
0
|
109
|
+
<%|= form(@album, :action=>'/baz') do |f| %>
|
110
|
+
1
|
111
|
+
<%= f.subform(:artist, :inputs=>[:name], :legend=>'Foo') %>
|
112
|
+
2
|
113
|
+
<%= erb(@nest, :locals=>{:f=>f}) %>
|
114
|
+
3
|
115
|
+
<%| end %>
|
116
|
+
4
|
117
|
+
END
|
118
|
+
end
|
119
|
+
|
120
|
+
r.get 'grid-block' do
|
121
|
+
@album = Album.load(:name=>'N', :copies_sold=>2, :id=>1)
|
122
|
+
@album.associations[:artist] = Artist.load(:name=>'A', :id=>2)
|
123
|
+
erb <<END
|
124
|
+
0
|
125
|
+
<%|= form(@album, {:action=>'/baz'}, :button=>'Sub') do |f| %>
|
126
|
+
1
|
127
|
+
<%|= f.subform(:artist, :inputs=>[:name], :legend=>'Foo', :grid=>true, :labels=>%w'Name') do %>
|
128
|
+
2
|
129
|
+
<%| end %>
|
130
|
+
3
|
131
|
+
<%| end %>
|
132
|
+
4
|
133
|
+
END
|
134
|
+
end
|
135
|
+
|
136
|
+
r.get 'grid-noblock' do
|
137
|
+
@album = Album.load(:name=>'N', :copies_sold=>2, :id=>1)
|
138
|
+
@album.associations[:artist] = Artist.load(:name=>'A', :id=>2)
|
139
|
+
erb <<END
|
140
|
+
0
|
141
|
+
<%|= form(@album, {:action=>'/baz'}, :button=>'Sub') do |f| %>
|
142
|
+
1
|
143
|
+
<%= f.subform(:artist, :inputs=>[:name], :legend=>'Foo', :grid=>true, :labels=>%w'Name') %>
|
144
|
+
2
|
145
|
+
<%| end %>
|
146
|
+
3
|
147
|
+
END
|
148
|
+
end
|
149
|
+
|
150
|
+
r.get 'grid-noblock-multiple' do
|
151
|
+
@artist = Artist.load(:name=>'A', :id=>2)
|
152
|
+
@artist.associations[:albums] = [Album.load(:name=>'N', :copies_sold=>2, :id=>1)]
|
153
|
+
erb <<END
|
154
|
+
0
|
155
|
+
<%|= form(@artist, {:action=>'/baz'}, :button=>'Sub') do |f| %>
|
156
|
+
1
|
157
|
+
<%= f.subform(:albums, :inputs=>[:name, :copies_sold], :legend=>'Foo', :grid=>true, :labels=>%w'Name Copies') %>
|
158
|
+
2
|
159
|
+
<%| end %>
|
160
|
+
3
|
161
|
+
END
|
162
|
+
end
|
163
|
+
|
164
|
+
r.get 'hash' do
|
165
|
+
erb "<%|= form({:action=>'/baz'}, :obj=>[:foo]) do |f| %> <%= f.input(:first) %> <%| end %>"
|
166
|
+
end
|
167
|
+
|
168
|
+
r.get 'legend' do
|
169
|
+
erb <<END
|
170
|
+
<%|= form([:foo, :bar], :action=>'/baz') do |f| %>
|
171
|
+
<p>FBB</p>
|
172
|
+
<%= f.inputs([:first, :last], :legend=>'Foo') %>
|
173
|
+
<p>FBB2</p>
|
174
|
+
<%| end %>
|
175
|
+
END
|
176
|
+
end
|
177
|
+
|
178
|
+
r.get 'combined' do
|
179
|
+
erb <<END
|
180
|
+
<%|= form([:foo, :bar], {:action=>'/baz'}, :inputs=>[:first], :button=>'xyz', :legend=>'123') do |f| %>
|
181
|
+
<p>FBB</p>
|
182
|
+
<%= f.input(:last) %>
|
183
|
+
<%| end %>
|
184
|
+
END
|
185
|
+
end
|
186
|
+
|
187
|
+
r.get 'hidden_tags' do
|
188
|
+
erb "<%= form([:foo, :bar], {:action=>'/baz'}, :hidden_tags=>[{'a'=>'b'}]) %>"
|
189
|
+
end
|
190
|
+
|
191
|
+
r.get 'noblock' do
|
192
|
+
erb "<%= form([:foo, :bar], {:action=>'/baz'}, :inputs=>[:first], :button=>'xyz', :legend=>'123') %>"
|
193
|
+
end
|
194
|
+
|
195
|
+
r.get 'noblock_post' do
|
196
|
+
erb "<%= form({:method=>:post}, :button=>'xyz') %>"
|
197
|
+
end
|
198
|
+
|
199
|
+
r.get 'noblock_empty' do
|
200
|
+
erb "<%= form(:action=>'/baz') %>"
|
201
|
+
end
|
202
|
+
end
|
data/spec/forme_spec.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
require_relative 'spec_helper'
|
2
2
|
|
3
3
|
describe "Forme plain forms" do
|
4
4
|
def sel(opts, s)
|
@@ -631,13 +631,19 @@ describe "Forme plain forms" do
|
|
631
631
|
@f.tag(:textarea, {:name=>:foo}, :bar).to_s.must_equal '<textarea name="foo">bar</textarea>'
|
632
632
|
end
|
633
633
|
|
634
|
+
it "#tag should accept a block" do
|
635
|
+
@f.tag(:div){@f.tag(:textarea)}.to_s.must_equal '<div><textarea></textarea></div>'
|
636
|
+
@f.tag(:div, :name=>'a'){@f.tag(:textarea)}.to_s.must_equal '<div name="a"><textarea></textarea></div>'
|
637
|
+
@f.tag(:div, {:name=>'a'}, ["text"]){@f.tag(:textarea)}.to_s.must_equal '<div name="a">text<textarea></textarea></div>'
|
638
|
+
end
|
639
|
+
|
634
640
|
it "#tag should accept children as procs" do
|
635
|
-
@f.tag(:div, {:class=>"foo"}, lambda{|t| t.
|
641
|
+
@f.tag(:div, {:class=>"foo"}, lambda{|t| t.tag(:input, :class=>t.attr[:class])}).to_s.must_equal '<div class="foo"><input class="foo"/></div>'
|
636
642
|
end
|
637
643
|
|
638
644
|
it "#tag should accept children as methods" do
|
639
645
|
o = Object.new
|
640
|
-
def o.foo(t) t.
|
646
|
+
def o.foo(t) t.tag(:input, :class=>t.attr[:class]) end
|
641
647
|
@f.tag(:div, {:class=>"foo"}, o.method(:foo)).to_s.must_equal '<div class="foo"><input class="foo"/></div>'
|
642
648
|
end
|
643
649
|
|
@@ -645,6 +651,10 @@ describe "Forme plain forms" do
|
|
645
651
|
@f.inputs([:textarea, :text]).to_s.must_equal '<fieldset class="inputs"><textarea></textarea><input type="text"/></fieldset>'
|
646
652
|
end
|
647
653
|
|
654
|
+
it "should have an #inputs method for multiple inputs wrapped in a fieldset when using an empty block" do
|
655
|
+
@f.inputs([:textarea, :text]){}.to_s.must_equal '<fieldset class="inputs"><textarea></textarea><input type="text"/></fieldset>'
|
656
|
+
end
|
657
|
+
|
648
658
|
it "should have default #inputs method accept an :attr option" do
|
649
659
|
@f.inputs([:textarea, :text], :legend=>'Inputs', :attr=>{:class=>'foo', :bar=>'baz'}).to_s.must_equal '<fieldset bar="baz" class="foo inputs"><legend>Inputs</legend><textarea></textarea><input type="text"/></fieldset>'
|
650
660
|
end
|
@@ -805,41 +815,45 @@ describe "Forme plain forms" do
|
|
805
815
|
proc{@f.input(:textarea, :wrapper=>Object.new).to_s}.must_raise(Forme::Error)
|
806
816
|
proc{@f.input(:textarea, :formatter=>nil).to_s}.must_raise(Forme::Error)
|
807
817
|
end
|
808
|
-
end
|
809
818
|
|
810
|
-
|
811
|
-
|
812
|
-
@f = Forme::Form.new
|
819
|
+
it "should handle :before and :after hook options" do
|
820
|
+
Forme.form({}, :before=>lambda{|f| f.tag(:input, :type=>:hidden, :name=>:a, :value=>'b')}, :after=>lambda{|f| f.tag(:input, :type=>:hidden, :name=>:c, :value=>'d')}){|f| f.tag(:input)}.to_s.must_equal '<form><input name="a" type="hidden" value="b"/><input/><input name="c" type="hidden" value="d"/></form>'
|
813
821
|
end
|
822
|
+
end
|
814
823
|
|
815
|
-
|
824
|
+
describe "Forme::Form :hidden_tags option " do
|
825
|
+
silence_warnings "should handle hash" do
|
816
826
|
Forme.form({}, :hidden_tags=>[{:a=>'b'}]).to_s.must_equal '<form><input name="a" type="hidden" value="b"/></form>'
|
817
827
|
end
|
818
828
|
|
819
|
-
|
829
|
+
silence_warnings "should handle array" do
|
820
830
|
Forme.form({}, :hidden_tags=>[["a ", "b"]]).to_s.must_equal '<form>a b</form>'
|
821
831
|
end
|
822
832
|
|
823
|
-
|
833
|
+
silence_warnings "should handle string" do
|
824
834
|
Forme.form({}, :hidden_tags=>["a "]).to_s.must_equal '<form>a </form>'
|
825
835
|
end
|
826
836
|
|
827
|
-
|
837
|
+
silence_warnings "should handle proc return hash" do
|
828
838
|
Forme.form({}, :hidden_tags=>[lambda{|tag| {:a=>'b'}}]).to_s.must_equal '<form><input name="a" type="hidden" value="b"/></form>'
|
829
839
|
end
|
830
840
|
|
831
|
-
|
832
|
-
Forme.form({
|
841
|
+
silence_warnings "should handle proc return hash when from takes a block" do
|
842
|
+
Forme.form({}, :hidden_tags=>[lambda{|tag| {:a=>'b'}}]){}.to_s.must_equal '<form><input name="a" type="hidden" value="b"/></form>'
|
843
|
+
end
|
844
|
+
|
845
|
+
silence_warnings "should handle proc return tag" do
|
846
|
+
Forme.form({:method=>'post'}, :hidden_tags=>[lambda{|tag| tag.tag(tag.attr[:method])}]).to_s.must_equal '<form method="post"><post></post></form>'
|
833
847
|
end
|
834
848
|
|
835
|
-
|
849
|
+
silence_warnings "should raise error for unhandled object" do
|
836
850
|
proc{Forme.form({}, :hidden_tags=>[Object.new])}.must_raise Forme::Error
|
837
851
|
end
|
838
852
|
end
|
839
853
|
|
840
854
|
describe "Forme custom" do
|
841
855
|
it "formatters can be specified as a proc" do
|
842
|
-
Forme::Form.new(:formatter=>proc{|i| i.
|
856
|
+
Forme::Form.new(:formatter=>proc{|i| i.tag(:textarea, i.opts[:name]=>:name)}).input(:text, :name=>'foo').to_s.must_equal '<textarea foo="name"></textarea>'
|
843
857
|
end
|
844
858
|
|
845
859
|
it "serializers can be specified as a proc" do
|
@@ -1056,6 +1070,7 @@ describe "Forme built-in custom" do
|
|
1056
1070
|
Forme::Form.new(:serializer=>:text).button().to_s.must_equal ""
|
1057
1071
|
Forme::Form.new(:serializer=>:text).inputs([[:textarea, {:label=>"Foo", :value=>"Bar"}]], :legend=>'Baz').to_s.must_equal "Baz\n---\nFoo: Bar\n\n"
|
1058
1072
|
Forme::Form.new(:serializer=>:text).tag(:p){|f| f.input(:textarea, :label=>"Foo", :value=>"Bar")}.to_s.must_equal "Foo: Bar\n\n"
|
1073
|
+
Forme::Form.new(:serializer=>:text).tag(:p, {}, ['a']).to_s.must_equal "a"
|
1059
1074
|
end
|
1060
1075
|
end
|
1061
1076
|
|
@@ -1,7 +1,6 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
require_relative 'sequel_helper'
|
3
3
|
|
4
|
-
require 'rubygems'
|
5
4
|
begin
|
6
5
|
require 'action_controller/railtie'
|
7
6
|
|
@@ -9,23 +8,22 @@ begin
|
|
9
8
|
require 'active_pack/gem_version'
|
10
9
|
rescue LoadError
|
11
10
|
end
|
12
|
-
|
11
|
+
require_relative '../lib/forme/rails'
|
13
12
|
|
14
13
|
if Rails.respond_to?(:version) && Rails.version.start_with?('4')
|
15
14
|
# Work around issue in backported openssl environments where
|
16
15
|
# secret is 64 bytes intead of 32 bytes
|
17
16
|
require 'active_support/message_encryptor'
|
18
|
-
ActiveSupport::MessageEncryptor.
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
}
|
17
|
+
def (ActiveSupport::MessageEncryptor).new(secret, *signature_key_or_options)
|
18
|
+
obj = allocate
|
19
|
+
obj.send(:initialize, secret[0, 32], *signature_key_or_options)
|
20
|
+
obj
|
21
|
+
end
|
24
22
|
end
|
25
23
|
|
26
24
|
class FormeRails < Rails::Application
|
27
25
|
routes.append do
|
28
|
-
%w'index inputs_block inputs_block_wrapper nest nest_sep nest_inputs nest_seq hash legend combined noblock noblock_post safe_buffer'.each do |action|
|
26
|
+
%w'index inputs_block inputs_block_wrapper nest nest_sep nest_inputs nest_seq hash legend combined hidden_tags noblock noblock_post safe_buffer'.each do |action|
|
29
27
|
get action, :controller=>'forme', :action=>action
|
30
28
|
end
|
31
29
|
end
|
@@ -194,6 +192,10 @@ END
|
|
194
192
|
END
|
195
193
|
end
|
196
194
|
|
195
|
+
def hidden_tags
|
196
|
+
render :inline => "<%= forme([:foo, :bar], {:action=>'/baz'}, :hidden_tags=>[{'a'=>'b'}]) %>"
|
197
|
+
end
|
198
|
+
|
197
199
|
def noblock
|
198
200
|
render :inline => "<%= forme([:foo, :bar], {:action=>'/baz'}, :inputs=>[:first], :button=>'xyz', :legend=>'123') %>"
|
199
201
|
end
|
@@ -265,6 +267,10 @@ describe "Forme Rails integration" do
|
|
265
267
|
sin_get('/combined').must_equal '<form action="/baz"><fieldset class="inputs"><legend>123</legend><input id="first" name="first" type="text" value="foo"/></fieldset> <p>FBB</p> <input id="last" name="last" type="text" value="bar"/> <input type="submit" value="xyz"/></form>'
|
266
268
|
end
|
267
269
|
|
270
|
+
silence_warnings "#form should support :hidden_tags option" do
|
271
|
+
sin_get('/hidden_tags').must_equal '<form action="/baz"><input name="a" type="hidden" value="b"/></form>'
|
272
|
+
end
|
273
|
+
|
268
274
|
it "#form should work without a block" do
|
269
275
|
sin_get('/noblock').must_equal '<form action="/baz"><fieldset class="inputs"><legend>123</legend><input id="first" name="first" type="text" value="foo"/></fieldset><input type="submit" value="xyz"/></form>'
|
270
276
|
end
|
@@ -1,8 +1,7 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
require_relative 'sequel_helper'
|
3
|
+
require_relative 'erb_helper'
|
4
4
|
|
5
|
-
require 'rubygems'
|
6
5
|
begin
|
7
6
|
require 'roda'
|
8
7
|
require 'tilt'
|
@@ -17,34 +16,42 @@ rescue LoadError
|
|
17
16
|
rescue LoadError
|
18
17
|
require 'tilt/erb'
|
19
18
|
end
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
if defined?(Roda::RodaVersionNumber) && Roda::RodaVersionNumber >= 30100
|
26
|
-
require 'roda/session_middleware'
|
27
|
-
opts[:sessions_convert_symbols] = true
|
28
|
-
use RodaSessionMiddleware, :secret=>SecureRandom.random_bytes(64), :key=>'rack.session'
|
29
|
-
else
|
30
|
-
use Rack::Session::Cookie, :secret => "__a_very_long_string__"
|
31
|
-
end
|
32
|
-
|
33
|
-
def erb(s, opts={})
|
34
|
-
render(opts.merge(:inline=>s))
|
19
|
+
else
|
20
|
+
begin
|
21
|
+
require 'erubi/capture_end'
|
22
|
+
require_relative 'erubi_capture_helper'
|
23
|
+
rescue LoadError
|
35
24
|
end
|
25
|
+
end
|
36
26
|
|
37
|
-
|
38
|
-
|
39
|
-
|
27
|
+
def FormeRodaTest(block=ERB_BLOCK)
|
28
|
+
Class.new(Roda) do
|
29
|
+
opts[:check_dynamic_arity] = opts[:check_arity] = :warn
|
30
|
+
|
31
|
+
if defined?(Roda::RodaVersionNumber) && Roda::RodaVersionNumber >= 30100
|
32
|
+
require 'roda/session_middleware'
|
33
|
+
opts[:sessions_convert_symbols] = true
|
34
|
+
use RodaSessionMiddleware, :secret=>SecureRandom.random_bytes(64), :key=>'rack.session'
|
35
|
+
else
|
36
|
+
use Rack::Session::Cookie, :secret => "__a_very_long_string__"
|
40
37
|
end
|
41
|
-
|
42
|
-
|
38
|
+
|
39
|
+
def erb(s, opts={})
|
40
|
+
render(opts.merge(:inline=>s))
|
43
41
|
end
|
44
|
-
|
45
|
-
|
42
|
+
|
43
|
+
route do |r|
|
44
|
+
r.get 'use_request_specific_token', :use do |use|
|
45
|
+
render :inline=>"[#{Base64.strict_encode64(send(:csrf_secret))}]<%= form({:method=>:post}, {:use_request_specific_token=>#{use == '1'}}) %>"
|
46
|
+
end
|
47
|
+
r.get 'hidden_tags2' do |use|
|
48
|
+
render :inline=>"<%= form({:method=>:post}, {:hidden_tags=>[{:foo=>'bar'}]}) %>"
|
49
|
+
end
|
50
|
+
r.get 'csrf', :use do |use|
|
51
|
+
render :inline=>"<%= form({:method=>:post}, {:csrf=>#{use == '1'}}) %>"
|
52
|
+
end
|
53
|
+
instance_exec(r, &block)
|
46
54
|
end
|
47
|
-
instance_exec(r, &ERB_BLOCK)
|
48
55
|
end
|
49
56
|
end
|
50
57
|
|
@@ -54,7 +61,7 @@ rescue LoadError
|
|
54
61
|
warn "unable to load rack/csrf, skipping roda csrf plugin spec"
|
55
62
|
else
|
56
63
|
describe "Forme Roda ERB integration with roda forme and csrf plugins" do
|
57
|
-
app = FormeRodaCSRFTest =
|
64
|
+
app = FormeRodaCSRFTest = FormeRodaTest()
|
58
65
|
app.plugin :csrf
|
59
66
|
app.plugin :forme
|
60
67
|
|
@@ -68,64 +75,89 @@ describe "Forme Roda ERB integration with roda forme and csrf plugins" do
|
|
68
75
|
end
|
69
76
|
end
|
70
77
|
|
78
|
+
module FormeRouteCsrfSpecs
|
79
|
+
extend Minitest::Spec::DSL
|
80
|
+
include FormeErbSpecs
|
81
|
+
|
82
|
+
it "should have a valid CSRF tag" do
|
83
|
+
output = sin_get('/use_request_specific_token/1')
|
84
|
+
output =~ /\[([^\]]+)\].*?value=\"([^\"]+)\"/
|
85
|
+
secret = $1
|
86
|
+
token = $2
|
87
|
+
app.new({'SCRIPT_NAME'=>'', 'PATH_INFO'=>'/use_request_specific_token/1', 'REQUEST_METHOD'=>'POST', 'rack.session'=>{'_roda_csrf_secret'=>secret}, 'rack.input'=>StringIO.new}).valid_csrf?(:token=>token).must_equal true
|
88
|
+
app.new({'SCRIPT_NAME'=>'', 'PATH_INFO'=>'/use_request_specific_token/2', 'REQUEST_METHOD'=>'POST', 'rack.session'=>{'_roda_csrf_secret'=>secret}, 'rack.input'=>StringIO.new}).valid_csrf?(:token=>token).must_equal false
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should handle the :use_request_specific_token => true option" do
|
92
|
+
output = sin_get('/use_request_specific_token/1')
|
93
|
+
output =~ /\[([^\]]+)\].*?value=\"([^\"]+)\"/
|
94
|
+
secret = $1
|
95
|
+
token = $2
|
96
|
+
app.new({'SCRIPT_NAME'=>'', 'PATH_INFO'=>'/use_request_specific_token/1', 'REQUEST_METHOD'=>'POST', 'rack.session'=>{'_roda_csrf_secret'=>secret}, 'rack.input'=>StringIO.new}).valid_csrf?(:token=>token).must_equal true
|
97
|
+
app.new({'SCRIPT_NAME'=>'', 'PATH_INFO'=>'/use_request_specific_token/2', 'REQUEST_METHOD'=>'POST', 'rack.session'=>{'_roda_csrf_secret'=>secret}, 'rack.input'=>StringIO.new}).valid_csrf?(:token=>token).must_equal false
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should handle the :use_request_specific_token => false option" do
|
101
|
+
output = sin_get('/use_request_specific_token/0')
|
102
|
+
output =~ /\[([^\]]+)\].*?value=\"([^\"]+)\"/
|
103
|
+
secret = $1
|
104
|
+
token = $2
|
105
|
+
app.new({'SCRIPT_NAME'=>'', 'PATH_INFO'=>'/use_request_specific_token/0', 'REQUEST_METHOD'=>'POST', 'rack.session'=>{'_roda_csrf_secret'=>secret}, 'rack.input'=>StringIO.new}).valid_csrf?(:token=>token).must_equal(plugin_opts.empty? ? false : true)
|
106
|
+
end
|
107
|
+
|
108
|
+
silence_warnings "should include :hidden_tags option" do
|
109
|
+
sin_get('/hidden_tags2').must_include 'name="foo" type="hidden" value="bar"'
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should handle the :csrf option" do
|
113
|
+
sin_get('/csrf/1').must_include '<input name="_csrf" type="hidden" value="'
|
114
|
+
sin_get('/csrf/0').wont_include '<input name="_csrf" type="hidden" value="'
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
71
118
|
begin
|
72
119
|
require 'roda/plugins/route_csrf'
|
120
|
+
require 'roda/plugins/capture_erb'
|
121
|
+
require 'roda/plugins/inject_erb'
|
73
122
|
rescue LoadError
|
74
|
-
warn "unable to load
|
123
|
+
warn "unable to load necessary Roda plugins, skipping forme_erubi_capture plugin spec"
|
75
124
|
else
|
125
|
+
describe "Forme Roda Erubi::CaptureEnd integration with roda forme_route_csrf" do
|
126
|
+
app = FormeRodaTest(ERUBI_CAPTURE_BLOCK)
|
127
|
+
app.plugin :forme_erubi_capture
|
128
|
+
app.plugin :render, :engine_opts=>{'erb'=>{:engine_class=>Erubi::CaptureEndEngine}}
|
129
|
+
|
130
|
+
define_method(:app){app}
|
131
|
+
define_method(:plugin_opts){{}}
|
132
|
+
define_method(:sin_get) do |path|
|
133
|
+
s = String.new
|
134
|
+
app.call(@rack.merge('PATH_INFO'=>path))[2].each{|str| s << str}
|
135
|
+
s.gsub(/\s+/, ' ').strip
|
136
|
+
end
|
137
|
+
|
138
|
+
include FormeRouteCsrfSpecs
|
139
|
+
end if defined?(ERUBI_CAPTURE_BLOCK)
|
140
|
+
|
76
141
|
[{}, {:require_request_specific_tokens=>false}].each do |plugin_opts|
|
77
142
|
describe "Forme Roda ERB integration with roda forme_route_csrf and route_csrf plugin with #{plugin_opts}" do
|
78
|
-
app =
|
143
|
+
app = FormeRodaTest()
|
79
144
|
app.plugin :forme_route_csrf
|
80
145
|
app.plugin :route_csrf, plugin_opts
|
81
146
|
|
147
|
+
define_method(:app){app}
|
148
|
+
define_method(:plugin_opts){plugin_opts}
|
82
149
|
define_method(:sin_get) do |path|
|
83
150
|
s = String.new
|
84
151
|
app.call(@rack.merge('PATH_INFO'=>path))[2].each{|str| s << str}
|
85
152
|
s.gsub(/\s+/, ' ').strip
|
86
153
|
end
|
87
154
|
|
88
|
-
include
|
89
|
-
|
90
|
-
it "should handle the :hidden_tags option" do
|
91
|
-
output = sin_get('/use_request_specific_token/1')
|
92
|
-
output =~ /\[([^\]]+)\].*?value=\"([^\"]+)\"/
|
93
|
-
secret = $1
|
94
|
-
token = $2
|
95
|
-
app.new({'SCRIPT_NAME'=>'', 'PATH_INFO'=>'/use_request_specific_token/1', 'REQUEST_METHOD'=>'POST', 'rack.session'=>{'_roda_csrf_secret'=>secret}, 'rack.input'=>StringIO.new}).valid_csrf?(:token=>token).must_equal true
|
96
|
-
app.new({'SCRIPT_NAME'=>'', 'PATH_INFO'=>'/use_request_specific_token/2', 'REQUEST_METHOD'=>'POST', 'rack.session'=>{'_roda_csrf_secret'=>secret}, 'rack.input'=>StringIO.new}).valid_csrf?(:token=>token).must_equal false
|
97
|
-
end
|
98
|
-
|
99
|
-
it "should handle the :use_request_specific_token => true option" do
|
100
|
-
output = sin_get('/use_request_specific_token/1')
|
101
|
-
output =~ /\[([^\]]+)\].*?value=\"([^\"]+)\"/
|
102
|
-
secret = $1
|
103
|
-
token = $2
|
104
|
-
app.new({'SCRIPT_NAME'=>'', 'PATH_INFO'=>'/use_request_specific_token/1', 'REQUEST_METHOD'=>'POST', 'rack.session'=>{'_roda_csrf_secret'=>secret}, 'rack.input'=>StringIO.new}).valid_csrf?(:token=>token).must_equal true
|
105
|
-
app.new({'SCRIPT_NAME'=>'', 'PATH_INFO'=>'/use_request_specific_token/2', 'REQUEST_METHOD'=>'POST', 'rack.session'=>{'_roda_csrf_secret'=>secret}, 'rack.input'=>StringIO.new}).valid_csrf?(:token=>token).must_equal false
|
106
|
-
end
|
107
|
-
|
108
|
-
it "should handle the :use_request_specific_token => false option" do
|
109
|
-
output = sin_get('/use_request_specific_token/0')
|
110
|
-
output =~ /\[([^\]]+)\].*?value=\"([^\"]+)\"/
|
111
|
-
secret = $1
|
112
|
-
token = $2
|
113
|
-
app.new({'SCRIPT_NAME'=>'', 'PATH_INFO'=>'/use_request_specific_token/0', 'REQUEST_METHOD'=>'POST', 'rack.session'=>{'_roda_csrf_secret'=>secret}, 'rack.input'=>StringIO.new}).valid_csrf?(:token=>token).must_equal(plugin_opts.empty? ? false : true)
|
114
|
-
end
|
115
|
-
|
116
|
-
it "should handle the :hidden_tags option" do
|
117
|
-
sin_get('/hidden_tags').must_include 'name="foo" type="hidden" value="bar"'
|
118
|
-
end
|
119
|
-
|
120
|
-
it "should handle the :csrf option" do
|
121
|
-
sin_get('/csrf/1').must_include '<input name="_csrf" type="hidden" value="'
|
122
|
-
sin_get('/csrf/0').wont_include '<input name="_csrf" type="hidden" value="'
|
123
|
-
end
|
155
|
+
include FormeRouteCsrfSpecs
|
124
156
|
end
|
125
157
|
|
126
158
|
describe "Forme Roda ERB Sequel integration with roda forme_set plugin and route_csrf plugin with #{plugin_opts}" do
|
127
159
|
before do
|
128
|
-
@app =
|
160
|
+
@app = FormeRodaTest()
|
129
161
|
@app.plugin :route_csrf, plugin_opts
|
130
162
|
@app.plugin(:forme_set, :secret=>'1'*64)
|
131
163
|
|
@@ -153,7 +185,11 @@ else
|
|
153
185
|
|
154
186
|
@app.route do |r|
|
155
187
|
r.get do
|
156
|
-
|
188
|
+
if @block = env[:block]
|
189
|
+
render(:inline=>'<% form(*env[:args]) do |f| %><%= @block.call(f) %><% end %>')
|
190
|
+
else
|
191
|
+
form(*env[:args])
|
192
|
+
end
|
157
193
|
end
|
158
194
|
r.post do
|
159
195
|
r.params.replace(env[:params])
|
@@ -173,6 +209,25 @@ else
|
|
173
209
|
end
|
174
210
|
meth == :forme_parse ? ret : h
|
175
211
|
end
|
212
|
+
|
213
|
+
it "should have subform work correctly" do
|
214
|
+
@app.route do |r|
|
215
|
+
@album = Album.load(:name=>'N', :copies_sold=>2, :id=>1)
|
216
|
+
@album.associations[:artist] = Artist.load(:name=>'A', :id=>2)
|
217
|
+
erb <<END
|
218
|
+
0
|
219
|
+
<% form(@album, {:action=>'/baz'}, :button=>'Sub') do |f| %>
|
220
|
+
1
|
221
|
+
<%= f.subform(:artist, :inputs=>[:name], :legend=>'Foo', :grid=>true, :labels=>%w'Name') %>
|
222
|
+
2
|
223
|
+
<% end %>
|
224
|
+
3
|
225
|
+
END
|
226
|
+
end
|
227
|
+
|
228
|
+
body = @app.call('REQUEST_METHOD'=>'GET')[2].join.gsub("\n", ' ').gsub(/ +/, ' ').chomp(' ')
|
229
|
+
body.sub(%r{<input name="_csrf" type="hidden" value="([^"]+)"/>}, '<input name="_csrf" type="hidden" value="csrf"/>').must_equal '0 <form action="/baz" class="forme album" method="post"><input name="_csrf" type="hidden" value="csrf"/> 1 <table><caption>Foo</caption><thead><tr><th>Name</th></tr></thead><tbody><input id="album_artist_attributes_id" name="album[artist_attributes][id]" type="hidden" value="2"/><tr><td class="string"><input id="album_artist_attributes_name" maxlength="255" name="album[artist_attributes][name]" type="text" value="A"/></td></tr></tbody></table> 2 <input type="submit" value="Sub"/></form>3'
|
230
|
+
end
|
176
231
|
|
177
232
|
it "#forme_set should include HMAC values if form includes inputs for obj" do
|
178
233
|
h = forme_set(@ab, :name=>'Foo')
|
data/spec/sequel_helper.rb
CHANGED
data/spec/sequel_i18n_helper.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
|
1
|
+
require_relative 'spec_helper'
|
2
2
|
|
3
3
|
begin
|
4
4
|
raise LoadError if defined?(JRUBY_VERSION) && /\A9\.2\./.match(JRUBY_VERSION)
|
5
|
-
|
5
|
+
require_relative 'sequel_i18n_helper'
|
6
6
|
rescue LoadError
|
7
7
|
warn "unable to load i18n, skipping i18n Sequel plugin spec"
|
8
8
|
else
|