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.
Files changed (65) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +5 -1
  3. data/.travis.yml +7 -11
  4. data/CHANGES.md +43 -3
  5. data/Gemfile +2 -5
  6. data/ISSUE_TEMPLATE.md +1 -1
  7. data/LICENSE.txt +1 -1
  8. data/README.md +7 -9
  9. data/Rakefile +6 -10
  10. data/lib/reform/contract.rb +7 -7
  11. data/lib/reform/contract/custom_error.rb +41 -0
  12. data/lib/reform/contract/validate.rb +10 -6
  13. data/lib/reform/errors.rb +27 -15
  14. data/lib/reform/form.rb +22 -11
  15. data/lib/reform/form/call.rb +1 -1
  16. data/lib/reform/form/composition.rb +2 -2
  17. data/lib/reform/form/dry.rb +22 -60
  18. data/lib/reform/form/dry/input_hash.rb +37 -0
  19. data/lib/reform/form/populator.rb +9 -11
  20. data/lib/reform/form/prepopulate.rb +3 -2
  21. data/lib/reform/form/validate.rb +19 -12
  22. data/lib/reform/result.rb +36 -9
  23. data/lib/reform/validation.rb +10 -8
  24. data/lib/reform/validation/groups.rb +2 -4
  25. data/lib/reform/version.rb +1 -1
  26. data/reform.gemspec +9 -9
  27. data/test/benchmarking.rb +10 -11
  28. data/test/call_test.rb +8 -8
  29. data/test/changed_test.rb +13 -13
  30. data/test/coercion_test.rb +56 -24
  31. data/test/composition_test.rb +49 -51
  32. data/test/contract/custom_error_test.rb +55 -0
  33. data/test/contract_test.rb +18 -18
  34. data/test/default_test.rb +3 -3
  35. data/test/deserialize_test.rb +14 -17
  36. data/test/docs/validation_test.rb +134 -0
  37. data/test/errors_test.rb +131 -86
  38. data/test/feature_test.rb +9 -11
  39. data/test/fixtures/dry_error_messages.yml +65 -52
  40. data/test/form_option_test.rb +3 -3
  41. data/test/form_test.rb +6 -6
  42. data/test/from_test.rb +17 -21
  43. data/test/inherit_test.rb +28 -35
  44. data/test/module_test.rb +23 -28
  45. data/test/parse_option_test.rb +12 -12
  46. data/test/parse_pipeline_test.rb +3 -3
  47. data/test/populate_test.rb +146 -93
  48. data/test/populator_skip_test.rb +3 -4
  49. data/test/prepopulator_test.rb +20 -21
  50. data/test/read_only_test.rb +12 -1
  51. data/test/readable_test.rb +7 -7
  52. data/test/reform_test.rb +38 -42
  53. data/test/save_test.rb +16 -19
  54. data/test/setup_test.rb +15 -15
  55. data/test/skip_if_test.rb +30 -19
  56. data/test/skip_setter_and_getter_test.rb +8 -9
  57. data/test/test_helper.rb +12 -5
  58. data/test/validate_test.rb +160 -140
  59. data/test/validation/dry_validation_test.rb +407 -236
  60. data/test/validation/result_test.rb +29 -31
  61. data/test/validation_library_provided_test.rb +3 -3
  62. data/test/virtual_test.rb +46 -6
  63. data/test/writeable_test.rb +13 -13
  64. metadata +32 -29
  65. data/test/readonly_test.rb +0 -14
@@ -4,7 +4,6 @@ class PopulatorSkipTest < MiniTest::Spec
4
4
  Album = Struct.new(:songs)
5
5
  Song = Struct.new(:title)
6
6
 
7
-
8
7
  class AlbumForm < TestForm
9
8
  collection :songs, populator: :my_populator do
10
9
  property :title
@@ -22,8 +21,8 @@ class PopulatorSkipTest < MiniTest::Spec
22
21
 
23
22
  form.validate(hash)
24
23
 
25
- form.songs.size.must_equal 2
24
+ assert_equal form.songs.size, 2
26
25
  assert_nil form.songs[0].title
27
- form.songs[1].title.must_equal "Bad"
26
+ assert_equal form.songs[1].title, "Bad"
28
27
  end
29
- end
28
+ end
@@ -1,17 +1,17 @@
1
- require 'test_helper'
1
+ require "test_helper"
2
2
 
3
3
  class PrepopulatorTest < MiniTest::Spec
4
4
  Song = Struct.new(:title, :band, :length)
