reform 0.2.7 → 1.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/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 }
|