reform 2.3.3 → 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 +4 -4
- data/.gitignore +1 -1
- data/.travis.yml +0 -3
- data/CHANGES.md +9 -0
- data/Gemfile +1 -1
- data/LICENSE.txt +1 -1
- data/README.md +5 -5
- data/Rakefile +1 -12
- data/lib/reform/contract/validate.rb +1 -1
- data/lib/reform/form/dry.rb +47 -9
- data/lib/reform/validation/groups.rb +0 -1
- data/lib/reform/version.rb +1 -1
- data/test/call_test.rb +23 -0
- data/test/changed_test.rb +6 -6
- data/test/coercion_test.rb +17 -17
- data/test/{composition_new_api.rb → composition_test.rb} +27 -28
- data/test/{contract_new_api.rb → contract_test.rb} +8 -8
- data/test/default_test.rb +2 -2
- data/test/deserialize_test.rb +8 -8
- data/test/docs/validation_test.rb +134 -0
- data/test/{errors_new_api.rb → errors_test.rb} +41 -41
- data/test/feature_test.rb +2 -2
- data/test/fixtures/dry_error_messages.yml +64 -54
- data/test/{form_option_new_api.rb → form_option_test.rb} +1 -1
- data/test/{form_new_api.rb → form_test.rb} +3 -3
- data/test/from_test.rb +10 -10
- data/test/{inherit_new_api.rb → inherit_test.rb} +17 -17
- data/test/{module_new_api.rb → module_test.rb} +10 -10
- data/test/parse_option_test.rb +7 -7
- data/test/parse_pipeline_test.rb +1 -1
- data/test/{populate_new_api.rb → populate_test.rb} +112 -53
- data/test/populator_skip_test.rb +2 -2
- data/test/prepopulator_test.rb +15 -15
- data/test/read_only_test.rb +2 -2
- data/test/readable_test.rb +3 -3
- data/test/{reform_new_api.rb → reform_test.rb} +19 -19
- data/test/{save_new_api.rb → save_test.rb} +4 -4
- data/test/setup_test.rb +9 -9
- data/test/{skip_if_new_api.rb → skip_if_test.rb} +12 -12
- data/test/skip_setter_and_getter_test.rb +6 -6
- data/test/test_helper.rb +5 -6
- data/test/{validate_new_api.rb → validate_test.rb} +65 -78
- data/test/validation/{dry_validation_new_api.rb → dry_validation_test.rb} +124 -123
- data/test/validation/result_test.rb +14 -14
- data/test/virtual_test.rb +7 -7
- data/test/writeable_test.rb +8 -8
- metadata +35 -68
- data/Appraisals +0 -8
- data/gemfiles/0.13.0.gemfile +0 -8
- data/gemfiles/1.5.0.gemfile +0 -9
- data/lib/reform/form/dry/new_api.rb +0 -45
- data/lib/reform/form/dry/old_api.rb +0 -61
- data/test/call_new_api.rb +0 -23
- data/test/call_old_api.rb +0 -23
- data/test/composition_old_api.rb +0 -184
- data/test/contract_old_api.rb +0 -77
- data/test/errors_old_api.rb +0 -230
- data/test/fixtures/dry_new_api_error_messages.yml +0 -104
- data/test/form_old_api.rb +0 -57
- data/test/form_option_old_api.rb +0 -24
- data/test/inherit_old_api.rb +0 -105
- data/test/module_old_api.rb +0 -146
- data/test/populate_old_api.rb +0 -304
- data/test/reform_old_api.rb +0 -202
- data/test/save_old_api.rb +0 -101
- data/test/skip_if_old_api.rb +0 -92
- data/test/validate_old_api.rb +0 -410
- data/test/validation/dry_validation_old_api.rb +0 -772
data/test/reform_old_api.rb
DELETED
@@ -1,202 +0,0 @@
|
|
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
|
-
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
|
-
required(:name).filled
|
76
|
-
required(:title).filled
|
77
|
-
end
|
78
|
-
end
|
79
|
-
let(:form) { ValidatingForm.new(comp) }
|
80
|
-
|
81
|
-
it "returns false when invalid" do
|
82
|
-
_(form.validate({})).must_equal false
|
83
|
-
end
|
84
|
-
|
85
|
-
it "populates errors" do
|
86
|
-
form.validate({})
|
87
|
-
_(form.errors.messages).must_equal({name: ["must be filled"], title: ["must be filled"]})
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
describe "#save" do
|
93
|
-
let(:comp) { OpenStruct.new }
|
94
|
-
let(:form) { SongForm.new(comp) }
|
95
|
-
|
96
|
-
before { form.validate("name" => "Diesel Boy") }
|
97
|
-
|
98
|
-
it "xxpushes data to models" do
|
99
|
-
form.save
|
100
|
-
|
101
|
-
_(comp.name).must_equal "Diesel Boy"
|
102
|
-
assert_nil comp.title
|
103
|
-
end
|
104
|
-
|
105
|
-
describe "#save with block" do
|
106
|
-
it do
|
107
|
-
hash = {}
|
108
|
-
|
109
|
-
form.save do |map|
|
110
|
-
hash = map
|
111
|
-
end
|
112
|
-
|
113
|
-
_(hash).must_equal({"name" => "Diesel Boy", "title" => nil})
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
describe "#model" do
|
119
|
-
it { _(form.model).must_equal comp }
|
120
|
-
end
|
121
|
-
|
122
|
-
describe "inheritance" do
|
123
|
-
class HitForm < SongForm
|
124
|
-
property :position
|
125
|
-
validation do
|
126
|
-
required(:position).filled
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
let(:form) { HitForm.new(OpenStruct.new()) }
|
131
|
-
it do
|
132
|
-
form.validate({"title" => "The Body"})
|
133
|
-
_(form.title).must_equal "The Body"
|
134
|
-
assert_nil form.position
|
135
|
-
_(form.errors.messages).must_equal({name: ["must be filled"], position: ["must be filled"]})
|
136
|
-
end
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
class OverridingAccessorsTest < BaseTest
|
141
|
-
class SongForm < TestForm
|
142
|
-
property :title
|
143
|
-
|
144
|
-
def title=(v) # used in #validate.
|
145
|
-
super v * 2
|
146
|
-
end
|
147
|
-
|
148
|
-
def title # used in #sync.
|
149
|
-
super.downcase
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
let(:song) { Song.new("Pray") }
|
154
|
-
subject { SongForm.new(song) }
|
155
|
-
|
156
|
-
# override reader for presentation.
|
157
|
-
it { _(subject.title).must_equal "pray" }
|
158
|
-
|
159
|
-
describe "#save" do
|
160
|
-
before { subject.validate("title" => "Hey Little World") }
|
161
|
-
|
162
|
-
# reader always used
|
163
|
-
it { _(subject.title).must_equal "hey little worldhey little world" }
|
164
|
-
|
165
|
-
# the reader is not used when saving/syncing.
|
166
|
-
it do
|
167
|
-
subject.save do |hash|
|
168
|
-
_(hash["title"]).must_equal "Hey Little WorldHey Little World"
|
169
|
-
end
|
170
|
-
end
|
171
|
-
|
172
|
-
# no reader or writer used when saving/syncing.
|
173
|
-
it do
|
174
|
-
song.extend(Saveable)
|
175
|
-
subject.save
|
176
|
-
_(song.title).must_equal "Hey Little WorldHey Little World"
|
177
|
-
end
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
class MethodInFormTest < MiniTest::Spec
|
182
|
-
class AlbumForm < TestForm
|
183
|
-
property :title
|
184
|
-
|
185
|
-
def title
|
186
|
-
"The Suffer And The Witness"
|
187
|
-
end
|
188
|
-
|
189
|
-
property :hit do
|
190
|
-
property :title
|
191
|
-
|
192
|
-
def title
|
193
|
-
"Drones"
|
194
|
-
end
|
195
|
-
end
|
196
|
-
end
|
197
|
-
|
198
|
-
# methods can be used instead of created accessors.
|
199
|
-
subject { AlbumForm.new(OpenStruct.new(hit: OpenStruct.new)) }
|
200
|
-
it { _(subject.title).must_equal "The Suffer And The Witness" }
|
201
|
-
it { _(subject.hit.title).must_equal "Drones" }
|
202
|
-
end
|
data/test/save_old_api.rb
DELETED
@@ -1,101 +0,0 @@
|
|
1
|
-
require "test_helper"
|
2
|
-
|
3
|
-
class SaveTest < BaseTest
|
4
|
-
Song = Struct.new(:title, :album, :composer)
|
5
|
-
Album = Struct.new(:name, :songs, :artist)
|
6
|
-
Artist = Struct.new(:name)
|
7
|
-
|
8
|
-
class AlbumForm < TestForm
|
9
|
-
property :name
|
10
|
-
validation do
|
11
|
-
required(:name).filled
|
12
|
-
end
|
13
|
-
|
14
|
-
collection :songs do
|
15
|
-
property :title
|
16
|
-
validation do
|
17
|
-
required(:title).filled
|
18
|
-
end
|
19
|
-
|
20
|
-
property :composer do
|
21
|
-
property :name
|
22
|
-
validation do
|
23
|
-
required(:name).filled
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
property :artist, save: false do
|
29
|
-
property :name
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
module Saveable
|
34
|
-
def save
|
35
|
-
@saved = true
|
36
|
-
end
|
37
|
-
|
38
|
-
def saved?
|
39
|
-
defined?(@saved) && @saved
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
let(:song) { Song.new("Broken").extend(Saveable) }
|
44
|
-
# let(:song_with_composer) { Song.new("Resist Stance", nil, composer).extend(Saveable) }
|
45
|
-
let(:composer) { Artist.new("Greg Graffin").extend(Saveable) }
|
46
|
-
let(:artist) { Artist.new("Bad Religion").extend(Saveable).extend(Saveable) }
|
47
|
-
let(:album) { Album.new("The Dissent Of Man", [song], artist).extend(Saveable) }
|
48
|
-
|
49
|
-
let(:form) { AlbumForm.new(album) }
|
50
|
-
|
51
|
-
it do
|
52
|
-
form.validate("songs" => [{"title" => "Fixed"}])
|
53
|
-
|
54
|
-
form.save
|
55
|
-
|
56
|
-
_(album.saved?).must_equal true
|
57
|
-
_(album.songs[0].title).must_equal "Fixed"
|
58
|
-
_(album.songs[0].saved?).must_equal true
|
59
|
-
assert_nil album.artist.saved?
|
60
|
-
end
|
61
|
-
|
62
|
-
describe "#sync with block" do
|
63
|
-
it do
|
64
|
-
form = AlbumForm.new(Album.new("Greatest Hits"))
|
65
|
-
|
66
|
-
form.validate(name: nil) # nil-out the title.
|
67
|
-
|
68
|
-
nested_hash = nil
|
69
|
-
form.sync do |hash|
|
70
|
-
nested_hash = hash
|
71
|
-
end
|
72
|
-
|
73
|
-
_(nested_hash).must_equal({"name" => nil, "artist" => nil})
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
# class SaveWithDynamicOptionsTest < MiniTest::Spec
|
79
|
-
# Song = Struct.new(:id, :title, :length) do
|
80
|
-
# include Saveable
|
81
|
-
# end
|
82
|
-
|
83
|
-
# class SongForm < TestForm
|
84
|
-
# property :title#, save: false
|
85
|
-
# property :length, virtual: true
|
86
|
-
# end
|
87
|
-
|
88
|
-
# let(:song) { Song.new }
|
89
|
-
# let(:form) { SongForm.new(song) }
|
90
|
-
|
91
|
-
# # we have access to original input value and outside parameters.
|
92
|
-
# it "xxx" do
|
93
|
-
# form.validate("title" => "A Poor Man's Memory", "length" => 10)
|
94
|
-
# length_seconds = 120
|
95
|
-
# form.save(length: lambda { |value, options| form.model.id = "#{value}: #{length_seconds}" })
|
96
|
-
|
97
|
-
# song.title.must_equal "A Poor Man's Memory"
|
98
|
-
# assert_nil song.length
|
99
|
-
# song.id.must_equal "10: 120"
|
100
|
-
# end
|
101
|
-
# end
|
data/test/skip_if_old_api.rb
DELETED
@@ -1,92 +0,0 @@
|
|
1
|
-
require "test_helper"
|
2
|
-
|
3
|
-
class SkipIfTest < BaseTest
|
4
|
-
let(:hit) { Song.new }
|
5
|
-
let(:album) { Album.new(nil, hit, [], nil) }
|
6
|
-
|
7
|
-
class AlbumForm < TestForm
|
8
|
-
property :title
|
9
|
-
|
10
|
-
property :hit, skip_if: ->(options) { options[:fragment]["title"] == "" } do
|
11
|
-
property :title
|
12
|
-
validation do
|
13
|
-
required(:title).filled
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
collection :songs, skip_if: :skip_song?, populate_if_empty: BaseTest::Song do
|
18
|
-
property :title
|
19
|
-
end
|
20
|
-
|
21
|
-
def skip_song?(options)
|
22
|
-
options[:fragment]["title"].nil?
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
# deserializes when present.
|
27
|
-
it do
|
28
|
-
form = AlbumForm.new(album)
|
29
|
-
_(form.validate("hit" => {"title" => "Altar Of Sacrifice"})).must_equal true
|
30
|
-
_(form.hit.title).must_equal "Altar Of Sacrifice"
|
31
|
-
end
|
32
|
-
|
33
|
-
# skips deserialization when not present.
|
34
|
-
it do
|
35
|
-
form = AlbumForm.new(Album.new)
|
36
|
-
_(form.validate("hit" => {"title" => ""})).must_equal true
|
37
|
-
assert_nil form.hit # hit hasn't been deserialised.
|
38
|
-
end
|
39
|
-
|
40
|
-
# skips deserialization when not present.
|
41
|
-
it do
|
42
|
-
form = AlbumForm.new(Album.new(nil, nil, []))
|
43
|
-
_(form.validate("songs" => [{"title" => "Waste Of Breath"}, {"title" => nil}])).must_equal true
|
44
|
-
_(form.songs.size).must_equal 1
|
45
|
-
_(form.songs[0].title).must_equal "Waste Of Breath"
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
class SkipIfAllBlankTest < BaseTest
|
50
|
-
# skip_if: :all_blank"
|
51
|
-
class AlbumForm < TestForm
|
52
|
-
collection :songs, skip_if: :all_blank, populate_if_empty: BaseTest::Song do
|
53
|
-
property :title
|
54
|
-
property :length
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
# create only one object.
|
59
|
-
it do
|
60
|
-
form = AlbumForm.new(OpenStruct.new(songs: []))
|
61
|
-
_(form.validate("songs" => [{"title" => "Apathy"}, {"title" => "", "length" => ""}])).must_equal true
|
62
|
-
_(form.songs.size).must_equal 1
|
63
|
-
_(form.songs[0].title).must_equal "Apathy"
|
64
|
-
end
|
65
|
-
|
66
|
-
it do
|
67
|
-
form = AlbumForm.new(OpenStruct.new(songs: []))
|
68
|
-
_(form.validate("songs" => [{"title" => "", "length" => ""}, {"title" => "Apathy"}])).must_equal true
|
69
|
-
_(form.songs.size).must_equal 1
|
70
|
-
_(form.songs[0].title).must_equal "Apathy"
|
71
|
-
end
|
72
|
-
|
73
|
-
it do
|
74
|
-
form = AlbumForm.new(OpenStruct.new(songs: []))
|
75
|
-
_(form.validate(:songs => [{:title=>"", :length => ""}, {:title=>"Apathy"}])).must_equal true
|
76
|
-
_(form.songs.size).must_equal 1
|
77
|
-
_(form.songs[0].title).must_equal "Apathy"
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
class InvalidOptionsCombinationTest < BaseTest
|
82
|
-
it do
|
83
|
-
assert_raises(Reform::Form::InvalidOptionsCombinationError) do
|
84
|
-
class AlbumForm < TestForm
|
85
|
-
collection :songs, skip_if: :all_blank, populator: -> {} do
|
86
|
-
property :title
|
87
|
-
property :length
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
data/test/validate_old_api.rb
DELETED
@@ -1,410 +0,0 @@
|
|
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 < TestContract
|
9
|
-
property :name
|
10
|
-
validation do
|
11
|
-
required(:name).filled
|
12
|
-
end
|
13
|
-
|
14
|
-
collection :songs do
|
15
|
-
property :title
|
16
|
-
validation do
|
17
|
-
required(:title).filled
|
18
|
-
end
|
19
|
-
|
20
|
-
property :composer do
|
21
|
-
validation do
|
22
|
-
required(:name).filled
|
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=>[\"must be filled\"], :\"songs.composer.name\"=>[\"must be filled\"]}"
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
# no configuration results in "sync" (formerly known as parse_strategy: :sync).
|
58
|
-
class ValidateWithoutConfigurationTest < MiniTest::Spec
|
59
|
-
Song = Struct.new(:title, :album, :composer)
|
60
|
-
Album = Struct.new(:name, :songs, :artist)
|
61
|
-
Artist = Struct.new(:name)
|
62
|
-
|
63
|
-
class AlbumForm < TestForm
|
64
|
-
property :name
|
65
|
-
validation do
|
66
|
-
required(:name).filled
|
67
|
-
end
|
68
|
-
|
69
|
-
collection :songs do
|
70
|
-
property :title
|
71
|
-
validation do
|
72
|
-
required(:title).filled
|
73
|
-
end
|
74
|
-
|
75
|
-
property :composer do
|
76
|
-
property :name
|
77
|
-
validation do
|
78
|
-
required(:name).filled
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
property :artist do
|
84
|
-
property :name
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
let(:song) { Song.new("Broken") }
|
89
|
-
let(:song_with_composer) { Song.new("Resist Stance", nil, composer) }
|
90
|
-
let(:composer) { Artist.new("Greg Graffin") }
|
91
|
-
let(:artist) { Artist.new("Bad Religion") }
|
92
|
-
let(:album) { Album.new("The Dissent Of Man", [song, song_with_composer], artist) }
|
93
|
-
|
94
|
-
let(:form) { AlbumForm.new(album) }
|
95
|
-
|
96
|
-
# valid.
|
97
|
-
it do
|
98
|
-
object_ids = {
|
99
|
-
song: form.songs[0].object_id, song_with_composer: form.songs[1].object_id,
|
100
|
-
artist: form.artist.object_id, composer: form.songs[1].composer.object_id
|
101
|
-
}
|
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
|
-
# model has not changed, yet.
|
125
|
-
_(album.name).must_equal "The Dissent Of Man"
|
126
|
-
_(album.songs[0].title).must_equal "Broken"
|
127
|
-
_(album.songs[1].title).must_equal "Resist Stance"
|
128
|
-
_(album.songs[1].composer.name).must_equal "Greg Graffin"
|
129
|
-
_(album.artist.name).must_equal "Bad Religion"
|
130
|
-
end
|
131
|
-
|
132
|
-
# with symbols.
|
133
|
-
it do
|
134
|
-
_(form.validate(
|
135
|
-
name: "Best Of",
|
136
|
-
songs: [{title: "The X-Creep"}, {title: "Trudging", composer: {name: "SNFU"}}],
|
137
|
-
artist: {name: "The Police"}
|
138
|
-
)).must_equal true
|
139
|
-
|
140
|
-
_(form.name).must_equal "Best Of"
|
141
|
-
_(form.songs[0].title).must_equal "The X-Creep"
|
142
|
-
_(form.songs[1].title).must_equal "Trudging"
|
143
|
-
_(form.songs[1].composer.name).must_equal "SNFU"
|
144
|
-
_(form.artist.name).must_equal "The Police"
|
145
|
-
end
|
146
|
-
|
147
|
-
# throws exception when no populators.
|
148
|
-
it do
|
149
|
-
album = Album.new("The Dissent Of Man", [])
|
150
|
-
|
151
|
-
assert_raises RuntimeError do
|
152
|
-
AlbumForm.new(album).validate(songs: {title: "Resist-Stance"})
|
153
|
-
end
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
class ValidateWithInternalPopulatorOptionTest < MiniTest::Spec
|
158
|
-
Song = Struct.new(:title, :album, :composer)
|
159
|
-
Album = Struct.new(:name, :songs, :artist)
|
160
|
-
Artist = Struct.new(:name)
|
161
|
-
|
162
|
-
class AlbumForm < TestForm
|
163
|
-
property :name
|
164
|
-
validation do
|
165
|
-
required(:name).filled
|
166
|
-
end
|
167
|
-
|
168
|
-
collection :songs,
|
169
|
-
internal_populator: ->(input, options) {
|
170
|
-
collection = options[:represented].songs
|
171
|
-
(item = collection[options[:index]]) ? item : collection.insert(options[:index], Song.new)
|
172
|
-
} do
|
173
|
-
property :title
|
174
|
-
validation do
|
175
|
-
required(:title).filled
|
176
|
-
end
|
177
|
-
|
178
|
-
property :composer, internal_populator: ->(input, options) { (item = options[:represented].composer) ? item : Artist.new } do
|
179
|
-
property :name
|
180
|
-
validation do
|
181
|
-
required(:name).filled
|
182
|
-
end
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
property :artist, internal_populator: ->(input, options) { (item = options[:represented].artist) ? item : Artist.new } do
|
187
|
-
property :name
|
188
|
-
validation do
|
189
|
-
required(:name).filled
|
190
|
-
end
|
191
|
-
end
|
192
|
-
end
|
193
|
-
|
194
|
-
let(:song) { Song.new("Broken") }
|
195
|
-
let(:song_with_composer) { Song.new("Resist Stance", nil, composer) }
|
196
|
-
let(:composer) { Artist.new("Greg Graffin") }
|
197
|
-
let(:artist) { Artist.new("Bad Religion") }
|
198
|
-
let(:album) { Album.new("The Dissent Of Man", [song, song_with_composer], artist) }
|
199
|
-
|
200
|
-
let(:form) { AlbumForm.new(album) }
|
201
|
-
|
202
|
-
# valid.
|
203
|
-
it("xxx") do
|
204
|
-
_(form.validate(
|
205
|
-
"name" => "Best Of",
|
206
|
-
"songs" => [{"title" => "Fallout"}, {"title" => "Roxanne", "composer" => {"name" => "Sting"}}],
|
207
|
-
"artist" => {"name" => "The Police"},
|
208
|
-
)).must_equal true
|
209
|
-
_(form.valid?).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
|
-
# model has not changed, yet.
|
221
|
-
_(album.name).must_equal "The Dissent Of Man"
|
222
|
-
_(album.songs[0].title).must_equal "Broken"
|
223
|
-
_(album.songs[1].title).must_equal "Resist Stance"
|
224
|
-
_(album.songs[1].composer.name).must_equal "Greg Graffin"
|
225
|
-
_(album.artist.name).must_equal "Bad Religion"
|
226
|
-
end
|
227
|
-
|
228
|
-
# invalid.
|
229
|
-
it do
|
230
|
-
_(form.validate(
|
231
|
-
"name" => "",
|
232
|
-
"songs" => [{"title" => "Fallout"}, {"title" => "Roxanne", "composer" => {"name" => ""}}],
|
233
|
-
"artist" => {"name" => ""},
|
234
|
-
)).must_equal false
|
235
|
-
_(form.valid?).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
|
-
# model has not changed, yet.
|
260
|
-
_(album.name).must_equal "The Dissent Of Man"
|
261
|
-
_(album.songs[0].title).must_equal "Broken"
|
262
|
-
_(album.songs[1].title).must_equal "Resist Stance"
|
263
|
-
_(album.songs[1].composer.name).must_equal "Greg Graffin"
|
264
|
-
_(album.songs.size).must_equal 2
|
265
|
-
_(album.artist.name).must_equal "Bad Religion"
|
266
|
-
end
|
267
|
-
|
268
|
-
# allow writeable: false even in the deserializer.
|
269
|
-
class SongForm < TestForm
|
270
|
-
property :title, deserializer: {writeable: false}
|
271
|
-
end
|
272
|
-
|
273
|
-
it do
|
274
|
-
form = SongForm.new(song = Song.new)
|
275
|
-
form.validate("title" => "Ignore me!")
|
276
|
-
assert_nil form.title
|
277
|
-
form.title = "Unopened"
|
278
|
-
form.sync # only the deserializer is marked as not-writeable.
|
279
|
-
_(song.title).must_equal "Unopened"
|
280
|
-
end
|
281
|
-
end
|
282
|
-
|
283
|
-
# # not sure if we should catch that in Reform or rather do that in disposable. this is https://github.com/trailblazer/reform/pull/104
|
284
|
-
# # describe ":populator with :empty" do
|
285
|
-
# # let(:form) {
|
286
|
-
# # Class.new(Reform::Form) do
|
287
|
-
# # collection :songs, :empty => true, :populator => lambda { |fragment, index, args|
|
288
|
-
# # songs[index] = args.binding[:form].new(Song.new)
|
289
|
-
# # } do
|
290
|
-
# # property :title
|
291
|
-
# # end
|
292
|
-
# # end
|
293
|
-
# # }
|
294
|
-
|
295
|
-
# # let(:params) {
|
296
|
-
# # {
|
297
|
-
# # "songs" => [{"title" => "Fallout"}, {"title" => "Roxanne"}]
|
298
|
-
# # }
|
299
|
-
# # }
|
300
|
-
|
301
|
-
# # subject { form.new(Album.new("Hits", [], [])) }
|
302
|
-
|
303
|
-
# # before { subject.validate(params) }
|
304
|
-
|
305
|
-
# # it { subject.songs[0].title.must_equal "Fallout" }
|
306
|
-
# # it { subject.songs[1].title.must_equal "Roxanne" }
|
307
|
-
# # end
|
308
|
-
|
309
|
-
# # test cardinalities.
|
310
|
-
# describe "with empty collection and cardinality" do
|
311
|
-
# let(:album) { Album.new }
|
312
|
-
|
313
|
-
# subject { Class.new(Reform::Form) do
|
314
|
-
# include Reform::Form::ActiveModel
|
315
|
-
# model :album
|
316
|
-
|
317
|
-
# collection :songs do
|
318
|
-
# property :title
|
319
|
-
# end
|
320
|
-
|
321
|
-
# property :hit do
|
322
|
-
# property :title
|
323
|
-
# end
|
324
|
-
|
325
|
-
# validates :songs, :length => {:minimum => 1}
|
326
|
-
# validates :hit, :presence => true
|
327
|
-
# end.new(album) }
|
328
|
-
|
329
|
-
# describe "invalid" do
|
330
|
-
# before { subject.validate({}).must_equal false }
|
331
|
-
|
332
|
-
# it do
|
333
|
-
# # ensure that only hit and songs keys are present
|
334
|
-
# subject.errors.messages.keys.sort.must_equal([:hit, :songs])
|
335
|
-
# # validate content of hit and songs keys
|
336
|
-
# subject.errors.messages[:hit].must_equal(["must be filled"])
|
337
|
-
# subject.errors.messages[:songs].first.must_match(/\Ais too short \(minimum is 1 characters?\)\z/)
|
338
|
-
# end
|
339
|
-
# end
|
340
|
-
|
341
|
-
# describe "valid" do
|
342
|
-
# let(:album) { Album.new(nil, Song.new, [Song.new("Urban Myth")]) }
|
343
|
-
|
344
|
-
# before {
|
345
|
-
# subject.validate({"songs" => [{"title"=>"Daddy, Brother, Lover, Little Boy"}], "hit" => {"title"=>"The Horse"}}).
|
346
|
-
# must_equal true
|
347
|
-
# }
|
348
|
-
|
349
|
-
# it { subject.errors.messages.must_equal({}) }
|
350
|
-
# end
|
351
|
-
# end
|
352
|
-
|
353
|
-
# # providing manual validator method allows accessing form's API.
|
354
|
-
# describe "with ::validate" do
|
355
|
-
# let(:form) {
|
356
|
-
# Class.new(Reform::Form) do
|
357
|
-
# property :title
|
358
|
-
|
359
|
-
# validate :title?
|
360
|
-
|
361
|
-
# def title?
|
362
|
-
# errors.add :title, "not lowercase" if title == "Fallout"
|
363
|
-
# end
|
364
|
-
# end
|
365
|
-
# }
|
366
|
-
|
367
|
-
# let(:params) { {"title" => "Fallout"} }
|
368
|
-
# let(:song) { Song.new("Englishman") }
|
369
|
-
|
370
|
-
# subject { form.new(song) }
|
371
|
-
|
372
|
-
# before { @res = subject.validate(params) }
|
373
|
-
|
374
|
-
# it { @res.must_equal false }
|
375
|
-
# it { subject.errors.messages.must_equal({:title=>["not lowercase"]}) }
|
376
|
-
# end
|
377
|
-
|
378
|
-
# # overriding the reader for a nested form should only be considered when rendering.
|
379
|
-
# describe "with overridden reader for nested form" do
|
380
|
-
# let(:form) {
|
381
|
-
# Class.new(Reform::Form) do
|
382
|
-
# property :band, :populate_if_empty => lambda { |*| Band.new } do
|
383
|
-
# property :label
|
384
|
-
# end
|
385
|
-
|
386
|
-
# collection :songs, :populate_if_empty => lambda { |*| Song.new } do
|
387
|
-
# property :title
|
388
|
-
# end
|
389
|
-
|
390
|
-
# def band
|
391
|
-
# raise "only call me when rendering the form!"
|
392
|
-
# end
|
393
|
-
|
394
|
-
# def songs
|
395
|
-
# raise "only call me when rendering the form!"
|
396
|
-
# end
|
397
|
-
# end.new(album)
|
398
|
-
# }
|
399
|
-
|
400
|
-
# let(:album) { Album.new }
|
401
|
-
|
402
|
-
# # don't use #artist when validating!
|
403
|
-
# it do
|
404
|
-
# form.validate("band" => {"label" => "Hellcat"}, "songs" => [{"title" => "Stand Your Ground"}, {"title" => "Otherside"}])
|
405
|
-
# form.sync
|
406
|
-
# album.band.label.must_equal "Hellcat"
|
407
|
-
# album.songs.first.title.must_equal "Stand Your Ground"
|
408
|
-
# end
|
409
|
-
# end
|
410
|
-
# end
|