reform 2.2.4 → 2.3.1
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/.rubocop.yml +30 -0
- data/.rubocop_todo.yml +460 -0
- data/.travis.yml +11 -6
- data/Appraisals +8 -0
- data/CHANGES.md +54 -4
- data/CONTRIBUTING.md +31 -0
- data/Gemfile +2 -16
- data/ISSUE_TEMPLATE.md +25 -0
- data/LICENSE.txt +1 -1
- data/README.md +5 -7
- data/Rakefile +18 -9
- data/gemfiles/0.13.0.gemfile +8 -0
- data/gemfiles/1.5.0.gemfile +9 -0
- data/lib/reform.rb +1 -0
- data/lib/reform/contract.rb +7 -17
- data/lib/reform/contract/custom_error.rb +41 -0
- data/lib/reform/contract/validate.rb +53 -23
- data/lib/reform/errors.rb +61 -0
- data/lib/reform/form.rb +36 -10
- data/lib/reform/form/call.rb +1 -1
- data/lib/reform/form/composition.rb +2 -2
- data/lib/reform/form/dry.rb +10 -58
- data/lib/reform/form/dry/input_hash.rb +37 -0
- data/lib/reform/form/dry/new_api.rb +46 -0
- data/lib/reform/form/dry/old_api.rb +61 -0
- data/lib/reform/form/populator.rb +11 -27
- data/lib/reform/form/prepopulate.rb +4 -3
- data/lib/reform/form/validate.rb +28 -13
- data/lib/reform/result.rb +90 -0
- data/lib/reform/validation.rb +19 -11
- data/lib/reform/validation/groups.rb +12 -27
- data/lib/reform/version.rb +1 -1
- data/reform.gemspec +15 -13
- data/test/benchmarking.rb +39 -6
- data/test/call_new_api.rb +23 -0
- data/test/{call_test.rb → call_old_api.rb} +4 -4
- data/test/changed_test.rb +8 -8
- data/test/coercion_test.rb +51 -19
- data/test/composition_new_api.rb +186 -0
- data/test/{composition_test.rb → composition_old_api.rb} +66 -31
- data/test/contract/custom_error_test.rb +55 -0
- data/test/contract_new_api.rb +77 -0
- data/test/{contract_test.rb → contract_old_api.rb} +13 -13
- data/test/default_test.rb +2 -2
- data/test/deserialize_test.rb +11 -14
- data/test/errors_new_api.rb +225 -0
- data/test/errors_old_api.rb +230 -0
- data/test/feature_test.rb +8 -10
- data/test/fixtures/dry_error_messages.yml +73 -23
- data/test/fixtures/dry_new_api_error_messages.yml +104 -0
- data/test/form_new_api.rb +57 -0
- data/test/{form_test.rb → form_old_api.rb} +5 -5
- data/test/form_option_new_api.rb +24 -0
- data/test/{form_option_test.rb → form_option_old_api.rb} +4 -4
- data/test/from_test.rb +9 -13
- data/test/inherit_new_api.rb +105 -0
- data/test/inherit_old_api.rb +105 -0
- data/test/{module_test.rb → module_new_api.rb} +20 -25
- data/test/module_old_api.rb +146 -0
- data/test/parse_option_test.rb +40 -0
- data/test/parse_pipeline_test.rb +3 -3
- data/test/populate_new_api.rb +304 -0
- data/test/{populate_test.rb → populate_old_api.rb} +83 -49
- data/test/populator_skip_test.rb +9 -9
- data/test/prepopulator_test.rb +8 -9
- data/test/read_only_test.rb +12 -1
- data/test/readable_test.rb +7 -7
- data/test/reform_new_api.rb +204 -0
- data/test/{reform_test.rb → reform_old_api.rb} +30 -51
- data/test/save_new_api.rb +101 -0
- data/test/{save_test.rb → save_old_api.rb} +32 -20
- data/test/setup_test.rb +8 -8
- data/test/{skip_if_test.rb → skip_if_new_api.rb} +23 -12
- data/test/skip_if_old_api.rb +92 -0
- data/test/skip_setter_and_getter_test.rb +3 -4
- data/test/test_helper.rb +25 -14
- data/test/validate_new_api.rb +408 -0
- data/test/{validate_test.rb → validate_old_api.rb} +59 -69
- data/test/validation/dry_validation_new_api.rb +836 -0
- data/test/validation/dry_validation_old_api.rb +772 -0
- data/test/validation/result_test.rb +77 -0
- data/test/validation_library_provided_test.rb +16 -0
- data/test/virtual_test.rb +47 -7
- data/test/writeable_test.rb +35 -6
- metadata +127 -56
- data/gemfiles/Gemfile.disposable-0.3 +0 -6
- data/lib/reform/contract/errors.rb +0 -43
- data/lib/reform/form/mongoid.rb +0 -37
- data/lib/reform/form/orm.rb +0 -26
- data/lib/reform/mongoid.rb +0 -4
- data/test/deprecation_test.rb +0 -27
- data/test/errors_test.rb +0 -165
- data/test/inherit_test.rb +0 -119
- data/test/readonly_test.rb +0 -14
- data/test/validation/dry_test.rb +0 -60
- data/test/validation/dry_validation_test.rb +0 -352
- data/test/validation/errors.yml +0 -4
@@ -5,27 +5,25 @@ class PopulatorTest < MiniTest::Spec
|
|
5
5
|
Album = Struct.new(:name, :songs, :artist)
|
6
6
|
Artist = Struct.new(:name)
|
7
7
|
|
8
|
-
class AlbumForm <
|
8
|
+
class AlbumForm < TestForm
|
9
9
|
property :name, populator: ->(options) { self.name = options[:fragment].reverse }
|
10
10
|
validation do
|
11
|
-
|
11
|
+
required(:name).filled
|
12
12
|
end
|
13
13
|
|
14
14
|
collection :songs,
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
(item = collection[index]) ? item : collection.insert(index, Song.new) } do
|
19
|
-
|
15
|
+
populator: ->(fragment:, model:, index:, **) {
|
16
|
+
(item = model[index]) ? item : model.insert(index, Song.new)
|
17
|
+
} do
|
20
18
|
property :title
|
21
19
|
validation do
|
22
|
-
|
20
|
+
required(:title).filled
|
23
21
|
end
|
24
22
|
|
25
|
-
property :composer, populator: ->(options) { options[:model] || self.composer= Artist.new } do
|
23
|
+
property :composer, populator: ->(options) { options[:model] || self.composer = Artist.new } do
|
26
24
|
property :name
|
27
25
|
validation do
|
28
|
-
|
26
|
+
required(:name).filled
|
29
27
|
end
|
30
28
|
end
|
31
29
|
end
|
@@ -37,13 +35,13 @@ class PopulatorTest < MiniTest::Spec
|
|
37
35
|
end
|
38
36
|
end
|
39
37
|
|
40
|
-
let
|
41
|
-
let
|
42
|
-
let
|
43
|
-
let
|
44
|
-
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) }
|
45
43
|
|
46
|
-
let
|
44
|
+
let(:form) { AlbumForm.new(album) }
|
47
45
|
|
48
46
|
it "runs populator on scalar" do
|
49
47
|
form.validate(
|
@@ -93,7 +91,6 @@ class PopulatorTest < MiniTest::Spec
|
|
93
91
|
form.songs.size.must_equal 4
|
94
92
|
form.artist.name.must_equal "Bad Religion"
|
95
93
|
|
96
|
-
|
97
94
|
# model has not changed, yet.
|
98
95
|
album.name.must_equal "The Dissent Of Man"
|
99
96
|
album.songs[0].title.must_equal "Broken"
|
@@ -107,7 +104,7 @@ end
|
|
107
104
|
class PopulateWithMethodTest < Minitest::Spec
|
108
105
|
Album = Struct.new(:title)
|
109
106
|
|
110
|
-
class AlbumForm <
|
107
|
+
class AlbumForm < TestForm
|
111
108
|
property :title, populator: :title!
|
112
109
|
|
113
110
|
def title!(options)
|
@@ -115,12 +112,54 @@ class PopulateWithMethodTest < Minitest::Spec
|
|
115
112
|
end
|
116
113
|
end
|
117
114
|
|
118
|
-
let
|
115
|
+
let(:form) { AlbumForm.new(Album.new) }
|
119
116
|
|
120
117
|
it "runs populator method" do
|
121
|
-
form.validate(
|
122
|
-
|
123
|
-
|
118
|
+
form.validate("title" => "override me!")
|
119
|
+
|
120
|
+
form.title.must_equal "!em edirrevo"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
class PopulateWithCallableTest < Minitest::Spec
|
125
|
+
Album = Struct.new(:title)
|
126
|
+
|
127
|
+
class TitlePopulator
|
128
|
+
include Uber::Callable
|
129
|
+
|
130
|
+
def call(form, options)
|
131
|
+
form.title = options[:fragment].reverse
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
class AlbumForm < TestForm
|
136
|
+
property :title, populator: TitlePopulator.new
|
137
|
+
end
|
138
|
+
|
139
|
+
let(:form) { AlbumForm.new(Album.new) }
|
140
|
+
|
141
|
+
it "runs populator method" do
|
142
|
+
form.validate("title" => "override me!")
|
143
|
+
|
144
|
+
form.title.must_equal "!em edirrevo"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
class PopulateWithProcTest < Minitest::Spec
|
149
|
+
Album = Struct.new(:title)
|
150
|
+
|
151
|
+
TitlePopulator = ->(options) do
|
152
|
+
options[:represented].title = options[:fragment].reverse
|
153
|
+
end
|
154
|
+
|
155
|
+
class AlbumForm < TestForm
|
156
|
+
property :title, populator: TitlePopulator
|
157
|
+
end
|
158
|
+
|
159
|
+
let(:form) { AlbumForm.new(Album.new) }
|
160
|
+
|
161
|
+
it "runs populator method" do
|
162
|
+
form.validate("title" => "override me!")
|
124
163
|
|
125
164
|
form.title.must_equal "!em edirrevo"
|
126
165
|
end
|
@@ -131,15 +170,13 @@ class PopulateIfEmptyTest < MiniTest::Spec
|
|
131
170
|
Album = Struct.new(:name, :songs, :artist)
|
132
171
|
Artist = Struct.new(:name)
|
133
172
|
|
134
|
-
let
|
135
|
-
let
|
136
|
-
let
|
137
|
-
let
|
138
|
-
let
|
139
|
-
|
140
|
-
|
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) }
|
141
178
|
|
142
|
-
class AlbumForm <
|
179
|
+
class AlbumForm < TestForm
|
143
180
|
property :name
|
144
181
|
|
145
182
|
collection :songs,
|
@@ -147,42 +184,42 @@ class PopulateIfEmptyTest < MiniTest::Spec
|
|
147
184
|
|
148
185
|
property :title
|
149
186
|
validation do
|
150
|
-
|
187
|
+
required(:title).filled
|
151
188
|
end
|
152
189
|
|
153
190
|
property :composer, populate_if_empty: :populate_composer! do # lambda works, too. in form context.
|
154
191
|
property :name
|
155
192
|
validation do
|
156
|
-
|
193
|
+
required(:name).filled
|
157
194
|
end
|
158
195
|
end
|
159
196
|
|
160
197
|
private
|
161
|
-
def populate_composer!(
|
198
|
+
def populate_composer!(options)
|
162
199
|
Artist.new
|
163
200
|
end
|
164
201
|
end
|
165
202
|
|
166
|
-
property :artist, populate_if_empty:
|
203
|
+
property :artist, populate_if_empty: ->(args) { create_artist(args[:fragment], args[:user_options]) } do # methods work, too.
|
167
204
|
property :name
|
168
205
|
end
|
169
206
|
|
170
|
-
|
207
|
+
private
|
171
208
|
class Sting < Artist
|
172
209
|
attr_accessor :args
|
173
210
|
end
|
174
211
|
def create_artist(input, user_options)
|
175
|
-
Sting.new.tap { |artist| artist.args=([input, user_options].to_s) }
|
212
|
+
Sting.new.tap { |artist| artist.args = ([input, user_options].to_s) }
|
176
213
|
end
|
177
214
|
end
|
178
215
|
|
179
|
-
let
|
216
|
+
let(:form) { AlbumForm.new(album) }
|
180
217
|
|
181
218
|
it do
|
182
219
|
form.songs.size.must_equal 2
|
183
220
|
|
184
221
|
form.validate(
|
185
|
-
"songs"
|
222
|
+
"songs" => [{"title" => "Fallout"}, {"title" => "Roxanne"},
|
186
223
|
{"title" => "Rime Of The Ancient Mariner"}, # new song.
|
187
224
|
{"title" => "Re-Education", "composer" => {"name" => "Rise Against"}}], # new song with new composer.
|
188
225
|
).must_equal true
|
@@ -201,7 +238,6 @@ class PopulateIfEmptyTest < MiniTest::Spec
|
|
201
238
|
form.songs.size.must_equal 4
|
202
239
|
form.artist.name.must_equal "Bad Religion"
|
203
240
|
|
204
|
-
|
205
241
|
# model has not changed, yet.
|
206
242
|
album.name.must_equal "The Dissent Of Man"
|
207
243
|
album.songs[0].title.must_equal "Broken"
|
@@ -222,22 +258,20 @@ class PopulateIfEmptyTest < MiniTest::Spec
|
|
222
258
|
# test lambda block arguments.
|
223
259
|
form.artist.model.args.to_s.must_equal "[{\"name\"=>\"From Autumn To Ashes\"}, nil]"
|
224
260
|
|
225
|
-
album.artist
|
261
|
+
assert_nil album.artist
|
226
262
|
end
|
227
263
|
end
|
228
264
|
|
229
|
-
|
230
265
|
# delete songs while deserializing.
|
231
266
|
class PopulateIfEmptyWithDeletionTest < MiniTest::Spec
|
232
267
|
Song = Struct.new(:title, :album, :composer)
|
233
268
|
Album = Struct.new(:name, :songs, :artist)
|
234
269
|
|
235
|
-
let
|
236
|
-
let
|
237
|
-
let
|
238
|
-
|
270
|
+
let(:song) { Song.new("Broken") }
|
271
|
+
let(:song2) { Song.new("Resist Stance") }
|
272
|
+
let(:album) { Album.new("The Dissent Of Man", [song, song2]) }
|
239
273
|
|
240
|
-
class AlbumForm <
|
274
|
+
class AlbumForm < TestForm
|
241
275
|
property :name
|
242
276
|
|
243
277
|
collection :songs,
|
@@ -245,7 +279,7 @@ class PopulateIfEmptyWithDeletionTest < MiniTest::Spec
|
|
245
279
|
|
246
280
|
property :title
|
247
281
|
validation do
|
248
|
-
|
282
|
+
required(:title).filled
|
249
283
|
end
|
250
284
|
end
|
251
285
|
|
@@ -255,11 +289,11 @@ class PopulateIfEmptyWithDeletionTest < MiniTest::Spec
|
|
255
289
|
end
|
256
290
|
end
|
257
291
|
|
258
|
-
let
|
292
|
+
let(:form) { AlbumForm.new(album) }
|
259
293
|
|
260
294
|
it do
|
261
295
|
form.validate(
|
262
|
-
"songs"
|
296
|
+
"songs" => [{"title" => "Broken, delete me!"}, {"title" => "Roxanne"}]
|
263
297
|
).must_equal true
|
264
298
|
|
265
299
|
form.errors.messages.inspect.must_equal "{}"
|
data/test/populator_skip_test.rb
CHANGED
@@ -4,14 +4,14 @@ class PopulatorSkipTest < MiniTest::Spec
|
|
4
4
|
Album = Struct.new(:songs)
|
5
5
|
Song = Struct.new(:title)
|
6
6
|
|
7
|
+
class AlbumForm < TestForm
|
8
|
+
collection :songs, populator: :my_populator do
|
9
|
+
property :title
|
10
|
+
end
|
7
11
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
return skip! if options[:fragment][:title] == "Good"
|
12
|
-
songs[options[:index]]
|
13
|
-
} do
|
14
|
-
property :title
|
12
|
+
def my_populator(options)
|
13
|
+
return skip! if options[:fragment][:title] == "Good"
|
14
|
+
songs[options[:index]]
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
@@ -22,7 +22,7 @@ class PopulatorSkipTest < MiniTest::Spec
|
|
22
22
|
form.validate(hash)
|
23
23
|
|
24
24
|
form.songs.size.must_equal 2
|
25
|
-
form.songs[0].title
|
25
|
+
assert_nil form.songs[0].title
|
26
26
|
form.songs[1].title.must_equal "Bad"
|
27
27
|
end
|
28
|
-
end
|
28
|
+
end
|
data/test/prepopulator_test.rb
CHANGED
@@ -1,17 +1,17 @@
|
|
1
|
-
require
|
1
|
+
require "test_helper"
|
2
2
|
|
3
3
|
class PrepopulatorTest < MiniTest::Spec
|
4
4
|
Song = Struct.new(:title, :band, :length)
|
5
5
|
Band = Struct.new(:name)
|
6
6
|
|
7
|
-
class AlbumForm <
|
8
|
-
property :title, prepopulator: ->(*){ self.title = "Another Day At Work" } # normal assignment.
|
7
|
+
class AlbumForm < TestForm
|
8
|
+
property :title, prepopulator: ->(*) { self.title = "Another Day At Work" } # normal assignment.
|
9
9
|
property :length
|
10
10
|
|
11
11
|
property :hit, prepopulator: ->(options) { self.hit = Song.new(options[:title]) } do # use user options.
|
12
12
|
property :title
|
13
13
|
|
14
|
-
property :band, prepopulator: ->(options){ self.band = my_band(options[:title]) } do # invoke your own code.
|
14
|
+
property :band, prepopulator: ->(options) { self.band = my_band(options[:title]) } do # invoke your own code.
|
15
15
|
property :name
|
16
16
|
end
|
17
17
|
|
@@ -24,7 +24,7 @@ class PrepopulatorTest < MiniTest::Spec
|
|
24
24
|
property :title
|
25
25
|
end
|
26
26
|
|
27
|
-
|
27
|
+
private
|
28
28
|
def prepopulate_songs!(options)
|
29
29
|
if songs == nil
|
30
30
|
self.songs = [Song.new, Song.new]
|
@@ -63,7 +63,7 @@ end
|
|
63
63
|
class PrepopulateWithoutConfiguration < MiniTest::Spec
|
64
64
|
Song = Struct.new(:title)
|
65
65
|
|
66
|
-
class AlbumForm <
|
66
|
+
class AlbumForm < TestForm
|
67
67
|
collection :songs do
|
68
68
|
property :title
|
69
69
|
end
|
@@ -78,12 +78,11 @@ class PrepopulateWithoutConfiguration < MiniTest::Spec
|
|
78
78
|
it { subject.songs.size.must_equal 0 }
|
79
79
|
end
|
80
80
|
|
81
|
-
|
82
81
|
class ManualPrepopulatorOverridingTest < MiniTest::Spec
|
83
82
|
Song = Struct.new(:title, :band, :length)
|
84
83
|
Band = Struct.new(:name)
|
85
84
|
|
86
|
-
class AlbumForm <
|
85
|
+
class AlbumForm < TestForm
|
87
86
|
property :title
|
88
87
|
property :length
|
89
88
|
|
@@ -109,4 +108,4 @@ class ManualPrepopulatorOverridingTest < MiniTest::Spec
|
|
109
108
|
form.hit.model.must_equal Song.new("Potemkin City Limits")
|
110
109
|
form.hit.title.must_equal "Potemkin City Limits"
|
111
110
|
end
|
112
|
-
end
|
111
|
+
end
|
data/test/read_only_test.rb
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
-
require
|
1
|
+
require "test_helper"
|
2
2
|
|
3
|
+
class ReadonlyTest < MiniTest::Spec
|
4
|
+
class SongForm < TestForm
|
5
|
+
property :artist
|
6
|
+
property :title, writeable: false
|
7
|
+
# TODO: what to do with virtual values?
|
8
|
+
end
|
3
9
|
|
10
|
+
let(:form) { SongForm.new(OpenStruct.new) }
|
11
|
+
|
12
|
+
it { form.readonly?(:artist).must_equal false }
|
13
|
+
it { form.readonly?(:title).must_equal true }
|
14
|
+
end
|
data/test/readable_test.rb
CHANGED
@@ -1,17 +1,17 @@
|
|
1
|
-
require
|
1
|
+
require "test_helper"
|
2
2
|
|
3
3
|
class ReadableTest < MiniTest::Spec
|
4
4
|
Credentials = Struct.new(:password)
|
5
5
|
|
6
|
-
class PasswordForm <
|
6
|
+
class PasswordForm < TestForm
|
7
7
|
property :password, readable: false
|
8
8
|
end
|
9
9
|
|
10
|
-
let
|
11
|
-
let
|
10
|
+
let(:cred) { Credentials.new }
|
11
|
+
let(:form) { PasswordForm.new(cred) }
|
12
12
|
|
13
13
|
it {
|
14
|
-
form.password
|
14
|
+
assert_nil form.password # password not read.
|
15
15
|
|
16
16
|
form.validate("password" => "123")
|
17
17
|
|
@@ -25,6 +25,6 @@ class ReadableTest < MiniTest::Spec
|
|
25
25
|
hash = nested
|
26
26
|
end
|
27
27
|
|
28
|
-
hash.must_equal("password"=> "123")
|
28
|
+
hash.must_equal("password" => "123")
|
29
29
|
}
|
30
|
-
end
|
30
|
+
end
|
@@ -0,0 +1,204 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class ReformTest < Minitest::Spec
|
4
|
+
let(:comp) { OpenStruct.new(name: "Duran Duran", title: "Rio") }
|
5
|
+
|
6
|
+
let(:form) { SongForm.new(comp) }
|
7
|
+
|
8
|
+
class SongForm < TestForm
|
9
|
+
property :name
|
10
|
+
property :title
|
11
|
+
|
12
|
+
validation do
|
13
|
+
params { required(:name).filled }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "(new) form with empty models" do
|
18
|
+
let(:comp) { OpenStruct.new }
|
19
|
+
|
20
|
+
it "returns empty fields" do
|
21
|
+
assert_nil form.title
|
22
|
+
form.name.must_be_nil
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "and submitted values" do
|
26
|
+
it "returns filled-out fields" do
|
27
|
+
form.validate("name" => "Duran Duran")
|
28
|
+
|
29
|
+
assert_nil form.title
|
30
|
+
form.name.must_equal "Duran Duran"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "(edit) form with existing models" do
|
36
|
+
it "returns filled-out fields" do
|
37
|
+
form.name.must_equal "Duran Duran"
|
38
|
+
form.title.must_equal "Rio"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "#validate" do
|
43
|
+
let(:comp) { OpenStruct.new }
|
44
|
+
|
45
|
+
it "ignores unmapped fields in input" do
|
46
|
+
form.validate("name" => "Duran Duran", :genre => "80s")
|
47
|
+
assert_raises NoMethodError do
|
48
|
+
form.genre
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
it "returns true when valid" do
|
53
|
+
form.validate("name" => "Duran Duran").must_equal true
|
54
|
+
end
|
55
|
+
|
56
|
+
it "exposes input via property accessors" do
|
57
|
+
form.validate("name" => "Duran Duran")
|
58
|
+
|
59
|
+
form.name.must_equal "Duran Duran"
|
60
|
+
end
|
61
|
+
|
62
|
+
it "doesn't change model properties" do
|
63
|
+
form.validate("name" => "Duran Duran")
|
64
|
+
|
65
|
+
assert_nil comp.name # don't touch model, yet.
|
66
|
+
end
|
67
|
+
|
68
|
+
# TODO: test errors. test valid.
|
69
|
+
describe "invalid input" do
|
70
|
+
class ValidatingForm < TestForm
|
71
|
+
property :name
|
72
|
+
property :title
|
73
|
+
|
74
|
+
validation do
|
75
|
+
params do
|
76
|
+
required(:name).filled
|
77
|
+
required(:title).filled
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
let(:form) { ValidatingForm.new(comp) }
|
82
|
+
|
83
|
+
it "returns false when invalid" do
|
84
|
+
form.validate({}).must_equal false
|
85
|
+
end
|
86
|
+
|
87
|
+
it "populates errors" do
|
88
|
+
form.validate({})
|
89
|
+
form.errors.messages.must_equal(name: ["must be filled"], title: ["must be filled"])
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe "#save" do
|
95
|
+
let(:comp) { OpenStruct.new }
|
96
|
+
let(:form) { SongForm.new(comp) }
|
97
|
+
|
98
|
+
before { form.validate("name" => "Diesel Boy") }
|
99
|
+
|
100
|
+
it "xxpushes data to models" do
|
101
|
+
form.save
|
102
|
+
|
103
|
+
comp.name.must_equal "Diesel Boy"
|
104
|
+
assert_nil comp.title
|
105
|
+
end
|
106
|
+
|
107
|
+
describe "#save with block" do
|
108
|
+
it do
|
109
|
+
hash = {}
|
110
|
+
|
111
|
+
form.save do |map|
|
112
|
+
hash = map
|
113
|
+
end
|
114
|
+
|
115
|
+
hash.must_equal("name" => "Diesel Boy", "title" => nil)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
describe "#model" do
|
121
|
+
it { form.model.must_equal comp }
|
122
|
+
end
|
123
|
+
|
124
|
+
describe "inheritance" do
|
125
|
+
class HitForm < SongForm
|
126
|
+
property :position
|
127
|
+
validation do
|
128
|
+
params { required(:position).filled }
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
let(:form) { HitForm.new(OpenStruct.new()) }
|
133
|
+
it do
|
134
|
+
form.validate("title" => "The Body")
|
135
|
+
form.title.must_equal "The Body"
|
136
|
+
assert_nil form.position
|
137
|
+
form.errors.messages.must_equal(name: ["must be filled"], position: ["must be filled"])
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
class OverridingAccessorsTest < BaseTest
|
143
|
+
class SongForm < TestForm
|
144
|
+
property :title
|
145
|
+
|
146
|
+
def title=(v) # used in #validate.
|
147
|
+
super v * 2
|
148
|
+
end
|
149
|
+
|
150
|
+
def title # used in #sync.
|
151
|
+
super.downcase
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
let(:song) { Song.new("Pray") }
|
156
|
+
subject { SongForm.new(song) }
|
157
|
+
|
158
|
+
# override reader for presentation.
|
159
|
+
it { subject.title.must_equal "pray" }
|
160
|
+
|
161
|
+
describe "#save" do
|
162
|
+
before { subject.validate("title" => "Hey Little World") }
|
163
|
+
|
164
|
+
# reader always used
|
165
|
+
it { subject.title.must_equal "hey little worldhey little world" }
|
166
|
+
|
167
|
+
# the reader is not used when saving/syncing.
|
168
|
+
it do
|
169
|
+
subject.save do |hash|
|
170
|
+
hash["title"].must_equal "Hey Little WorldHey Little World"
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
# no reader or writer used when saving/syncing.
|
175
|
+
it do
|
176
|
+
song.extend(Saveable)
|
177
|
+
subject.save
|
178
|
+
song.title.must_equal "Hey Little WorldHey Little World"
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
class MethodInFormTest < MiniTest::Spec
|
184
|
+
class AlbumForm < TestForm
|
185
|
+
property :title
|
186
|
+
|
187
|
+
def title
|
188
|
+
"The Suffer And The Witness"
|
189
|
+
end
|
190
|
+
|
191
|
+
property :hit do
|
192
|
+
property :title
|
193
|
+
|
194
|
+
def title
|
195
|
+
"Drones"
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
# methods can be used instead of created accessors.
|
201
|
+
subject { AlbumForm.new(OpenStruct.new(hit: OpenStruct.new)) }
|
202
|
+
it { subject.title.must_equal "The Suffer And The Witness" }
|
203
|
+
it { subject.hit.title.must_equal "Drones" }
|
204
|
+
end
|