reform 0.2.7 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES.md +18 -0
- data/Gemfile +2 -1
- data/README.md +196 -17
- data/TODO.md +14 -3
- data/database.sqlite3 +0 -0
- data/lib/reform.rb +9 -1
- data/lib/reform/composition.rb +41 -34
- data/lib/reform/contract.rb +109 -0
- data/lib/reform/contract/errors.rb +33 -0
- data/lib/reform/contract/setup.rb +44 -0
- data/lib/reform/contract/validate.rb +48 -0
- data/lib/reform/form.rb +13 -309
- data/lib/reform/form/active_model.rb +8 -5
- data/lib/reform/form/active_record.rb +30 -37
- data/lib/reform/form/coercion.rb +10 -11
- data/lib/reform/form/composition.rb +40 -50
- data/lib/reform/form/multi_parameter_attributes.rb +6 -1
- data/lib/reform/form/save.rb +61 -0
- data/lib/reform/form/sync.rb +60 -0
- data/lib/reform/form/validate.rb +104 -0
- data/lib/reform/form/virtual_attributes.rb +3 -5
- data/lib/reform/representer.rb +17 -3
- data/lib/reform/version.rb +1 -1
- data/reform.gemspec +2 -1
- data/test/active_model_test.rb +0 -92
- data/test/as_test.rb +75 -0
- data/test/coercion_test.rb +26 -8
- data/test/composition_test.rb +8 -8
- data/test/contract_test.rb +57 -0
- data/test/errors_test.rb +37 -10
- data/test/feature_test.rb +28 -0
- data/test/form_builder_test.rb +105 -0
- data/test/form_composition_test.rb +30 -13
- data/test/nested_form_test.rb +12 -18
- data/test/reform_test.rb +11 -6
- data/test/save_test.rb +81 -0
- data/test/setup_test.rb +38 -0
- data/test/sync_test.rb +39 -0
- data/test/test_helper.rb +36 -2
- data/test/validate_test.rb +191 -0
- metadata +42 -4
@@ -1,10 +1,9 @@
|
|
1
|
-
|
2
|
-
class Form
|
1
|
+
class Reform::Form < Reform::Contract
|
3
2
|
# TODO: this should be in Representer namespace.
|
4
3
|
module EmptyAttributesOptions
|
5
4
|
def options
|
6
5
|
empty_fields = representable_attrs.
|
7
|
-
find_all { |d| d
|
6
|
+
find_all { |d| d[:empty] }.
|
8
7
|
collect { |d| d.name.to_sym }
|
9
8
|
|
10
9
|
super.exclude!(empty_fields)
|
@@ -14,11 +13,10 @@ module Reform
|
|
14
13
|
module ReadonlyAttributesOptions
|
15
14
|
def options
|
16
15
|
readonly_fields = representable_attrs.
|
17
|
-
find_all { |d| d
|
16
|
+
find_all { |d| d[:virtual] }.
|
18
17
|
collect { |d| d.name.to_sym }
|
19
18
|
|
20
19
|
super.exclude!(readonly_fields)
|
21
20
|
end
|
22
21
|
end
|
23
|
-
end
|
24
22
|
end
|
data/lib/reform/representer.rb
CHANGED
@@ -3,6 +3,12 @@ require 'representable/decorator'
|
|
3
3
|
|
4
4
|
module Reform
|
5
5
|
class Representer < Representable::Decorator
|
6
|
+
include Representable::Hash::AllowSymbols
|
7
|
+
|
8
|
+
extend Uber::InheritableAttr
|
9
|
+
inheritable_attr :options
|
10
|
+
# self.options = {}
|
11
|
+
|
6
12
|
# Invokes #to_hash and/or #from_hash with #options. This provides a hook for other
|
7
13
|
# modules to add options for the representational process.
|
8
14
|
module WithOptions
|
@@ -42,11 +48,16 @@ module Reform
|
|
42
48
|
|
43
49
|
def nested_forms(&block)
|
44
50
|
clone_config!.
|
45
|
-
find_all { |attr| attr
|
46
|
-
collect { |attr| [attr, represented.send(attr.getter)] }. # DISCUSS: can't we do this with the Binding itself?
|
51
|
+
find_all { |attr| attr[:form] }.
|
47
52
|
each(&block)
|
48
53
|
end
|
49
54
|
|
55
|
+
def self.for(options)
|
56
|
+
clone.tap do |representer|
|
57
|
+
representer.options = options
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
50
61
|
def self.clone # called in inheritable_attr :representer_class.
|
51
62
|
Class.new(self) # By subclassing, representable_attrs.clone is called.
|
52
63
|
end
|
@@ -62,7 +73,10 @@ module Reform
|
|
62
73
|
def self.inline_representer(base_module, name, options, &block)
|
63
74
|
name = name.to_s.singularize.camelize
|
64
75
|
|
65
|
-
Class.new(
|
76
|
+
Class.new(self.options[:form_class]) do
|
77
|
+
# TODO: this will soon become a generic feature in representable.
|
78
|
+
include *options[:features].reverse if options[:features]
|
79
|
+
|
66
80
|
instance_exec &block
|
67
81
|
|
68
82
|
@form_name = name
|
data/lib/reform/version.rb
CHANGED
data/reform.gemspec
CHANGED
@@ -18,7 +18,8 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
spec.add_dependency "representable", "~> 1.
|
21
|
+
spec.add_dependency "representable", "~> 1.8.1"
|
22
|
+
spec.add_dependency "disposable", "~> 0.0.3"
|
22
23
|
spec.add_dependency "uber", "~> 0.0.4"
|
23
24
|
spec.add_dependency "activemodel"
|
24
25
|
spec.add_development_dependency "bundler", "~> 1.3"
|
data/test/active_model_test.rb
CHANGED
@@ -94,98 +94,6 @@ class NewActiveModelTest < MiniTest::Spec # TODO: move to test/rails/
|
|
94
94
|
end
|
95
95
|
end
|
96
96
|
|
97
|
-
class FormBuilderCompatTest < MiniTest::Spec
|
98
|
-
let (:form_class) {
|
99
|
-
Class.new(Reform::Form) do
|
100
|
-
include Reform::Form::ActiveModel::FormBuilderMethods
|
101
|
-
|
102
|
-
property :artist do
|
103
|
-
property :name
|
104
|
-
validates :name, :presence => true
|
105
|
-
end
|
106
|
-
|
107
|
-
collection :songs do
|
108
|
-
property :title
|
109
|
-
property :release_date
|
110
|
-
validates :title, :presence => true
|
111
|
-
end
|
112
|
-
|
113
|
-
class LabelForm < Reform::Form
|
114
|
-
property :name
|
115
|
-
end
|
116
|
-
|
117
|
-
property :label, :form => LabelForm
|
118
|
-
end
|
119
|
-
}
|
120
|
-
|
121
|
-
let (:song) { OpenStruct.new }
|
122
|
-
let (:form) { form_class.new(OpenStruct.new(
|
123
|
-
:artist => Artist.new(:name => "Propagandhi"),
|
124
|
-
:songs => [song],
|
125
|
-
:label => OpenStruct.new)) }
|
126
|
-
|
127
|
-
it "respects _attributes params hash" do
|
128
|
-
form.validate("artist_attributes" => {"name" => "Blink 182"},
|
129
|
-
"songs_attributes" => {"0" => {"title" => "Damnit"}})
|
130
|
-
|
131
|
-
form.artist.name.must_equal "Blink 182"
|
132
|
-
form.songs.first.title.must_equal "Damnit"
|
133
|
-
end
|
134
|
-
|
135
|
-
it "allows nested collection and property to be missing" do
|
136
|
-
form.validate({})
|
137
|
-
|
138
|
-
form.artist.name.must_equal "Propagandhi"
|
139
|
-
|
140
|
-
form.songs.size.must_equal 1
|
141
|
-
form.songs[0].model.must_equal song # this is a weird test.
|
142
|
-
end
|
143
|
-
|
144
|
-
it "defines _attributes= setter so Rails' FB works properly" do
|
145
|
-
form.must_respond_to("artist_attributes=")
|
146
|
-
form.must_respond_to("songs_attributes=")
|
147
|
-
form.must_respond_to("label_attributes=")
|
148
|
-
end
|
149
|
-
|
150
|
-
describe "deconstructed date parameters" do
|
151
|
-
let(:form_attributes) do
|
152
|
-
{
|
153
|
-
"artist_attributes" => {"name" => "Blink 182"},
|
154
|
-
"songs_attributes" => {"0" => {"title" => "Damnit", "release_date(1i)" => release_year,
|
155
|
-
"release_date(2i)" => release_month, "release_date(3i)" => release_day}}
|
156
|
-
}
|
157
|
-
end
|
158
|
-
let(:release_year) { "1997" }
|
159
|
-
let(:release_month) { "9" }
|
160
|
-
let(:release_day) { "27" }
|
161
|
-
|
162
|
-
describe "with valid parameters" do
|
163
|
-
it "creates a date" do
|
164
|
-
form.validate(form_attributes)
|
165
|
-
|
166
|
-
form.songs.first.release_date.must_equal Date.new(1997, 9, 27)
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
%w(year month day).each do |date_attr|
|
171
|
-
describe "when the #{date_attr} is missing" do
|
172
|
-
let(:"release_#{date_attr}") { "" }
|
173
|
-
|
174
|
-
it "rejects the date" do
|
175
|
-
form.validate(form_attributes)
|
176
|
-
|
177
|
-
form.songs.first.release_date.must_be_nil
|
178
|
-
end
|
179
|
-
end
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
it "returns flat errors hash" do
|
184
|
-
form.validate("artist_attributes" => {"name" => ""},
|
185
|
-
"songs_attributes" => {"0" => {"title" => ""}})
|
186
|
-
form.errors.messages.must_equal(:"artist.name" => ["can't be blank"], :"songs.title" => ["can't be blank"])
|
187
|
-
end
|
188
|
-
end
|
189
97
|
|
190
98
|
class ActiveModelWithCompositionTest < MiniTest::Spec
|
191
99
|
class HitForm < Reform::Form
|
data/test/as_test.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class AsTest < BaseTest
|
4
|
+
class AlbumForm < Reform::Form
|
5
|
+
property :name, :as => :title
|
6
|
+
|
7
|
+
property :single, :as => :hit do
|
8
|
+
property :title
|
9
|
+
end
|
10
|
+
|
11
|
+
collection :tracks, :as => :songs do
|
12
|
+
property :name, :as => :title
|
13
|
+
end
|
14
|
+
|
15
|
+
property :band do
|
16
|
+
property :company, :as => :label do
|
17
|
+
property :business, :as => :name
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
let (:song2) { Song.new("Roxanne") }
|
23
|
+
|
24
|
+
let (:params) {
|
25
|
+
{
|
26
|
+
"name" => "Best Of The Police",
|
27
|
+
"single" => {"title" => "So Lonely"},
|
28
|
+
"tracks" => [{"name" => "Message In A Bottle"}, {"name" => "Roxanne"}]
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
32
|
+
subject { AlbumForm.new(Album.new("Best Of", hit, [Song.new("Fallout"), song2])) }
|
33
|
+
|
34
|
+
it { subject.name.must_equal "Best Of" }
|
35
|
+
it { subject.single.title.must_equal "Roxanne" }
|
36
|
+
it { subject.tracks[0].name.must_equal "Fallout" }
|
37
|
+
it { subject.tracks[1].name.must_equal "Roxanne" }
|
38
|
+
|
39
|
+
|
40
|
+
describe "#validate" do
|
41
|
+
|
42
|
+
|
43
|
+
before { subject.validate(params) }
|
44
|
+
|
45
|
+
it { subject.name.must_equal "Best Of The Police" }
|
46
|
+
it { subject.single.title.must_equal "So Lonely" }
|
47
|
+
it { subject.tracks[0].name.must_equal "Message In A Bottle" }
|
48
|
+
it { subject.tracks[1].name.must_equal "Roxanne" }
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
describe "#sync" do
|
53
|
+
before {
|
54
|
+
subject.tracks[1].name = "Livin' Ain't No Crime"
|
55
|
+
subject.sync
|
56
|
+
}
|
57
|
+
|
58
|
+
it { song2.title.must_equal "Livin' Ain't No Crime" }
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
describe "#save (nested hash)" do
|
63
|
+
before { subject.validate(params) }
|
64
|
+
|
65
|
+
it do
|
66
|
+
hash = nil
|
67
|
+
|
68
|
+
subject.save do |f, nested_hash|
|
69
|
+
hash = nested_hash
|
70
|
+
end
|
71
|
+
|
72
|
+
hash.must_equal({"title"=>"Best Of The Police", "hit"=>{"title"=>"So Lonely"}, "songs"=>[{"title"=>"Message In A Bottle"}, {"title"=>"Roxanne"}]})
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/test/coercion_test.rb
CHANGED
@@ -1,18 +1,36 @@
|
|
1
1
|
require "test_helper"
|
2
2
|
require "reform/form/coercion"
|
3
3
|
|
4
|
-
class CoercionTest <
|
5
|
-
|
6
|
-
|
4
|
+
class CoercionTest < BaseTest
|
5
|
+
subject do
|
6
|
+
Class.new(Reform::Form) do
|
7
7
|
include Reform::Form::Coercion
|
8
8
|
|
9
|
-
property :
|
10
|
-
|
9
|
+
property :released_at, :type => DateTime
|
10
|
+
|
11
|
+
property :hit do
|
12
|
+
property :length, :type => Integer
|
13
|
+
property :good, :type => Virtus::Attribute::Boolean
|
14
|
+
end
|
11
15
|
|
12
|
-
|
13
|
-
|
16
|
+
property :band do
|
17
|
+
property :label do
|
18
|
+
property :value, :type => Float
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end.new(OpenStruct.new(
|
22
|
+
:released_at => "31/03/1981",
|
23
|
+
:hit => OpenStruct.new(:length => "312"),
|
24
|
+
:band => Band.new(OpenStruct.new(:value => "9999.99"))
|
25
|
+
))
|
14
26
|
end
|
15
27
|
|
28
|
+
it { subject.released_at.must_be_kind_of DateTime }
|
29
|
+
it { subject.released_at.must_equal DateTime.parse("Tue, 31 Mar 1981 00:00:00 +0000") }
|
30
|
+
it { subject.hit.length.must_equal 312 }
|
31
|
+
it { subject.band.label.value.must_equal 9999.99 }
|
32
|
+
|
33
|
+
|
16
34
|
it "allows coercion in validate" do
|
17
35
|
form = Class.new(Reform::Form) do
|
18
36
|
include Reform::Form::Coercion
|
@@ -21,6 +39,6 @@ class CoercionTest < MiniTest::Spec
|
|
21
39
|
end.new(OpenStruct.new())
|
22
40
|
|
23
41
|
form.validate("id" => "1")
|
24
|
-
form.
|
42
|
+
form.id.must_equal 1
|
25
43
|
end
|
26
44
|
end
|
data/test/composition_test.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
class CompositionTest < ReformSpec
|
2
2
|
class SongAndArtist < Reform::Composition
|
3
|
-
map({:artist => [:name], :song => [:title]}) #SongAndArtistMap.representable_attrs
|
3
|
+
map({:artist => [[:name]], :song => [[:title]]}) #SongAndArtistMap.representable_attrs
|
4
4
|
end
|
5
5
|
|
6
6
|
let (:comp) { SongAndArtist.new(:artist => @artist=OpenStruct.new, :song => rio) }
|
@@ -21,17 +21,16 @@ class CompositionTest < ReformSpec
|
|
21
21
|
comp.artist.object_id.must_equal @artist.object_id
|
22
22
|
end
|
23
23
|
|
24
|
-
|
24
|
+
|
25
|
+
describe "::from" do
|
25
26
|
it "creates the same mapping" do
|
26
27
|
comp =
|
27
|
-
|
28
|
-
map_from(
|
28
|
+
Reform::Composition.from(
|
29
29
|
Class.new(Reform::Representer) do
|
30
30
|
property :name, :on => :artist
|
31
31
|
property :title, :on => :song
|
32
32
|
end
|
33
|
-
)
|
34
|
-
end.
|
33
|
+
).
|
35
34
|
new(:artist => duran, :song => rio)
|
36
35
|
|
37
36
|
comp.name.must_equal "Duran Duran"
|
@@ -39,6 +38,7 @@ class CompositionTest < ReformSpec
|
|
39
38
|
end
|
40
39
|
end
|
41
40
|
|
41
|
+
|
42
42
|
describe "#nested_hash_for" do
|
43
43
|
it "returns nested hash" do
|
44
44
|
comp.nested_hash_for(:name => "Jimi Hendrix", :title => "Fire").must_equal({:artist=>{:name=>"Jimi Hendrix"}, :song=>{:title=>"Fire"}})
|
@@ -50,8 +50,8 @@ class CompositionTest < ReformSpec
|
|
50
50
|
|
51
51
|
it "works with strings in map" do
|
52
52
|
Class.new(Reform::Composition) do
|
53
|
-
map(:artist => ["name"])
|
54
|
-
end.new(
|
53
|
+
map(:artist => [["name"]])
|
54
|
+
end.new({}).nested_hash_for(:name => "Jimi Hendrix").must_equal({:artist=>{:name=>"Jimi Hendrix"}})
|
55
55
|
end
|
56
56
|
end
|
57
57
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ContractTest < BaseTest
|
4
|
+
class AlbumContract < Reform::Contract
|
5
|
+
property :title
|
6
|
+
validates :title, :presence => true, :length => {:minimum => 3}
|
7
|
+
|
8
|
+
property :hit do
|
9
|
+
property :title
|
10
|
+
validates :title, :presence => true
|
11
|
+
end
|
12
|
+
|
13
|
+
collection :songs do
|
14
|
+
property :title
|
15
|
+
validates :title, :presence => true
|
16
|
+
end
|
17
|
+
|
18
|
+
validates :songs, :length => {:minimum => 4}
|
19
|
+
|
20
|
+
property :band do # yepp, people do crazy stuff like that.
|
21
|
+
validates :label, :presence => true
|
22
|
+
|
23
|
+
property :label do
|
24
|
+
property :name
|
25
|
+
validates :name, :presence => true
|
26
|
+
end
|
27
|
+
# TODO: make band a required object.
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
let (:album) { Album.new(nil, Song.new, [Song.new, Song.new], Band.new() ) }
|
32
|
+
subject { AlbumContract.new(album) }
|
33
|
+
|
34
|
+
|
35
|
+
describe "invalid" do
|
36
|
+
before {
|
37
|
+
res = subject.validate
|
38
|
+
res.must_equal false
|
39
|
+
}
|
40
|
+
|
41
|
+
it { subject.errors.messages.must_equal({:"hit.title"=>["can't be blank"], :"songs.title"=>["can't be blank"], :"band.label"=>["can't be blank"], :songs=>["is too short (minimum is 4 characters)"], :title=>["can't be blank", "is too short (minimum is 3 characters)"]}) }
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
describe "valid" do
|
46
|
+
let (:album) { Album.new(
|
47
|
+
"Keeper Of The Seven Keys",
|
48
|
+
nil,
|
49
|
+
[Song.new("Initiation"), Song.new("I'm Alive"), Song.new("A Little Time"), Song.new("Future World"),],
|
50
|
+
Band.new(Label.new("Noise"))
|
51
|
+
) }
|
52
|
+
|
53
|
+
before { subject.validate.must_equal true }
|
54
|
+
|
55
|
+
it { subject.errors.messages.must_equal({}) }
|
56
|
+
end
|
57
|
+
end
|
data/test/errors_test.rb
CHANGED
@@ -14,6 +14,14 @@ class ErrorsTest < MiniTest::Spec
|
|
14
14
|
validates :title, :presence => true
|
15
15
|
end
|
16
16
|
|
17
|
+
property :band do # yepp, people do crazy stuff like that.
|
18
|
+
property :label do
|
19
|
+
property :name
|
20
|
+
validates :name, :presence => true
|
21
|
+
end
|
22
|
+
# TODO: make band a required object.
|
23
|
+
end
|
24
|
+
|
17
25
|
validates :title, :presence => true
|
18
26
|
end
|
19
27
|
|
@@ -21,7 +29,9 @@ class ErrorsTest < MiniTest::Spec
|
|
21
29
|
OpenStruct.new(
|
22
30
|
:title => "Blackhawks Over Los Angeles",
|
23
31
|
:hit => song,
|
24
|
-
:songs => songs # TODO: document this requirement
|
32
|
+
:songs => songs, # TODO: document this requirement,
|
33
|
+
|
34
|
+
:band => Struct.new(:name, :label).new("Epitaph", OpenStruct.new),
|
25
35
|
)
|
26
36
|
end
|
27
37
|
let (:song) { OpenStruct.new(:title => "Downtown") }
|
@@ -39,7 +49,9 @@ class ErrorsTest < MiniTest::Spec
|
|
39
49
|
form.errors.messages.must_equal({
|
40
50
|
:title => ["can't be blank"],
|
41
51
|
:"hit.title"=>["can't be blank"],
|
42
|
-
:"songs.title"=>["can't be blank"]
|
52
|
+
:"songs.title"=>["can't be blank"],
|
53
|
+
:"band.label.name"=>["can't be blank"]
|
54
|
+
})
|
43
55
|
end
|
44
56
|
|
45
57
|
it do
|
@@ -55,36 +67,51 @@ class ErrorsTest < MiniTest::Spec
|
|
55
67
|
form.errors.messages.must_equal({
|
56
68
|
:title => ["can't be blank"],
|
57
69
|
:"hit.title" => ["can't be blank"],
|
58
|
-
:"songs.title"=> ["can't be blank"]
|
59
|
-
|
70
|
+
:"songs.title"=> ["can't be blank"],
|
71
|
+
:"band.label.name"=>["can't be blank"]
|
72
|
+
})
|
73
|
+
end
|
60
74
|
end
|
61
75
|
|
76
|
+
|
62
77
|
describe "#validate with main form invalid" do
|
63
|
-
before { @result = form.validate("title"=>"") }
|
78
|
+
before { @result = form.validate("title"=>"", "band"=>{"label"=>{:name => "Fat Wreck"}}) }
|
64
79
|
|
65
80
|
it { @result.must_equal false }
|
66
81
|
it { form.errors.messages.must_equal({:title=>["can't be blank"]}) }
|
67
82
|
end
|
68
83
|
|
84
|
+
|
69
85
|
describe "#validate with middle nested form invalid" do
|
70
|
-
before { @result = form.validate("hit"=>{"title" => ""}) }
|
86
|
+
before { @result = form.validate("hit"=>{"title" => ""}, "band"=>{"label"=>{:name => "Fat Wreck"}}) }
|
71
87
|
|
72
88
|
it { @result.must_equal false }
|
73
89
|
it { form.errors.messages.must_equal({:"hit.title"=>["can't be blank"]}) }
|
74
90
|
end
|
75
91
|
|
76
|
-
|
77
|
-
|
92
|
+
|
93
|
+
describe "#validate with collection form invalid" do
|
94
|
+
before { @result = form.validate("songs"=>[{"title" => ""}], "band"=>{"label"=>{:name => "Fat Wreck"}}) }
|
78
95
|
|
79
96
|
it { @result.must_equal false }
|
80
|
-
it { form.errors.messages.must_equal({:"songs.title"=>["can't be blank"]}) }
|
97
|
+
it( "xxxx") { form.errors.messages.must_equal({:"songs.title"=>["can't be blank"]}) }
|
81
98
|
end
|
82
99
|
|
100
|
+
|
101
|
+
describe "#validate with collection and 2-level-nested invalid" do
|
102
|
+
before { @result = form.validate("songs"=>[{"title" => ""}], "band" => {"label" => {}}) }
|
103
|
+
|
104
|
+
it { @result.must_equal false }
|
105
|
+
it { form.errors.messages.must_equal({:"songs.title"=>["can't be blank"], :"band.label.name"=>["can't be blank"]}) }
|
106
|
+
end
|
107
|
+
|
108
|
+
|
83
109
|
describe "correct #validate" do
|
84
110
|
before { @result = form.validate(
|
85
111
|
"hit" => {"title" => "Sacrifice"},
|
86
112
|
"title" => "Second Heat",
|
87
|
-
"songs" => [{"title"=>"Heart Of A Lion"}]
|
113
|
+
"songs" => [{"title"=>"Heart Of A Lion"}],
|
114
|
+
"band" => {"label"=>{:name => "Fat Wreck"}}
|
88
115
|
) }
|
89
116
|
|
90
117
|
it { @result.must_equal true }
|