5
5
  Band = Struct.new(:name)
6
6
 
7
7
  class AlbumForm < TestForm
8
- property :title, prepopulator: ->(*){ self.title = "Another Day At Work" } # normal assignment.
8
+ property :title, prepopulator: ->(*) { self.title = "Another Day At Work" } # normal assignment.
9
9
  property :length
10
10
 
11
11
  property :hit, prepopulator: ->(options) { self.hit = Song.new(options[:title]) } do # use user options.
12
12
  property :title
13
13
 
14
- property :band, prepopulator: ->(options){ self.band = my_band(options[:title]) } do # invoke your own code.
14
+ property :band, prepopulator: ->(options) { self.band = my_band(options[:title]) } do # invoke your own code.
15
15
  property :name
16
16
  end
17
17
 
@@ -24,7 +24,7 @@ class PrepopulatorTest < MiniTest::Spec
24
24
  property :title
25
25
  end
26
26
 
27
- private
27
+ private
28
28
  def prepopulate_songs!(options)
29
29
  if songs == nil
30
30
  self.songs = [Song.new, Song.new]
@@ -37,25 +37,25 @@ class PrepopulatorTest < MiniTest::Spec
37
37
  it do
38
38
  form = AlbumForm.new(OpenStruct.new(length: 1)).prepopulate!(title: "Potemkin City Limits")
39
39
 
40
- form.length.must_equal 1
41
- form.title.must_equal "Another Day At Work"
42
- form.hit.model.must_equal Song.new("Potemkin City Limits")
43
- form.songs.size.must_equal 2
44
- form.songs[0].model.must_equal Song.new
45
- form.songs[1].model.must_equal Song.new
46
- form.songs[1].model.must_equal Song.new
40
+ assert_equal form.length, 1
41
+ assert_equal form.title, "Another Day At Work"
42
+ assert_equal form.hit.model, Song.new("Potemkin City Limits")
43
+ assert_equal form.songs.size, 2
44
+ assert_equal form.songs[0].model, Song.new
45
+ assert_equal form.songs[1].model, Song.new
46
+ assert_equal form.songs[1].model, Song.new
47
47
  # prepopulate works more than 1 level, recursive.
48
48
  # it also passes options properly down there.
49
- form.hit.band.model.must_equal Band.new("Potemkin City Limits")
49
+ assert_equal form.hit.band.model, Band.new("Potemkin City Limits")
50
50
  end
51
51
 
52
52
  # add to existing collection.
53
53
  it do
54
54
  form = AlbumForm.new(OpenStruct.new(songs: [Song.new])).prepopulate!
55
55
 
56
- form.songs.size.must_equal 2
57
- form.songs[0].model.must_equal Song.new
58
- form.songs[1].model.must_equal Song.new
56
+ assert_equal form.songs.size, 2
57
+ assert_equal form.songs[0].model, Song.new
58
+ assert_equal form.songs[1].model, Song.new
59
59
  end
60
60
  end
61
61
 
@@ -75,10 +75,9 @@ class PrepopulateWithoutConfiguration < MiniTest::Spec
75
75
 
76
76
  subject { AlbumForm.new(OpenStruct.new(songs: [], hit: nil)).prepopulate! }
77
77
 
78
- it { subject.songs.size.must_equal 0 }
78
+ it { assert_equal subject.songs.size, 0 }
79
79
  end
80
80
 
81
-
82
81
  class ManualPrepopulatorOverridingTest < MiniTest::Spec
83
82
  Song = Struct.new(:title, :band, :length)
84
83
  Band = Struct.new(:name)
@@ -105,8 +104,8 @@ class ManualPrepopulatorOverridingTest < MiniTest::Spec
105
104
  it do
106
105
  form = AlbumForm.new(OpenStruct.new(length: 1)).prepopulate!(title: "Potemkin City Limits")
107
106
 
108
- form.length.must_equal 1
109
- form.hit.model.must_equal Song.new("Potemkin City Limits")
110
- form.hit.title.must_equal "Potemkin City Limits"
107
+ assert_equal form.length, 1
108
+ assert_equal form.hit.model, Song.new("Potemkin City Limits")
109
+ assert_equal form.hit.title, "Potemkin City Limits"
111
110
  end
112
- end
111
+ end
@@ -1,3 +1,14 @@
1
- require 'test_helper'
1
+ require "test_helper"
2
2
 
3
+ class ReadonlyTest < MiniTest::Spec
4
+ class SongForm < TestForm
5
+ property :artist
6
+ property :title, writeable: false
7
+ # TODO: what to do with virtual values?
8
+ end
3
9
 
