forme 1.8.0 → 1.12.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 +50 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +181 -9
- data/lib/forme/bs3.rb +1 -1
- data/lib/forme/form.rb +1 -1
- data/lib/forme/transformers/error_handler.rb +46 -1
- data/lib/forme/transformers/formatter.rb +57 -43
- data/lib/forme/transformers/inputs_wrapper.rb +2 -2
- data/lib/forme/transformers/labeler.rb +19 -0
- data/lib/forme/transformers/serializer.rb +1 -12
- data/lib/forme/transformers/wrapper.rb +1 -1
- data/lib/forme/version.rb +1 -1
- data/lib/forme.rb +27 -0
- data/lib/roda/plugins/forme_route_csrf.rb +15 -1
- data/lib/roda/plugins/forme_set.rb +214 -0
- data/lib/sequel/plugins/forme.rb +10 -5
- data/lib/sequel/plugins/forme_set.rb +50 -28
- data/spec/bs3_reference_spec.rb +4 -4
- data/spec/bs3_sequel_plugin_spec.rb +45 -45
- data/spec/bs3_spec.rb +1 -1
- data/spec/erb_helper.rb +2 -2
- data/spec/forme_spec.rb +75 -18
- data/spec/rails_integration_spec.rb +43 -26
- data/spec/roda_integration_spec.rb +357 -1
- data/spec/sequel_i18n_plugin_spec.rb +5 -4
- data/spec/sequel_plugin_spec.rb +61 -54
- data/spec/sequel_set_plugin_spec.rb +63 -14
- data/spec/spec_helper.rb +2 -2
- metadata +25 -6
@@ -3,31 +3,48 @@ require File.join(File.dirname(File.expand_path(__FILE__)), 'sequel_helper.rb')
|
|
3
3
|
|
4
4
|
require 'rubygems'
|
5
5
|
begin
|
6
|
-
require 'action_controller/railtie'
|
7
|
-
rescue LoadError
|
8
|
-
warn "unable to load rails, skipping rails spec"
|
9
|
-
else
|
10
|
-
begin
|
11
|
-
require 'active_pack/gem_version'
|
12
|
-
rescue LoadError
|
13
|
-
end
|
14
|
-
require 'forme/rails'
|
6
|
+
require 'action_controller/railtie'
|
15
7
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
get action, :controller=>'forme', :action=>action
|
20
|
-
end
|
8
|
+
begin
|
9
|
+
require 'active_pack/gem_version'
|
10
|
+
rescue LoadError
|
21
11
|
end
|
22
|
-
|
23
|
-
config.middleware.delete(ActionDispatch::ShowExceptions)
|
24
|
-
config.middleware.delete(Rack::Lock)
|
25
|
-
config.secret_key_base = 'foo'*15
|
26
|
-
config.secret_token = 'secret token'*15 if Rails.respond_to?(:version) && Rails.version < '5.2'
|
27
|
-
config.eager_load = true
|
28
|
-
initialize!
|
29
|
-
end
|
12
|
+
require 'forme/rails'
|
30
13
|
|
14
|
+
if Rails.respond_to?(:version) && Rails.version.start_with?('4')
|
15
|
+
# Work around issue in backported openssl environments where
|
16
|
+
# secret is 64 bytes intead of 32 bytes
|
17
|
+
require 'active_support/message_encryptor'
|
18
|
+
ActiveSupport::MessageEncryptor.send :prepend, Module.new {
|
19
|
+
def initialize(secret, *signature_key_or_options)
|
20
|
+
secret = secret[0, 32]
|
21
|
+
super
|
22
|
+
end
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
class FormeRails < Rails::Application
|
27
|
+
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|
|
29
|
+
get action, :controller=>'forme', :action=>action
|
30
|
+
end
|
31
|
+
end
|
32
|
+
config.active_support.deprecation = :stderr
|
33
|
+
config.middleware.delete(ActionDispatch::HostAuthorization) if defined?(ActionDispatch::HostAuthorization)
|
34
|
+
config.middleware.delete(ActionDispatch::ShowExceptions)
|
35
|
+
config.middleware.delete(Rack::Lock)
|
36
|
+
config.secret_key_base = 'foo'*15
|
37
|
+
config.secret_token = 'secret token'*15 if Rails.respond_to?(:version) && Rails.version < '5.2'
|
38
|
+
config.eager_load = true
|
39
|
+
begin
|
40
|
+
initialize!
|
41
|
+
rescue NoMethodError
|
42
|
+
raise LoadError
|
43
|
+
end
|
44
|
+
end
|
45
|
+
rescue LoadError
|
46
|
+
warn "unable to load or setup rails, skipping rails spec"
|
47
|
+
else
|
31
48
|
class FormeController < ActionController::Base
|
32
49
|
helper Forme::Rails::ERB
|
33
50
|
|
@@ -219,20 +236,20 @@ describe "Forme Rails integration" do
|
|
219
236
|
end
|
220
237
|
|
221
238
|
it "#form should correctly handle situation where multiple templates are used with same form object" do
|
222
|
-
sin_get('/nest_sep').must_equal
|
239
|
+
sin_get('/nest_sep').must_equal '0 <form action="/baz"> 1 <p>FBB</p> 2 n1 <div> n2 <input id="first" name="first" type="text" value="foo"/> <input id="last" name="last" type="text" value="bar"/> n3 </div> n4 <fieldset class="inputs"><legend>Foo</legend><input id="first" name="first" type="text" value="foo"/><input id="last" name="last" type="text" value="bar"/></fieldset> n5 3 </form>4'
|
223
240
|
end
|
224
241
|
|
225
242
|
it "#form should correctly handle situation where multiple templates are used with same form object" do
|
226
|
-
sin_get('/nest_inputs').must_equal
|
243
|
+
sin_get('/nest_inputs').must_equal '0 <form action="/baz"> 1 <p>FBB</p> 2 n1 <fieldset class="inputs"> n2 <input id="first" name="first" type="text" value="foo"/> <input id="last" name="last" type="text" value="bar"/> n3 </fieldset> n4 <fieldset class="inputs"><legend>Foo</legend><input id="first" name="first" type="text" value="foo"/><input id="last" name="last" type="text" value="bar"/></fieldset> n5 3 </form>4'
|
227
244
|
end
|
228
245
|
|
229
246
|
unless defined?(JRUBY_VERSION) && JRUBY_VERSION =~ /\A9\.0/ && defined?(ActionPack::VERSION::STRING) && ActionPack::VERSION::STRING >= '5.1'
|
230
247
|
it "#form should correctly handle situation Sequel integration with subforms where multiple templates are used with same form object" do
|
231
|
-
sin_get('/nest_seq').sub(%r{<input name
|
248
|
+
sin_get('/nest_seq').sub(%r{<input name="authenticity_token" type="hidden" value="([^"]+)"/>}, '<input name="authenticity_token" type="hidden" value="csrf"/>').must_equal '0 <form action="/baz" class="forme album" method="post"><input name="authenticity_token" type="hidden" value="csrf"/> 1 <input id="album_artist_attributes_id" name="album[artist_attributes][id]" type="hidden" value="2"/><fieldset class="inputs"><legend>Foo</legend><label>Name: <input id="album_artist_attributes_name" maxlength="255" name="album[artist_attributes][name]" type="text" value="A"/></label></fieldset> 2 n1 <input id="album_artist_attributes_id" name="album[artist_attributes][id]" type="hidden" value="2"/><fieldset class="inputs"><legend>Artist</legend> n2 <label>Name2: <input id="album_artist_attributes_name2" name="album[artist_attributes][name2]" type="text" value="A2"/></label> n3 </fieldset> n4 <input id="album_artist_attributes_id" name="album[artist_attributes][id]" type="hidden" value="2"/><fieldset class="inputs"><legend>Bar</legend><label>Name3: <input id="album_artist_attributes_name3" name="album[artist_attributes][name3]" type="text" value="A3"/></label></fieldset> n5 3 </form>4'
|
232
249
|
end
|
233
250
|
|
234
251
|
it "#form should work without a block with hidden tags" do
|
235
|
-
sin_get('/noblock_post').sub(%r{<input name
|
252
|
+
sin_get('/noblock_post').sub(%r{<input name="authenticity_token" type="hidden" value="([^"]+)"/>}, '<input name="authenticity_token" type="hidden" value="csrf"/>').must_equal '<form method="post"><input name="authenticity_token" type="hidden" value="csrf"/><input type="submit" value="xyz"/></form>'
|
236
253
|
end
|
237
254
|
end
|
238
255
|
|
@@ -20,7 +20,15 @@ rescue LoadError
|
|
20
20
|
end
|
21
21
|
|
22
22
|
class FormeRodaTest < Roda
|
23
|
-
|
23
|
+
opts[:check_dynamic_arity] = opts[:check_arity] = :warn
|
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
|
24
32
|
|
25
33
|
def erb(s, opts={})
|
26
34
|
render(opts.merge(:inline=>s))
|
@@ -114,6 +122,354 @@ else
|
|
114
122
|
sin_get('/csrf/0').wont_include '<input name="_csrf" type="hidden" value="'
|
115
123
|
end
|
116
124
|
end
|
125
|
+
|
126
|
+
describe "Forme Roda ERB Sequel integration with roda forme_set plugin and route_csrf plugin with #{plugin_opts}" do
|
127
|
+
before do
|
128
|
+
@app = Class.new(FormeRodaTest)
|
129
|
+
@app.plugin :route_csrf, plugin_opts
|
130
|
+
@app.plugin(:forme_set, :secret=>'1'*64)
|
131
|
+
|
132
|
+
@ab = Album.new
|
133
|
+
end
|
134
|
+
|
135
|
+
def forme_parse(*args, &block)
|
136
|
+
_forme_set(:forme_parse, *args, &block)
|
137
|
+
end
|
138
|
+
|
139
|
+
def forme_set(*args, &block)
|
140
|
+
_forme_set(:forme_set, *args, &block)
|
141
|
+
end
|
142
|
+
|
143
|
+
def forme_call(params)
|
144
|
+
@app.call('REQUEST_METHOD'=>'POST', 'rack.input'=>StringIO.new, :params=>params)
|
145
|
+
end
|
146
|
+
|
147
|
+
def _forme_set(meth, obj, orig_hash, *form_args, &block)
|
148
|
+
hash = {}
|
149
|
+
forme_set_block = orig_hash.delete(:forme_set_block)
|
150
|
+
orig_hash.each{|k,v| hash[k.to_s] = v}
|
151
|
+
album = @ab
|
152
|
+
ret, _, data, hmac = nil
|
153
|
+
|
154
|
+
@app.route do |r|
|
155
|
+
r.get do
|
156
|
+
form(*env[:args], &env[:block]).to_s
|
157
|
+
end
|
158
|
+
r.post do
|
159
|
+
r.params.replace(env[:params])
|
160
|
+
ret = send(meth, album, &forme_set_block)
|
161
|
+
nil
|
162
|
+
end
|
163
|
+
end
|
164
|
+
body = @app.call('REQUEST_METHOD'=>'GET', :args=>[album, *form_args], :block=>block)[2].join
|
165
|
+
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
|
166
|
+
csrf = $1
|
167
|
+
data = $2
|
168
|
+
hmac = $3
|
169
|
+
data.gsub!(""", '"') if data
|
170
|
+
h = {"album"=>hash, "_forme_set_data"=>data, "_forme_set_data_hmac"=>hmac, "_csrf"=>csrf}
|
171
|
+
if data && hmac
|
172
|
+
forme_call(h)
|
173
|
+
end
|
174
|
+
meth == :forme_parse ? ret : h
|
175
|
+
end
|
176
|
+
|
177
|
+
it "#forme_set should include HMAC values if form includes inputs for obj" do
|
178
|
+
h = forme_set(@ab, :name=>'Foo')
|
179
|
+
proc{forme_call(h)}.must_raise Roda::RodaPlugins::FormeSet::Error
|
180
|
+
@ab.name.must_be_nil
|
181
|
+
@ab.copies_sold.must_be_nil
|
182
|
+
|
183
|
+
h = forme_set(@ab, :name=>'Foo'){|f| f.input(:name)}
|
184
|
+
hmac = h.delete("_forme_set_data_hmac")
|
185
|
+
proc{forme_call(h)}.must_raise Roda::RodaPlugins::FormeSet::Error
|
186
|
+
proc{forme_call(h.merge("_forme_set_data_hmac"=>hmac+'1'))}.must_raise Roda::RodaPlugins::FormeSet::Error
|
187
|
+
data = h["_forme_set_data"]
|
188
|
+
data.sub!(/"csrf":\["_csrf","./, "\"csrf\":[\"_csrf\",\"|")
|
189
|
+
hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA512.new, '1'*64, data)
|
190
|
+
proc{forme_call(h.merge("_forme_set_data_hmac"=>hmac))}.must_raise Roda::RodaPlugins::FormeSet::Error
|
191
|
+
@ab.name.must_equal 'Foo'
|
192
|
+
@ab.copies_sold.must_be_nil
|
193
|
+
|
194
|
+
forme_set(@ab, :copies_sold=>100){|f| f.input(:name)}
|
195
|
+
@ab.name.must_be_nil
|
196
|
+
@ab.copies_sold.must_be_nil
|
197
|
+
end
|
198
|
+
|
199
|
+
it "#forme_set should handle custom form namespaces" do
|
200
|
+
forme_set(@ab, {"album"=>{"name"=>'Foo', 'copies_sold'=>'100'}}, {}, :namespace=>'album'){|f| f.input(:name); f.input(:copies_sold)}
|
201
|
+
@ab.name.must_equal 'Foo'
|
202
|
+
@ab.copies_sold.must_equal 100
|
203
|
+
|
204
|
+
proc{forme_set(@ab, {"a"=>{"name"=>'Foo'}}, {}, :namespace=>'album'){|f| f.input(:name); f.input(:copies_sold)}}.must_raise Roda::RodaPlugins::FormeSet::Error
|
205
|
+
end
|
206
|
+
|
207
|
+
it "#forme_set should call plugin block if there is an error with the form submission hmac not matching data" do
|
208
|
+
@app.plugin :forme_set do |error_type, _|
|
209
|
+
request.on{error_type.to_s}
|
210
|
+
end
|
211
|
+
|
212
|
+
h = forme_set(@ab, :name=>'Foo')
|
213
|
+
forme_call(h)[2].must_equal ['missing_data']
|
214
|
+
|
215
|
+
h = forme_set(@ab, :name=>'Foo'){|f| f.input(:name)}
|
216
|
+
hmac = h.delete("_forme_set_data_hmac")
|
217
|
+
forme_call(h)[2].must_equal ['missing_hmac']
|
218
|
+
|
219
|
+
forme_call(h.merge("_forme_set_data_hmac"=>hmac+'1'))[2].must_equal ['hmac_mismatch']
|
220
|
+
|
221
|
+
data = h["_forme_set_data"]
|
222
|
+
data.sub!(/"csrf":\["_csrf","./, "\"csrf\":[\"_csrf\",\"|")
|
223
|
+
hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA512.new, '1'*64, data)
|
224
|
+
forme_call(h.merge("_forme_set_data_hmac"=>hmac))[2].must_equal ['csrf_mismatch']
|
225
|
+
|
226
|
+
h = forme_set(@ab, :name=>'Foo')
|
227
|
+
h.delete('album')
|
228
|
+
forme_call(h)[2].must_equal ['missing_namespace']
|
229
|
+
end
|
230
|
+
|
231
|
+
it "#forme_set should raise if plugin block does not raise or throw" do
|
232
|
+
@app.plugin :forme_set do |_, obj|
|
233
|
+
obj
|
234
|
+
end
|
235
|
+
h = forme_set(@ab, :name=>'Foo'){|f| f.input(:name)}
|
236
|
+
h.delete("_forme_set_data_hmac")
|
237
|
+
proc{forme_call(h)}.must_raise Roda::RodaPlugins::FormeSet::Error
|
238
|
+
end
|
239
|
+
|
240
|
+
it "#forme_set should only set values in the form" do
|
241
|
+
forme_set(@ab, :name=>'Foo')
|
242
|
+
@ab.name.must_be_nil
|
243
|
+
|
244
|
+
forme_set(@ab, :name=>'Foo'){|f| f.input(:name)}
|
245
|
+
@ab.name.must_equal 'Foo'
|
246
|
+
|
247
|
+
forme_set(@ab, 'copies_sold'=>'1'){|f| f.input(:name)}
|
248
|
+
@ab.name.must_be_nil
|
249
|
+
@ab.copies_sold.must_be_nil
|
250
|
+
|
251
|
+
forme_set(@ab, 'name'=>'Bar', 'copies_sold'=>'1'){|f| f.input(:name); f.input(:copies_sold)}
|
252
|
+
@ab.name.must_equal 'Bar'
|
253
|
+
@ab.copies_sold.must_equal 1
|
254
|
+
end
|
255
|
+
|
256
|
+
it "#forme_set should handle form_versions" do
|
257
|
+
h = forme_set(@ab, {:name=>'Foo'}){|f| f.input(:name)}
|
258
|
+
@ab.name.must_equal 'Foo'
|
259
|
+
|
260
|
+
obj = nil
|
261
|
+
version = nil
|
262
|
+
name = nil
|
263
|
+
forme_set_block = proc do |v, o|
|
264
|
+
obj = o
|
265
|
+
name = o.name
|
266
|
+
version = v
|
267
|
+
end
|
268
|
+
h2 = forme_set(@ab, {:name=>'Foo', :forme_set_block=>forme_set_block}, {}, :form_version=>1){|f| f.input(:name)}
|
269
|
+
obj.must_be_same_as @ab
|
270
|
+
name.must_equal 'Foo'
|
271
|
+
version.must_equal 1
|
272
|
+
|
273
|
+
forme_call(h)
|
274
|
+
obj.must_be_same_as @ab
|
275
|
+
version.must_be_nil
|
276
|
+
|
277
|
+
forme_set(@ab, {:name=>'Bar', :forme_set_block=>forme_set_block}, {}, :form_version=>2){|f| f.input(:name)}
|
278
|
+
obj.must_be_same_as @ab
|
279
|
+
name.must_equal 'Bar'
|
280
|
+
version.must_equal 2
|
281
|
+
|
282
|
+
h['album']['name'] = 'Baz'
|
283
|
+
forme_call(h)
|
284
|
+
obj.must_be_same_as @ab
|
285
|
+
name.must_equal 'Baz'
|
286
|
+
version.must_be_nil
|
287
|
+
|
288
|
+
forme_call(h2)
|
289
|
+
obj.must_be_same_as @ab
|
290
|
+
version.must_equal 1
|
291
|
+
end
|
292
|
+
|
293
|
+
it "#forme_set should work for forms without blocks" do
|
294
|
+
forme_set(@ab, {:name=>'Foo'}, {}, :inputs=>[:name])
|
295
|
+
@ab.name.must_equal 'Foo'
|
296
|
+
end
|
297
|
+
|
298
|
+
it "#forme_set should handle different ways to specify parameter names" do
|
299
|
+
[{:attr=>{:name=>'foo'}}, {:attr=>{'name'=>:foo}}, {:name=>'foo'}, {:name=>'bar[foo]'}, {:key=>:foo}].each do |opts|
|
300
|
+
forme_set(@ab, name=>'Foo'){|f| f.input(:name, opts)}
|
301
|
+
@ab.name.must_be_nil
|
302
|
+
|
303
|
+
forme_set(@ab, 'foo'=>'Foo'){|f| f.input(:name, opts)}
|
304
|
+
@ab.name.must_equal 'Foo'
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
it "#forme_set should ignore values where key is explicitly set to nil" do
|
309
|
+
forme_set(@ab, :name=>'Foo'){|f| f.input(:name, :key=>nil)}
|
310
|
+
@ab.forme_set(:name=>'Foo')
|
311
|
+
@ab.name.must_be_nil
|
312
|
+
@ab.forme_set(nil=>'Foo')
|
313
|
+
@ab.name.must_be_nil
|
314
|
+
end
|
315
|
+
|
316
|
+
it "#forme_set should skip inputs with disabled/readonly formatter set on input" do
|
317
|
+
[:disabled, :readonly, ::Forme::Formatter::Disabled, ::Forme::Formatter::ReadOnly].each do |formatter|
|
318
|
+
forme_set(@ab, :name=>'Foo'){|f| f.input(:name, :formatter=>formatter)}
|
319
|
+
@ab.name.must_be_nil
|
320
|
+
end
|
321
|
+
|
322
|
+
forme_set(@ab, :name=>'Foo'){|f| f.input(:name, :formatter=>:default)}
|
323
|
+
@ab.name.must_equal 'Foo'
|
324
|
+
end
|
325
|
+
|
326
|
+
it "#forme_set should skip inputs with disabled/readonly formatter set on Form" do
|
327
|
+
[:disabled, :readonly, ::Forme::Formatter::Disabled, ::Forme::Formatter::ReadOnly].each do |formatter|
|
328
|
+
forme_set(@ab, {:name=>'Foo'}, {}, :formatter=>:disabled){|f| f.input(:name)}
|
329
|
+
@ab.name.must_be_nil
|
330
|
+
end
|
331
|
+
|
332
|
+
forme_set(@ab, {:name=>'Foo'}, {}, :formatter=>:default){|f| f.input(:name)}
|
333
|
+
@ab.name.must_equal 'Foo'
|
334
|
+
end
|
335
|
+
|
336
|
+
it "#forme_set should skip inputs with disabled/readonly formatter set using with_opts" do
|
337
|
+
[:disabled, :readonly, ::Forme::Formatter::Disabled, ::Forme::Formatter::ReadOnly].each do |formatter|
|
338
|
+
forme_set(@ab, :name=>'Foo'){|f| f.with_opts(:formatter=>formatter){f.input(:name)}}
|
339
|
+
@ab.name.must_be_nil
|
340
|
+
end
|
341
|
+
|
342
|
+
forme_set(@ab, :name=>'Foo'){|f| f.with_opts(:formatter=>:default){f.input(:name)}}
|
343
|
+
@ab.name.must_equal 'Foo'
|
344
|
+
end
|
345
|
+
|
346
|
+
it "#forme_set should prefer input formatter to with_opts formatter" do
|
347
|
+
forme_set(@ab, :name=>'Foo'){|f| f.with_opts(:formatter=>:default){f.input(:name, :formatter=>:readonly)}}
|
348
|
+
@ab.name.must_be_nil
|
349
|
+
|
350
|
+
forme_set(@ab, :name=>'Foo'){|f| f.with_opts(:formatter=>:readonly){f.input(:name, :formatter=>:default)}}
|
351
|
+
@ab.name.must_equal 'Foo'
|
352
|
+
end
|
353
|
+
|
354
|
+
it "#forme_set should prefer with_opts formatter to form formatter" do
|
355
|
+
forme_set(@ab, {:name=>'Foo'}, {}, :formatter=>:default){|f| f.with_opts(:formatter=>:readonly){f.input(:name)}}
|
356
|
+
@ab.name.must_be_nil
|
357
|
+
|
358
|
+
forme_set(@ab, {:name=>'Foo'}, {}, :formatter=>:readonly){|f| f.with_opts(:formatter=>:default){f.input(:name)}}
|
359
|
+
@ab.name.must_equal 'Foo'
|
360
|
+
end
|
361
|
+
|
362
|
+
it "#forme_set should handle setting values for associated objects" do
|
363
|
+
forme_set(@ab, :artist_id=>'1')
|
364
|
+
@ab.artist_id.must_be_nil
|
365
|
+
|
366
|
+
forme_set(@ab, :artist_id=>'1'){|f| f.input(:artist)}
|
367
|
+
@ab.artist_id.must_equal 1
|
368
|
+
|
369
|
+
forme_set(@ab, 'tag_pks'=>%w'1 2'){|f| f.input(:artist)}
|
370
|
+
@ab.artist_id.must_be_nil
|
371
|
+
@ab.tag_pks.must_equal []
|
372
|
+
|
373
|
+
forme_set(@ab, 'artist_id'=>'1', 'tag_pks'=>%w'1 2'){|f| f.input(:artist); f.input(:tags)}
|
374
|
+
@ab.artist_id.must_equal 1
|
375
|
+
@ab.tag_pks.must_equal [1, 2]
|
376
|
+
end
|
377
|
+
|
378
|
+
it "#forme_set should handle validations for filtered associations" do
|
379
|
+
[
|
380
|
+
[{:dataset=>proc{|ds| ds.exclude(:id=>1)}},
|
381
|
+
{:dataset=>proc{|ds| ds.exclude(:id=>1)}}],
|
382
|
+
[{:options=>Artist.exclude(:id=>1).select_order_map([:name, :id])},
|
383
|
+
{:options=>Tag.exclude(:id=>1).select_order_map(:id), :name=>'tag_pks[]'}],
|
384
|
+
[{:options=>Artist.exclude(:id=>1).all, :text_method=>:name, :value_method=>:id},
|
385
|
+
{:options=>Tag.exclude(:id=>1).all, :text_method=>:name, :value_method=>:id}],
|
386
|
+
].each do |artist_opts, tag_opts|
|
387
|
+
@ab.forme_validations.clear
|
388
|
+
forme_set(@ab, 'artist_id'=>'1', 'tag_pks'=>%w'1 2'){|f| f.input(:artist, artist_opts); f.input(:tags, tag_opts)}
|
389
|
+
@ab.artist_id.must_equal 1
|
390
|
+
@ab.tag_pks.must_equal [1, 2]
|
391
|
+
@ab.valid?.must_equal false
|
392
|
+
@ab.errors[:artist_id].must_equal ['invalid value submitted']
|
393
|
+
@ab.errors[:tag_pks].must_equal ['invalid value submitted']
|
394
|
+
|
395
|
+
@ab.forme_validations.clear
|
396
|
+
forme_set(@ab, 'artist_id'=>'1', 'tag_pks'=>%w'2'){|f| f.input(:artist, artist_opts); f.input(:tags, tag_opts)}
|
397
|
+
@ab.forme_set('artist_id'=>'1', 'tag_pks'=>['2'])
|
398
|
+
@ab.artist_id.must_equal 1
|
399
|
+
@ab.tag_pks.must_equal [2]
|
400
|
+
@ab.valid?.must_equal false
|
401
|
+
@ab.errors[:artist_id].must_equal ['invalid value submitted']
|
402
|
+
@ab.errors[:tag_pks].must_be_nil
|
403
|
+
|
404
|
+
@ab.forme_validations.clear
|
405
|
+
forme_set(@ab, 'artist_id'=>'2', 'tag_pks'=>%w'2'){|f| f.input(:artist, artist_opts); f.input(:tags, tag_opts)}
|
406
|
+
@ab.valid?.must_equal true
|
407
|
+
end
|
408
|
+
end
|
409
|
+
|
410
|
+
it "#forme_set should not require associated values for many_to_one association with select boxes" do
|
411
|
+
forme_set(@ab, {}){|f| f.input(:artist)}
|
412
|
+
@ab.valid?.must_equal true
|
413
|
+
|
414
|
+
forme_set(@ab, {'artist_id'=>nil}){|f| f.input(:artist)}
|
415
|
+
@ab.valid?.must_equal true
|
416
|
+
|
417
|
+
forme_set(@ab, {'artist_id'=>''}){|f| f.input(:artist)}
|
418
|
+
@ab.valid?.must_equal true
|
419
|
+
end
|
420
|
+
|
421
|
+
it "#forme_set should not require associated values for many_to_one association with radio buttons" do
|
422
|
+
forme_set(@ab, {}){|f| f.input(:artist, :as=>:radio)}
|
423
|
+
@ab.valid?.must_equal true
|
424
|
+
end
|
425
|
+
|
426
|
+
it "#forme_set should require associated values for many_to_one association with select boxes when :required is used" do
|
427
|
+
forme_set(@ab, {}){|f| f.input(:artist, :required=>true)}
|
428
|
+
@ab.valid?.must_equal false
|
429
|
+
@ab.errors[:artist_id].must_equal ['invalid value submitted']
|
430
|
+
end
|
431
|
+
|
432
|
+
it "#forme_set should require associated values for many_to_one association with radio buttons when :required is used" do
|
433
|
+
forme_set(@ab, {}){|f| f.input(:artist, :as=>:radio, :required=>true)}
|
434
|
+
@ab.valid?.must_equal false
|
435
|
+
@ab.errors[:artist_id].must_equal ['invalid value submitted']
|
436
|
+
end
|
437
|
+
|
438
|
+
it "#forme_set should handle cases where currently associated values is nil" do
|
439
|
+
def @ab.tag_pks; nil; end
|
440
|
+
forme_set(@ab, :tag_pks=>['1']){|f| f.input(:tags)}
|
441
|
+
@ab.valid?.must_equal true
|
442
|
+
end
|
443
|
+
|
444
|
+
it "#forme_parse should return hash with values and validations" do
|
445
|
+
forme_parse(@ab, :name=>'Foo'){|f| f.input(:name)}.must_equal(:values=>{:name=>'Foo'}, :validations=>{}, :form_version=>nil)
|
446
|
+
|
447
|
+
hash = forme_parse(@ab, :name=>'Foo', 'artist_id'=>'1') do |f|
|
448
|
+
f.input(:name)
|
449
|
+
f.input(:artist, :dataset=>proc{|ds| ds.exclude(:id=>1)})
|
450
|
+
end
|
451
|
+
hash.must_equal(:values=>{:name=>'Foo', :artist_id=>'1'}, :validations=>{:artist_id=>[:valid, false]}, :form_version=>nil)
|
452
|
+
|
453
|
+
@ab.set(hash[:values])
|
454
|
+
@ab.valid?.must_equal true
|
455
|
+
|
456
|
+
@ab.forme_validations.merge!(hash[:validations])
|
457
|
+
@ab.valid?.must_equal false
|
458
|
+
@ab.errors[:artist_id].must_equal ['invalid value submitted']
|
459
|
+
|
460
|
+
@ab = Album.new
|
461
|
+
hash = forme_parse(@ab, {:name=>'Foo', 'artist_id'=>'1'}, {}, :form_version=>1) do |f|
|
462
|
+
f.input(:name)
|
463
|
+
f.input(:artist, :dataset=>proc{|ds| ds.exclude(:id=>2)})
|
464
|
+
end
|
465
|
+
hash.must_equal(:values=>{:name=>'Foo', :artist_id=>'1'}, :validations=>{:artist_id=>[:valid, true]}, :form_version=>1)
|
466
|
+
@ab.set(hash[:values])
|
467
|
+
@ab.valid?.must_equal true
|
468
|
+
|
469
|
+
@ab.forme_validations.merge!(hash[:validations])
|
470
|
+
@ab.valid?.must_equal true
|
471
|
+
end
|
472
|
+
end
|
117
473
|
end
|
118
474
|
end
|
119
475
|
end
|