reform 2.2.4 → 2.3.3
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 +11 -6
- data/Appraisals +8 -0
- data/CHANGES.md +57 -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 +16 -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 +45 -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 +14 -13
- data/test/benchmarking.rb +39 -6
- data/test/call_new_api.rb +23 -0
- data/test/call_old_api.rb +23 -0
- data/test/changed_test.rb +14 -14
- data/test/coercion_test.rb +57 -25
- data/test/composition_new_api.rb +186 -0
- data/test/composition_old_api.rb +184 -0
- data/test/contract/custom_error_test.rb +55 -0
- data/test/contract_new_api.rb +77 -0
- data/test/contract_old_api.rb +77 -0
- data/test/default_test.rb +4 -4
- data/test/deserialize_test.rb +17 -20
- data/test/errors_new_api.rb +225 -0
- data/test/errors_old_api.rb +230 -0
- data/test/feature_test.rb +10 -12
- 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} +8 -8
- data/test/form_option_new_api.rb +24 -0
- data/test/{form_option_test.rb → form_option_old_api.rb} +5 -5
- data/test/from_test.rb +18 -22
- 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} +26 -31
- data/test/module_old_api.rb +146 -0
- data/test/parse_option_test.rb +40 -0
- data/test/parse_pipeline_test.rb +4 -4
- data/test/populate_new_api.rb +304 -0
- data/test/populate_old_api.rb +304 -0
- data/test/populator_skip_test.rb +11 -11
- data/test/prepopulator_test.rb +23 -24
- data/test/read_only_test.rb +12 -1
- data/test/readable_test.rb +9 -9
- data/test/reform_new_api.rb +204 -0
- data/test/{reform_test.rb → reform_old_api.rb} +44 -65
- data/test/save_new_api.rb +101 -0
- data/test/save_old_api.rb +101 -0
- data/test/setup_test.rb +17 -17
- data/test/skip_if_new_api.rb +85 -0
- data/test/skip_if_old_api.rb +92 -0
- data/test/skip_setter_and_getter_test.rb +9 -10
- data/test/test_helper.rb +25 -14
- data/test/validate_new_api.rb +453 -0
- data/test/{validate_test.rb → validate_old_api.rb} +121 -131
- data/test/validation/dry_validation_new_api.rb +835 -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 +38 -9
- metadata +111 -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/call_test.rb +0 -23
- data/test/composition_test.rb +0 -149
- data/test/contract_test.rb +0 -77
- data/test/deprecation_test.rb +0 -27
- data/test/errors_test.rb +0 -165
- data/test/inherit_test.rb +0 -119
- data/test/populate_test.rb +0 -270
- data/test/readonly_test.rb +0 -14
- data/test/save_test.rb +0 -89
- data/test/skip_if_test.rb +0 -74
- data/test/validation/dry_test.rb +0 -60
- data/test/validation/dry_validation_test.rb +0 -352
- data/test/validation/errors.yml +0 -4
@@ -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
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class ContractTest < MiniTest::Spec
|
4
|
+
Song = Struct.new(:title, :album, :composer)
|
5
|
+
Album = Struct.new(:name, :duration, :songs, :artist)
|
6
|
+
Artist = Struct.new(:name)
|
7
|
+
|
8
|
+
class ArtistForm < TestForm
|
9
|
+
property :name
|
10
|
+
end
|
11
|
+
|
12
|
+
class AlbumForm < TestContract
|
13
|
+
property :name
|
14
|
+
|
15
|
+
properties :duration
|
16
|
+
properties :year, :style, readable: false
|
17
|
+
|
18
|
+
validation do
|
19
|
+
params { required(:name).filled }
|
20
|
+
end
|
21
|
+
|
22
|
+
collection :songs do
|
23
|
+
property :title
|
24
|
+
validation do
|
25
|
+
params { required(:title).filled }
|
26
|
+
end
|
27
|
+
|
28
|
+
property :composer do
|
29
|
+
property :name
|
30
|
+
validation do
|
31
|
+
params { required(:name).filled }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
property :artist, form: ArtistForm
|
37
|
+
end
|
38
|
+
|
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
|
+
|
45
|
+
let(:form) { AlbumForm.new(album) }
|
46
|
+
|
47
|
+
# accept `property form: SongForm`.
|
48
|
+
it do
|
49
|
+
_(form.artist).must_be_instance_of ArtistForm
|
50
|
+
end
|
51
|
+
|
52
|
+
describe ".properties" do
|
53
|
+
it "defines a property when called with one argument" do
|
54
|
+
_(form).must_respond_to :duration
|
55
|
+
end
|
56
|
+
|
57
|
+
it "defines several properties when called with multiple arguments" do
|
58
|
+
_(form).must_respond_to :year
|
59
|
+
_(form).must_respond_to :style
|
60
|
+
end
|
61
|
+
|
62
|
+
it "passes options to each property when options are provided" do
|
63
|
+
readable = AlbumForm.new(album).options_for(:style)[:readable]
|
64
|
+
_(readable).must_equal false
|
65
|
+
end
|
66
|
+
|
67
|
+
it "returns the list of defined properties" do
|
68
|
+
returned_value = AlbumForm.properties(:hello, :world, virtual: true)
|
69
|
+
_(returned_value).must_equal %i[hello world]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "#options_for" do
|
74
|
+
it { _(AlbumForm.options_for(:name).extend(Declarative::Inspect).inspect).must_equal "#<Disposable::Twin::Definition: @options={:private_name=>:name, :name=>\"name\"}>" }
|
75
|
+
it { _(AlbumForm.new(album).options_for(:name).extend(Declarative::Inspect).inspect).must_equal "#<Disposable::Twin::Definition: @options={:private_name=>:name, :name=>\"name\"}>" }
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class ContractTest < MiniTest::Spec
|
4
|
+
Song = Struct.new(:title, :album, :composer)
|
5
|
+
Album = Struct.new(:name, :duration, :songs, :artist)
|
6
|
+
Artist = Struct.new(:name)
|
7
|
+
|
8
|
+
class ArtistForm < TestForm
|
9
|
+
property :name
|
10
|
+
end
|
11
|
+
|
12
|
+
class AlbumForm < TestContract
|
13
|
+
property :name
|
14
|
+
|
15
|
+
properties :duration
|
16
|
+
properties :year, :style, readable: false
|
17
|
+
|
18
|
+
validation do
|
19
|
+
required(:name).filled
|
20
|
+
end
|
21
|
+
|
22
|
+
collection :songs do
|
23
|
+
property :title
|
24
|
+
validation do
|
25
|
+
required(:title).filled
|
26
|
+
end
|
27
|
+
|
28
|
+
property :composer do
|
29
|
+
property :name
|
30
|
+
validation do
|
31
|
+
required(:name).filled
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
property :artist, form: ArtistForm
|
37
|
+
end
|
38
|
+
|
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
|
+
|
45
|
+
let(:form) { AlbumForm.new(album) }
|
46
|
+
|
47
|
+
# accept `property form: SongForm`.
|
48
|
+
it do
|
49
|
+
_(form.artist).must_be_instance_of ArtistForm
|
50
|
+
end
|
51
|
+
|
52
|
+
describe ".properties" do
|
53
|
+
it "defines a property when called with one argument" do
|
54
|
+
_(form).must_respond_to :duration
|
55
|
+
end
|
56
|
+
|
57
|
+
it "defines several properties when called with multiple arguments" do
|
58
|
+
_(form).must_respond_to :year
|
59
|
+
_(form).must_respond_to :style
|
60
|
+
end
|
61
|
+
|
62
|
+
it "passes options to each property when options are provided" do
|
63
|
+
readable = AlbumForm.new(album).options_for(:style)[:readable]
|
64
|
+
_(readable).must_equal false
|
65
|
+
end
|
66
|
+
|
67
|
+
it "returns the list of defined properties" do
|
68
|
+
returned_value = AlbumForm.properties(:hello, :world, virtual: true)
|
69
|
+
_(returned_value).must_equal %i[hello world]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "#options_for" do
|
74
|
+
it { _(AlbumForm.options_for(:name).extend(Declarative::Inspect).inspect).must_equal "#<Disposable::Twin::Definition: @options={:private_name=>:name, :name=>\"name\"}>" }
|
75
|
+
it { _(AlbumForm.new(album).options_for(:name).extend(Declarative::Inspect).inspect).must_equal "#<Disposable::Twin::Definition: @options={:private_name=>:name, :name=>\"name\"}>" }
|
76
|
+
end
|
77
|
+
end
|
data/test/default_test.rb
CHANGED
@@ -5,7 +5,7 @@ class DefaultTest < 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, default: "Wrong"
|
10
10
|
|
11
11
|
collection :songs do
|
@@ -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.must_equal "Wrong"
|
20
|
-
form.songs[0].title.must_equal "It's Catching Up"
|
19
|
+
_(form.name).must_equal "Wrong"
|
20
|
+
_(form.songs[0].title).must_equal "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
|
@@ -6,11 +6,11 @@ class DeserializeTest < MiniTest::Spec
|
|
6
6
|
Album = Struct.new(:title, :artist)
|
7
7
|
Artist = Struct.new(:name, :callname)
|
8
8
|
|
9
|
-
class JsonAlbumForm <
|
9
|
+
class JsonAlbumForm < TestForm
|
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,13 +41,13 @@ class DeserializeTest < MiniTest::Spec
|
|
43
41
|
|
44
42
|
form.validate(json)
|
45
43
|
|
46
|
-
form.title.must_equal "Apocalypse Soon"
|
47
|
-
form.artist.name.must_equal "Mute"
|
48
|
-
form.artist.model.object_id.must_equal artist_id
|
44
|
+
_(form.title).must_equal "Apocalypse Soon"
|
45
|
+
_(form.artist.name).must_equal "Mute"
|
46
|
+
_(form.artist.model.object_id).must_equal artist_id
|
49
47
|
end
|
50
48
|
|
51
49
|
describe "infering the deserializer from another form should NOT copy its populators" do
|
52
|
-
class CompilationForm <
|
50
|
+
class CompilationForm < TestForm
|
53
51
|
property :artist, populator: ->(options) { self.artist = Artist.new(nil, options[:fragment].to_s) } do
|
54
52
|
property :name
|
55
53
|
end
|
@@ -62,20 +60,19 @@ 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.must_equal Artist.new("Horowitz", %{{"name"=>"Horowitz"}})
|
65
|
+
_(form.artist.model).must_equal 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)
|
76
73
|
Artist = Struct.new(:name)
|
77
74
|
|
78
|
-
class AlbumForm <
|
75
|
+
class AlbumForm < TestForm
|
79
76
|
property :title
|
80
77
|
property :artist, populate_if_empty: Artist do
|
81
78
|
property :name
|
@@ -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
|
+
_(form.validate(json) { |params|
|
98
95
|
deserializer.new(form).from_json(params)
|
99
|
-
|
96
|
+
}).must_equal true # with block must return result, too.
|
100
97
|
|
101
|
-
form.title.must_equal "Apocalypse Soon"
|
102
|
-
form.artist.name.must_equal "Mute"
|
98
|
+
_(form.title).must_equal "Apocalypse Soon"
|
99
|
+
_(form.artist.name).must_equal "Mute"
|
103
100
|
end
|
104
101
|
end
|
@@ -0,0 +1,225 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class ErrorsTest < MiniTest::Spec
|
4
|
+
class AlbumForm < TestForm
|
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
|
14
|
+
|
15
|
+
property :hit do
|
16
|
+
property :title
|
17
|
+
validation do
|
18
|
+
params { required(:title).filled }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
collection :songs do
|
23
|
+
property :title
|
24
|
+
validation do
|
25
|
+
params { required(:title).filled }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
property :band do # yepp, people do crazy stuff like that.
|
30
|
+
property :name
|
31
|
+
property :label do
|
32
|
+
property :name
|
33
|
+
validation do
|
34
|
+
params { required(:name).filled }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
# TODO: make band a required object.
|
38
|
+
|
39
|
+
validation do
|
40
|
+
config.messages.load_paths << "test/fixtures/dry_new_api_error_messages.yml"
|
41
|
+
|
42
|
+
params { required(:name).filled }
|
43
|
+
|
44
|
+
rule(:name) { key.failure(:good_musical_taste?) if value == "Nickelback" }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
validation do
|
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
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
let(:album_title) { "Blackhawks Over Los Angeles" }
|
60
|
+
let(:album) do
|
61
|
+
OpenStruct.new(
|
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")
|
67
|
+
)
|
68
|
+
end
|
69
|
+
let(:song) { OpenStruct.new(title: "Downtown") }
|
70
|
+
let(:songs) { [song = OpenStruct.new(title: "Calling"), song] }
|
71
|
+
let(:form) { AlbumForm.new(album) }
|
72
|
+
|
73
|
+
describe "#validate with invalid array property" do
|
74
|
+
it do
|
75
|
+
_(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
|
+
)).must_equal false
|
83
|
+
_(form.errors.messages).must_equal(artists: {0 => ["must be a string"], 2 => ["must be a string"]})
|
84
|
+
_(form.errors.size).must_equal(1)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe "#errors without #validate" do
|
89
|
+
it do
|
90
|
+
_(form.errors.size).must_equal 0
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe "blank everywhere" do
|
95
|
+
before do
|
96
|
+
form.validate(
|
97
|
+
"hit" => {"title" => ""},
|
98
|
+
"title" => "",
|
99
|
+
"songs" => [{"title" => ""}, {"title" => ""}],
|
100
|
+
"producer" => {"name" => ""}
|
101
|
+
)
|
102
|
+
end
|
103
|
+
|
104
|
+
it do
|
105
|
+
_(form.errors.messages).must_equal(
|
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
|
+
)
|
112
|
+
end
|
113
|
+
|
114
|
+
# it do
|
115
|
+
# form.errors.must_equal({:title => ["must be filled"]})
|
116
|
+
# TODO: this should only contain local errors?
|
117
|
+
# end
|
118
|
+
|
119
|
+
# nested forms keep their own Errors:
|
120
|
+
it { _(form.producer.errors.messages).must_equal(name: ["must be filled"]) }
|
121
|
+
it { _(form.hit.errors.messages).must_equal(title: ["must be filled"]) }
|
122
|
+
it { _(form.songs[0].errors.messages).must_equal(title: ["must be filled"]) }
|
123
|
+
|
124
|
+
it do
|
125
|
+
_(form.errors.messages).must_equal(
|
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
|
+
_(form.errors.size).must_equal(5)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
describe "#validate with main form invalid" do
|
137
|
+
it do
|
138
|
+
_(form.validate("title" => "", "band" => {"label" => {name: "Fat Wreck"}}, "producer" => nil)).must_equal false
|
139
|
+
_(form.errors.messages).must_equal(title: ["must be filled"], producer: ["must be a hash"])
|
140
|
+
_(form.errors.size).must_equal(2)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe "#validate with middle nested form invalid" do
|
145
|
+
before { @result = form.validate("hit" => {"title" => ""}, "band" => {"label" => {name: "Fat Wreck"}}) }
|
146
|
+
|
147
|
+
it { _(@result).must_equal false }
|
148
|
+
it { _(form.errors.messages).must_equal("hit.title": ["must be filled"]) }
|
149
|
+
it { _(form.errors.size).must_equal(1) }
|
150
|
+
end
|
151
|
+
|
152
|
+
describe "#validate with collection form invalid" do
|
153
|
+
before { @result = form.validate("songs" => [{"title" => ""}], "band" => {"label" => {name: "Fat Wreck"}}) }
|
154
|
+
|
155
|
+
it { _(@result).must_equal false }
|
156
|
+
it { _(form.errors.messages).must_equal("songs.title": ["must be filled"]) }
|
157
|
+
it { _(form.errors.size).must_equal(1) }
|
158
|
+
end
|
159
|
+
|
160
|
+
describe "#validate with collection and 2-level-nested invalid" do
|
161
|
+
before { @result = form.validate("songs" => [{"title" => ""}], "band" => {"label" => {}}) }
|
162
|
+
|
163
|
+
it { _(@result).must_equal false }
|
164
|
+
it { _(form.errors.messages).must_equal("songs.title": ["must be filled"], "band.label.name": ["must be filled"]) }
|
165
|
+
it { _(form.errors.size).must_equal(2) }
|
166
|
+
end
|
167
|
+
|
168
|
+
describe "#validate with nested form using :base invalid" do
|
169
|
+
it do
|
170
|
+
result = form.validate("songs" => [{"title" => "Someday"}], "band" => {"name" => "Nickelback", "label" => {"name" => "Roadrunner Records"}})
|
171
|
+
_(result).must_equal false
|
172
|
+
_(form.errors.messages).must_equal("band.name": ["you're a bad person"])
|
173
|
+
_(form.errors.size).must_equal(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
|
+
_(result).must_equal false
|
182
|
+
_(form.errors.messages).must_equal(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
|
+
_(form.errors.messages).must_equal(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
|
+
_(form.errors.messages).must_equal(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
|
+
_(form.errors.messages).must_equal(title: ["must be filled"], "band.name": ["you're a bad person"], policy: ["error_text", "another error"])
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
describe "correct #validate" do
|
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 { _(@result).must_equal true }
|
206
|
+
it { _(form.hit.title).must_equal "Sacrifice" }
|
207
|
+
it { _(form.title).must_equal "Second Heat" }
|
208
|
+
it { _(form.songs.first.title).must_equal "Heart Of A Lion" }
|
209
|
+
it do
|
210
|
+
skip "WE DON'T NEED COUNT AND EMPTY? ON THE CORE ERRORS OBJECT"
|
211
|
+
_(form.errors.size).must_equal(0)
|
212
|
+
_(form.errors.empty?).must_equal(true)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
describe "Errors#to_s" do
|
217
|
+
before { form.validate("songs" => [{"title" => ""}], "band" => {"label" => {}}) }
|
218
|
+
|
219
|
+
# to_s is aliased to messages
|
220
|
+
it {
|
221
|
+
skip "why do we need Errors#to_s ?"
|
222
|
+
_(form.errors.to_s).must_equal "{:\"songs.title\"=>[\"must be filled\"], :\"band.label.name\"=>[\"must be filled\"]}"
|
223
|
+
}
|
224
|
+
end
|
225
|
+
end
|