10
+ let(:form) { SongForm.new(OpenStruct.new) }
11
+
12
+ it { refute form.readonly?(:artist) }
13
+ it { assert form.readonly?(:title) }
14
+ end
@@ -1,4 +1,4 @@
1
- require 'test_helper'
1
+ require "test_helper"
2
2
 
3
3
  class ReadableTest < MiniTest::Spec
4
4
  Credentials = Struct.new(:password)
@@ -7,24 +7,24 @@ class ReadableTest < MiniTest::Spec
7
7
  property :password, readable: false
8
8
  end
9
9
 
10
- let (:cred) { Credentials.new }
11
- let (:form) { PasswordForm.new(cred) }
10
+ let(:cred) { Credentials.new }
11
+ let(:form) { PasswordForm.new(cred) }
12
12
 
13
13
  it {
14
14
  assert_nil form.password # password not read.
15
15
 
16
16
  form.validate("password" => "123")
17
17
 
18
- form.password.must_equal "123"
18
+ assert_equal form.password, "123"
19
19
 
20
20
  form.sync
21
- cred.password.must_equal "123" # password written.
21
+ assert_equal cred.password, "123" # password written.
22
22
 
23
23
  hash = {}
24
24
  form.save do |nested|
25
25
  hash = nested
26
26
  end
27
27
 
28
- hash.must_equal("password"=> "123")
28
+ assert_equal hash, "password" => "123"
29
29
  }
30
- end
30
+ end
data/test/reform_test.rb CHANGED
@@ -1,26 +1,25 @@
1
- require 'test_helper'
1
+ require "test_helper"
2
2
 
3
- # TODO: this test should be removed.
4
3
  class ReformTest < Minitest::Spec
5
- let (:comp) { OpenStruct.new(:name => "Duran Duran", :title => "Rio") }
4
+ let(:comp) { OpenStruct.new(name: "Duran Duran", title: "Rio") }
6
5
 
7
- let (:form) { SongForm.new(comp) }
6
+ let(:form) { SongForm.new(comp) }
8
7
 
9
8
  class SongForm < TestForm
10
9
  property :name
11
10
  property :title
12
11
 
13
12
  validation do
14
- required(:name).filled
13
+ params { required(:name).filled }
15
14
  end
16
15
  end
17
16
 
18
17
  describe "(new) form with empty models" do
19
- let (:comp) { OpenStruct.new }
18
+ let(:comp) { OpenStruct.new }
20
19
 
21
20
  it "returns empty fields" do
22
21
  assert_nil form.title
23
- form.name.must_equal nil
22
+ assert_nil form.name
24
23
  end
25
24
 
26
25
  describe "and submitted values" do
@@ -28,20 +27,20 @@ class ReformTest < Minitest::Spec
28
27
  form.validate("name" => "Duran Duran")
29
28
 
30
29
  assert_nil form.title
31
- form.name.must_equal "Duran Duran"
30
+ assert_equal form.name, "Duran Duran"
32
31
  end
33
32
  end
34
33
  end
35
34
 
36
35
  describe "(edit) form with existing models" do
37
36
  it "returns filled-out fields" do
38
- form.name.must_equal "Duran Duran"
39
- form.title.must_equal "Rio"
37
+ assert_equal form.name, "Duran Duran"
38
+ assert_equal form.title, "Rio"
40
39
  end
41
40
  end
42
41
 
43
42
  describe "#validate" do
44
- let (:comp) { OpenStruct.new }
43
+ let(:comp) { OpenStruct.new }
45
44
 
46
45
  it "ignores unmapped fields in input" do
47
46
  form.validate("name" => "Duran Duran", :genre => "80s")
@@ -51,13 +50,13 @@ class ReformTest < Minitest::Spec
51
50
  end
52
51
 
53
52
  it "returns true when valid" do
54
- form.validate("name" => "Duran Duran").must_equal true
53
+ assert_equal form.validate("name" => "Duran Duran"), true
55
54
  end
56
55
 
57
56
  it "exposes input via property accessors" do
58
57
  form.validate("name" => "Duran Duran")
59
58
 
60
- form.name.must_equal "Duran Duran"
59
+ assert_equal form.name, "Duran Duran"
61
60
  end
62
61
 
63
62
  it "doesn't change model properties" do
@@ -73,33 +72,35 @@ class ReformTest < Minitest::Spec
73
72
  property :title
74
73
 
75
74
  validation do
