forme 1.9.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 +70 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +396 -202
- data/lib/forme/bs3.rb +19 -5
- data/lib/forme/erb.rb +18 -16
- data/lib/forme/form.rb +151 -118
- 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/error_handler.rb +46 -1
- data/lib/forme/transformers/formatter.rb +36 -35
- data/lib/forme/transformers/helper.rb +0 -1
- data/lib/forme/transformers/inputs_wrapper.rb +6 -6
- data/lib/forme/transformers/labeler.rb +19 -0
- data/lib/forme/transformers/wrapper.rb +1 -1
- 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 +62 -0
- data/lib/roda/plugins/forme_route_csrf.rb +16 -20
- data/lib/roda/plugins/forme_set.rb +177 -0
- data/lib/sequel/plugins/forme.rb +42 -55
- data/lib/sequel/plugins/forme_i18n.rb +3 -1
- data/lib/sequel/plugins/forme_set.rb +50 -28
- 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 +80 -29
- data/spec/rails_integration_spec.rb +47 -24
- data/spec/roda_integration_spec.rb +459 -48
- 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 +25 -8
- data/spec/sequel_set_plugin_spec.rb +10 -3
- data/spec/shared_erb_specs.rb +75 -0
- data/spec/sinatra_integration_spec.rb +5 -6
- data/spec/spec_helper.rb +23 -5
- metadata +30 -8
- data/lib/forme/erb_form.rb +0 -74
@@ -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,26 +16,42 @@ rescue LoadError
|
|
17
16
|
rescue LoadError
|
18
17
|
require 'tilt/erb'
|
19
18
|
end
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
def erb(s, opts={})
|
26
|
-
render(opts.merge(:inline=>s))
|
19
|
+
else
|
20
|
+
begin
|
21
|
+
require 'erubi/capture_end'
|
22
|
+
require_relative 'erubi_capture_helper'
|
23
|
+
rescue LoadError
|
27
24
|
end
|
25
|
+
end
|
28
26
|
|
29
|
-
|
30
|
-
|
31
|
-
|
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__"
|
32
37
|
end
|
33
|
-
|
34
|
-
|
38
|
+
|
39
|
+
def erb(s, opts={})
|
40
|
+
render(opts.merge(:inline=>s))
|
35
41
|
end
|
36
|
-
|
37
|
-
|
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)
|
38
54
|
end
|
39
|
-
instance_exec(r, &ERB_BLOCK)
|
40
55
|
end
|
41
56
|
end
|
42
57
|
|
@@ -46,7 +61,7 @@ rescue LoadError
|
|
46
61
|
warn "unable to load rack/csrf, skipping roda csrf plugin spec"
|
47
62
|
else
|
48
63
|
describe "Forme Roda ERB integration with roda forme and csrf plugins" do
|
49
|
-
app = FormeRodaCSRFTest =
|
64
|
+
app = FormeRodaCSRFTest = FormeRodaTest()
|
50
65
|
app.plugin :csrf
|
51
66
|
app.plugin :forme
|
52
67
|
|
@@ -60,58 +75,454 @@ describe "Forme Roda ERB integration with roda forme and csrf plugins" do
|
|
60
75
|
end
|
61
76
|
end
|
62
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
|
+
|
63
118
|
begin
|
64
119
|
require 'roda/plugins/route_csrf'
|
120
|
+
require 'roda/plugins/capture_erb'
|
121
|
+
require 'roda/plugins/inject_erb'
|
65
122
|
rescue LoadError
|
66
|
-
warn "unable to load
|
123
|
+
warn "unable to load necessary Roda plugins, skipping forme_erubi_capture plugin spec"
|
67
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
|
+
|
68
141
|
[{}, {:require_request_specific_tokens=>false}].each do |plugin_opts|
|
69
142
|
describe "Forme Roda ERB integration with roda forme_route_csrf and route_csrf plugin with #{plugin_opts}" do
|
70
|
-
app =
|
143
|
+
app = FormeRodaTest()
|
71
144
|
app.plugin :forme_route_csrf
|
72
145
|
app.plugin :route_csrf, plugin_opts
|
73
146
|
|
147
|
+
define_method(:app){app}
|
148
|
+
define_method(:plugin_opts){plugin_opts}
|
74
149
|
define_method(:sin_get) do |path|
|
75
150
|
s = String.new
|
76
151
|
app.call(@rack.merge('PATH_INFO'=>path))[2].each{|str| s << str}
|
77
152
|
s.gsub(/\s+/, ' ').strip
|
78
153
|
end
|
79
154
|
|
80
|
-
include
|
155
|
+
include FormeRouteCsrfSpecs
|
156
|
+
end
|
157
|
+
|
158
|
+
describe "Forme Roda ERB Sequel integration with roda forme_set plugin and route_csrf plugin with #{plugin_opts}" do
|
159
|
+
before do
|
160
|
+
@app = FormeRodaTest()
|
161
|
+
@app.plugin :route_csrf, plugin_opts
|
162
|
+
@app.plugin(:forme_set, :secret=>'1'*64)
|
163
|
+
|
164
|
+
@ab = Album.new
|
165
|
+
end
|
166
|
+
|
167
|
+
def forme_parse(*args, &block)
|
168
|
+
_forme_set(:forme_parse, *args, &block)
|
169
|
+
end
|
170
|
+
|
171
|
+
def forme_set(*args, &block)
|
172
|
+
_forme_set(:forme_set, *args, &block)
|
173
|
+
end
|
174
|
+
|
175
|
+
def forme_call(params)
|
176
|
+
@app.call('REQUEST_METHOD'=>'POST', 'rack.input'=>StringIO.new, :params=>params)
|
177
|
+
end
|
178
|
+
|
179
|
+
def _forme_set(meth, obj, orig_hash, *form_args, &block)
|
180
|
+
hash = {}
|
181
|
+
forme_set_block = orig_hash.delete(:forme_set_block)
|
182
|
+
orig_hash.each{|k,v| hash[k.to_s] = v}
|
183
|
+
album = @ab
|
184
|
+
ret, _, data, hmac = nil
|
185
|
+
|
186
|
+
@app.route do |r|
|
187
|
+
r.get do
|
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
|
193
|
+
end
|
194
|
+
r.post do
|
195
|
+
r.params.replace(env[:params])
|
196
|
+
ret = send(meth, album, &forme_set_block)
|
197
|
+
nil
|
198
|
+
end
|
199
|
+
end
|
200
|
+
body = @app.call('REQUEST_METHOD'=>'GET', :args=>[album, *form_args], :block=>block)[2].join
|
201
|
+
body =~ %r|<input name="_csrf" type="hidden" value="([^"]+)"/>.*<input name="_forme_set_data" type="hidden" value="([^"]+)"/><input name="_forme_set_data_hmac" type="hidden" value="([^"]+)"/>|n
|
202
|
+
csrf = $1
|
203
|
+
data = $2
|
204
|
+
hmac = $3
|
205
|
+
data.gsub!(""", '"') if data
|
206
|
+
h = {"album"=>hash, "_forme_set_data"=>data, "_forme_set_data_hmac"=>hmac, "_csrf"=>csrf}
|
207
|
+
if data && hmac
|
208
|
+
forme_call(h)
|
209
|
+
end
|
210
|
+
meth == :forme_parse ? ret : h
|
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
|
231
|
+
|
232
|
+
it "#forme_set should include HMAC values if form includes inputs for obj" do
|
233
|
+
h = forme_set(@ab, :name=>'Foo')
|
234
|
+
proc{forme_call(h)}.must_raise Roda::RodaPlugins::FormeSet::Error
|
235
|
+
@ab.name.must_be_nil
|
236
|
+
@ab.copies_sold.must_be_nil
|
237
|
+
|
238
|
+
h = forme_set(@ab, :name=>'Foo'){|f| f.input(:name)}
|
239
|
+
hmac = h.delete("_forme_set_data_hmac")
|
240
|
+
proc{forme_call(h)}.must_raise Roda::RodaPlugins::FormeSet::Error
|
241
|
+
proc{forme_call(h.merge("_forme_set_data_hmac"=>hmac+'1'))}.must_raise Roda::RodaPlugins::FormeSet::Error
|
242
|
+
data = h["_forme_set_data"]
|
243
|
+
data.sub!(/"csrf":\["_csrf","./, "\"csrf\":[\"_csrf\",\"|")
|
244
|
+
hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA512.new, '1'*64, data)
|
245
|
+
proc{forme_call(h.merge("_forme_set_data_hmac"=>hmac))}.must_raise Roda::RodaPlugins::FormeSet::Error
|
246
|
+
@ab.name.must_equal 'Foo'
|
247
|
+
@ab.copies_sold.must_be_nil
|
81
248
|
|
82
|
-
|
83
|
-
|
84
|
-
|
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
|
249
|
+
forme_set(@ab, :copies_sold=>100){|f| f.input(:name)}
|
250
|
+
@ab.name.must_be_nil
|
251
|
+
@ab.copies_sold.must_be_nil
|
89
252
|
end
|
90
253
|
|
91
|
-
it "should handle
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
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
|
254
|
+
it "#forme_set should handle custom form namespaces" do
|
255
|
+
forme_set(@ab, {"album"=>{"name"=>'Foo', 'copies_sold'=>'100'}}, {}, :namespace=>'album'){|f| f.input(:name); f.input(:copies_sold)}
|
256
|
+
@ab.name.must_equal 'Foo'
|
257
|
+
@ab.copies_sold.must_equal 100
|
258
|
+
|
259
|
+
proc{forme_set(@ab, {"a"=>{"name"=>'Foo'}}, {}, :namespace=>'album'){|f| f.input(:name); f.input(:copies_sold)}}.must_raise Roda::RodaPlugins::FormeSet::Error
|
98
260
|
end
|
99
261
|
|
100
|
-
it "should
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
262
|
+
it "#forme_set should call plugin block if there is an error with the form submission hmac not matching data" do
|
263
|
+
@app.plugin :forme_set do |error_type, _|
|
264
|
+
request.on{error_type.to_s}
|
265
|
+
end
|
266
|
+
|
267
|
+
h = forme_set(@ab, :name=>'Foo')
|
268
|
+
forme_call(h)[2].must_equal ['missing_data']
|
269
|
+
|
270
|
+
h = forme_set(@ab, :name=>'Foo'){|f| f.input(:name)}
|
271
|
+
hmac = h.delete("_forme_set_data_hmac")
|
272
|
+
forme_call(h)[2].must_equal ['missing_hmac']
|
273
|
+
|
274
|
+
forme_call(h.merge("_forme_set_data_hmac"=>hmac+'1'))[2].must_equal ['hmac_mismatch']
|
275
|
+
|
276
|
+
data = h["_forme_set_data"]
|
277
|
+
data.sub!(/"csrf":\["_csrf","./, "\"csrf\":[\"_csrf\",\"|")
|
278
|
+
hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA512.new, '1'*64, data)
|
279
|
+
forme_call(h.merge("_forme_set_data_hmac"=>hmac))[2].must_equal ['csrf_mismatch']
|
280
|
+
|
281
|
+
h = forme_set(@ab, :name=>'Foo')
|
282
|
+
h.delete('album')
|
283
|
+
forme_call(h)[2].must_equal ['missing_namespace']
|
106
284
|
end
|
107
285
|
|
108
|
-
it "should
|
109
|
-
|
286
|
+
it "#forme_set should raise if plugin block does not raise or throw" do
|
287
|
+
@app.plugin :forme_set do |_, obj|
|
288
|
+
obj
|
289
|
+
end
|
290
|
+
h = forme_set(@ab, :name=>'Foo'){|f| f.input(:name)}
|
291
|
+
h.delete("_forme_set_data_hmac")
|
292
|
+
proc{forme_call(h)}.must_raise Roda::RodaPlugins::FormeSet::Error
|
110
293
|
end
|
111
294
|
|
112
|
-
it "should
|
113
|
-
|
114
|
-
|
295
|
+
it "#forme_set should only set values in the form" do
|
296
|
+
forme_set(@ab, :name=>'Foo')
|
297
|
+
@ab.name.must_be_nil
|
298
|
+
|
299
|
+
forme_set(@ab, :name=>'Foo'){|f| f.input(:name)}
|
300
|
+
@ab.name.must_equal 'Foo'
|
301
|
+
|
302
|
+
forme_set(@ab, 'copies_sold'=>'1'){|f| f.input(:name)}
|
303
|
+
@ab.name.must_be_nil
|
304
|
+
@ab.copies_sold.must_be_nil
|
305
|
+
|
306
|
+
forme_set(@ab, 'name'=>'Bar', 'copies_sold'=>'1'){|f| f.input(:name); f.input(:copies_sold)}
|
307
|
+
@ab.name.must_equal 'Bar'
|
308
|
+
@ab.copies_sold.must_equal 1
|
309
|
+
end
|
310
|
+
|
311
|
+
it "#forme_set should handle form_versions" do
|
312
|
+
h = forme_set(@ab, {:name=>'Foo'}){|f| f.input(:name)}
|
313
|
+
@ab.name.must_equal 'Foo'
|
314
|
+
|
315
|
+
obj = nil
|
316
|
+
version = nil
|
317
|
+
name = nil
|
318
|
+
forme_set_block = proc do |v, o|
|
319
|
+
obj = o
|
320
|
+
name = o.name
|
321
|
+
version = v
|
322
|
+
end
|
323
|
+
h2 = forme_set(@ab, {:name=>'Foo', :forme_set_block=>forme_set_block}, {}, :form_version=>1){|f| f.input(:name)}
|
324
|
+
obj.must_be_same_as @ab
|
325
|
+
name.must_equal 'Foo'
|
326
|
+
version.must_equal 1
|
327
|
+
|
328
|
+
forme_call(h)
|
329
|
+
obj.must_be_same_as @ab
|
330
|
+
version.must_be_nil
|
331
|
+
|
332
|
+
forme_set(@ab, {:name=>'Bar', :forme_set_block=>forme_set_block}, {}, :form_version=>2){|f| f.input(:name)}
|
333
|
+
obj.must_be_same_as @ab
|
334
|
+
name.must_equal 'Bar'
|
335
|
+
version.must_equal 2
|
336
|
+
|
337
|
+
h['album']['name'] = 'Baz'
|
338
|
+
forme_call(h)
|
339
|
+
obj.must_be_same_as @ab
|
340
|
+
name.must_equal 'Baz'
|
341
|
+
version.must_be_nil
|
342
|
+
|
343
|
+
forme_call(h2)
|
344
|
+
obj.must_be_same_as @ab
|
345
|
+
version.must_equal 1
|
346
|
+
end
|
347
|
+
|
348
|
+
it "#forme_set should work for forms without blocks" do
|
349
|
+
forme_set(@ab, {:name=>'Foo'}, {}, :inputs=>[:name])
|
350
|
+
@ab.name.must_equal 'Foo'
|
351
|
+
end
|
352
|
+
|
353
|
+
it "#forme_set should handle different ways to specify parameter names" do
|
354
|
+
[{:attr=>{:name=>'foo'}}, {:attr=>{'name'=>:foo}}, {:name=>'foo'}, {:name=>'bar[foo]'}, {:key=>:foo}].each do |opts|
|
355
|
+
forme_set(@ab, name=>'Foo'){|f| f.input(:name, opts)}
|
356
|
+
@ab.name.must_be_nil
|
357
|
+
|
358
|
+
forme_set(@ab, 'foo'=>'Foo'){|f| f.input(:name, opts)}
|
359
|
+
@ab.name.must_equal 'Foo'
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
it "#forme_set should ignore values where key is explicitly set to nil" do
|
364
|
+
forme_set(@ab, :name=>'Foo'){|f| f.input(:name, :key=>nil)}
|
365
|
+
@ab.forme_set(:name=>'Foo')
|
366
|
+
@ab.name.must_be_nil
|
367
|
+
@ab.forme_set(nil=>'Foo')
|
368
|
+
@ab.name.must_be_nil
|
369
|
+
end
|
370
|
+
|
371
|
+
it "#forme_set should skip inputs with disabled/readonly formatter set on input" do
|
372
|
+
[:disabled, :readonly, ::Forme::Formatter::Disabled, ::Forme::Formatter::ReadOnly].each do |formatter|
|
373
|
+
forme_set(@ab, :name=>'Foo'){|f| f.input(:name, :formatter=>formatter)}
|
374
|
+
@ab.name.must_be_nil
|
375
|
+
end
|
376
|
+
|
377
|
+
forme_set(@ab, :name=>'Foo'){|f| f.input(:name, :formatter=>:default)}
|
378
|
+
@ab.name.must_equal 'Foo'
|
379
|
+
end
|
380
|
+
|
381
|
+
it "#forme_set should skip inputs with disabled/readonly formatter set on Form" do
|
382
|
+
[:disabled, :readonly, ::Forme::Formatter::Disabled, ::Forme::Formatter::ReadOnly].each do |formatter|
|
383
|
+
forme_set(@ab, {:name=>'Foo'}, {}, :formatter=>:disabled){|f| f.input(:name)}
|
384
|
+
@ab.name.must_be_nil
|
385
|
+
end
|
386
|
+
|
387
|
+
forme_set(@ab, {:name=>'Foo'}, {}, :formatter=>:default){|f| f.input(:name)}
|
388
|
+
@ab.name.must_equal 'Foo'
|
389
|
+
end
|
390
|
+
|
391
|
+
it "#forme_set should skip inputs with disabled/readonly formatter set using with_opts" do
|
392
|
+
[:disabled, :readonly, ::Forme::Formatter::Disabled, ::Forme::Formatter::ReadOnly].each do |formatter|
|
393
|
+
forme_set(@ab, :name=>'Foo'){|f| f.with_opts(:formatter=>formatter){f.input(:name)}}
|
394
|
+
@ab.name.must_be_nil
|
395
|
+
end
|
396
|
+
|
397
|
+
forme_set(@ab, :name=>'Foo'){|f| f.with_opts(:formatter=>:default){f.input(:name)}}
|
398
|
+
@ab.name.must_equal 'Foo'
|
399
|
+
end
|
400
|
+
|
401
|
+
it "#forme_set should prefer input formatter to with_opts formatter" do
|
402
|
+
forme_set(@ab, :name=>'Foo'){|f| f.with_opts(:formatter=>:default){f.input(:name, :formatter=>:readonly)}}
|
403
|
+
@ab.name.must_be_nil
|
404
|
+
|
405
|
+
forme_set(@ab, :name=>'Foo'){|f| f.with_opts(:formatter=>:readonly){f.input(:name, :formatter=>:default)}}
|
406
|
+
@ab.name.must_equal 'Foo'
|
407
|
+
end
|
408
|
+
|
409
|
+
it "#forme_set should prefer with_opts formatter to form formatter" do
|
410
|
+
forme_set(@ab, {:name=>'Foo'}, {}, :formatter=>:default){|f| f.with_opts(:formatter=>:readonly){f.input(:name)}}
|
411
|
+
@ab.name.must_be_nil
|
412
|
+
|
413
|
+
forme_set(@ab, {:name=>'Foo'}, {}, :formatter=>:readonly){|f| f.with_opts(:formatter=>:default){f.input(:name)}}
|
414
|
+
@ab.name.must_equal 'Foo'
|
415
|
+
end
|
416
|
+
|
417
|
+
it "#forme_set should handle setting values for associated objects" do
|
418
|
+
forme_set(@ab, :artist_id=>'1')
|
419
|
+
@ab.artist_id.must_be_nil
|
420
|
+
|
421
|
+
forme_set(@ab, :artist_id=>'1'){|f| f.input(:artist)}
|
422
|
+
@ab.artist_id.must_equal 1
|
423
|
+
|
424
|
+
forme_set(@ab, 'tag_pks'=>%w'1 2'){|f| f.input(:artist)}
|
425
|
+
@ab.artist_id.must_be_nil
|
426
|
+
@ab.tag_pks.must_equal []
|
427
|
+
|
428
|
+
forme_set(@ab, 'artist_id'=>'1', 'tag_pks'=>%w'1 2'){|f| f.input(:artist); f.input(:tags)}
|
429
|
+
@ab.artist_id.must_equal 1
|
430
|
+
@ab.tag_pks.must_equal [1, 2]
|
431
|
+
end
|
432
|
+
|
433
|
+
it "#forme_set should handle validations for filtered associations" do
|
434
|
+
[
|
435
|
+
[{:dataset=>proc{|ds| ds.exclude(:id=>1)}},
|
436
|
+
{:dataset=>proc{|ds| ds.exclude(:id=>1)}}],
|
437
|
+
[{:options=>Artist.exclude(:id=>1).select_order_map([:name, :id])},
|
438
|
+
{:options=>Tag.exclude(:id=>1).select_order_map(:id), :name=>'tag_pks[]'}],
|
439
|
+
[{:options=>Artist.exclude(:id=>1).all, :text_method=>:name, :value_method=>:id},
|
440
|
+
{:options=>Tag.exclude(:id=>1).all, :text_method=>:name, :value_method=>:id}],
|
441
|
+
].each do |artist_opts, tag_opts|
|
442
|
+
@ab.forme_validations.clear
|
443
|
+
forme_set(@ab, 'artist_id'=>'1', 'tag_pks'=>%w'1 2'){|f| f.input(:artist, artist_opts); f.input(:tags, tag_opts)}
|
444
|
+
@ab.artist_id.must_equal 1
|
445
|
+
@ab.tag_pks.must_equal [1, 2]
|
446
|
+
@ab.valid?.must_equal false
|
447
|
+
@ab.errors[:artist_id].must_equal ['invalid value submitted']
|
448
|
+
@ab.errors[:tag_pks].must_equal ['invalid value submitted']
|
449
|
+
|
450
|
+
@ab.forme_validations.clear
|
451
|
+
forme_set(@ab, 'artist_id'=>'1', 'tag_pks'=>%w'2'){|f| f.input(:artist, artist_opts); f.input(:tags, tag_opts)}
|
452
|
+
@ab.forme_set('artist_id'=>'1', 'tag_pks'=>['2'])
|
453
|
+
@ab.artist_id.must_equal 1
|
454
|
+
@ab.tag_pks.must_equal [2]
|
455
|
+
@ab.valid?.must_equal false
|
456
|
+
@ab.errors[:artist_id].must_equal ['invalid value submitted']
|
457
|
+
@ab.errors[:tag_pks].must_be_nil
|
458
|
+
|
459
|
+
@ab.forme_validations.clear
|
460
|
+
forme_set(@ab, 'artist_id'=>'2', 'tag_pks'=>%w'2'){|f| f.input(:artist, artist_opts); f.input(:tags, tag_opts)}
|
461
|
+
@ab.valid?.must_equal true
|
462
|
+
end
|
463
|
+
end
|
464
|
+
|
465
|
+
it "#forme_set should not require associated values for many_to_one association with select boxes" do
|
466
|
+
forme_set(@ab, {}){|f| f.input(:artist)}
|
467
|
+
@ab.valid?.must_equal true
|
468
|
+
|
469
|
+
forme_set(@ab, {'artist_id'=>nil}){|f| f.input(:artist)}
|
470
|
+
@ab.valid?.must_equal true
|
471
|
+
|
472
|
+
forme_set(@ab, {'artist_id'=>''}){|f| f.input(:artist)}
|
473
|
+
@ab.valid?.must_equal true
|
474
|
+
end
|
475
|
+
|
476
|
+
it "#forme_set should not require associated values for many_to_one association with radio buttons" do
|
477
|
+
forme_set(@ab, {}){|f| f.input(:artist, :as=>:radio)}
|
478
|
+
@ab.valid?.must_equal true
|
479
|
+
end
|
480
|
+
|
481
|
+
it "#forme_set should require associated values for many_to_one association with select boxes when :required is used" do
|
482
|
+
forme_set(@ab, {}){|f| f.input(:artist, :required=>true)}
|
483
|
+
@ab.valid?.must_equal false
|
484
|
+
@ab.errors[:artist_id].must_equal ['invalid value submitted']
|
485
|
+
end
|
486
|
+
|
487
|
+
it "#forme_set should require associated values for many_to_one association with radio buttons when :required is used" do
|
488
|
+
forme_set(@ab, {}){|f| f.input(:artist, :as=>:radio, :required=>true)}
|
489
|
+
@ab.valid?.must_equal false
|
490
|
+
@ab.errors[:artist_id].must_equal ['invalid value submitted']
|
491
|
+
end
|
492
|
+
|
493
|
+
it "#forme_set should handle cases where currently associated values is nil" do
|
494
|
+
def @ab.tag_pks; nil; end
|
495
|
+
forme_set(@ab, :tag_pks=>['1']){|f| f.input(:tags)}
|
496
|
+
@ab.valid?.must_equal true
|
497
|
+
end
|
498
|
+
|
499
|
+
it "#forme_parse should return hash with values and validations" do
|
500
|
+
forme_parse(@ab, :name=>'Foo'){|f| f.input(:name)}.must_equal(:values=>{:name=>'Foo'}, :validations=>{}, :form_version=>nil)
|
501
|
+
|
502
|
+
hash = forme_parse(@ab, :name=>'Foo', 'artist_id'=>'1') do |f|
|
503
|
+
f.input(:name)
|
504
|
+
f.input(:artist, :dataset=>proc{|ds| ds.exclude(:id=>1)})
|
505
|
+
end
|
506
|
+
hash.must_equal(:values=>{:name=>'Foo', :artist_id=>'1'}, :validations=>{:artist_id=>[:valid, false]}, :form_version=>nil)
|
507
|
+
|
508
|
+
@ab.set(hash[:values])
|
509
|
+
@ab.valid?.must_equal true
|
510
|
+
|
511
|
+
@ab.forme_validations.merge!(hash[:validations])
|
512
|
+
@ab.valid?.must_equal false
|
513
|
+
@ab.errors[:artist_id].must_equal ['invalid value submitted']
|
514
|
+
|
515
|
+
@ab = Album.new
|
516
|
+
hash = forme_parse(@ab, {:name=>'Foo', 'artist_id'=>'1'}, {}, :form_version=>1) do |f|
|
517
|
+
f.input(:name)
|
518
|
+
f.input(:artist, :dataset=>proc{|ds| ds.exclude(:id=>2)})
|
519
|
+
end
|
520
|
+
hash.must_equal(:values=>{:name=>'Foo', :artist_id=>'1'}, :validations=>{:artist_id=>[:valid, true]}, :form_version=>1)
|
521
|
+
@ab.set(hash[:values])
|
522
|
+
@ab.valid?.must_equal true
|
523
|
+
|
524
|
+
@ab.forme_validations.merge!(hash[:validations])
|
525
|
+
@ab.valid?.must_equal true
|
115
526
|
end
|
116
527
|
end
|
117
528
|
end
|
data/spec/sequel_helper.rb
CHANGED
data/spec/sequel_i18n_helper.rb
CHANGED
@@ -1,7 +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
|
+
require_relative 'sequel_i18n_helper'
|
5
6
|
rescue LoadError
|
6
7
|
warn "unable to load i18n, skipping i18n Sequel plugin spec"
|
7
8
|
else
|