reform 1.2.6 → 2.0.0.beta1
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/.travis.yml +6 -1
- data/CHANGES.md +14 -0
- data/Gemfile +3 -2
- data/README.md +225 -283
- data/Rakefile +27 -0
- data/TODO.md +12 -0
- data/database.sqlite3 +0 -0
- data/gemfiles/Gemfile.rails-3.0 +1 -0
- data/gemfiles/Gemfile.rails-3.1 +1 -0
- data/gemfiles/Gemfile.rails-3.2 +1 -0
- data/gemfiles/Gemfile.rails-4.0 +1 -0
- data/lib/reform.rb +0 -1
- data/lib/reform/contract.rb +64 -170
- data/lib/reform/contract/validate.rb +10 -13
- data/lib/reform/form.rb +74 -19
- data/lib/reform/form/active_model.rb +19 -14
- data/lib/reform/form/coercion.rb +1 -13
- data/lib/reform/form/composition.rb +2 -24
- data/lib/reform/form/multi_parameter_attributes.rb +43 -62
- data/lib/reform/form/populator.rb +85 -0
- data/lib/reform/form/prepopulate.rb +13 -43
- data/lib/reform/form/validate.rb +29 -90
- data/lib/reform/form/validation/unique_validator.rb +13 -0
- data/lib/reform/version.rb +1 -1
- data/reform.gemspec +7 -7
- data/test/active_model_test.rb +43 -0
- data/test/changed_test.rb +23 -51
- data/test/coercion_test.rb +1 -7
- data/test/composition_test.rb +128 -34
- data/test/contract_test.rb +27 -86
- data/test/feature_test.rb +43 -6
- data/test/fields_test.rb +2 -12
- data/test/form_builder_test.rb +28 -25
- data/test/form_option_test.rb +19 -0
- data/test/from_test.rb +0 -75
- data/test/inherit_test.rb +178 -117
- data/test/model_reflections_test.rb +1 -1
- data/test/populate_test.rb +226 -0
- data/test/prepopulator_test.rb +112 -0
- data/test/readable_test.rb +2 -4
- data/test/save_test.rb +56 -112
- data/test/setup_test.rb +48 -0
- data/test/skip_if_test.rb +5 -2
- data/test/skip_setter_and_getter_test.rb +54 -0
- data/test/test_helper.rb +3 -1
- data/test/uniqueness_test.rb +41 -0
- data/test/validate_test.rb +325 -289
- data/test/virtual_test.rb +1 -3
- data/test/writeable_test.rb +3 -4
- metadata +35 -39
- data/lib/reform/composition.rb +0 -63
- data/lib/reform/contract/setup.rb +0 -50
- data/lib/reform/form/changed.rb +0 -9
- data/lib/reform/form/sync.rb +0 -116
- data/lib/reform/representer.rb +0 -84
- data/test/empty_test.rb +0 -58
- data/test/form_composition_test.rb +0 -145
- data/test/nested_form_test.rb +0 -197
- data/test/prepopulate_test.rb +0 -85
- data/test/sync_option_test.rb +0 -83
- data/test/sync_test.rb +0 -56
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
|
data/test/skip_if_test.rb
CHANGED
@@ -10,10 +10,13 @@ class SkipIfTest < BaseTest
|
|
10
10
|
validates :title, presence: true
|
11
11
|
end
|
12
12
|
|
13
|
-
collection :songs, skip_if:
|
14
|
-
populate_if_empty: BaseTest::Song do
|
13
|
+
collection :songs, skip_if: :skip_song?, populate_if_empty: BaseTest::Song do
|
15
14
|
property :title
|
16
15
|
end
|
16
|
+
|
17
|
+
def skip_song?(fragment, options)
|
18
|
+
fragment["title"].nil?
|
19
|
+
end
|
17
20
|
end
|
18
21
|
|
19
22
|
|
@@ -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
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
require 'reform'
|
2
2
|
require 'minitest/autorun'
|
3
|
+
require "representable/debug"
|
4
|
+
require "pp"
|
3
5
|
|
4
6
|
class ReformSpec < MiniTest::Spec
|
5
7
|
let (:duran) { Struct.new(:name).new("Duran Duran") }
|
@@ -59,4 +61,4 @@ MiniTest::Spec.class_eval do
|
|
59
61
|
@saved
|
60
62
|
end
|
61
63
|
end
|
62
|
-
end
|
64
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
require "reform/form/validation/unique_validator.rb"
|
4
|
+
|
5
|
+
class UniquenessValidatorOnCreateTest < MiniTest::Spec
|
6
|
+
class SongForm < Reform::Form
|
7
|
+
property :title
|
8
|
+
validates :title, unique: true
|
9
|
+
end
|
10
|
+
|
11
|
+
it do
|
12
|
+
Song.delete_all
|
13
|
+
|
14
|
+
form = SongForm.new(Song.new)
|
15
|
+
form.validate("title" => "How Many Tears").must_equal true
|
16
|
+
form.save
|
17
|
+
|
18
|
+
form = SongForm.new(Song.new)
|
19
|
+
form.validate("title" => "How Many Tears").must_equal false
|
20
|
+
form.errors.to_s.must_equal "{:title=>[\"title must be unique.\"]}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class UniquenessValidatorOnUpdateTest < MiniTest::Spec
|
25
|
+
class SongForm < Reform::Form
|
26
|
+
property :title
|
27
|
+
validates :title, unique: true
|
28
|
+
end
|
29
|
+
|
30
|
+
it do
|
31
|
+
Song.delete_all
|
32
|
+
@song = Song.create(title: "How Many Tears")
|
33
|
+
|
34
|
+
form = SongForm.new(@song)
|
35
|
+
form.validate("title" => "How Many Tears").must_equal true
|
36
|
+
form.save
|
37
|
+
|
38
|
+
form = SongForm.new(@song)
|
39
|
+
form.validate("title" => "How Many Tears").must_equal true
|
40
|
+
end
|
41
|
+
end
|
data/test/validate_test.rb
CHANGED
@@ -1,362 +1,398 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
it { subject.songs[0].must_be_kind_of Reform::Form }
|
29
|
-
it { subject.songs[0].title.must_equal "Fallout" }
|
30
|
-
|
31
|
-
it { subject.songs[1].must_be_kind_of Reform::Form }
|
32
|
-
it { subject.songs[1].title.must_equal "Roxanne" }
|
33
|
-
|
34
|
-
# don't touch model.
|
35
|
-
it { hit.title.must_equal nil }
|
36
|
-
it { song1.title.must_equal nil }
|
37
|
-
it { song2.title.must_equal nil }
|
38
|
-
end
|
39
|
-
|
40
|
-
describe "not populated properly raises error" do
|
41
|
-
it do
|
42
|
-
assert_raises Reform::Form::Validate::DeserializeError do
|
43
|
-
AlbumForm.new(Album.new).validate("hit" => {"title" => "Roxanne"})
|
3
|
+
# tests: -------
|
4
|
+
# Contract
|
5
|
+
# validate
|
6
|
+
# errors
|
7
|
+
|
8
|
+
# Form
|
9
|
+
# validate
|
10
|
+
# errors
|
11
|
+
|
12
|
+
class ContractValidateTest < MiniTest::Spec
|
13
|
+
Song = Struct.new(:title, :album, :composer)
|
14
|
+
Album = Struct.new(:name, :songs, :artist)
|
15
|
+
Artist = Struct.new(:name)
|
16
|
+
|
17
|
+
class AlbumForm < Reform::Contract
|
18
|
+
property :name
|
19
|
+
validates :name, presence: true
|
20
|
+
|
21
|
+
collection :songs do
|
22
|
+
property :title
|
23
|
+
validates :title, presence: true
|
24
|
+
|
25
|
+
property :composer do
|
26
|
+
validates :name, presence: true
|
27
|
+
property :name
|
44
28
|
end
|
45
29
|
end
|
46
|
-
end
|
47
|
-
|
48
|
-
# TODO: the following tests go to populate_test.rb
|
49
|
-
describe "manual setup with populator" do
|
50
|
-
let (:form) {
|
51
|
-
Class.new(Reform::Form) do
|
52
|
-
property :hit, :populator => lambda { |fragment, args|
|
53
|
-
puts "******************* #{fragment}"
|
54
|
-
|
55
|
-
hit or self.hit = args.binding[:form].new(Song.new)
|
56
|
-
} do
|
57
|
-
property :title
|
58
|
-
end
|
59
|
-
end
|
60
|
-
}
|
61
|
-
|
62
|
-
let (:params) {
|
63
|
-
{
|
64
|
-
"hit" => {"title" => "Roxanne"},
|
65
|
-
# "songs" => [{"title" => "Fallout"}, {"title" => "Roxanne"}]
|
66
|
-
}
|
67
|
-
}
|
68
30
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
it { subject.hit.title.must_equal "Roxanne" }
|
31
|
+
property :artist do
|
32
|
+
property :name
|
33
|
+
end
|
74
34
|
end
|
75
35
|
|
36
|
+
let (:song) { Song.new("Broken") }
|
37
|
+
let (:song_with_composer) { Song.new("Resist Stance", nil, composer) }
|
38
|
+
let (:composer) { Artist.new("Greg Graffin") }
|
39
|
+
let (:artist) { Artist.new("Bad Religion") }
|
40
|
+
let (:album) { Album.new("The Dissent Of Man", [song, song_with_composer], artist) }
|
76
41
|
|
77
|
-
|
78
|
-
let (:form) {
|
79
|
-
Class.new(Reform::Form) do
|
80
|
-
collection :songs, :populator => lambda { |fragment, index, args|
|
81
|
-
songs[index] or songs[index] = args.binding[:form].new(Song.new)
|
82
|
-
} do
|
83
|
-
property :title
|
84
|
-
end
|
85
|
-
end
|
86
|
-
}
|
87
|
-
|
88
|
-
let (:params) {
|
89
|
-
{
|
90
|
-
"songs" => [{"title" => "Fallout"}, {"title" => "Roxanne"}]
|
91
|
-
}
|
92
|
-
}
|
93
|
-
let (:song) { Song.new("Englishman") }
|
94
|
-
|
95
|
-
subject { form.new(Album.new("Hits", nil, [song])) }
|
96
|
-
|
97
|
-
before { subject.validate(params) }
|
42
|
+
let (:form) { AlbumForm.new(album) }
|
98
43
|
|
99
|
-
|
100
|
-
|
101
|
-
|
44
|
+
# valid
|
45
|
+
it do
|
46
|
+
form.validate.must_equal true
|
47
|
+
form.errors.messages.inspect.must_equal "{}"
|
102
48
|
end
|
103
49
|
|
50
|
+
# invalid
|
51
|
+
it do
|
52
|
+
album.songs[1].composer.name = nil
|
53
|
+
album.name = nil
|
104
54
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
# collection :songs, :empty => true, :populator => lambda { |fragment, index, args|
|
110
|
-
# songs[index] = args.binding[:form].new(Song.new)
|
111
|
-
# } do
|
112
|
-
# property :title
|
113
|
-
# end
|
114
|
-
# end
|
115
|
-
# }
|
55
|
+
form.validate.must_equal false
|
56
|
+
form.errors.messages.inspect.must_equal "{:\"songs.composer.name\"=>[\"can't be blank\"], :name=>[\"can't be blank\"]}"
|
57
|
+
end
|
58
|
+
end
|
116
59
|
|
117
|
-
# let (:params) {
|
118
|
-
# {
|
119
|
-
# "songs" => [{"title" => "Fallout"}, {"title" => "Roxanne"}]
|
120
|
-
# }
|
121
|
-
# }
|
122
60
|
|
123
|
-
|
61
|
+
# no configuration results in "sync" (formerly known as parse_strategy: :sync).
|
62
|
+
class ValidateWithoutConfigurationTest < MiniTest::Spec
|
63
|
+
Song = Struct.new(:title, :album, :composer)
|
64
|
+
Album = Struct.new(:name, :songs, :artist)
|
65
|
+
Artist = Struct.new(:name)
|
124
66
|
|
125
|
-
|
67
|
+
class AlbumForm < Reform::Form
|
68
|
+
property :name
|
69
|
+
validates :name, presence: true
|
126
70
|
|
127
|
-
|
128
|
-
# it { subject.songs[1].title.must_equal "Roxanne" }
|
129
|
-
# end
|
71
|
+
collection :songs do
|
130
72
|
|
73
|
+
property :title
|
74
|
+
validates :title, presence: true
|
131
75
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
collection :songs, :populate_if_empty => Song do
|
136
|
-
property :title
|
137
|
-
end
|
76
|
+
property :composer do
|
77
|
+
property :name
|
78
|
+
validates :name, presence: true
|
138
79
|
end
|
139
|
-
|
140
|
-
|
141
|
-
let (:params) {
|
142
|
-
{
|
143
|
-
"songs" => [{"title" => "Fallout"}, {"title" => "Roxanne"}]
|
144
|
-
}
|
145
|
-
}
|
146
|
-
let (:song) { Song.new("Englishman") }
|
147
|
-
|
148
|
-
subject { form.new(Album.new("Hits", nil, [song])) }
|
80
|
+
end
|
149
81
|
|
150
|
-
|
82
|
+
property :artist do
|
83
|
+
property :name
|
84
|
+
end
|
85
|
+
end
|
151
86
|
|
152
|
-
|
153
|
-
|
154
|
-
|
87
|
+
let (:song) { Song.new("Broken") }
|
88
|
+
let (:song_with_composer) { Song.new("Resist Stance", nil, composer) }
|
89
|
+
let (:composer) { Artist.new("Greg Graffin") }
|
90
|
+
let (:artist) { Artist.new("Bad Religion") }
|
91
|
+
let (:album) { Album.new("The Dissent Of Man", [song, song_with_composer], artist) }
|
92
|
+
|
93
|
+
let (:form) { AlbumForm.new(album) }
|
94
|
+
|
95
|
+
# valid.
|
96
|
+
it do
|
97
|
+
object_ids = {song: form.songs[0].object_id, song_with_composer: form.songs[1].object_id,
|
98
|
+
artist: form.artist.object_id, composer: form.songs[1].composer.object_id}
|
99
|
+
|
100
|
+
form.validate(
|
101
|
+
"name" => "Best Of",
|
102
|
+
"songs" => [{"title" => "Fallout"}, {"title" => "Roxanne", "composer" => {"name" => "Sting"}}],
|
103
|
+
"artist" => {"name" => "The Police"},
|
104
|
+
).must_equal true
|
105
|
+
|
106
|
+
form.errors.messages.inspect.must_equal "{}"
|
107
|
+
|
108
|
+
# form has updated.
|
109
|
+
form.name.must_equal "Best Of"
|
110
|
+
form.songs[0].title.must_equal "Fallout"
|
111
|
+
form.songs[1].title.must_equal "Roxanne"
|
112
|
+
form.songs[1].composer.name.must_equal "Sting"
|
113
|
+
form.artist.name.must_equal "The Police"
|
114
|
+
|
115
|
+
# objects are still the same.
|
116
|
+
form.songs[0].object_id.must_equal object_ids[:song]
|
117
|
+
form.songs[1].object_id.must_equal object_ids[:song_with_composer]
|
118
|
+
form.songs[1].composer.object_id.must_equal object_ids[:composer]
|
119
|
+
form.artist.object_id.must_equal object_ids[:artist]
|
120
|
+
|
121
|
+
|
122
|
+
# model has not changed, yet.
|
123
|
+
album.name.must_equal "The Dissent Of Man"
|
124
|
+
album.songs[0].title.must_equal "Broken"
|
125
|
+
album.songs[1].title.must_equal "Resist Stance"
|
126
|
+
album.songs[1].composer.name.must_equal "Greg Graffin"
|
127
|
+
album.artist.name.must_equal "Bad Religion"
|
155
128
|
end
|
129
|
+
end
|
156
130
|
|
131
|
+
class ValidateWithDeserializerOptionTest < MiniTest::Spec
|
132
|
+
Song = Struct.new(:title, :album, :composer)
|
133
|
+
Album = Struct.new(:name, :songs, :artist)
|
134
|
+
Artist = Struct.new(:name)
|
157
135
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
property :hit, :populate_if_empty => lambda { |fragment, args| Song.new } do
|
162
|
-
property :title
|
163
|
-
end
|
136
|
+
class AlbumForm < Reform::Form
|
137
|
+
property :name
|
138
|
+
validates :name, presence: true
|
164
139
|
|
165
|
-
|
166
|
-
|
167
|
-
|
140
|
+
collection :songs,
|
141
|
+
deserializer: {instance: lambda { |fragment, index, options|
|
142
|
+
collection = options.binding.get
|
143
|
+
(item = collection[index]) ? item : collection.insert(index, Song.new) },
|
144
|
+
setter: nil} do
|
168
145
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
146
|
+
property :title
|
147
|
+
validates :title, presence: true
|
148
|
+
|
149
|
+
property :composer, deserializer: { instance: lambda { |fragment, options| (item = options.binding.get) ? item : Artist.new } } do
|
150
|
+
property :name
|
151
|
+
validates :name, presence: true
|
174
152
|
end
|
175
|
-
|
153
|
+
end
|
176
154
|
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
}
|
183
|
-
}
|
155
|
+
property :artist, deserializer: { instance: lambda { |fragment, options| (item = options.binding.get) ? item : Artist.new } } do
|
156
|
+
property :name
|
157
|
+
validates :name, presence: true
|
158
|
+
end
|
159
|
+
end
|
184
160
|
|
185
|
-
|
186
|
-
|
187
|
-
|
161
|
+
let (:song) { Song.new("Broken") }
|
162
|
+
let (:song_with_composer) { Song.new("Resist Stance", nil, composer) }
|
163
|
+
let (:composer) { Artist.new("Greg Graffin") }
|
164
|
+
let (:artist) { Artist.new("Bad Religion") }
|
165
|
+
let (:album) { Album.new("The Dissent Of Man", [song, song_with_composer], artist) }
|
166
|
+
|
167
|
+
let (:form) { AlbumForm.new(album) }
|
168
|
+
|
169
|
+
# valid.
|
170
|
+
it("xxx") do
|
171
|
+
form.validate(
|
172
|
+
"name" => "Best Of",
|
173
|
+
"songs" => [{"title" => "Fallout"}, {"title" => "Roxanne", "composer" => {"name" => "Sting"}}],
|
174
|
+
"artist" => {"name" => "The Police"},
|
175
|
+
).must_equal true
|
176
|
+
|
177
|
+
form.errors.messages.inspect.must_equal "{}"
|
178
|
+
|
179
|
+
# form has updated.
|
180
|
+
form.name.must_equal "Best Of"
|
181
|
+
form.songs[0].title.must_equal "Fallout"
|
182
|
+
form.songs[1].title.must_equal "Roxanne"
|
183
|
+
form.songs[1].composer.name.must_equal "Sting"
|
184
|
+
form.artist.name.must_equal "The Police"
|
185
|
+
|
186
|
+
|
187
|
+
# model has not changed, yet.
|
188
|
+
album.name.must_equal "The Dissent Of Man"
|
189
|
+
album.songs[0].title.must_equal "Broken"
|
190
|
+
album.songs[1].title.must_equal "Resist Stance"
|
191
|
+
album.songs[1].composer.name.must_equal "Greg Graffin"
|
192
|
+
album.artist.name.must_equal "Bad Religion"
|
193
|
+
end
|
188
194
|
|
189
|
-
|
195
|
+
# invalid.
|
196
|
+
it do
|
197
|
+
form.validate(
|
198
|
+
"name" => "",
|
199
|
+
"songs" => [{"title" => "Fallout"}, {"title" => "Roxanne", "composer" => {"name" => ""}}],
|
200
|
+
"artist" => {"name" => ""},
|
201
|
+
).must_equal false
|
190
202
|
|
191
|
-
|
192
|
-
|
193
|
-
it { subject.songs[1].title.must_equal "Roxanne" }
|
203
|
+
form.errors.messages.inspect.must_equal "{:\"songs.composer.name\"=>[\"can't be blank\"], :\"artist.name\"=>[\"can't be blank\"], :name=>[\"can't be blank\"]}"
|
204
|
+
end
|
194
205
|
|
195
|
-
|
196
|
-
|
197
|
-
|
206
|
+
# adding to collection via :instance.
|
207
|
+
# valid.
|
208
|
+
it do
|
209
|
+
form.validate(
|
210
|
+
"songs" => [{"title" => "Fallout"}, {"title" => "Roxanne"}, {"title" => "Rime Of The Ancient Mariner"}],
|
211
|
+
).must_equal true
|
212
|
+
|
213
|
+
form.errors.messages.inspect.must_equal "{}"
|
214
|
+
|
215
|
+
# form has updated.
|
216
|
+
form.name.must_equal "The Dissent Of Man"
|
217
|
+
form.songs[0].title.must_equal "Fallout"
|
218
|
+
form.songs[1].title.must_equal "Roxanne"
|
219
|
+
form.songs[1].composer.name.must_equal "Greg Graffin"
|
220
|
+
form.songs[1].title.must_equal "Roxanne"
|
221
|
+
form.songs[2].title.must_equal "Rime Of The Ancient Mariner" # new song added.
|
222
|
+
form.songs.size.must_equal 3
|
223
|
+
form.artist.name.must_equal "Bad Religion"
|
224
|
+
|
225
|
+
|
226
|
+
# model has not changed, yet.
|
227
|
+
album.name.must_equal "The Dissent Of Man"
|
228
|
+
album.songs[0].title.must_equal "Broken"
|
229
|
+
album.songs[1].title.must_equal "Resist Stance"
|
230
|
+
album.songs[1].composer.name.must_equal "Greg Graffin"
|
231
|
+
album.songs.size.must_equal 2
|
232
|
+
album.artist.name.must_equal "Bad Religion"
|
233
|
+
end
|
198
234
|
|
199
|
-
it { subject.band.label.name.must_equal "Epitaph" }
|
200
235
|
|
236
|
+
# allow writeable: false even in the deserializer.
|
237
|
+
class SongForm < Reform::Form
|
238
|
+
property :title, deserializer: {writeable: false}
|
239
|
+
end
|
201
240
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
241
|
+
it do
|
242
|
+
form = SongForm.new(song = Song.new)
|
243
|
+
form.validate("title" => "Ignore me!")
|
244
|
+
form.title.must_equal nil
|
245
|
+
form.title = "Unopened"
|
246
|
+
form.sync # only the deserializer is marked as not-writeable.
|
247
|
+
song.title.must_equal "Unopened"
|
248
|
+
end
|
249
|
+
end
|
206
250
|
|
207
|
-
before { subject.validate(params) }
|
208
251
|
|
209
|
-
|
210
|
-
|
211
|
-
|
252
|
+
# # not sure if we should catch that in Reform or rather do that in disposable. this is https://github.com/apotonick/reform/pull/104
|
253
|
+
# # describe ":populator with :empty" do
|
254
|
+
# # let (:form) {
|
255
|
+
# # Class.new(Reform::Form) do
|
256
|
+
# # collection :songs, :empty => true, :populator => lambda { |fragment, index, args|
|
257
|
+
# # songs[index] = args.binding[:form].new(Song.new)
|
258
|
+
# # } do
|
259
|
+
# # property :title
|
260
|
+
# # end
|
261
|
+
# # end
|
262
|
+
# # }
|
212
263
|
|
264
|
+
# # let (:params) {
|
265
|
+
# # {
|
266
|
+
# # "songs" => [{"title" => "Fallout"}, {"title" => "Roxanne"}]
|
267
|
+
# # }
|
268
|
+
# # }
|
213
269
|
|
214
|
-
|
215
|
-
let (:form) {
|
216
|
-
Class.new(Reform::Form) do
|
217
|
-
property :hit, :populate_if_empty => Song do
|
218
|
-
property :title
|
219
|
-
end
|
220
|
-
end
|
221
|
-
}
|
270
|
+
# # subject { form.new(Album.new("Hits", [], [])) }
|
222
271
|
|
223
|
-
|
224
|
-
{
|
225
|
-
"hit" => {"title" => "Roxanne"},
|
226
|
-
}
|
227
|
-
}
|
272
|
+
# # before { subject.validate(params) }
|
228
273
|
|
229
|
-
|
230
|
-
|
274
|
+
# # it { subject.songs[0].title.must_equal "Fallout" }
|
275
|
+
# # it { subject.songs[1].title.must_equal "Roxanne" }
|
276
|
+
# # end
|
231
277
|
|
232
|
-
before { subject.validate(params) }
|
233
278
|
|
234
|
-
|
235
|
-
|
279
|
+
# # test cardinalities.
|
280
|
+
# describe "with empty collection and cardinality" do
|
281
|
+
# let (:album) { Album.new }
|
236
282
|
|
283
|
+
# subject { Class.new(Reform::Form) do
|
284
|
+
# include Reform::Form::ActiveModel
|
285
|
+
# model :album
|
237
286
|
|
287
|
+
# collection :songs do
|
288
|
+
# property :title
|
289
|
+
# end
|
238
290
|
|
239
|
-
|
240
|
-
|
241
|
-
|
291
|
+
# property :hit do
|
292
|
+
# property :title
|
293
|
+
# end
|
242
294
|
|
243
|
-
|
244
|
-
|
245
|
-
|
295
|
+
# validates :songs, :length => {:minimum => 1}
|
296
|
+
# validates :hit, :presence => true
|
297
|
+
# end.new(album) }
|
246
298
|
|
247
|
-
collection :songs do
|
248
|
-
property :title
|
249
|
-
end
|
250
299
|
|
251
|
-
|
252
|
-
|
253
|
-
end
|
300
|
+
# describe "invalid" do
|
301
|
+
# before { subject.validate({}).must_equal false }
|
254
302
|
|
255
|
-
|
256
|
-
|
257
|
-
|
303
|
+
# it do
|
304
|
+
# # ensure that only hit and songs keys are present
|
305
|
+
# subject.errors.messages.keys.sort.must_equal([:hit, :songs])
|
306
|
+
# # validate content of hit and songs keys
|
307
|
+
# subject.errors.messages[:hit].must_equal(["can't be blank"])
|
308
|
+
# subject.errors.messages[:songs].first.must_match(/\Ais too short \(minimum is 1 characters?\)\z/)
|
309
|
+
# end
|
310
|
+
# end
|
258
311
|
|
259
312
|
|
260
|
-
|
261
|
-
|
313
|
+
# describe "valid" do
|
314
|
+
# let (:album) { Album.new(nil, Song.new, [Song.new("Urban Myth")]) }
|
262
315
|
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
316
|
+
# before {
|
317
|
+
# subject.validate({"songs" => [{"title"=>"Daddy, Brother, Lover, Little Boy"}], "hit" => {"title"=>"The Horse"}}).
|
318
|
+
# must_equal true
|
319
|
+
# }
|
267
320
|
|
321
|
+
# it { subject.errors.messages.must_equal({}) }
|
322
|
+
# end
|
323
|
+
# end
|
268
324
|
|
269
|
-
describe "valid" do
|
270
|
-
let (:album) { Album.new(nil, Song.new, [Song.new("Urban Myth")]) }
|
271
325
|
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
326
|
+
# describe "with symbols" do
|
327
|
+
# let (:album) { OpenStruct.new(:band => OpenStruct.new(:label => OpenStruct.new(:name => "Epitaph"))) }
|
328
|
+
# subject { ErrorsTest::AlbumForm.new(album) }
|
329
|
+
# let (:params) { {:band => {:label => {:name => "Stiff"}}, :title => "House Of Fun"} }
|
276
330
|
|
277
|
-
|
278
|
-
|
279
|
-
|
331
|
+
# before {
|
332
|
+
# subject.validate(params).must_equal true
|
333
|
+
# }
|
280
334
|
|
335
|
+
# it { subject.band.label.name.must_equal "Stiff" }
|
336
|
+
# it { subject.title.must_equal "House Of Fun" }
|
337
|
+
# end
|
281
338
|
|
282
|
-
describe "with symbols" do
|
283
|
-
let (:album) { OpenStruct.new(:band => OpenStruct.new(:label => OpenStruct.new(:name => "Epitaph"))) }
|
284
|
-
subject { ErrorsTest::AlbumForm.new(album) }
|
285
|
-
let (:params) { {:band => {:label => {:name => "Stiff"}}, :title => "House Of Fun"} }
|
286
339
|
|
287
|
-
|
288
|
-
|
289
|
-
|
340
|
+
# # providing manual validator method allows accessing form's API.
|
341
|
+
# describe "with ::validate" do
|
342
|
+
# let (:form) {
|
343
|
+
# Class.new(Reform::Form) do
|
344
|
+
# property :title
|
290
345
|
|
291
|
-
|
292
|
-
it { subject.title.must_equal "House Of Fun" }
|
293
|
-
end
|
346
|
+
# validate :title?
|
294
347
|
|
348
|
+
# def title?
|
349
|
+
# errors.add :title, "not lowercase" if title == "Fallout"
|
350
|
+
# end
|
351
|
+
# end
|
352
|
+
# }
|
295
353
|
|
296
|
-
|
297
|
-
|
298
|
-
let (:form) {
|
299
|
-
Class.new(Reform::Form) do
|
300
|
-
property :title
|
354
|
+
# let (:params) { {"title" => "Fallout"} }
|
355
|
+
# let (:song) { Song.new("Englishman") }
|
301
356
|
|
302
|
-
|
357
|
+
# subject { form.new(song) }
|
303
358
|
|
304
|
-
|
305
|
-
errors.add :title, "not lowercase" if title == "Fallout"
|
306
|
-
end
|
307
|
-
end
|
308
|
-
}
|
359
|
+
# before { @res = subject.validate(params) }
|
309
360
|
|
310
|
-
|
311
|
-
|
361
|
+
# it { @res.must_equal false }
|
362
|
+
# it { subject.errors.messages.must_equal({:title=>["not lowercase"]}) }
|
363
|
+
# end
|
312
364
|
|
313
|
-
subject { form.new(song) }
|
314
365
|
|
315
|
-
|
366
|
+
# # overriding the reader for a nested form should only be considered when rendering.
|
367
|
+
# describe "with overridden reader for nested form" do
|
368
|
+
# let (:form) {
|
369
|
+
# Class.new(Reform::Form) do
|
370
|
+
# property :band, :populate_if_empty => lambda { |*| Band.new } do
|
371
|
+
# property :label
|
372
|
+
# end
|
316
373
|
|
317
|
-
|
318
|
-
|
319
|
-
|
374
|
+
# collection :songs, :populate_if_empty => lambda { |*| Song.new } do
|
375
|
+
# property :title
|
376
|
+
# end
|
320
377
|
|
378
|
+
# def band
|
379
|
+
# raise "only call me when rendering the form!"
|
380
|
+
# end
|
321
381
|
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
property :label
|
328
|
-
end
|
329
|
-
|
330
|
-
collection :songs, :populate_if_empty => lambda { |*| Song.new } do
|
331
|
-
property :title
|
332
|
-
end
|
333
|
-
|
334
|
-
def band
|
335
|
-
raise "only call me when rendering the form!"
|
336
|
-
end
|
337
|
-
|
338
|
-
def songs
|
339
|
-
raise "only call me when rendering the form!"
|
340
|
-
end
|
341
|
-
end.new(album)
|
342
|
-
}
|
343
|
-
|
344
|
-
let (:album) { Album.new }
|
345
|
-
|
346
|
-
# don't use #artist when validating!
|
347
|
-
it do
|
348
|
-
form.validate("band" => {"label" => "Hellcat"}, "songs" => [{"title" => "Stand Your Ground"}, {"title" => "Otherside"}])
|
349
|
-
form.sync
|
350
|
-
album.band.label.must_equal "Hellcat"
|
351
|
-
album.songs.first.title.must_equal "Stand Your Ground"
|
352
|
-
end
|
353
|
-
end
|
354
|
-
end
|
382
|
+
# def songs
|
383
|
+
# raise "only call me when rendering the form!"
|
384
|
+
# end
|
385
|
+
# end.new(album)
|
386
|
+
# }
|
355
387
|
|
356
|
-
#
|
357
|
-
# title=(params[:title])
|
358
|
-
# song.validate(params[:song], errors)
|
388
|
+
# let (:album) { Album.new }
|
359
389
|
|
360
|
-
#
|
361
|
-
#
|
362
|
-
#
|
390
|
+
# # don't use #artist when validating!
|
391
|
+
# it do
|
392
|
+
# form.validate("band" => {"label" => "Hellcat"}, "songs" => [{"title" => "Stand Your Ground"}, {"title" => "Otherside"}])
|
393
|
+
# form.sync
|
394
|
+
# album.band.label.must_equal "Hellcat"
|
395
|
+
# album.songs.first.title.must_equal "Stand Your Ground"
|
396
|
+
# end
|
397
|
+
# end
|
398
|
+
# end
|