76
- required(:name).filled
77
- required(:title).filled
75
+ params do
76
+ required(:name).filled
77
+ required(:title).filled
78
+ end
78
79
  end
79
80
  end
80
- let (:form) { ValidatingForm.new(comp) }
81
+ let(:form) { ValidatingForm.new(comp) }
81
82
 
82
83
  it "returns false when invalid" do
83
- form.validate({}).must_equal false
84
+ assert_equal form.validate({}), false
84
85
  end
85
86
 
86
87
  it "populates errors" do
87
88
  form.validate({})
88
- form.errors.messages.must_equal({:name=>["must be filled"], :title=>["must be filled"]})
89
+ assert_equal form.errors.messages, name: ["must be filled"], title: ["must be filled"]
89
90
  end
90
91
  end
91
92
  end
92
93
 
93
94
  describe "#save" do
94
- let (:comp) { OpenStruct.new }
95
- let (:form) { SongForm.new(comp) }
95
+ let(:comp) { OpenStruct.new }
96
+ let(:form) { SongForm.new(comp) }
96
97
 
97
98
  before { form.validate("name" => "Diesel Boy") }
98
99
 
99
100
  it "xxpushes data to models" do
100
101
  form.save
101
102
 
102
- comp.name.must_equal "Diesel Boy"
103
+ assert_equal comp.name, "Diesel Boy"
103
104
  assert_nil comp.title
104
105
  end
105
106
 
@@ -111,42 +112,39 @@ class ReformTest < Minitest::Spec
111
112
  hash = map
112
113
  end
113
114
 
114
- hash.must_equal({"name"=>"Diesel Boy", "title" => nil})
115
+ assert_equal hash, "name" => "Diesel Boy", "title" => nil
115
116
  end
116
117
  end
117
118
  end
118
119
 
119
-
120
120
  describe "#model" do
121
- it { form.model.must_equal comp }
121
+ it { assert_equal form.model, comp }
122
122
  end
123
123
 
124
-
125
124
  describe "inheritance" do
126
125
  class HitForm < SongForm
127
126
  property :position
128
127
  validation do
129
- required(:position).filled
128
+ params { required(:position).filled }
130
129
  end
131
130
  end
132
131
 
133
- let (:form) { HitForm.new(OpenStruct.new()) }
132
+ let(:form) { HitForm.new(OpenStruct.new()) }
134
133
  it do
135
- form.validate({"title" => "The Body"})
136
- form.title.must_equal "The Body"
134
+ form.validate("title" => "The Body")
135
+ assert_equal form.title, "The Body"
137
136
  assert_nil form.position
138
- form.errors.messages.must_equal({:name=>["must be filled"], :position=>["must be filled"]})
137
+ assert_equal form.errors.messages, name: ["must be filled"], position: ["must be filled"]
139
138
  end
140
139
  end
141
140
  end
142
141
 
143
-
144
142
  class OverridingAccessorsTest < BaseTest
145
143
  class SongForm < TestForm
146
144
  property :title
147
145
 
148
146
  def title=(v) # used in #validate.
149
- super v*2
147
+ super v * 2
150
148
  end
151
149
 
152
150
  def title # used in #sync.
@@ -154,23 +152,22 @@ class OverridingAccessorsTest < BaseTest
154
152
  end
155
153
  end
156
154
 
157
- let (:song) { Song.new("Pray") }
155
+ let(:song) { Song.new("Pray") }
158
156
  subject { SongForm.new(song) }
159
157
 
160
158
  # override reader for presentation.
161
- it { subject.title.must_equal "pray" }
162
-
159
+ it { assert_equal subject.title, "pray" }
163
160
 
164
161
  describe "#save" do
165
162
  before { subject.validate("title" => "Hey Little World") }
166
163
 
167
164
  # reader always used
168
- it { subject.title.must_equal "hey little worldhey little world" }
165
+ it { assert_equal subject.title, "hey little worldhey little world" }
169
166
 
170
167
  # the reader is not used when saving/syncing.
171
168
  it do
172
169
  subject.save do |hash|
173
- hash["title"].must_equal "Hey Little WorldHey Little World"
170
+ assert_equal hash["title"], "Hey Little WorldHey Little World"
174
171
  end
175
172
  end
176
173
 
@@ -178,12 +175,11 @@ class OverridingAccessorsTest < BaseTest
178
175
  it do
179
176
  song.extend(Saveable)
180
177
  subject.save
181
- song.title.must_equal "Hey Little WorldHey Little World"
178
+ assert_equal song.title, "Hey Little WorldHey Little World"
182
179
  end
