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