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
@@ -0,0 +1,55 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class CustomerErrorTest < MiniTest::Spec
|
4
|
+
let(:key) { :name }
|
5
|
+
let(:error_text) { "text2" }
|
6
|
+
let(:starting_error) { [OpenStruct.new(errors: {title: ["text1"]})] }
|
7
|
+
|
8
|
+
let(:custom_error) { Reform::Contract::CustomError.new(key, error_text, @results) }
|
9
|
+
|
10
|
+
before { @results = starting_error }
|
11
|
+
|
12
|
+
it "base class structure" do
|
13
|
+
assert_equal custom_error.success?, false
|
14
|
+
assert_equal custom_error.failure?, true
|
15
|
+
assert_equal custom_error.errors, key => [error_text]
|
16
|
+
assert_equal custom_error.messages, key => [error_text]
|
17
|
+
assert_equal custom_error.hint, {}
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "updates @results accordingly" do
|
21
|
+
it "add new key" do
|
22
|
+
custom_error
|
23
|
+
|
24
|
+
assert_equal @results.size, 2
|
25
|
+
errors = @results.map(&:errors)
|
26
|
+
|
27
|
+
assert_equal errors[0], starting_error.first.errors
|
28
|
+
assert_equal errors[1], custom_error.errors
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "when key error already exists in @results" do
|
32
|
+
let(:key) { :title }
|
33
|
+
|
34
|
+
it "merge errors text" do
|
35
|
+
custom_error
|
36
|
+
|
37
|
+
assert_equal @results.size, 1
|
38
|
+
|
39
|
+
assert_equal @results.first.errors.values, [%w[text1 text2]]
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "add error text is already" do
|
43
|
+
let(:error_text) { "text1" }
|
44
|
+
|
45
|
+
it 'does not create duplicates' do
|
46
|
+
custom_error
|
47
|
+
|
48
|
+
assert_equal @results.size, 1
|
49
|
+
|
50
|
+
assert_equal @results.first.errors.values, [%w[text1]]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/test/contract_test.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "test_helper"
|
2
2
|
|
3
3
|
class ContractTest < MiniTest::Spec
|
4
4
|
Song = Struct.new(:title, :album, :composer)
|
@@ -16,19 +16,19 @@ class ContractTest < MiniTest::Spec
|
|
16
16
|
properties :year, :style, readable: false
|
17
17
|
|
18
18
|
validation do
|
19
|
-
required(:name).filled
|
19
|
+
params { required(:name).filled }
|
20
20
|
end
|
21
21
|
|
22
22
|
collection :songs do
|
23
23
|
property :title
|
24
24
|
validation do
|
25
|
-
required(:title).filled
|
25
|
+
params { required(:title).filled }
|
26
26
|
end
|
27
27
|
|
28
28
|
property :composer do
|
29
29
|
property :name
|
30
30
|
validation do
|
31
|
-
required(:name).filled
|
31
|
+
params { required(:name).filled }
|
32
32
|
end
|
33
33
|
end
|
34
34
|
end
|
@@ -36,42 +36,42 @@ class ContractTest < MiniTest::Spec
|
|
36
36
|
property :artist, form: ArtistForm
|
37
37
|
end
|
38
38
|
|
39
|
-
let
|
40
|
-
let
|
41
|
-
let
|
42
|
-
let
|
43
|
-
let
|
39
|
+
let(:song) { Song.new("Broken") }
|
40
|
+
let(:song_with_composer) { Song.new("Resist Stance", nil, composer) }
|
41
|
+
let(:composer) { Artist.new("Greg Graffin") }
|
42
|
+
let(:artist) { Artist.new("Bad Religion") }
|
43
|
+
let(:album) { Album.new("The Dissent Of Man", 123, [song, song_with_composer], artist) }
|
44
44
|
|
45
|
-
let
|
45
|
+
let(:form) { AlbumForm.new(album) }
|
46
46
|
|
47
47
|
# accept `property form: SongForm`.
|
48
48
|
it do
|
49
|
-
form.artist.
|
49
|
+
assert form.artist.is_a? ArtistForm
|
50
50
|
end
|
51
51
|
|
52
52
|
describe ".properties" do
|
53
53
|
it "defines a property when called with one argument" do
|
54
|
-
form
|
54
|
+
assert_respond_to form, :duration
|
55
55
|
end
|
56
56
|
|
57
57
|
it "defines several properties when called with multiple arguments" do
|
58
|
-
form
|
59
|
-
form
|
58
|
+
assert_respond_to form, :year
|
59
|
+
assert_respond_to form, :style
|
60
60
|
end
|
61
61
|
|
62
62
|
it "passes options to each property when options are provided" do
|
63
63
|
readable = AlbumForm.new(album).options_for(:style)[:readable]
|
64
|
-
readable
|
64
|
+
assert_equal readable, false
|
65
65
|
end
|
66
66
|
|
67
67
|
it "returns the list of defined properties" do
|
68
68
|
returned_value = AlbumForm.properties(:hello, :world, virtual: true)
|
69
|
-
returned_value
|
69
|
+
assert_equal returned_value, %i[hello world]
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
73
73
|
describe "#options_for" do
|
74
|
-
it { AlbumForm.options_for(:name).extend(Declarative::Inspect).inspect
|
75
|
-
it { AlbumForm.new(album).options_for(:name).extend(Declarative::Inspect).inspect
|
74
|
+
it { assert_equal AlbumForm.options_for(:name).extend(Declarative::Inspect).inspect, "#<Disposable::Twin::Definition: @options={:private_name=>:name, :name=>\"name\"}>" }
|
75
|
+
it { assert_equal AlbumForm.new(album).options_for(:name).extend(Declarative::Inspect).inspect, "#<Disposable::Twin::Definition: @options={:private_name=>:name, :name=>\"name\"}>" }
|
76
76
|
end
|
77
77
|
end
|
data/test/default_test.rb
CHANGED
@@ -16,7 +16,7 @@ class DefaultTest < Minitest::Spec
|
|
16
16
|
it do
|
17
17
|
form = AlbumForm.new(Album.new(nil, [Song.new]))
|
18
18
|
|
19
|
-
form.name
|
20
|
-
form.songs[0].title
|
19
|
+
assert_equal form.name, "Wrong"
|
20
|
+
assert_equal form.songs[0].title, "It's Catching Up"
|
21
21
|
end
|
22
|
-
end
|
22
|
+
end
|
data/test/deserialize_test.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "test_helper"
|
2
2
|
require "representable/json"
|
3
3
|
|
4
4
|
class DeserializeTest < MiniTest::Spec
|
@@ -10,7 +10,7 @@ class DeserializeTest < MiniTest::Spec
|
|
10
10
|
module Json
|
11
11
|
def deserialize(params)
|
12
12
|
deserializer.new(self).
|
13
|
-
|
13
|
+
# extend(Representable::Debug).
|
14
14
|
from_json(params)
|
15
15
|
end
|
16
16
|
|
@@ -18,7 +18,7 @@ class DeserializeTest < MiniTest::Spec
|
|
18
18
|
Disposable::Rescheme.from(self.class,
|
19
19
|
include: [Representable::JSON],
|
20
20
|
superclass: Representable::Decorator,
|
21
|
-
definitions_from:
|
21
|
+
definitions_from: ->(inline) { inline.definitions },
|
22
22
|
options_from: :deserializer,
|
23
23
|
exclude_options: [:populator]
|
24
24
|
)
|
@@ -26,15 +26,13 @@ class DeserializeTest < MiniTest::Spec
|
|
26
26
|
end
|
27
27
|
include Json
|
28
28
|
|
29
|
-
|
30
29
|
property :title
|
31
30
|
property :artist, populate_if_empty: Artist do
|
32
31
|
property :name
|
33
32
|
end
|
34
33
|
end
|
35
34
|
|
36
|
-
|
37
|
-
let (:artist) { Artist.new("A-ha") }
|
35
|
+
let(:artist) { Artist.new("A-ha") }
|
38
36
|
it do
|
39
37
|
artist_id = artist.object_id
|
40
38
|
|
@@ -43,9 +41,9 @@ class DeserializeTest < MiniTest::Spec
|
|
43
41
|
|
44
42
|
form.validate(json)
|
45
43
|
|
46
|
-
form.title
|
47
|
-
form.artist.name
|
48
|
-
form.artist.model.object_id
|
44
|
+
assert_equal form.title, "Apocalypse Soon"
|
45
|
+
assert_equal form.artist.name, "Mute"
|
46
|
+
assert_equal form.artist.model.object_id, artist_id
|
49
47
|
end
|
50
48
|
|
51
49
|
describe "infering the deserializer from another form should NOT copy its populators" do
|
@@ -62,14 +60,13 @@ class DeserializeTest < MiniTest::Spec
|
|
62
60
|
# also tests the Form#deserializer API. # FIXME.
|
63
61
|
it "uses deserializer inferred from JsonAlbumForm but deserializes/populates to CompilationForm" do
|
64
62
|
form = CompilationForm.new(Album.new)
|
65
|
-
form.validate("artist"=> {"name" => "Horowitz"}) # the deserializer doesn't know symbols.
|
63
|
+
form.validate("artist" => {"name" => "Horowitz"}) # the deserializer doesn't know symbols.
|
66
64
|
form.sync
|
67
|
-
form.artist.model
|
65
|
+
assert_equal form.artist.model, Artist.new("Horowitz", %{{"name"=>"Horowitz"}})
|
68
66
|
end
|
69
67
|
end
|
70
68
|
end
|
71
69
|
|
72
|
-
|
73
70
|
class ValidateWithBlockTest < MiniTest::Spec
|
74
71
|
Song = Struct.new(:title, :album, :composer)
|
75
72
|
Album = Struct.new(:title, :artist)
|
@@ -90,15 +87,15 @@ class ValidateWithBlockTest < MiniTest::Spec
|
|
90
87
|
deserializer = Disposable::Rescheme.from(AlbumForm,
|
91
88
|
include: [Representable::JSON],
|
92
89
|
superclass: Representable::Decorator,
|
93
|
-
definitions_from:
|
90
|
+
definitions_from: ->(inline) { inline.definitions },
|
94
91
|
options_from: :deserializer
|
95
92
|
)
|
96
93
|
|
97
|
-
form.validate(json)
|
94
|
+
assert form.validate(json) { |params|
|
98
95
|
deserializer.new(form).from_json(params)
|
99
|
-
|
96
|
+
} # with block must return result, too.
|
100
97
|
|
101
|
-
form.title
|
102
|
-
form.artist.name
|
98
|
+
assert_equal form.title, "Apocalypse Soon"
|
99
|
+
assert_equal form.artist.name, "Mute"
|
103
100
|
end
|
104
101
|
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'reform/form/dry'
|
3
|
+
|
4
|
+
class DocsDryVTest < Minitest::Spec
|
5
|
+
#:basic
|
6
|
+
class AlbumForm < Reform::Form
|
7
|
+
feature Reform::Form::Dry
|
8
|
+
|
9
|
+
property :name
|
10
|
+
|
11
|
+
validation do
|
12
|
+
params do
|
13
|
+
required(:name).filled
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
#:basic end
|
18
|
+
|
19
|
+
it 'validates correctly' do
|
20
|
+
form = DocsDryVTest::AlbumForm.new(Album.new(nil, nil, nil))
|
21
|
+
result = form.call(name: nil)
|
22
|
+
|
23
|
+
refute result.success?
|
24
|
+
assert_equal({ name: ['must be filled'] }, form.errors.messages)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class DocsDryVWithRulesTest < Minitest::Spec
|
29
|
+
#:basic_with_rules
|
30
|
+
class AlbumForm < Reform::Form
|
31
|
+
feature Reform::Form::Dry
|
32
|
+
|
33
|
+
property :name
|
34
|
+
|
35
|
+
validation name: :default do
|
36
|
+
option :form
|
37
|
+
|
38
|
+
params do
|
39
|
+
required(:name).filled
|
40
|
+
end
|
41
|
+
|
42
|
+
rule(:name) do
|
43
|
+
key.failure('must be unique') if Album.where.not(id: form.model.id).where(name: value).exists?
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
#:basic_with_rules end
|
48
|
+
|
49
|
+
it 'validates correctly' do
|
50
|
+
Album = Struct.new(:name, :songs, :artist, :user)
|
51
|
+
form = DocsDryVWithRulesTest::AlbumForm.new(Album.new(nil, nil, nil, nil))
|
52
|
+
result = form.call(name: nil)
|
53
|
+
|
54
|
+
refute result.success?
|
55
|
+
assert_equal({ name: ['must be filled'] }, form.errors.messages)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class DryVWithNestedTest < Minitest::Spec
|
60
|
+
#:nested
|
61
|
+
class AlbumForm < Reform::Form
|
62
|
+
feature Reform::Form::Dry
|
63
|
+
|
64
|
+
property :name
|
65
|
+
|
66
|
+
validation do
|
67
|
+
params { required(:name).filled }
|
68
|
+
end
|
69
|
+
|
70
|
+
property :artist do
|
71
|
+
property :name
|
72
|
+
|
73
|
+
validation do
|
74
|
+
params { required(:name).filled }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
#:nested end
|
79
|
+
|
80
|
+
it 'validates correctly' do
|
81
|
+
form = DryVWithNestedTest::AlbumForm.new(Album.new(nil, nil, Artist.new(nil)))
|
82
|
+
result = form.call(name: nil, artist: { name: '' })
|
83
|
+
|
84
|
+
refute result.success?
|
85
|
+
assert_equal({ name: ['must be filled'], 'artist.name': ['must be filled'] }, form.errors.messages)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
class DryVValGroupTest < Minitest::Spec
|
90
|
+
class AlbumForm < Reform::Form
|
91
|
+
feature Reform::Form::Dry
|
92
|
+
|
93
|
+
property :name
|
94
|
+
property :artist
|
95
|
+
#:validation_groups
|
96
|
+
validation name: :default do
|
97
|
+
params { required(:name).filled }
|
98
|
+
end
|
99
|
+
|
100
|
+
validation name: :artist, if: :default do
|
101
|
+
params { required(:artist).filled }
|
102
|
+
end
|
103
|
+
|
104
|
+
validation name: :famous, after: :default do
|
105
|
+
params { optional(:artist) }
|
106
|
+
|
107
|
+
rule(:artist) do
|
108
|
+
if value
|
109
|
+
key.failure('only famous artist') unless value =~ /famous/
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
#:validation_groups end
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'validates correctly' do
|
117
|
+
form = DryVValGroupTest::AlbumForm.new(Album.new(nil, nil, nil))
|
118
|
+
result = form.call(name: nil)
|
119
|
+
|
120
|
+
refute result.success?
|
121
|
+
assert_equal({ name: ['must be filled'] }, result.errors.messages)
|
122
|
+
|
123
|
+
result = form.call(name: 'Title')
|
124
|
+
refute result.success?
|
125
|
+
assert_equal({ artist: ['must be filled'] }, result.errors.messages)
|
126
|
+
|
127
|
+
result = form.call(name: 'Title', artist: 'Artist')
|
128
|
+
refute result.success?
|
129
|
+
assert_equal({ artist: ['only famous artist'] }, result.errors.messages)
|
130
|
+
|
131
|
+
result = form.call(name: 'Title', artist: 'Artist famous')
|
132
|
+
assert result.success?
|
133
|
+
end
|
134
|
+
end
|
data/test/errors_test.rb
CHANGED
@@ -1,24 +1,28 @@
|
|
1
1
|
require "test_helper"
|
2
2
|
|
3
|
-
# TODO:
|
4
|
-
# This test should, at some point soon, only test the `Errors` object and its
|
5
|
-
# Rails-ish API. No validation specifics, etc. to be tested here.
|
6
|
-
|
7
3
|
class ErrorsTest < MiniTest::Spec
|
8
4
|
class AlbumForm < TestForm
|
9
5
|
property :title
|
6
|
+
validation do
|
7
|
+
params { required(:title).filled }
|
8
|
+
end
|
9
|
+
|
10
|
+
property :artists, default: []
|
11
|
+
property :producer do
|
12
|
+
property :name
|
13
|
+
end
|
10
14
|
|
11
15
|
property :hit do
|
12
16
|
property :title
|
13
17
|
validation do
|
14
|
-
required(:title).filled
|
18
|
+
params { required(:title).filled }
|
15
19
|
end
|
16
20
|
end
|
17
21
|
|
18
22
|
collection :songs do
|
19
23
|
property :title
|
20
24
|
validation do
|
21
|
-
required(:title).filled
|
25
|
+
params { required(:title).filled }
|
22
26
|
end
|
23
27
|
end
|
24
28
|
|
@@ -27,154 +31,195 @@ class ErrorsTest < MiniTest::Spec
|
|
27
31
|
property :label do
|
28
32
|
property :name
|
29
33
|
validation do
|
30
|
-
required(:name).filled
|
34
|
+
params { required(:name).filled }
|
31
35
|
end
|
32
36
|
end
|
33
37
|
# TODO: make band a required object.
|
34
38
|
|
35
39
|
validation do
|
36
|
-
|
37
|
-
config.messages_file = "test/fixtures/dry_error_messages.yml"
|
40
|
+
config.messages.load_paths << "test/fixtures/dry_error_messages.yml"
|
38
41
|
|
39
|
-
|
40
|
-
value != "Nickelback"
|
41
|
-
end
|
42
|
-
end
|
42
|
+
params { required(:name).filled }
|
43
43
|
|
44
|
-
|
44
|
+
rule(:name) { key.failure(:good_musical_taste?) if value == "Nickelback" }
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
48
|
validation do
|
49
|
-
|
49
|
+
params do
|
50
|
+
required(:title).filled
|
51
|
+
required(:artists).each(:str?)
|
52
|
+
required(:producer).hash do
|
53
|
+
required(:name).filled
|
54
|
+
end
|
55
|
+
end
|
50
56
|
end
|
51
57
|
end
|
52
58
|
|
53
|
-
let
|
59
|
+
let(:album_title) { "Blackhawks Over Los Angeles" }
|
60
|
+
let(:album) do
|
54
61
|
OpenStruct.new(
|
55
|
-
:
|
56
|
-
:
|
57
|
-
:
|
58
|
-
:
|
62
|
+
title: album_title,
|
63
|
+
hit: song,
|
64
|
+
songs: songs, # TODO: document this requirement,
|
65
|
+
band: Struct.new(:name, :label).new("Epitaph", OpenStruct.new),
|
66
|
+
producer: Struct.new(:name).new("Sun Records")
|
59
67
|
)
|
60
68
|
end
|
61
|
-
let
|
62
|
-
let
|
63
|
-
let
|
69
|
+
let(:song) { OpenStruct.new(title: "Downtown") }
|
70
|
+
let(:songs) { [song = OpenStruct.new(title: "Calling"), song] }
|
71
|
+
let(:form) { AlbumForm.new(album) }
|
64
72
|
|
73
|
+
describe "#validate with invalid array property" do
|
74
|
+
it do
|
75
|
+
refute form.validate(
|
76
|
+
title: "Swimming Pool - EP",
|
77
|
+
band: {
|
78
|
+
name: "Marie Madeleine",
|
79
|
+
label: {name: "Ekler'o'shocK"}
|
80
|
+
},
|
81
|
+
artists: [42, "Good Charlotte", 43]
|
82
|
+
)
|
83
|
+
assert_equal form.errors.messages, artists: {0 => ["must be a string"], 2 => ["must be a string"]}
|
84
|
+
assert_equal form.errors.size, 1
|
85
|
+
end
|
86
|
+
end
|
65
87
|
|
66
88
|
describe "#errors without #validate" do
|
67
89
|
it do
|
68
|
-
form.errors.size
|
90
|
+
assert_equal form.errors.size, 0
|
69
91
|
end
|
70
92
|
end
|
71
93
|
|
72
94
|
describe "blank everywhere" do
|
73
|
-
before
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
:title => ["must be filled"],
|
81
|
-
:"hit.title"=>["must be filled"],
|
82
|
-
:"songs.title"=>["must be filled"],
|
83
|
-
:"band.label.name"=>["must be filled"]
|
84
|
-
})
|
95
|
+
before do
|
96
|
+
form.validate(
|
97
|
+
"hit" => {"title" => ""},
|
98
|
+
"title" => "",
|
99
|
+
"songs" => [{"title" => ""}, {"title" => ""}],
|
100
|
+
"producer" => {"name" => ""}
|
101
|
+
)
|
85
102
|
end
|
86
103
|
|
87
104
|
it do
|
88
|
-
|
89
|
-
|
105
|
+
assert_equal form.errors.messages,{
|
106
|
+
title: ["must be filled"],
|
107
|
+
"hit.title": ["must be filled"],
|
108
|
+
"songs.title": ["must be filled"],
|
109
|
+
"band.label.name": ["must be filled"],
|
110
|
+
"producer.name": ["must be filled"]
|
111
|
+
}
|
90
112
|
end
|
91
113
|
|
114
|
+
# it do
|
115
|
+
# form.errors.must_equal({:title => ["must be filled"]})
|
116
|
+
# TODO: this should only contain local errors?
|
117
|
+
# end
|
118
|
+
|
92
119
|
# nested forms keep their own Errors:
|
93
|
-
it { form.
|
94
|
-
it { form.
|
120
|
+
it { assert_equal form.producer.errors.messages, name: ["must be filled"] }
|
121
|
+
it { assert_equal form.hit.errors.messages, title: ["must be filled"] }
|
122
|
+
it { assert_equal form.songs[0].errors.messages, title: ["must be filled"] }
|
95
123
|
|
96
124
|
it do
|
97
|
-
form.errors.messages
|
98
|
-
:
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
125
|
+
assert_equal form.errors.messages, {
|
126
|
+
title: ["must be filled"],
|
127
|
+
"hit.title": ["must be filled"],
|
128
|
+
"songs.title": ["must be filled"],
|
129
|
+
"band.label.name": ["must be filled"],
|
130
|
+
"producer.name": ["must be filled"]
|
131
|
+
}
|
132
|
+
assert_equal form.errors.size, 5
|
104
133
|
end
|
105
134
|
end
|
106
135
|
|
107
|
-
|
108
136
|
describe "#validate with main form invalid" do
|
109
137
|
it do
|
110
|
-
form.validate("title"=>"", "band"=>{"label"=>{:
|
111
|
-
form.errors.messages
|
112
|
-
form.errors.size
|
138
|
+
refute form.validate("title" => "", "band" => {"label" => {name: "Fat Wreck"}}, "producer" => nil)
|
139
|
+
assert_equal form.errors.messages, title: ["must be filled"], producer: ["must be a hash"]
|
140
|
+
assert_equal form.errors.size, 2
|
113
141
|
end
|
114
142
|
end
|
115
143
|
|
116
|
-
|
117
144
|
describe "#validate with middle nested form invalid" do
|
118
|
-
before { @result = form.validate("hit"=>{"title" => ""}, "band"=>{"label"=>{:
|
145
|
+
before { @result = form.validate("hit" => {"title" => ""}, "band" => {"label" => {name: "Fat Wreck"}}) }
|
119
146
|
|
120
|
-
it { @result
|
121
|
-
it { form.errors.messages
|
122
|
-
it { form.errors.size
|
147
|
+
it { refute @result }
|
148
|
+
it { assert_equal form.errors.messages, "hit.title": ["must be filled"] }
|
149
|
+
it { assert_equal form.errors.size, 1 }
|
123
150
|
end
|
124
151
|
|
125
|
-
|
126
152
|
describe "#validate with collection form invalid" do
|
127
|
-
before { @result = form.validate("songs"=>[{"title" => ""}], "band"=>{"label"=>{:
|
153
|
+
before { @result = form.validate("songs" => [{"title" => ""}], "band" => {"label" => {name: "Fat Wreck"}}) }
|
128
154
|
|
129
|
-
it { @result
|
130
|
-
it { form.errors.messages
|
131
|
-
it { form.errors.size
|
155
|
+
it { refute @result }
|
156
|
+
it { assert_equal form.errors.messages, "songs.title": ["must be filled"] }
|
157
|
+
it { assert_equal form.errors.size, 1 }
|
132
158
|
end
|
133
159
|
|
134
|
-
|
135
160
|
describe "#validate with collection and 2-level-nested invalid" do
|
136
|
-
before { @result = form.validate("songs"=>[{"title" => ""}], "band" => {"label" => {}}) }
|
161
|
+
before { @result = form.validate("songs" => [{"title" => ""}], "band" => {"label" => {}}) }
|
137
162
|
|
138
|
-
it { @result
|
139
|
-
it { form.errors.messages
|
140
|
-
it { form.errors.size
|
163
|
+
it { refute @result }
|
164
|
+
it { assert_equal form.errors.messages, "songs.title": ["must be filled"], "band.label.name": ["must be filled"] }
|
165
|
+
it { assert_equal form.errors.size, 2 }
|
141
166
|
end
|
142
167
|
|
143
168
|
describe "#validate with nested form using :base invalid" do
|
144
169
|
it do
|
145
|
-
result = form.validate("songs"=>[{"title" => "Someday"}], "band" => {"name" => "Nickelback", "label" => {"name" => "Roadrunner Records"}})
|
146
|
-
result
|
147
|
-
form.errors.messages
|
148
|
-
form.errors.size
|
170
|
+
result = form.validate("songs" => [{"title" => "Someday"}], "band" => {"name" => "Nickelback", "label" => {"name" => "Roadrunner Records"}})
|
171
|
+
refute result
|
172
|
+
assert_equal form.errors.messages, "band.name": ["you're a bad person"]
|
173
|
+
assert_equal form.errors.size, 1
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
describe "#add" do
|
178
|
+
let(:album_title) { nil }
|
179
|
+
it do
|
180
|
+
result = form.validate("songs" => [{"title" => "Someday"}], "band" => {"name" => "Nickelback", "label" => {"name" => "Roadrunner Records"}})
|
181
|
+
refute result
|
182
|
+
assert_equal form.errors.messages, title: ["must be filled"], "band.name": ["you're a bad person"]
|
183
|
+
# add a new custom error
|
184
|
+
form.errors.add(:policy, "error_text")
|
185
|
+
assert_equal form.errors.messages, title: ["must be filled"], "band.name": ["you're a bad person"], policy: ["error_text"]
|
186
|
+
# does not duplicate errors
|
187
|
+
form.errors.add(:title, "must be filled")
|
188
|
+
assert_equal form.errors.messages, title: ["must be filled"], "band.name": ["you're a bad person"], policy: ["error_text"]
|
189
|
+
# merge existing errors
|
190
|
+
form.errors.add(:policy, "another error")
|
191
|
+
assert_equal form.errors.messages, title: ["must be filled"], "band.name": ["you're a bad person"], policy: ["error_text", "another error"]
|
149
192
|
end
|
150
193
|
end
|
151
194
|
|
152
195
|
describe "correct #validate" do
|
153
|
-
before
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
it {
|
163
|
-
it { form.
|
196
|
+
before do
|
197
|
+
@result = form.validate(
|
198
|
+
"hit" => {"title" => "Sacrifice"},
|
199
|
+
"title" => "Second Heat",
|
200
|
+
"songs" => [{"title" => "Heart Of A Lion"}],
|
201
|
+
"band" => {"label" => {name: "Fat Wreck"}}
|
202
|
+
)
|
203
|
+
end
|
204
|
+
|
205
|
+
it { assert @result }
|
206
|
+
it { assert_equal form.hit.title, "Sacrifice" }
|
207
|
+
it { assert_equal form.title, "Second Heat" }
|
208
|
+
it { assert_equal form.songs.first.title, "Heart Of A Lion" }
|
164
209
|
it do
|
165
210
|
skip "WE DON'T NEED COUNT AND EMPTY? ON THE CORE ERRORS OBJECT"
|
166
|
-
form.errors.size
|
167
|
-
form.errors.empty
|
211
|
+
assert_equal form.errors.size, 0
|
212
|
+
assert form.errors.empty
|
168
213
|
end
|
169
214
|
end
|
170
215
|
|
171
|
-
|
172
216
|
describe "Errors#to_s" do
|
173
|
-
before { form.validate("songs"=>[{"title" => ""}], "band" => {"label" => {}}) }
|
217
|
+
before { form.validate("songs" => [{"title" => ""}], "band" => {"label" => {}}) }
|
174
218
|
|
175
219
|
# to_s is aliased to messages
|
176
220
|
it {
|
177
221
|
skip "why do we need Errors#to_s ?"
|
178
|
-
form.errors.to_s
|
222
|
+
assert_equal form.errors.to_s, "{:\"songs.title\"=>[\"must be filled\"], :\"band.label.name\"=>[\"must be filled\"]}"
|
223
|
+
}
|
179
224
|
end
|
180
225
|
end
|