183
180
  end
184
181
  end
185
182
 
186
-
187
183
  class MethodInFormTest < MiniTest::Spec
188
184
  class AlbumForm < TestForm
189
185
  property :title
@@ -202,7 +198,7 @@ class MethodInFormTest < MiniTest::Spec
202
198
  end
203
199
 
204
200
  # methods can be used instead of created accessors.
205
- subject { AlbumForm.new(OpenStruct.new(:hit => OpenStruct.new)) }
206
- it { subject.title.must_equal "The Suffer And The Witness" }
207
- it { subject.hit.title.must_equal "Drones" }
201
+ subject { AlbumForm.new(OpenStruct.new(hit: OpenStruct.new)) }
202
+ it { assert_equal subject.title, "The Suffer And The Witness" }
203
+ it { assert_equal subject.hit.title, "Drones" }
208
204
  end
data/test/save_test.rb CHANGED
@@ -1,4 +1,4 @@
1
- require 'test_helper'
1
+ require "test_helper"
2
2
 
3
3
  class SaveTest < BaseTest
4
4
  Song = Struct.new(:title, :album, :composer)
@@ -8,19 +8,19 @@ class SaveTest < BaseTest
8
8
  class AlbumForm < TestForm
9
9
  property :name
10
10
  validation do
11
- required(:name).filled
11
+ params { required(:name).filled }
12
12
  end
13
13
 
14
14
  collection :songs do
15
15
  property :title
16
16
  validation do
17
- required(:title).filled
17
+ params { required(:title).filled }
18
18
  end
19
19
 
20
20
  property :composer do
21
21
  property :name
22
22
  validation do
23
- required(:name).filled
23
+ params { required(:name).filled }
24
24
  end
25
25
  end
26
26
  end
@@ -40,24 +40,22 @@ class SaveTest < BaseTest
40
40
  end
41
41
  end
42
42
 
43
+ let(:song) { Song.new("Broken").extend(Saveable) }
44
+ # let(:song_with_composer) { Song.new("Resist Stance", nil, composer).extend(Saveable) }
45
+ let(:composer) { Artist.new("Greg Graffin").extend(Saveable) }
46
+ let(:artist) { Artist.new("Bad Religion").extend(Saveable).extend(Saveable) }
47
+ let(:album) { Album.new("The Dissent Of Man", [song], artist).extend(Saveable) }
43
48
 
44
- let (:song) { Song.new("Broken").extend(Saveable) }
45
- # let (:song_with_composer) { Song.new("Resist Stance", nil, composer).extend(Saveable) }
46
- let (:composer) { Artist.new("Greg Graffin").extend(Saveable) }
47
- let (:artist) { Artist.new("Bad Religion").extend(Saveable).extend(Saveable) }
48
- let (:album) { Album.new("The Dissent Of Man", [song], artist).extend(Saveable) }
49
-
50
- let (:form) { AlbumForm.new(album) }
51
-
49
+ let(:form) { AlbumForm.new(album) }
52
50
 
53
51
  it do
54
52
  form.validate("songs" => [{"title" => "Fixed"}])
55
53
 
56
54
  form.save
57
55
 
58
- album.saved?.must_equal true
59
- album.songs[0].title.must_equal "Fixed"
60
- album.songs[0].saved?.must_equal true
56
+ assert album.saved?
57
+ assert_equal album.songs[0].title, "Fixed"
58
+ assert album.songs[0].saved?
61
59
  assert_nil album.artist.saved?
62
60
  end
63
61
 
@@ -72,12 +70,11 @@ class SaveTest < BaseTest
72
70
  nested_hash = hash
73
71
  end
74
72
 
75
- nested_hash.must_equal({"name"=>nil, "artist"=>nil})
73
+ assert_equal nested_hash, "name" => nil, "artist" => nil
76
74
  end
77
75
  end
78
76
  end
79
77
 
80
-
81
78
  # class SaveWithDynamicOptionsTest < MiniTest::Spec
82
79
  # Song = Struct.new(:id, :title, :length) do
83
80
  # include Saveable
@@ -88,8 +85,8 @@ end
88
85
  # property :length, virtual: true
89
86
  # end
90
87
 
91
- # let (:song) { Song.new }
92
- # let (:form) { SongForm.new(song) }
88
+ # let(:song) { Song.new }
89
+ # let(:form) { SongForm.new(song) }
93
90
 
94
91
  # # we have access to original input value and outside parameters.
95
92
  # it "xxx" do