reform 2.2.4
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 +7 -0
- data/.gitignore +18 -0
- data/.travis.yml +11 -0
- data/CHANGES.md +415 -0
- data/Gemfile +19 -0
- data/LICENSE.txt +22 -0
- data/README.md +339 -0
- data/Rakefile +15 -0
- data/TODO.md +45 -0
- data/gemfiles/Gemfile.disposable-0.3 +6 -0
- data/lib/reform.rb +8 -0
- data/lib/reform/contract.rb +77 -0
- data/lib/reform/contract/errors.rb +43 -0
- data/lib/reform/contract/validate.rb +33 -0
- data/lib/reform/form.rb +94 -0
- data/lib/reform/form/call.rb +23 -0
- data/lib/reform/form/coercion.rb +3 -0
- data/lib/reform/form/composition.rb +34 -0
- data/lib/reform/form/dry.rb +67 -0
- data/lib/reform/form/module.rb +27 -0
- data/lib/reform/form/mongoid.rb +37 -0
- data/lib/reform/form/orm.rb +26 -0
- data/lib/reform/form/populator.rb +123 -0
- data/lib/reform/form/prepopulate.rb +24 -0
- data/lib/reform/form/validate.rb +60 -0
- data/lib/reform/mongoid.rb +4 -0
- data/lib/reform/validation.rb +40 -0
- data/lib/reform/validation/groups.rb +73 -0
- data/lib/reform/version.rb +3 -0
- data/reform.gemspec +29 -0
- data/test/benchmarking.rb +26 -0
- data/test/call_test.rb +23 -0
- data/test/changed_test.rb +41 -0
- data/test/coercion_test.rb +66 -0
- data/test/composition_test.rb +149 -0
- data/test/contract_test.rb +77 -0
- data/test/default_test.rb +22 -0
- data/test/deprecation_test.rb +27 -0
- data/test/deserialize_test.rb +104 -0
- data/test/errors_test.rb +165 -0
- data/test/feature_test.rb +65 -0
- data/test/fixtures/dry_error_messages.yml +44 -0
- data/test/form_option_test.rb +24 -0
- data/test/form_test.rb +57 -0
- data/test/from_test.rb +75 -0
- data/test/inherit_test.rb +119 -0
- data/test/module_test.rb +142 -0
- data/test/parse_pipeline_test.rb +15 -0
- data/test/populate_test.rb +270 -0
- data/test/populator_skip_test.rb +28 -0
- data/test/prepopulator_test.rb +112 -0
- data/test/read_only_test.rb +3 -0
- data/test/readable_test.rb +30 -0
- data/test/readonly_test.rb +14 -0
- data/test/reform_test.rb +223 -0
- data/test/save_test.rb +89 -0
- data/test/setup_test.rb +48 -0
- data/test/skip_if_test.rb +74 -0
- data/test/skip_setter_and_getter_test.rb +54 -0
- data/test/test_helper.rb +49 -0
- data/test/validate_test.rb +420 -0
- data/test/validation/dry_test.rb +60 -0
- data/test/validation/dry_validation_test.rb +352 -0
- data/test/validation/errors.yml +4 -0
- data/test/virtual_test.rb +24 -0
- data/test/writeable_test.rb +29 -0
- metadata +265 -0
data/test/setup_test.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class SetupTest < MiniTest::Spec
|
4
|
+
Song = Struct.new(:title, :album, :composer)
|
5
|
+
Album = Struct.new(:name, :songs, :artist)
|
6
|
+
Artist = Struct.new(:name)
|
7
|
+
|
8
|
+
class AlbumForm < Reform::Form
|
9
|
+
property :name
|
10
|
+
collection :songs do
|
11
|
+
property :title
|
12
|
+
|
13
|
+
property :composer do
|
14
|
+
property :name
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
property :artist do
|
19
|
+
property :name
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
let (:song) { Song.new("Broken") }
|
24
|
+
let (:song_with_composer) { Song.new("Resist Stance", nil, composer) }
|
25
|
+
let (:composer) { Artist.new("Greg Graffin") }
|
26
|
+
let (:artist) { Artist.new("Bad Religion") }
|
27
|
+
|
28
|
+
describe "with nested objects" do
|
29
|
+
let (:album) { Album.new("The Dissent Of Man", [song, song_with_composer], artist) }
|
30
|
+
|
31
|
+
it do
|
32
|
+
form = AlbumForm.new(album)
|
33
|
+
|
34
|
+
form.name.must_equal "The Dissent Of Man"
|
35
|
+
form.songs[0].title.must_equal "Broken"
|
36
|
+
form.songs[0].composer.must_equal nil
|
37
|
+
form.songs[1].title.must_equal "Resist Stance"
|
38
|
+
form.songs[1].composer.name.must_equal "Greg Graffin"
|
39
|
+
form.artist.name.must_equal "Bad Religion"
|
40
|
+
|
41
|
+
# make sure all is wrapped in forms.
|
42
|
+
form.songs[0].must_be_kind_of Reform::Form
|
43
|
+
form.songs[1].must_be_kind_of Reform::Form
|
44
|
+
form.songs[1].composer.must_be_kind_of Reform::Form
|
45
|
+
form.artist.must_be_kind_of Reform::Form
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class SkipIfTest < BaseTest
|
4
|
+
|
5
|
+
class AlbumForm < Reform::Form
|
6
|
+
property :title
|
7
|
+
|
8
|
+
property :hit, skip_if: lambda { |options| options[:fragment]["title"]=="" } do
|
9
|
+
property :title
|
10
|
+
validation do
|
11
|
+
key(:title).required
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
collection :songs, skip_if: :skip_song?, populate_if_empty: BaseTest::Song do
|
16
|
+
property :title
|
17
|
+
end
|
18
|
+
|
19
|
+
def skip_song?(options)
|
20
|
+
options[:fragment]["title"].nil?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
let (:hit) { Song.new }
|
26
|
+
let (:album) { Album.new(nil, hit, [], nil) }
|
27
|
+
|
28
|
+
# deserializes when present.
|
29
|
+
it do
|
30
|
+
form = AlbumForm.new(album)
|
31
|
+
form.validate("hit" => {"title" => "Altar Of Sacrifice"}).must_equal true
|
32
|
+
form.hit.title.must_equal "Altar Of Sacrifice"
|
33
|
+
end
|
34
|
+
|
35
|
+
# skips deserialization when not present.
|
36
|
+
it do
|
37
|
+
form = AlbumForm.new(Album.new)
|
38
|
+
form.validate("hit" => {"title" => ""}).must_equal true
|
39
|
+
form.hit.must_equal nil # hit hasn't been deserialised.
|
40
|
+
end
|
41
|
+
|
42
|
+
# skips deserialization when not present.
|
43
|
+
it do
|
44
|
+
form = AlbumForm.new(Album.new(nil, nil, []))
|
45
|
+
form.validate("songs" => [{"title" => "Waste Of Breath"}, {"title" => nil}]).must_equal true
|
46
|
+
form.songs.size.must_equal 1
|
47
|
+
form.songs[0].title.must_equal "Waste Of Breath"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class SkipIfAllBlankTest < BaseTest
|
52
|
+
# skip_if: :all_blank"
|
53
|
+
class AlbumForm < Reform::Form
|
54
|
+
collection :songs, skip_if: :all_blank, populate_if_empty: BaseTest::Song do
|
55
|
+
property :title
|
56
|
+
property :length
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# create only one object.
|
61
|
+
it do
|
62
|
+
form = AlbumForm.new(OpenStruct.new(songs: []))
|
63
|
+
form.validate("songs" => [{"title"=>"Apathy"}, {"title"=>"", "length" => ""}]).must_equal true
|
64
|
+
form.songs.size.must_equal 1
|
65
|
+
form.songs[0].title.must_equal "Apathy"
|
66
|
+
end
|
67
|
+
|
68
|
+
it do
|
69
|
+
form = AlbumForm.new(OpenStruct.new(songs: []))
|
70
|
+
form.validate("songs" => [{"title"=>"", "length" => ""}, {"title"=>"Apathy"}]).must_equal true
|
71
|
+
form.songs.size.must_equal 1
|
72
|
+
form.songs[0].title.must_equal "Apathy"
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
# Overridden setter won't be called in setup.
|
4
|
+
# Overridden getter won't be called in sync.
|
5
|
+
class SetupSkipSetterAndGetterTest < MiniTest::Spec
|
6
|
+
Song = Struct.new(:title, :album, :composer)
|
7
|
+
Album = Struct.new(:title, :artist)
|
8
|
+
Artist = Struct.new(:name)
|
9
|
+
|
10
|
+
class AlbumForm < Reform::Form
|
11
|
+
property :title
|
12
|
+
|
13
|
+
def title
|
14
|
+
super.upcase
|
15
|
+
end
|
16
|
+
|
17
|
+
def title=(v)
|
18
|
+
super v.reverse
|
19
|
+
end
|
20
|
+
|
21
|
+
property :artist do
|
22
|
+
property :name
|
23
|
+
|
24
|
+
def name
|
25
|
+
super.downcase
|
26
|
+
end
|
27
|
+
|
28
|
+
def name=(v)
|
29
|
+
super v.chop
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
let (:artist) { Artist.new("Bad Religion") }
|
35
|
+
|
36
|
+
|
37
|
+
it do
|
38
|
+
album = Album.new("Greatest Hits", artist)
|
39
|
+
form = AlbumForm.new(album)
|
40
|
+
|
41
|
+
form.title.must_equal "GREATEST HITS"
|
42
|
+
form.artist.name.must_equal "bad religion"
|
43
|
+
|
44
|
+
form.validate("title" => "Resiststance", "artist" => {"name" => "Greg Graffin"})
|
45
|
+
|
46
|
+
form.title.must_equal "ECNATSTSISER" # first, setter called, then getter.
|
47
|
+
form.artist.name.must_equal "greg graffi"
|
48
|
+
|
49
|
+
form.sync
|
50
|
+
|
51
|
+
album.title.must_equal "ecnatstsiseR" # setter called, but not getter.
|
52
|
+
album.artist.name.must_equal "Greg Graffi"
|
53
|
+
end
|
54
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require "reform"
|
2
|
+
require 'minitest/autorun'
|
3
|
+
require "representable/debug"
|
4
|
+
require "declarative/testing"
|
5
|
+
require "pp"
|
6
|
+
|
7
|
+
class BaseTest < MiniTest::Spec
|
8
|
+
class AlbumForm < Reform::Form
|
9
|
+
property :title
|
10
|
+
|
11
|
+
property :hit do
|
12
|
+
property :title
|
13
|
+
end
|
14
|
+
|
15
|
+
collection :songs do
|
16
|
+
property :title
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
Song = Struct.new(:title, :length)
|
21
|
+
Album = Struct.new(:title, :hit, :songs, :band)
|
22
|
+
Band = Struct.new(:label)
|
23
|
+
Label = Struct.new(:name)
|
24
|
+
Length = Struct.new(:minutes, :seconds)
|
25
|
+
|
26
|
+
|
27
|
+
let (:hit) { Song.new("Roxanne") }
|
28
|
+
end
|
29
|
+
|
30
|
+
MiniTest::Spec.class_eval do
|
31
|
+
module Saveable
|
32
|
+
def save
|
33
|
+
@saved = true
|
34
|
+
end
|
35
|
+
|
36
|
+
def saved?
|
37
|
+
@saved
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
require "reform/form/dry"
|
43
|
+
Reform::Contract.class_eval do
|
44
|
+
feature Reform::Form::Dry
|
45
|
+
end
|
46
|
+
# FIXME!
|
47
|
+
Reform::Form.class_eval do
|
48
|
+
feature Reform::Form::Dry
|
49
|
+
end
|
@@ -0,0 +1,420 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ContractValidateTest < MiniTest::Spec
|
4
|
+
Song = Struct.new(:title, :album, :composer)
|
5
|
+
Album = Struct.new(:name, :songs, :artist)
|
6
|
+
Artist = Struct.new(:name)
|
7
|
+
|
8
|
+
class AlbumForm < Reform::Contract
|
9
|
+
property :name
|
10
|
+
validation do
|
11
|
+
key(:name).required
|
12
|
+
end
|
13
|
+
|
14
|
+
collection :songs do
|
15
|
+
property :title
|
16
|
+
validation do
|
17
|
+
key(:title).required
|
18
|
+
end
|
19
|
+
|
20
|
+
property :composer do
|
21
|
+
validation do
|
22
|
+
key(:name).required
|
23
|
+
end
|
24
|
+
property :name
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
property :artist do
|
29
|
+
property :name
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
let (:song) { Song.new("Broken") }
|
34
|
+
let (:song_with_composer) { Song.new("Resist Stance", nil, composer) }
|
35
|
+
let (:composer) { Artist.new("Greg Graffin") }
|
36
|
+
let (:artist) { Artist.new("Bad Religion") }
|
37
|
+
let (:album) { Album.new("The Dissent Of Man", [song, song_with_composer], artist) }
|
38
|
+
|
39
|
+
let (:form) { AlbumForm.new(album) }
|
40
|
+
|
41
|
+
# valid
|
42
|
+
it do
|
43
|
+
form.validate.must_equal true
|
44
|
+
form.errors.messages.inspect.must_equal "{}"
|
45
|
+
end
|
46
|
+
|
47
|
+
# invalid
|
48
|
+
it do
|
49
|
+
album.songs[1].composer.name = nil
|
50
|
+
album.name = nil
|
51
|
+
|
52
|
+
form.validate.must_equal false
|
53
|
+
form.errors.messages.inspect.must_equal "{:name=>[\"is missing\"], :\"songs.composer.name\"=>[\"is missing\"]}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
# no configuration results in "sync" (formerly known as parse_strategy: :sync).
|
59
|
+
class ValidateWithoutConfigurationTest < MiniTest::Spec
|
60
|
+
Song = Struct.new(:title, :album, :composer)
|
61
|
+
Album = Struct.new(:name, :songs, :artist)
|
62
|
+
Artist = Struct.new(:name)
|
63
|
+
|
64
|
+
class AlbumForm < Reform::Form
|
65
|
+
property :name
|
66
|
+
validation do
|
67
|
+
key(:name).required
|
68
|
+
end
|
69
|
+
|
70
|
+
collection :songs do
|
71
|
+
|
72
|
+
property :title
|
73
|
+
validation do
|
74
|
+
key(:title).required
|
75
|
+
end
|
76
|
+
|
77
|
+
property :composer do
|
78
|
+
property :name
|
79
|
+
validation do
|
80
|
+
key(:name).required
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
property :artist do
|
86
|
+
property :name
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
let (:song) { Song.new("Broken") }
|
91
|
+
let (:song_with_composer) { Song.new("Resist Stance", nil, composer) }
|
92
|
+
let (:composer) { Artist.new("Greg Graffin") }
|
93
|
+
let (:artist) { Artist.new("Bad Religion") }
|
94
|
+
let (:album) { Album.new("The Dissent Of Man", [song, song_with_composer], artist) }
|
95
|
+
|
96
|
+
let (:form) { AlbumForm.new(album) }
|
97
|
+
|
98
|
+
# valid.
|
99
|
+
it do
|
100
|
+
object_ids = {song: form.songs[0].object_id, song_with_composer: form.songs[1].object_id,
|
101
|
+
artist: form.artist.object_id, composer: form.songs[1].composer.object_id}
|
102
|
+
|
103
|
+
form.validate(
|
104
|
+
"name" => "Best Of",
|
105
|
+
"songs" => [{"title" => "Fallout"}, {"title" => "Roxanne", "composer" => {"name" => "Sting"}}],
|
106
|
+
"artist" => {"name" => "The Police"},
|
107
|
+
).must_equal true
|
108
|
+
|
109
|
+
form.errors.messages.inspect.must_equal "{}"
|
110
|
+
|
111
|
+
# form has updated.
|
112
|
+
form.name.must_equal "Best Of"
|
113
|
+
form.songs[0].title.must_equal "Fallout"
|
114
|
+
form.songs[1].title.must_equal "Roxanne"
|
115
|
+
form.songs[1].composer.name.must_equal "Sting"
|
116
|
+
form.artist.name.must_equal "The Police"
|
117
|
+
|
118
|
+
# objects are still the same.
|
119
|
+
form.songs[0].object_id.must_equal object_ids[:song]
|
120
|
+
form.songs[1].object_id.must_equal object_ids[:song_with_composer]
|
121
|
+
form.songs[1].composer.object_id.must_equal object_ids[:composer]
|
122
|
+
form.artist.object_id.must_equal object_ids[:artist]
|
123
|
+
|
124
|
+
|
125
|
+
# model has not changed, yet.
|
126
|
+
album.name.must_equal "The Dissent Of Man"
|
127
|
+
album.songs[0].title.must_equal "Broken"
|
128
|
+
album.songs[1].title.must_equal "Resist Stance"
|
129
|
+
album.songs[1].composer.name.must_equal "Greg Graffin"
|
130
|
+
album.artist.name.must_equal "Bad Religion"
|
131
|
+
end
|
132
|
+
|
133
|
+
# with symbols.
|
134
|
+
it do
|
135
|
+
form.validate(
|
136
|
+
name: "Best Of",
|
137
|
+
songs: [{title: "The X-Creep"}, {title: "Trudging", composer: {name: "SNFU"}}],
|
138
|
+
artist: {name: "The Police"},
|
139
|
+
).must_equal true
|
140
|
+
|
141
|
+
form.name.must_equal "Best Of"
|
142
|
+
form.songs[0].title.must_equal "The X-Creep"
|
143
|
+
form.songs[1].title.must_equal "Trudging"
|
144
|
+
form.songs[1].composer.name.must_equal "SNFU"
|
145
|
+
form.artist.name.must_equal "The Police"
|
146
|
+
end
|
147
|
+
|
148
|
+
# throws exception when no populators.
|
149
|
+
it do
|
150
|
+
album = Album.new("The Dissent Of Man", [])
|
151
|
+
|
152
|
+
assert_raises RuntimeError do
|
153
|
+
AlbumForm.new(album).validate(songs: {title: "Resist-Stance"})
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
class ValidateWithInternalPopulatorOptionTest < MiniTest::Spec
|
159
|
+
Song = Struct.new(:title, :album, :composer)
|
160
|
+
Album = Struct.new(:name, :songs, :artist)
|
161
|
+
Artist = Struct.new(:name)
|
162
|
+
|
163
|
+
class AlbumForm < Reform::Form
|
164
|
+
property :name
|
165
|
+
validation do
|
166
|
+
key(:name).required
|
167
|
+
end
|
168
|
+
|
169
|
+
collection :songs,
|
170
|
+
internal_populator: lambda { |input, options|
|
171
|
+
collection = options[:represented].songs
|
172
|
+
(item = collection[options[:index]]) ? item : collection.insert(options[:index], Song.new) } do
|
173
|
+
|
174
|
+
property :title
|
175
|
+
validation do
|
176
|
+
key(:title).required
|
177
|
+
end
|
178
|
+
|
179
|
+
property :composer, internal_populator: lambda { |input, options| (item = options[:represented].composer) ? item : Artist.new } do
|
180
|
+
property :name
|
181
|
+
validation do
|
182
|
+
key(:name).required
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
property :artist, internal_populator: lambda { |input, options| (item = options[:represented].artist) ? item : Artist.new } do
|
188
|
+
property :name
|
189
|
+
validation do
|
190
|
+
key(:name).required
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
let (:song) { Song.new("Broken") }
|
196
|
+
let (:song_with_composer) { Song.new("Resist Stance", nil, composer) }
|
197
|
+
let (:composer) { Artist.new("Greg Graffin") }
|
198
|
+
let (:artist) { Artist.new("Bad Religion") }
|
199
|
+
let (:album) { Album.new("The Dissent Of Man", [song, song_with_composer], artist) }
|
200
|
+
|
201
|
+
let (:form) { AlbumForm.new(album) }
|
202
|
+
|
203
|
+
# valid.
|
204
|
+
it("xxx") do
|
205
|
+
form.validate(
|
206
|
+
"name" => "Best Of",
|
207
|
+
"songs" => [{"title" => "Fallout"}, {"title" => "Roxanne", "composer" => {"name" => "Sting"}}],
|
208
|
+
"artist" => {"name" => "The Police"},
|
209
|
+
).must_equal true
|
210
|
+
|
211
|
+
form.errors.messages.inspect.must_equal "{}"
|
212
|
+
|
213
|
+
# form has updated.
|
214
|
+
form.name.must_equal "Best Of"
|
215
|
+
form.songs[0].title.must_equal "Fallout"
|
216
|
+
form.songs[1].title.must_equal "Roxanne"
|
217
|
+
form.songs[1].composer.name.must_equal "Sting"
|
218
|
+
form.artist.name.must_equal "The Police"
|
219
|
+
|
220
|
+
|
221
|
+
# model has not changed, yet.
|
222
|
+
album.name.must_equal "The Dissent Of Man"
|
223
|
+
album.songs[0].title.must_equal "Broken"
|
224
|
+
album.songs[1].title.must_equal "Resist Stance"
|
225
|
+
album.songs[1].composer.name.must_equal "Greg Graffin"
|
226
|
+
album.artist.name.must_equal "Bad Religion"
|
227
|
+
end
|
228
|
+
|
229
|
+
# invalid.
|
230
|
+
it do
|
231
|
+
form.validate(
|
232
|
+
"name" => "",
|
233
|
+
"songs" => [{"title" => "Fallout"}, {"title" => "Roxanne", "composer" => {"name" => ""}}],
|
234
|
+
"artist" => {"name" => ""},
|
235
|
+
).must_equal false
|
236
|
+
|
237
|
+
form.errors.messages.inspect.must_equal "{:name=>[\"must be filled\"], :\"songs.composer.name\"=>[\"must be filled\"], :\"artist.name\"=>[\"must be filled\"]}"
|
238
|
+
end
|
239
|
+
|
240
|
+
# adding to collection via :instance.
|
241
|
+
# valid.
|
242
|
+
it do
|
243
|
+
form.validate(
|
244
|
+
"songs" => [{"title" => "Fallout"}, {"title" => "Roxanne"}, {"title" => "Rime Of The Ancient Mariner"}],
|
245
|
+
).must_equal true
|
246
|
+
|
247
|
+
form.errors.messages.inspect.must_equal "{}"
|
248
|
+
|
249
|
+
# form has updated.
|
250
|
+
form.name.must_equal "The Dissent Of Man"
|
251
|
+
form.songs[0].title.must_equal "Fallout"
|
252
|
+
form.songs[1].title.must_equal "Roxanne"
|
253
|
+
form.songs[1].composer.name.must_equal "Greg Graffin"
|
254
|
+
form.songs[1].title.must_equal "Roxanne"
|
255
|
+
form.songs[2].title.must_equal "Rime Of The Ancient Mariner" # new song added.
|
256
|
+
form.songs.size.must_equal 3
|
257
|
+
form.artist.name.must_equal "Bad Religion"
|
258
|
+
|
259
|
+
|
260
|
+
# model has not changed, yet.
|
261
|
+
album.name.must_equal "The Dissent Of Man"
|
262
|
+
album.songs[0].title.must_equal "Broken"
|
263
|
+
album.songs[1].title.must_equal "Resist Stance"
|
264
|
+
album.songs[1].composer.name.must_equal "Greg Graffin"
|
265
|
+
album.songs.size.must_equal 2
|
266
|
+
album.artist.name.must_equal "Bad Religion"
|
267
|
+
end
|
268
|
+
|
269
|
+
|
270
|
+
# allow writeable: false even in the deserializer.
|
271
|
+
class SongForm < Reform::Form
|
272
|
+
property :title, deserializer: {writeable: false}
|
273
|
+
end
|
274
|
+
|
275
|
+
it do
|
276
|
+
form = SongForm.new(song = Song.new)
|
277
|
+
form.validate("title" => "Ignore me!")
|
278
|
+
form.title.must_equal nil
|
279
|
+
form.title = "Unopened"
|
280
|
+
form.sync # only the deserializer is marked as not-writeable.
|
281
|
+
song.title.must_equal "Unopened"
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
# # not sure if we should catch that in Reform or rather do that in disposable. this is https://github.com/apotonick/reform/pull/104
|
286
|
+
# # describe ":populator with :empty" do
|
287
|
+
# # let (:form) {
|
288
|
+
# # Class.new(Reform::Form) do
|
289
|
+
# # collection :songs, :empty => true, :populator => lambda { |fragment, index, args|
|
290
|
+
# # songs[index] = args.binding[:form].new(Song.new)
|
291
|
+
# # } do
|
292
|
+
# # property :title
|
293
|
+
# # end
|
294
|
+
# # end
|
295
|
+
# # }
|
296
|
+
|
297
|
+
# # let (:params) {
|
298
|
+
# # {
|
299
|
+
# # "songs" => [{"title" => "Fallout"}, {"title" => "Roxanne"}]
|
300
|
+
# # }
|
301
|
+
# # }
|
302
|
+
|
303
|
+
# # subject { form.new(Album.new("Hits", [], [])) }
|
304
|
+
|
305
|
+
# # before { subject.validate(params) }
|
306
|
+
|
307
|
+
# # it { subject.songs[0].title.must_equal "Fallout" }
|
308
|
+
# # it { subject.songs[1].title.must_equal "Roxanne" }
|
309
|
+
# # end
|
310
|
+
|
311
|
+
|
312
|
+
# # test cardinalities.
|
313
|
+
# describe "with empty collection and cardinality" do
|
314
|
+
# let (:album) { Album.new }
|
315
|
+
|
316
|
+
# subject { Class.new(Reform::Form) do
|
317
|
+
# include Reform::Form::ActiveModel
|
318
|
+
# model :album
|
319
|
+
|
320
|
+
# collection :songs do
|
321
|
+
# property :title
|
322
|
+
# end
|
323
|
+
|
324
|
+
# property :hit do
|
325
|
+
# property :title
|
326
|
+
# end
|
327
|
+
|
328
|
+
# validates :songs, :length => {:minimum => 1}
|
329
|
+
# validates :hit, :presence => true
|
330
|
+
# end.new(album) }
|
331
|
+
|
332
|
+
|
333
|
+
# describe "invalid" do
|
334
|
+
# before { subject.validate({}).must_equal false }
|
335
|
+
|
336
|
+
# it do
|
337
|
+
# # ensure that only hit and songs keys are present
|
338
|
+
# subject.errors.messages.keys.sort.must_equal([:hit, :songs])
|
339
|
+
# # validate content of hit and songs keys
|
340
|
+
# subject.errors.messages[:hit].must_equal(["must be filled"])
|
341
|
+
# subject.errors.messages[:songs].first.must_match(/\Ais too short \(minimum is 1 characters?\)\z/)
|
342
|
+
# end
|
343
|
+
# end
|
344
|
+
|
345
|
+
|
346
|
+
# describe "valid" do
|
347
|
+
# let (:album) { Album.new(nil, Song.new, [Song.new("Urban Myth")]) }
|
348
|
+
|
349
|
+
# before {
|
350
|
+
# subject.validate({"songs" => [{"title"=>"Daddy, Brother, Lover, Little Boy"}], "hit" => {"title"=>"The Horse"}}).
|
351
|
+
# must_equal true
|
352
|
+
# }
|
353
|
+
|
354
|
+
# it { subject.errors.messages.must_equal({}) }
|
355
|
+
# end
|
356
|
+
# end
|
357
|
+
|
358
|
+
|
359
|
+
|
360
|
+
|
361
|
+
|
362
|
+
# # providing manual validator method allows accessing form's API.
|
363
|
+
# describe "with ::validate" do
|
364
|
+
# let (:form) {
|
365
|
+
# Class.new(Reform::Form) do
|
366
|
+
# property :title
|
367
|
+
|
368
|
+
# validate :title?
|
369
|
+
|
370
|
+
# def title?
|
371
|
+
# errors.add :title, "not lowercase" if title == "Fallout"
|
372
|
+
# end
|
373
|
+
# end
|
374
|
+
# }
|
375
|
+
|
376
|
+
# let (:params) { {"title" => "Fallout"} }
|
377
|
+
# let (:song) { Song.new("Englishman") }
|
378
|
+
|
379
|
+
# subject { form.new(song) }
|
380
|
+
|
381
|
+
# before { @res = subject.validate(params) }
|
382
|
+
|
383
|
+
# it { @res.must_equal false }
|
384
|
+
# it { subject.errors.messages.must_equal({:title=>["not lowercase"]}) }
|
385
|
+
# end
|
386
|
+
|
387
|
+
|
388
|
+
# # overriding the reader for a nested form should only be considered when rendering.
|
389
|
+
# describe "with overridden reader for nested form" do
|
390
|
+
# let (:form) {
|
391
|
+
# Class.new(Reform::Form) do
|
392
|
+
# property :band, :populate_if_empty => lambda { |*| Band.new } do
|
393
|
+
# property :label
|
394
|
+
# end
|
395
|
+
|
396
|
+
# collection :songs, :populate_if_empty => lambda { |*| Song.new } do
|
397
|
+
# property :title
|
398
|
+
# end
|
399
|
+
|
400
|
+
# def band
|
401
|
+
# raise "only call me when rendering the form!"
|
402
|
+
# end
|
403
|
+
|
404
|
+
# def songs
|
405
|
+
# raise "only call me when rendering the form!"
|
406
|
+
# end
|
407
|
+
# end.new(album)
|
408
|
+
# }
|
409
|
+
|
410
|
+
# let (:album) { Album.new }
|
411
|
+
|
412
|
+
# # don't use #artist when validating!
|
413
|
+
# it do
|
414
|
+
# form.validate("band" => {"label" => "Hellcat"}, "songs" => [{"title" => "Stand Your Ground"}, {"title" => "Otherside"}])
|
415
|
+
# form.sync
|
416
|
+
# album.band.label.must_equal "Hellcat"
|
417
|
+
# album.songs.first.title.must_equal "Stand Your Ground"
|
418
|
+
# end
|
419
|
+
# end
|
420
|
+
# end
|