reform 2.2.4 → 2.6.2
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/.github/workflows/ci.yml +17 -0
- data/.gitignore +5 -1
- data/CHANGES.md +80 -4
- data/CONTRIBUTING.md +31 -0
- data/Gemfile +2 -16
- data/ISSUE_TEMPLATE.md +25 -0
- data/LICENSE.txt +1 -1
- data/README.md +10 -12
- data/Rakefile +6 -10
- data/lib/reform/contract/custom_error.rb +41 -0
- data/lib/reform/contract/validate.rb +53 -23
- data/lib/reform/contract.rb +7 -17
- data/lib/reform/errors.rb +61 -0
- data/lib/reform/form/call.rb +1 -1
- data/lib/reform/form/composition.rb +2 -2
- data/lib/reform/form/dry/input_hash.rb +37 -0
- data/lib/reform/form/dry.rb +25 -35
- data/lib/reform/form/populator.rb +20 -26
- data/lib/reform/form/prepopulate.rb +5 -4
- data/lib/reform/form/validate.rb +29 -14
- data/lib/reform/form.rb +36 -10
- data/lib/reform/result.rb +90 -0
- data/lib/reform/validation/groups.rb +12 -28
- data/lib/reform/validation.rb +19 -11
- data/lib/reform/version.rb +1 -1
- data/lib/reform.rb +1 -0
- data/reform.gemspec +13 -13
- data/test/benchmarking.rb +39 -6
- data/test/call_test.rb +9 -9
- data/test/changed_test.rb +14 -14
- data/test/coercion_test.rb +57 -25
- data/test/composition_test.rb +85 -49
- data/test/contract/custom_error_test.rb +55 -0
- data/test/contract_test.rb +20 -20
- data/test/default_test.rb +4 -4
- data/test/deserialize_test.rb +17 -20
- data/test/docs/validation_test.rb +134 -0
- data/test/errors_test.rb +142 -82
- data/test/feature_test.rb +10 -12
- data/test/fixtures/dry_error_messages.yml +83 -23
- data/test/form_option_test.rb +5 -5
- data/test/form_test.rb +8 -8
- data/test/from_test.rb +18 -22
- data/test/inherit_test.rb +54 -68
- data/test/module_test.rb +27 -32
- data/test/parse_option_test.rb +40 -0
- data/test/parse_pipeline_test.rb +4 -4
- data/test/populate_test.rb +217 -100
- data/test/populator_skip_test.rb +11 -11
- data/test/prepopulator_test.rb +24 -25
- data/test/read_only_test.rb +12 -1
- data/test/readable_test.rb +9 -9
- data/test/reform_test.rb +47 -66
- data/test/save_test.rb +35 -23
- data/test/setup_test.rb +17 -17
- data/test/skip_if_test.rb +33 -22
- data/test/skip_setter_and_getter_test.rb +9 -10
- data/test/test_helper.rb +23 -14
- data/test/validate_test.rb +165 -145
- data/test/validation/dry_validation_test.rb +630 -146
- 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 +46 -38
- data/.travis.yml +0 -11
- data/gemfiles/Gemfile.disposable-0.3 +0 -6
- data/lib/reform/contract/errors.rb +0 -43
- data/lib/reform/form/mongoid.rb +0 -37
- data/lib/reform/form/orm.rb +0 -26
- data/lib/reform/mongoid.rb +0 -4
- data/test/deprecation_test.rb +0 -27
- data/test/readonly_test.rb +0 -14
- data/test/validation/dry_test.rb +0 -60
- data/test/validation/errors.yml +0 -4
data/test/benchmarking.rb
CHANGED
|
@@ -1,15 +1,48 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
3
|
-
require
|
|
1
|
+
require "reform"
|
|
2
|
+
require "benchmark/ips"
|
|
3
|
+
require "reform/form/dry"
|
|
4
4
|
|
|
5
5
|
class BandForm < Reform::Form
|
|
6
|
-
|
|
6
|
+
feature Reform::Form::Dry
|
|
7
|
+
property :name #, validates: {presence: true}
|
|
8
|
+
collection :songs do
|
|
9
|
+
property :title #, validates: {presence: true}
|
|
10
|
+
end
|
|
11
|
+
end
|
|
7
12
|
|
|
13
|
+
class OptimizedBandForm < Reform::Form
|
|
14
|
+
feature Reform::Form::Dry
|
|
15
|
+
property :name #, validates: {presence: true}
|
|
8
16
|
collection :songs do
|
|
9
|
-
property :title
|
|
17
|
+
property :title #, validates: {presence: true}
|
|
18
|
+
|
|
19
|
+
def deserializer(*args)
|
|
20
|
+
# DISCUSS: should we simply delegate to class and sort out memoizing there?
|
|
21
|
+
self.class.deserializer_class || self.class.deserializer_class = deserializer!(*args)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def deserializer(*args)
|
|
26
|
+
# DISCUSS: should we simply delegate to class and sort out memoizing there?
|
|
27
|
+
self.class.deserializer_class || self.class.deserializer_class = deserializer!(*args)
|
|
10
28
|
end
|
|
11
29
|
end
|
|
12
30
|
|
|
31
|
+
songs = 10.times.collect { OpenStruct.new(title: "Be Stag") }
|
|
32
|
+
band = OpenStruct.new(name: "Teenage Bottlerock", songs: songs)
|
|
33
|
+
|
|
34
|
+
unoptimized_form = BandForm.new(band)
|
|
35
|
+
optimized_form = OptimizedBandForm.new(band)
|
|
36
|
+
|
|
37
|
+
songs_params = songs_params = 10.times.collect { {title: "Commando"} }
|
|
38
|
+
|
|
39
|
+
Benchmark.ips do |x|
|
|
40
|
+
x.report("2.2") { BandForm.new(band).validate("name" => "Ramones", "songs" => songs_params) }
|
|
41
|
+
x.report("2.3") { OptimizedBandForm.new(band).validate("name" => "Ramones", "songs" => songs_params) }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
exit
|
|
45
|
+
|
|
13
46
|
songs = 50.times.collect { OpenStruct.new(title: "Be Stag") }
|
|
14
47
|
band = OpenStruct.new(name: "Teenage Bottlerock", songs: songs)
|
|
15
48
|
|
|
@@ -23,4 +56,4 @@ time = Benchmark.measure do
|
|
|
23
56
|
end
|
|
24
57
|
end
|
|
25
58
|
|
|
26
|
-
puts time
|
|
59
|
+
puts time
|
data/test/call_test.rb
CHANGED
|
@@ -3,21 +3,21 @@ require "test_helper"
|
|
|
3
3
|
class CallTest < Minitest::Spec
|
|
4
4
|
Song = Struct.new(:title)
|
|
5
5
|
|
|
6
|
-
class SongForm <
|
|
6
|
+
class SongForm < TestForm
|
|
7
7
|
property :title
|
|
8
8
|
|
|
9
9
|
validation do
|
|
10
|
-
|
|
10
|
+
params { required(:title).filled }
|
|
11
11
|
end
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
let
|
|
14
|
+
let(:form) { SongForm.new(Song.new) }
|
|
15
15
|
|
|
16
|
-
it { form.(title: "True North").success
|
|
17
|
-
it { form.(title: "True North").failure
|
|
18
|
-
it { form.(title: "").success
|
|
19
|
-
it { form.(title: "").failure
|
|
16
|
+
it { assert form.(title: "True North").success? }
|
|
17
|
+
it { refute form.(title: "True North").failure? }
|
|
18
|
+
it { refute form.(title: "").success? }
|
|
19
|
+
it { assert form.(title: "").failure? }
|
|
20
20
|
|
|
21
|
-
it { form.(title: "True North").errors.messages
|
|
22
|
-
it { form.(title: "").errors.messages
|
|
21
|
+
it { assert_equal form.(title: "True North").errors.messages, {} }
|
|
22
|
+
it { assert_equal form.(title: "").errors.messages, title: ["must be filled"] }
|
|
23
23
|
end
|
data/test/changed_test.rb
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
1
|
+
require "test_helper"
|
|
2
|
+
require "reform/form/coercion"
|
|
3
3
|
|
|
4
4
|
class ChangedTest < MiniTest::Spec
|
|
5
5
|
Song = Struct.new(:title, :album, :composer)
|
|
6
6
|
Album = Struct.new(:name, :songs, :artist)
|
|
7
7
|
Artist = Struct.new(:name)
|
|
8
8
|
|
|
9
|
-
class AlbumForm <
|
|
9
|
+
class AlbumForm < TestForm
|
|
10
10
|
property :name
|
|
11
11
|
|
|
12
12
|
collection :songs do
|
|
@@ -18,24 +18,24 @@ class ChangedTest < MiniTest::Spec
|
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
let
|
|
22
|
-
let
|
|
23
|
-
let
|
|
21
|
+
let(:song_with_composer) { Song.new("Resist Stance", nil, composer) }
|
|
22
|
+
let(:composer) { Artist.new("Greg Graffin") }
|
|
23
|
+
let(:album) { Album.new("The Dissent Of Man", [song_with_composer]) }
|
|
24
24
|
|
|
25
|
-
let
|
|
25
|
+
let(:form) { AlbumForm.new(album) }
|
|
26
26
|
|
|
27
27
|
# nothing changed after setup.
|
|
28
28
|
it do
|
|
29
|
-
form.changed?(:name)
|
|
30
|
-
form.songs[0].changed?(:title)
|
|
31
|
-
form.songs[0].composer.changed?(:name)
|
|
29
|
+
refute form.changed?(:name)
|
|
30
|
+
refute form.songs[0].changed?(:title)
|
|
31
|
+
refute form.songs[0].composer.changed?(:name)
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
# after validate, things might have changed.
|
|
35
35
|
it do
|
|
36
36
|
form.validate("name" => "Out Of Bounds", "songs" => [{"composer" => {"name" => "Ingemar Jansson & Mikael Danielsson"}}])
|
|
37
|
-
form.changed?(:name)
|
|
38
|
-
form.songs[0].changed?(:title)
|
|
39
|
-
form.songs[0].composer.changed?(:name)
|
|
37
|
+
assert form.changed?(:name)
|
|
38
|
+
refute form.songs[0].changed?(:title)
|
|
39
|
+
assert form.songs[0].composer.changed?(:name)
|
|
40
40
|
end
|
|
41
|
-
end
|
|
41
|
+
end
|
data/test/coercion_test.rb
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
require "test_helper"
|
|
2
2
|
require "reform/form/coercion"
|
|
3
|
+
require "disposable/twin/property/hash"
|
|
3
4
|
|
|
4
5
|
class CoercionTest < BaseTest
|
|
5
6
|
class Irreversible
|
|
6
7
|
def self.call(value)
|
|
7
|
-
value*2
|
|
8
|
+
value * 2
|
|
8
9
|
end
|
|
9
10
|
end
|
|
10
11
|
|
|
11
|
-
class Form <
|
|
12
|
+
class Form < TestForm
|
|
12
13
|
feature Coercion
|
|
14
|
+
include Disposable::Twin::Property::Hash
|
|
13
15
|
|
|
14
|
-
property :released_at, type: Types::
|
|
16
|
+
property :released_at, type: Types::Params::DateTime
|
|
15
17
|
|
|
16
18
|
property :hit do
|
|
17
|
-
property :length, type: Types::
|
|
18
|
-
property :good, type: Types::
|
|
19
|
+
property :length, type: Types::Params::Integer
|
|
20
|
+
property :good, type: Types::Params::Bool
|
|
19
21
|
end
|
|
20
22
|
|
|
21
23
|
property :band do
|
|
@@ -23,44 +25,74 @@ class CoercionTest < BaseTest
|
|
|
23
25
|
property :value, type: Irreversible
|
|
24
26
|
end
|
|
25
27
|
end
|
|
28
|
+
|
|
29
|
+
property :metadata, field: :hash do
|
|
30
|
+
property :publication_settings do
|
|
31
|
+
property :featured, type: Types::Params::Bool
|
|
32
|
+
end
|
|
33
|
+
end
|
|
26
34
|
end
|
|
27
35
|
|
|
28
36
|
subject do
|
|
29
37
|
Form.new(album)
|
|
30
38
|
end
|
|
31
39
|
|
|
32
|
-
let
|
|
40
|
+
let(:album) do
|
|
33
41
|
OpenStruct.new(
|
|
34
|
-
:
|
|
35
|
-
:
|
|
36
|
-
:
|
|
42
|
+
released_at: "31/03/1981",
|
|
43
|
+
hit: OpenStruct.new(length: "312"),
|
|
44
|
+
band: Band.new(OpenStruct.new(value: "9999.99")),
|
|
45
|
+
metadata: {}
|
|
37
46
|
)
|
|
38
|
-
|
|
47
|
+
end
|
|
39
48
|
|
|
40
49
|
# it { subject.released_at.must_be_kind_of DateTime }
|
|
41
|
-
it { subject.released_at
|
|
42
|
-
it { subject.hit.length
|
|
43
|
-
it { subject.band.label.value
|
|
44
|
-
|
|
50
|
+
it { assert_equal subject.released_at, "31/03/1981" } # NO coercion in setup.
|
|
51
|
+
it { assert_equal subject.hit.length, "312" }
|
|
52
|
+
it { assert_equal subject.band.label.value, "9999.99" }
|
|
45
53
|
|
|
46
|
-
let
|
|
54
|
+
let(:params) do
|
|
47
55
|
{
|
|
48
|
-
:
|
|
49
|
-
:
|
|
50
|
-
|
|
56
|
+
released_at: "30/03/1981",
|
|
57
|
+
hit: {
|
|
58
|
+
length: "312",
|
|
59
|
+
good: "0",
|
|
60
|
+
},
|
|
61
|
+
band: {
|
|
62
|
+
label: {
|
|
63
|
+
value: "9999.99"
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
metadata: {
|
|
67
|
+
publication_settings: {
|
|
68
|
+
featured: "0"
|
|
69
|
+
}
|
|
70
|
+
}
|
|
51
71
|
}
|
|
52
|
-
|
|
53
|
-
|
|
72
|
+
end
|
|
54
73
|
|
|
55
74
|
# validate
|
|
56
75
|
describe "#validate" do
|
|
57
76
|
before { subject.validate(params) }
|
|
58
77
|
|
|
59
|
-
it { subject.released_at
|
|
60
|
-
it { subject.hit.length
|
|
61
|
-
it { subject.hit.good
|
|
62
|
-
it { subject.band.label.value
|
|
78
|
+
it { assert_equal subject.released_at, DateTime.parse("30/03/1981") }
|
|
79
|
+
it { assert_equal subject.hit.length, 312 }
|
|
80
|
+
it { assert_equal subject.hit.good, false }
|
|
81
|
+
it { assert_equal subject.band.label.value, "9999.999999.99" } # coercion happened once.
|
|
82
|
+
it { assert_equal subject.metadata.publication_settings.featured, false }
|
|
63
83
|
end
|
|
64
84
|
|
|
65
|
-
#
|
|
85
|
+
# sync
|
|
86
|
+
describe "#sync" do
|
|
87
|
+
before do
|
|
88
|
+
assert subject.validate(params)
|
|
89
|
+
subject.sync
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
it { assert_equal album.released_at, DateTime.parse("30/03/1981") }
|
|
93
|
+
it { assert_equal album.hit.length, 312 }
|
|
94
|
+
it { assert_equal album.hit.good, false }
|
|
95
|
+
it { assert_nil album.metadata[:publication_settings] }
|
|
96
|
+
it { assert_equal album.metadata["publication_settings"]["featured"], false }
|
|
97
|
+
end
|
|
66
98
|
end
|
data/test/composition_test.rb
CHANGED
|
@@ -1,56 +1,94 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "test_helper"
|
|
2
|
+
|
|
3
|
+
class FormCompositionInheritanceTest < MiniTest::Spec
|
|
4
|
+
module SizePrice
|
|
5
|
+
include Reform::Form::Module
|
|
6
|
+
|
|
7
|
+
property :price
|
|
8
|
+
property :size
|
|
9
|
+
|
|
10
|
+
module InstanceMethods
|
|
11
|
+
def price(for_size: size)
|
|
12
|
+
case for_size.to_sym
|
|
13
|
+
when :s then super() * 1
|
|
14
|
+
when :m then super() * 2
|
|
15
|
+
when :l then super() * 3
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class OutfitForm < TestForm
|
|
22
|
+
include Reform::Form::Composition
|
|
23
|
+
include SizePrice
|
|
24
|
+
|
|
25
|
+
property :price, inherit: true, on: :tshirt
|
|
26
|
+
property :size, inherit: true, on: :measurement
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
let(:measurement) { Measurement.new(:l) }
|
|
30
|
+
let(:tshirt) { Tshirt.new(2, :m) }
|
|
31
|
+
let(:form) { OutfitForm.new(tshirt: tshirt, measurement: measurement) }
|
|
32
|
+
|
|
33
|
+
Tshirt = Struct.new(:price, :size)
|
|
34
|
+
Measurement = Struct.new(:size)
|
|
35
|
+
|
|
36
|
+
it { assert_equal form.price, 6 }
|
|
37
|
+
it { assert_equal form.price(for_size: :s), 2 }
|
|
38
|
+
end
|
|
2
39
|
|
|
3
40
|
class FormCompositionTest < MiniTest::Spec
|
|
4
41
|
Song = Struct.new(:id, :title, :band)
|
|
5
42
|
Requester = Struct.new(:id, :name, :requester)
|
|
6
43
|
Band = Struct.new(:title)
|
|
7
44
|
|
|
8
|
-
class RequestForm <
|
|
45
|
+
class RequestForm < TestForm
|
|
9
46
|
include Composition
|
|
10
47
|
|
|
11
|
-
property :name, :
|
|
12
|
-
property :requester_id, :
|
|
13
|
-
properties :title, :id, :
|
|
48
|
+
property :name, on: :requester
|
|
49
|
+
property :requester_id, on: :requester, from: :id
|
|
50
|
+
properties :title, :id, on: :song
|
|
14
51
|
# property :channel # FIXME: what about the "main model"?
|
|
15
|
-
property :channel, :
|
|
16
|
-
property :requester, :
|
|
17
|
-
property :captcha, :
|
|
52
|
+
property :channel, virtual: true, on: :song
|
|
53
|
+
property :requester, on: :requester
|
|
54
|
+
property :captcha, on: :song, virtual: true
|
|
18
55
|
|
|
19
56
|
validation do
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
57
|
+
params do
|
|
58
|
+
required(:name).filled
|
|
59
|
+
required(:title).filled
|
|
60
|
+
end
|
|
23
61
|
end
|
|
24
62
|
|
|
25
|
-
property :band,
|
|
63
|
+
property :band, on: :song do
|
|
26
64
|
property :title
|
|
27
65
|
end
|
|
28
66
|
end
|
|
29
67
|
|
|
30
|
-
let
|
|
31
|
-
let
|
|
32
|
-
let
|
|
33
|
-
let
|
|
68
|
+
let(:form) { RequestForm.new(song: song, requester: requester) }
|
|
69
|
+
let(:song) { Song.new(1, "Rio", band) }
|
|
70
|
+
let(:requester) { Requester.new(2, "Duran Duran", "MCP") }
|
|
71
|
+
let(:band) { Band.new("Duran^2") }
|
|
34
72
|
|
|
35
73
|
# delegation form -> composition works
|
|
36
|
-
it { form.id
|
|
37
|
-
it { form.title
|
|
38
|
-
it { form.name
|
|
39
|
-
it { form.requester_id
|
|
40
|
-
it { form.channel
|
|
41
|
-
it { form.requester
|
|
42
|
-
it { form.captcha
|
|
74
|
+
it { assert_equal form.id, 1 }
|
|
75
|
+
it { assert_equal form.title, "Rio" }
|
|
76
|
+
it { assert_equal form.name, "Duran Duran" }
|
|
77
|
+
it { assert_equal form.requester_id, 2 }
|
|
78
|
+
it { assert_nil form.channel }
|
|
79
|
+
it { assert_equal form.requester, "MCP" } # same name as composed model.
|
|
80
|
+
it { assert_nil form.captcha }
|
|
43
81
|
|
|
44
82
|
# #model just returns <Composition>.
|
|
45
|
-
it { form.mapper.
|
|
83
|
+
it { assert form.mapper.is_a? Disposable::Composition }
|
|
46
84
|
|
|
47
85
|
# #model[] -> composed models
|
|
48
|
-
it { form.model[:requester]
|
|
49
|
-
it { form.model[:song]
|
|
50
|
-
|
|
86
|
+
it { assert_equal form.model[:requester], requester }
|
|
87
|
+
it { assert_equal form.model[:song], song }
|
|
51
88
|
|
|
52
89
|
it "creates Composition for you" do
|
|
53
|
-
form.validate("title" => "Greyhound", "name" => "Frenzal Rhomb")
|
|
90
|
+
assert_equal form.validate("title" => "Greyhound", "name" => "Frenzal Rhomb"), true
|
|
91
|
+
assert_equal form.validate("title" => "", "name" => "Frenzal Rhomb"), false
|
|
54
92
|
end
|
|
55
93
|
|
|
56
94
|
describe "#save" do
|
|
@@ -63,7 +101,7 @@ class FormCompositionTest < MiniTest::Spec
|
|
|
63
101
|
hash[:title] = form.title
|
|
64
102
|
end
|
|
65
103
|
|
|
66
|
-
hash
|
|
104
|
+
assert_equal hash, name: "Duran Duran", title: "Rio"
|
|
67
105
|
end
|
|
68
106
|
|
|
69
107
|
it "provides nested symbolized hash as second block argument" do
|
|
@@ -75,11 +113,10 @@ class FormCompositionTest < MiniTest::Spec
|
|
|
75
113
|
hash = map
|
|
76
114
|
end
|
|
77
115
|
|
|
78
|
-
hash
|
|
79
|
-
:
|
|
80
|
-
:
|
|
81
|
-
|
|
82
|
-
)
|
|
116
|
+
assert_equal hash, {
|
|
117
|
+
song: {"title" => "Greyhound", "id" => 1, "channel" => "JJJ", "captcha" => "wonderful", "band" => {"title" => "Duran^2"}},
|
|
118
|
+
requester: {"name" => "Frenzal Rhomb", "id" => 2, "requester" => "MCP"}
|
|
119
|
+
}
|
|
83
120
|
end
|
|
84
121
|
|
|
85
122
|
it "xxx pushes data to models and calls #save when no block passed" do
|
|
@@ -88,16 +125,16 @@ class FormCompositionTest < MiniTest::Spec
|
|
|
88
125
|
band.extend(Saveable)
|
|
89
126
|
|
|
90
127
|
form.validate("title" => "Greyhound", "name" => "Frenzal Rhomb", "captcha" => "1337")
|
|
91
|
-
form.captcha
|
|
128
|
+
assert_equal form.captcha, "1337" # TODO: move to separate test.
|
|
92
129
|
|
|
93
130
|
form.save
|
|
94
131
|
|
|
95
|
-
requester.name
|
|
96
|
-
requester.saved
|
|
97
|
-
song.title
|
|
98
|
-
song.saved
|
|
99
|
-
song.band.title
|
|
100
|
-
song.band.saved
|
|
132
|
+
assert_equal requester.name, "Frenzal Rhomb"
|
|
133
|
+
assert_equal requester.saved?, true
|
|
134
|
+
assert_equal song.title, "Greyhound"
|
|
135
|
+
assert_equal song.saved?, true
|
|
136
|
+
assert_equal song.band.title, "Duran^2"
|
|
137
|
+
assert_equal song.band.saved?, true
|
|
101
138
|
end
|
|
102
139
|
|
|
103
140
|
it "returns true when models all save successfully" do
|
|
@@ -105,7 +142,7 @@ class FormCompositionTest < MiniTest::Spec
|
|
|
105
142
|
requester.extend(Saveable)
|
|
106
143
|
band.extend(Saveable)
|
|
107
144
|
|
|
108
|
-
form.save
|
|
145
|
+
assert_equal form.save, true
|
|
109
146
|
end
|
|
110
147
|
|
|
111
148
|
it "returns false when one or more models don't save successfully" do
|
|
@@ -119,21 +156,20 @@ class FormCompositionTest < MiniTest::Spec
|
|
|
119
156
|
requester.extend(Saveable)
|
|
120
157
|
band.extend(Saveable)
|
|
121
158
|
|
|
122
|
-
form.save
|
|
159
|
+
assert_equal form.save, false
|
|
123
160
|
end
|
|
124
161
|
end
|
|
125
162
|
end
|
|
126
163
|
|
|
127
|
-
|
|
128
164
|
class FormCompositionCollectionTest < MiniTest::Spec
|
|
129
165
|
Book = Struct.new(:id, :name)
|
|
130
166
|
Library = Struct.new(:id) do
|
|
131
167
|
def books
|
|
132
|
-
[Book.new(1,"My book")]
|
|
168
|
+
[Book.new(1, "My book")]
|
|
133
169
|
end
|
|
134
170
|
end
|
|
135
171
|
|
|
136
|
-
class LibraryForm <
|
|
172
|
+
class LibraryForm < TestForm
|
|
137
173
|
include Reform::Form::Composition
|
|
138
174
|
|
|
139
175
|
collection :books, on: :library do
|
|
@@ -142,8 +178,8 @@ class FormCompositionCollectionTest < MiniTest::Spec
|
|
|
142
178
|
end
|
|
143
179
|
end
|
|
144
180
|
|
|
145
|
-
let
|
|
146
|
-
let
|
|
181
|
+
let(:form) { LibraryForm.new(library: library) }
|
|
182
|
+
let(:library) { Library.new(2) }
|
|
147
183
|
|
|
148
|
-
it { form.save
|
|
184
|
+
it { form.save { |hash| assert_equal hash, { library: { "books" => [{ "id" => 1, "name" => "My book" }] } } } }
|
|
149
185
|
end
|
|
@@ -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,34 +1,34 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "test_helper"
|
|
2
2
|
|
|
3
3
|
class ContractTest < MiniTest::Spec
|
|
4
4
|
Song = Struct.new(:title, :album, :composer)
|
|
5
5
|
Album = Struct.new(:name, :duration, :songs, :artist)
|
|
6
6
|
Artist = Struct.new(:name)
|
|
7
7
|
|
|
8
|
-
class ArtistForm <
|
|
8
|
+
class ArtistForm < TestForm
|
|
9
9
|
property :name
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
class AlbumForm <
|
|
12
|
+
class AlbumForm < TestContract
|
|
13
13
|
property :name
|
|
14
14
|
|
|
15
15
|
properties :duration
|
|
16
16
|
properties :year, :style, readable: false
|
|
17
17
|
|
|
18
18
|
validation do
|
|
19
|
-
|
|
19
|
+
params { required(:name).filled }
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
collection :songs do
|
|
23
23
|
property :title
|
|
24
24
|
validation do
|
|
25
|
-
|
|
25
|
+
params { required(:title).filled }
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
property :composer do
|
|
29
29
|
property :name
|
|
30
30
|
validation do
|
|
31
|
-
|
|
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
|
@@ -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
|
|
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
|