reform 2.3.0.rc1 → 2.5.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 +5 -5
- data/.gitignore +5 -1
- data/.travis.yml +7 -11
- data/CHANGES.md +43 -3
- data/Gemfile +2 -5
- data/ISSUE_TEMPLATE.md +1 -1
- data/LICENSE.txt +1 -1
- data/README.md +7 -9
- data/Rakefile +6 -10
- data/lib/reform/contract.rb +7 -7
- data/lib/reform/contract/custom_error.rb +41 -0
- data/lib/reform/contract/validate.rb +10 -6
- data/lib/reform/errors.rb +27 -15
- data/lib/reform/form.rb +22 -11
- data/lib/reform/form/call.rb +1 -1
- data/lib/reform/form/composition.rb +2 -2
- data/lib/reform/form/dry.rb +22 -60
- data/lib/reform/form/dry/input_hash.rb +37 -0
- data/lib/reform/form/populator.rb +9 -11
- data/lib/reform/form/prepopulate.rb +3 -2
- data/lib/reform/form/validate.rb +19 -12
- data/lib/reform/result.rb +36 -9
- data/lib/reform/validation.rb +10 -8
- data/lib/reform/validation/groups.rb +2 -4
- data/lib/reform/version.rb +1 -1
- data/reform.gemspec +9 -9
- data/test/benchmarking.rb +10 -11
- data/test/call_test.rb +8 -8
- data/test/changed_test.rb +13 -13
- data/test/coercion_test.rb +56 -24
- data/test/composition_test.rb +49 -51
- data/test/contract/custom_error_test.rb +55 -0
- data/test/contract_test.rb +18 -18
- data/test/default_test.rb +3 -3
- data/test/deserialize_test.rb +14 -17
- data/test/docs/validation_test.rb +134 -0
- data/test/errors_test.rb +131 -86
- data/test/feature_test.rb +9 -11
- data/test/fixtures/dry_error_messages.yml +65 -52
- data/test/form_option_test.rb +3 -3
- data/test/form_test.rb +6 -6
- data/test/from_test.rb +17 -21
- data/test/inherit_test.rb +28 -35
- data/test/module_test.rb +23 -28
- data/test/parse_option_test.rb +12 -12
- data/test/parse_pipeline_test.rb +3 -3
- data/test/populate_test.rb +146 -93
- data/test/populator_skip_test.rb +3 -4
- data/test/prepopulator_test.rb +20 -21
- data/test/read_only_test.rb +12 -1
- data/test/readable_test.rb +7 -7
- data/test/reform_test.rb +38 -42
- data/test/save_test.rb +16 -19
- data/test/setup_test.rb +15 -15
- data/test/skip_if_test.rb +30 -19
- data/test/skip_setter_and_getter_test.rb +8 -9
- data/test/test_helper.rb +12 -5
- data/test/validate_test.rb +160 -140
- data/test/validation/dry_validation_test.rb +407 -236
- data/test/validation/result_test.rb +29 -31
- data/test/validation_library_provided_test.rb +3 -3
- data/test/virtual_test.rb +46 -6
- data/test/writeable_test.rb +13 -13
- metadata +32 -29
- data/test/readonly_test.rb +0 -14
data/test/module_test.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require "test_helper"
|
2
|
-
require
|
2
|
+
require "reform/form/coercion"
|
3
3
|
|
4
4
|
class ModuleInclusionTest < MiniTest::Spec
|
5
5
|
module BandPropertyForm
|
@@ -9,7 +9,7 @@ class ModuleInclusionTest < MiniTest::Spec
|
|
9
9
|
property :title
|
10
10
|
|
11
11
|
validation do
|
12
|
-
required(:title).filled
|
12
|
+
params { required(:title).filled }
|
13
13
|
end
|
14
14
|
|
15
15
|
def id # gets mixed into Form, too.
|
@@ -22,11 +22,11 @@ class ModuleInclusionTest < MiniTest::Spec
|
|
22
22
|
end
|
23
23
|
|
24
24
|
validation do
|
25
|
-
required(:band).filled
|
25
|
+
params { required(:band).filled }
|
26
26
|
end
|
27
27
|
|
28
|
-
include Dry
|
29
|
-
property :cool, type:
|
28
|
+
include Dry.Types(default: :nominal) # allows using Types::* in module.
|
29
|
+
property :cool, type: Types::Params::Bool # test coercion.
|
30
30
|
end
|
31
31
|
|
32
32
|
# TODO: test if works, move stuff into inherit_schema!
|
@@ -36,15 +36,14 @@ class ModuleInclusionTest < MiniTest::Spec
|
|
36
36
|
collection :airplays do
|
37
37
|
property :station
|
38
38
|
validation do
|
39
|
-
required(:station).filled
|
39
|
+
params { required(:station).filled }
|
40
40
|
end
|
41
41
|
end
|
42
42
|
validation do
|
43
|
-
required(:airplays).filled
|
43
|
+
params { required(:airplays).filled }
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
-
|
48
47
|
# test:
|
49
48
|
# by including BandPropertyForm into multiple classes we assure that options hashes don't get messed up by AM:V.
|
50
49
|
class HitForm < TestForm
|
@@ -58,31 +57,29 @@ class ModuleInclusionTest < MiniTest::Spec
|
|
58
57
|
include BandPropertyForm
|
59
58
|
end
|
60
59
|
|
61
|
-
|
62
|
-
let (:song) { OpenStruct.new(:band => OpenStruct.new(:title => "Time Again")) }
|
60
|
+
let(:song) { OpenStruct.new(band: OpenStruct.new(title: "Time Again")) }
|
63
61
|
|
64
62
|
# nested form from module is present and creates accessor.
|
65
|
-
it { SongForm.new(song).band.title
|
63
|
+
it { assert_equal SongForm.new(song).band.title, "Time Again" }
|
66
64
|
|
67
65
|
# methods from module get included.
|
68
|
-
it { SongForm.new(song).id
|
69
|
-
it { SongForm.new(song).band.id
|
66
|
+
it { assert_equal SongForm.new(song).id, 1 }
|
67
|
+
it { assert_equal SongForm.new(song).band.id, 2 }
|
70
68
|
|
71
69
|
# validators get inherited.
|
72
70
|
it do
|
73
71
|
form = SongForm.new(OpenStruct.new)
|
74
72
|
form.validate({})
|
75
|
-
form.errors.messages
|
73
|
+
assert_equal form.errors.messages, band: ["must be filled"]
|
76
74
|
end
|
77
75
|
|
78
76
|
# coercion works
|
79
77
|
it do
|
80
78
|
form = SongForm.new(OpenStruct.new)
|
81
|
-
form.validate(
|
82
|
-
form.cool
|
79
|
+
form.validate(cool: "1")
|
80
|
+
assert form.cool
|
83
81
|
end
|
84
82
|
|
85
|
-
|
86
83
|
# include a module into a module into a class :)
|
87
84
|
module AlbumFormModule
|
88
85
|
include Reform::Form::Module
|
@@ -90,7 +87,7 @@ class ModuleInclusionTest < MiniTest::Spec
|
|
90
87
|
|
91
88
|
property :name
|
92
89
|
validation do
|
93
|
-
required(:name).filled
|
90
|
+
params { required(:name).filled }
|
94
91
|
end
|
95
92
|
end
|
96
93
|
|
@@ -98,21 +95,20 @@ class ModuleInclusionTest < MiniTest::Spec
|
|
98
95
|
include AlbumFormModule
|
99
96
|
|
100
97
|
# pp heritage
|
101
|
-
property :band, :
|
98
|
+
property :band, inherit: true do
|
102
99
|
property :label
|
103
100
|
validation do
|
104
|
-
required(:label).filled
|
101
|
+
params { required(:label).filled }
|
105
102
|
end
|
106
103
|
end
|
107
104
|
end
|
108
105
|
|
109
106
|
it do
|
110
|
-
form = AlbumForm.new(OpenStruct.new(:
|
111
|
-
form.validate(
|
112
|
-
form.errors.messages
|
107
|
+
form = AlbumForm.new(OpenStruct.new(band: OpenStruct.new))
|
108
|
+
form.validate("band" => {})
|
109
|
+
assert_equal form.errors.messages, "band.title": ["must be filled"], "band.label": ["must be filled"], name: ["must be filled"]
|
113
110
|
end
|
114
111
|
|
115
|
-
|
116
112
|
describe "module with custom accessors" do
|
117
113
|
module SongModule
|
118
114
|
include Reform::Form::Module
|
@@ -131,12 +127,11 @@ class ModuleInclusionTest < MiniTest::Spec
|
|
131
127
|
include SongModule
|
132
128
|
end
|
133
129
|
|
134
|
-
let
|
130
|
+
let(:song) { OpenStruct.new(id: 1, title: "Instant Mash") }
|
135
131
|
|
136
132
|
it do
|
137
|
-
IncludingSongForm.new(song).id
|
138
|
-
IncludingSongForm.new(song).title
|
133
|
+
assert_equal IncludingSongForm.new(song).id, 1
|
134
|
+
assert_equal IncludingSongForm.new(song).title, "INSTANT MASH"
|
139
135
|
end
|
140
136
|
end
|
141
137
|
end
|
142
|
-
|
data/test/parse_option_test.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "test_helper"
|
2
2
|
|
3
3
|
class ParseOptionTest < MiniTest::Spec
|
4
4
|
Comment = Struct.new(:content, :user)
|
@@ -9,32 +9,32 @@ class ParseOptionTest < MiniTest::Spec
|
|
9
9
|
property :user, parse: false
|
10
10
|
end
|
11
11
|
|
12
|
-
let
|
13
|
-
let
|
12
|
+
let(:current_user) { User.new("Peter") }
|
13
|
+
let(:form) { CommentForm.new(Comment.new, user: current_user) }
|
14
14
|
|
15
15
|
it do
|
16
|
-
form.user
|
16
|
+
assert_equal form.user, current_user
|
17
17
|
|
18
18
|
lorem = "Lorem ipsum dolor sit amet..."
|
19
19
|
form.validate("content" => lorem, "user" => "not the current user")
|
20
20
|
|
21
|
-
form.content
|
22
|
-
form.user
|
21
|
+
assert_equal form.content, lorem
|
22
|
+
assert_equal form.user, current_user
|
23
23
|
end
|
24
24
|
|
25
25
|
describe "using ':parse' option doesn't override other ':deserialize' options" do
|
26
26
|
class ArticleCommentForm < TestForm
|
27
27
|
property :content
|
28
|
-
property :article, deserializer: {
|
29
|
-
property :user, parse: false, deserializer: {
|
28
|
+
property :article, deserializer: {instance: "Instance"}
|
29
|
+
property :user, parse: false, deserializer: {instance: "Instance"}
|
30
30
|
end
|
31
31
|
|
32
32
|
it do
|
33
|
-
ArticleCommentForm.definitions.get(:user)[:deserializer][:writeable]
|
34
|
-
ArticleCommentForm.definitions.get(:user)[:deserializer][:instance]
|
33
|
+
assert_equal ArticleCommentForm.definitions.get(:user)[:deserializer][:writeable], false
|
34
|
+
assert_equal ArticleCommentForm.definitions.get(:user)[:deserializer][:instance], "Instance"
|
35
35
|
|
36
|
-
ArticleCommentForm.definitions.get(:article)[:deserializer][:writeable]
|
37
|
-
ArticleCommentForm.definitions.get(:article)[:deserializer][:instance]
|
36
|
+
assert ArticleCommentForm.definitions.get(:article)[:deserializer][:writeable]
|
37
|
+
assert_equal ArticleCommentForm.definitions.get(:article)[:deserializer][:instance], "Instance"
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
data/test/parse_pipeline_test.rb
CHANGED
@@ -4,12 +4,12 @@ class ParsePipelineTest < MiniTest::Spec
|
|
4
4
|
Album = Struct.new(:name)
|
5
5
|
|
6
6
|
class AlbumForm < TestForm
|
7
|
-
property :name, deserializer: {
|
7
|
+
property :name, deserializer: {parse_pipeline: ->(input, options) { Representable::Pipeline[->(ipt, opts) { opts[:represented].name = ipt.inspect }] }}
|
8
8
|
end
|
9
9
|
|
10
10
|
it "allows passing :parse_pipeline directly" do
|
11
11
|
form = AlbumForm.new(Album.new)
|
12
12
|
form.validate("name" => "Greatest Hits")
|
13
|
-
form.name
|
13
|
+
assert_equal form.name, "{\"name\"=>\"Greatest Hits\"}"
|
14
14
|
end
|
15
|
-
end
|
15
|
+
end
|
data/test/populate_test.rb
CHANGED
@@ -8,22 +8,22 @@ class PopulatorTest < MiniTest::Spec
|
|
8
8
|
class AlbumForm < TestForm
|
9
9
|
property :name, populator: ->(options) { self.name = options[:fragment].reverse }
|
10
10
|
validation do
|
11
|
-
required(:name).filled
|
11
|
+
params { required(:name).filled }
|
12
12
|
end
|
13
13
|
|
14
14
|
collection :songs,
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
populator: ->(fragment:, model:, index:, **) {
|
16
|
+
(item = model[index]) ? item : model.insert(index, Song.new)
|
17
|
+
} do
|
18
18
|
property :title
|
19
19
|
validation do
|
20
|
-
required(:title).filled
|
20
|
+
params { required(:title).filled }
|
21
21
|
end
|
22
22
|
|
23
|
-
property :composer, populator: ->(options) { options[:model] || self.composer= Artist.new } do
|
23
|
+
property :composer, populator: ->(options) { options[:model] || self.composer = Artist.new } do
|
24
24
|
property :name
|
25
25
|
validation do
|
26
|
-
required(:name).filled
|
26
|
+
params { required(:name).filled }
|
27
27
|
end
|
28
28
|
end
|
29
29
|
end
|
@@ -35,20 +35,20 @@ class PopulatorTest < MiniTest::Spec
|
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
|
-
let
|
39
|
-
let
|
40
|
-
let
|
41
|
-
let
|
42
|
-
let
|
38
|
+
let(:song) { Song.new("Broken") }
|
39
|
+
let(:song_with_composer) { Song.new("Resist Stance", nil, composer) }
|
40
|
+
let(:composer) { Artist.new("Greg Graffin") }
|
41
|
+
let(:artist) { Artist.new("Bad Religion") }
|
42
|
+
let(:album) { Album.new("The Dissent Of Man", [song, song_with_composer], artist) }
|
43
43
|
|
44
|
-
let
|
44
|
+
let(:form) { AlbumForm.new(album) }
|
45
45
|
|
46
46
|
it "runs populator on scalar" do
|
47
47
|
form.validate(
|
48
48
|
"name" => "override me!"
|
49
49
|
)
|
50
50
|
|
51
|
-
form.name
|
51
|
+
assert_equal form.name, "!em edirrevo"
|
52
52
|
end
|
53
53
|
|
54
54
|
# changing existing property :artist.
|
@@ -60,7 +60,7 @@ class PopulatorTest < MiniTest::Spec
|
|
60
60
|
"artist" => {"name" => "Marcus Miller"}
|
61
61
|
)
|
62
62
|
|
63
|
-
form.artist.model.object_id
|
63
|
+
assert_equal form.artist.model.object_id, old_id
|
64
64
|
end
|
65
65
|
|
66
66
|
# use populator for default value on scalars?
|
@@ -68,37 +68,36 @@ class PopulatorTest < MiniTest::Spec
|
|
68
68
|
# adding to collection via :populator.
|
69
69
|
# valid.
|
70
70
|
it "yyy" do
|
71
|
-
form.validate(
|
71
|
+
assert form.validate(
|
72
72
|
"songs" => [{"title" => "Fallout"}, {"title" => "Roxanne"},
|
73
73
|
{"title" => "Rime Of The Ancient Mariner"}, # new song.
|
74
74
|
{"title" => "Re-Education", "composer" => {"name" => "Rise Against"}}], # new song with new composer.
|
75
|
-
)
|
75
|
+
)
|
76
76
|
|
77
|
-
form.errors.messages.inspect
|
77
|
+
assert_equal form.errors.messages.inspect, "{}"
|
78
78
|
|
79
79
|
# form has updated.
|
80
|
-
form.name
|
81
|
-
form.songs[0].title
|
82
|
-
form.songs[1].title
|
83
|
-
form.songs[1].composer.name
|
80
|
+
assert_equal form.name, "The Dissent Of Man"
|
81
|
+
assert_equal form.songs[0].title, "Fallout"
|
82
|
+
assert_equal form.songs[1].title, "Roxanne"
|
83
|
+
assert_equal form.songs[1].composer.name, "Greg Graffin"
|
84
84
|
|
85
|
-
form.songs[1].composer.model.
|
86
|
-
|
87
|
-
form.songs[1].title.must_equal "Roxanne"
|
88
|
-
form.songs[2].title.must_equal "Rime Of The Ancient Mariner" # new song added.
|
89
|
-
form.songs[3].title.must_equal "Re-Education"
|
90
|
-
form.songs[3].composer.name.must_equal "Rise Against"
|
91
|
-
form.songs.size.must_equal 4
|
92
|
-
form.artist.name.must_equal "Bad Religion"
|
85
|
+
form.songs[1].composer.model.is_a? Artist
|
93
86
|
|
87
|
+
assert_equal form.songs[1].title, "Roxanne"
|
88
|
+
assert_equal form.songs[2].title, "Rime Of The Ancient Mariner" # new song added.
|
89
|
+
assert_equal form.songs[3].title, "Re-Education"
|
90
|
+
assert_equal form.songs[3].composer.name, "Rise Against"
|
91
|
+
assert_equal form.songs.size, 4
|
92
|
+
assert_equal form.artist.name, "Bad Religion"
|
94
93
|
|
95
94
|
# model has not changed, yet.
|
96
|
-
album.name
|
97
|
-
album.songs[0].title
|
98
|
-
album.songs[1].title
|
99
|
-
album.songs[1].composer.name
|
100
|
-
album.songs.size
|
101
|
-
album.artist.name
|
95
|
+
assert_equal album.name, "The Dissent Of Man"
|
96
|
+
assert_equal album.songs[0].title, "Broken"
|
97
|
+
assert_equal album.songs[1].title, "Resist Stance"
|
98
|
+
assert_equal album.songs[1].composer.name, "Greg Graffin"
|
99
|
+
assert_equal album.songs.size, 2
|
100
|
+
assert_equal album.artist.name, "Bad Religion"
|
102
101
|
end
|
103
102
|
end
|
104
103
|
|
@@ -113,12 +112,12 @@ class PopulateWithMethodTest < Minitest::Spec
|
|
113
112
|
end
|
114
113
|
end
|
115
114
|
|
116
|
-
let
|
115
|
+
let(:form) { AlbumForm.new(Album.new) }
|
117
116
|
|
118
117
|
it "runs populator method" do
|
119
118
|
form.validate("title" => "override me!")
|
120
119
|
|
121
|
-
form.title
|
120
|
+
assert_equal form.title, "!em edirrevo"
|
122
121
|
end
|
123
122
|
end
|
124
123
|
|
@@ -137,12 +136,12 @@ class PopulateWithCallableTest < Minitest::Spec
|
|
137
136
|
property :title, populator: TitlePopulator.new
|
138
137
|
end
|
139
138
|
|
140
|
-
let
|
139
|
+
let(:form) { AlbumForm.new(Album.new) }
|
141
140
|
|
142
141
|
it "runs populator method" do
|
143
142
|
form.validate("title" => "override me!")
|
144
143
|
|
145
|
-
form.title
|
144
|
+
assert_equal form.title, "!em edirrevo"
|
146
145
|
end
|
147
146
|
end
|
148
147
|
|
@@ -157,12 +156,12 @@ class PopulateWithProcTest < Minitest::Spec
|
|
157
156
|
property :title, populator: TitlePopulator
|
158
157
|
end
|
159
158
|
|
160
|
-
let
|
159
|
+
let(:form) { AlbumForm.new(Album.new) }
|
161
160
|
|
162
161
|
it "runs populator method" do
|
163
162
|
form.validate("title" => "override me!")
|
164
163
|
|
165
|
-
form.title
|
164
|
+
assert_equal form.title, "!em edirrevo"
|
166
165
|
end
|
167
166
|
end
|
168
167
|
|
@@ -171,13 +170,11 @@ class PopulateIfEmptyTest < MiniTest::Spec
|
|
171
170
|
Album = Struct.new(:name, :songs, :artist)
|
172
171
|
Artist = Struct.new(:name)
|
173
172
|
|
174
|
-
let
|
175
|
-
let
|
176
|
-
let
|
177
|
-
let
|
178
|
-
let
|
179
|
-
|
180
|
-
|
173
|
+
let(:song) { Song.new("Broken") }
|
174
|
+
let(:song_with_composer) { Song.new("Resist Stance", nil, composer) }
|
175
|
+
let(:composer) { Artist.new("Greg Graffin") }
|
176
|
+
let(:artist) { Artist.new("Bad Religion") }
|
177
|
+
let(:album) { Album.new("The Dissent Of Man", [song, song_with_composer], artist) }
|
181
178
|
|
182
179
|
class AlbumForm < TestForm
|
183
180
|
property :name
|
@@ -187,13 +184,13 @@ class PopulateIfEmptyTest < MiniTest::Spec
|
|
187
184
|
|
188
185
|
property :title
|
189
186
|
validation do
|
190
|
-
required(:title).filled
|
187
|
+
params { required(:title).filled }
|
191
188
|
end
|
192
189
|
|
193
190
|
property :composer, populate_if_empty: :populate_composer! do # lambda works, too. in form context.
|
194
191
|
property :name
|
195
192
|
validation do
|
196
|
-
required(:name).filled
|
193
|
+
params { required(:name).filled }
|
197
194
|
end
|
198
195
|
end
|
199
196
|
|
@@ -203,52 +200,51 @@ class PopulateIfEmptyTest < MiniTest::Spec
|
|
203
200
|
end
|
204
201
|
end
|
205
202
|
|
206
|
-
property :artist, populate_if_empty:
|
203
|
+
property :artist, populate_if_empty: ->(args) { create_artist(args[:fragment], args[:user_options]) } do # methods work, too.
|
207
204
|
property :name
|
208
205
|
end
|
209
206
|
|
210
|
-
|
207
|
+
private
|
211
208
|
class Sting < Artist
|
212
209
|
attr_accessor :args
|
213
210
|
end
|
214
211
|
def create_artist(input, user_options)
|
215
|
-
Sting.new.tap { |artist| artist.args=([input, user_options].to_s) }
|
212
|
+
Sting.new.tap { |artist| artist.args = ([input, user_options].to_s) }
|
216
213
|
end
|
217
214
|
end
|
218
215
|
|
219
|
-
let
|
216
|
+
let(:form) { AlbumForm.new(album) }
|
220
217
|
|
221
218
|
it do
|
222
|
-
form.songs.size
|
219
|
+
assert_equal form.songs.size, 2
|
223
220
|
|
224
|
-
form.validate(
|
225
|
-
"songs"
|
221
|
+
assert form.validate(
|
222
|
+
"songs" => [{"title" => "Fallout"}, {"title" => "Roxanne"},
|
226
223
|
{"title" => "Rime Of The Ancient Mariner"}, # new song.
|
227
224
|
{"title" => "Re-Education", "composer" => {"name" => "Rise Against"}}], # new song with new composer.
|
228
|
-
)
|
225
|
+
)
|
229
226
|
|
230
|
-
form.errors.messages.inspect
|
227
|
+
assert_equal form.errors.messages.inspect, "{}"
|
231
228
|
|
232
229
|
# form has updated.
|
233
|
-
form.name
|
234
|
-
form.songs[0].title
|
235
|
-
form.songs[1].title
|
236
|
-
form.songs[1].composer.name
|
237
|
-
form.songs[1].title
|
238
|
-
form.songs[2].title
|
239
|
-
form.songs[3].title
|
240
|
-
form.songs[3].composer.name
|
241
|
-
form.songs.size
|
242
|
-
form.artist.name
|
243
|
-
|
230
|
+
assert_equal form.name, "The Dissent Of Man"
|
231
|
+
assert_equal form.songs[0].title, "Fallout"
|
232
|
+
assert_equal form.songs[1].title, "Roxanne"
|
233
|
+
assert_equal form.songs[1].composer.name, "Greg Graffin"
|
234
|
+
assert_equal form.songs[1].title, "Roxanne"
|
235
|
+
assert_equal form.songs[2].title, "Rime Of The Ancient Mariner" # new song added.
|
236
|
+
assert_equal form.songs[3].title, "Re-Education"
|
237
|
+
assert_equal form.songs[3].composer.name, "Rise Against"
|
238
|
+
assert_equal form.songs.size, 4
|
239
|
+
assert_equal form.artist.name, "Bad Religion"
|
244
240
|
|
245
241
|
# model has not changed, yet.
|
246
|
-
album.name
|
247
|
-
album.songs[0].title
|
248
|
-
album.songs[1].title
|
249
|
-
album.songs[1].composer.name
|
250
|
-
album.songs.size
|
251
|
-
album.artist.name
|
242
|
+
assert_equal album.name, "The Dissent Of Man"
|
243
|
+
assert_equal album.songs[0].title, "Broken"
|
244
|
+
assert_equal album.songs[1].title, "Resist Stance"
|
245
|
+
assert_equal album.songs[1].composer.name, "Greg Graffin"
|
246
|
+
assert_equal album.songs.size, 2
|
247
|
+
assert_equal album.artist.name, "Bad Religion"
|
252
248
|
end
|
253
249
|
|
254
250
|
# trigger artist populator. lambda calling form instance method.
|
@@ -256,26 +252,24 @@ class PopulateIfEmptyTest < MiniTest::Spec
|
|
256
252
|
form = AlbumForm.new(album = Album.new)
|
257
253
|
form.validate("artist" => {"name" => "From Autumn To Ashes"})
|
258
254
|
|
259
|
-
form.artist.name
|
255
|
+
assert_equal form.artist.name, "From Autumn To Ashes"
|
260
256
|
# test lambda was executed in form context.
|
261
|
-
form.artist.model.
|
257
|
+
assert form.artist.model.is_a? AlbumForm::Sting
|
262
258
|
# test lambda block arguments.
|
263
|
-
form.artist.model.args.to_s
|
259
|
+
assert_equal form.artist.model.args.to_s, "[{\"name\"=>\"From Autumn To Ashes\"}, nil]"
|
264
260
|
|
265
261
|
assert_nil album.artist
|
266
262
|
end
|
267
263
|
end
|
268
264
|
|
269
|
-
|
270
265
|
# delete songs while deserializing.
|
271
266
|
class PopulateIfEmptyWithDeletionTest < MiniTest::Spec
|
272
267
|
Song = Struct.new(:title, :album, :composer)
|
273
268
|
Album = Struct.new(:name, :songs, :artist)
|
274
269
|
|
275
|
-
let
|
276
|
-
let
|
277
|
-
let
|
278
|
-
|
270
|
+
let(:song) { Song.new("Broken") }
|
271
|
+
let(:song2) { Song.new("Resist Stance") }
|
272
|
+
let(:album) { Album.new("The Dissent Of Man", [song, song2]) }
|
279
273
|
|
280
274
|
class AlbumForm < TestForm
|
281
275
|
property :name
|
@@ -285,7 +279,7 @@ class PopulateIfEmptyWithDeletionTest < MiniTest::Spec
|
|
285
279
|
|
286
280
|
property :title
|
287
281
|
validation do
|
288
|
-
required(:title).filled
|
282
|
+
params { required(:title).filled }
|
289
283
|
end
|
290
284
|
end
|
291
285
|
|
@@ -295,16 +289,75 @@ class PopulateIfEmptyWithDeletionTest < MiniTest::Spec
|
|
295
289
|
end
|
296
290
|
end
|
297
291
|
|
298
|
-
let
|
292
|
+
let(:form) { AlbumForm.new(album) }
|
299
293
|
|
300
294
|
it do
|
301
|
-
form.validate(
|
302
|
-
"songs"
|
303
|
-
)
|
295
|
+
assert form.validate(
|
296
|
+
"songs" => [{"title" => "Broken, delete me!"}, {"title" => "Roxanne"}]
|
297
|
+
)
|
298
|
+
|
299
|
+
assert_equal form.errors.messages.inspect, "{}"
|
304
300
|
|
305
|
-
form.
|
301
|
+
assert_equal form.songs.size, 1
|
302
|
+
assert_equal form.songs[0].title, "Roxanne"
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
class PopulateWithFormKeyTest < MiniTest::Spec
|
307
|
+
Song = Struct.new(:title, :album, :composer)
|
308
|
+
Album = Struct.new(:name, :songs, :artist)
|
306
309
|
|
307
|
-
|
308
|
-
|
310
|
+
let(:song) { Song.new('Broken') }
|
311
|
+
let(:song2) { Song.new('Resist Stance') }
|
312
|
+
let(:album) { Album.new('The Dissent Of Man', [song, song2]) }
|
313
|
+
|
314
|
+
class SongForm < TestForm
|
315
|
+
property :title
|
316
|
+
|
317
|
+
validation do
|
318
|
+
params { required(:title).filled }
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
class AlbumForm < TestForm
|
323
|
+
property :name
|
324
|
+
|
325
|
+
collection :songs, form: SongForm, populator: :populator!, model_identifier: :title
|
326
|
+
|
327
|
+
def populator!(fragment:, **)
|
328
|
+
item = songs.find { |song| song.title == fragment['title'] }
|
329
|
+
if item && fragment['delete'] == '1'
|
330
|
+
songs.delete(item)
|
331
|
+
return skip!
|
332
|
+
end
|
333
|
+
item || songs.append(Song.new)
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
let(:form) { AlbumForm.new(album) }
|
338
|
+
|
339
|
+
it do
|
340
|
+
assert_equal 2, form.songs.size
|
341
|
+
|
342
|
+
assert form.validate(
|
343
|
+
'songs' => [
|
344
|
+
{ 'title' => 'Broken' },
|
345
|
+
{ 'title' => 'Resist Stance' },
|
346
|
+
{ 'title' => 'Rime Of The Ancient Mariner' }
|
347
|
+
]
|
348
|
+
)
|
349
|
+
|
350
|
+
assert_equal 3, form.songs.size
|
351
|
+
|
352
|
+
assert form.validate(
|
353
|
+
'songs' => [
|
354
|
+
{ 'title' => 'Broken', 'delete' => '1' },
|
355
|
+
{ 'title' => 'Resist Stance' },
|
356
|
+
{ 'title' => 'Rime Of The Ancient Mariner' }
|
357
|
+
]
|
358
|
+
)
|
359
|
+
assert_equal 2, form.songs.size
|
360
|
+
assert_equal 'Resist Stance', form.songs.first.title
|
361
|
+
assert_equal 'Rime Of The Ancient Mariner', form.songs.last.title
|
309
362
|
end
|
310
363
|
end
|