reform 2.2.4 → 2.3.1

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 (99) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +5 -1
  3. data/.rubocop.yml +30 -0
  4. data/.rubocop_todo.yml +460 -0
  5. data/.travis.yml +11 -6
  6. data/Appraisals +8 -0
  7. data/CHANGES.md +54 -4
  8. data/CONTRIBUTING.md +31 -0
  9. data/Gemfile +2 -16
  10. data/ISSUE_TEMPLATE.md +25 -0
  11. data/LICENSE.txt +1 -1
  12. data/README.md +5 -7
  13. data/Rakefile +18 -9
  14. data/gemfiles/0.13.0.gemfile +8 -0
  15. data/gemfiles/1.5.0.gemfile +9 -0
  16. data/lib/reform.rb +1 -0
  17. data/lib/reform/contract.rb +7 -17
  18. data/lib/reform/contract/custom_error.rb +41 -0
  19. data/lib/reform/contract/validate.rb +53 -23
  20. data/lib/reform/errors.rb +61 -0
  21. data/lib/reform/form.rb +36 -10
  22. data/lib/reform/form/call.rb +1 -1
  23. data/lib/reform/form/composition.rb +2 -2
  24. data/lib/reform/form/dry.rb +10 -58
  25. data/lib/reform/form/dry/input_hash.rb +37 -0
  26. data/lib/reform/form/dry/new_api.rb +46 -0
  27. data/lib/reform/form/dry/old_api.rb +61 -0
  28. data/lib/reform/form/populator.rb +11 -27
  29. data/lib/reform/form/prepopulate.rb +4 -3
  30. data/lib/reform/form/validate.rb +28 -13
  31. data/lib/reform/result.rb +90 -0
  32. data/lib/reform/validation.rb +19 -11
  33. data/lib/reform/validation/groups.rb +12 -27
  34. data/lib/reform/version.rb +1 -1
  35. data/reform.gemspec +15 -13
  36. data/test/benchmarking.rb +39 -6
  37. data/test/call_new_api.rb +23 -0
  38. data/test/{call_test.rb → call_old_api.rb} +4 -4
  39. data/test/changed_test.rb +8 -8
  40. data/test/coercion_test.rb +51 -19
  41. data/test/composition_new_api.rb +186 -0
  42. data/test/{composition_test.rb → composition_old_api.rb} +66 -31
  43. data/test/contract/custom_error_test.rb +55 -0
  44. data/test/contract_new_api.rb +77 -0
  45. data/test/{contract_test.rb → contract_old_api.rb} +13 -13
  46. data/test/default_test.rb +2 -2
  47. data/test/deserialize_test.rb +11 -14
  48. data/test/errors_new_api.rb +225 -0
  49. data/test/errors_old_api.rb +230 -0
  50. data/test/feature_test.rb +8 -10
  51. data/test/fixtures/dry_error_messages.yml +73 -23
  52. data/test/fixtures/dry_new_api_error_messages.yml +104 -0
  53. data/test/form_new_api.rb +57 -0
  54. data/test/{form_test.rb → form_old_api.rb} +5 -5
  55. data/test/form_option_new_api.rb +24 -0
  56. data/test/{form_option_test.rb → form_option_old_api.rb} +4 -4
  57. data/test/from_test.rb +9 -13
  58. data/test/inherit_new_api.rb +105 -0
  59. data/test/inherit_old_api.rb +105 -0
  60. data/test/{module_test.rb → module_new_api.rb} +20 -25
  61. data/test/module_old_api.rb +146 -0
  62. data/test/parse_option_test.rb +40 -0
  63. data/test/parse_pipeline_test.rb +3 -3
  64. data/test/populate_new_api.rb +304 -0
  65. data/test/{populate_test.rb → populate_old_api.rb} +83 -49
  66. data/test/populator_skip_test.rb +9 -9
  67. data/test/prepopulator_test.rb +8 -9
  68. data/test/read_only_test.rb +12 -1
  69. data/test/readable_test.rb +7 -7
  70. data/test/reform_new_api.rb +204 -0
  71. data/test/{reform_test.rb → reform_old_api.rb} +30 -51
  72. data/test/save_new_api.rb +101 -0
  73. data/test/{save_test.rb → save_old_api.rb} +32 -20
  74. data/test/setup_test.rb +8 -8
  75. data/test/{skip_if_test.rb → skip_if_new_api.rb} +23 -12
  76. data/test/skip_if_old_api.rb +92 -0
  77. data/test/skip_setter_and_getter_test.rb +3 -4
  78. data/test/test_helper.rb +25 -14
  79. data/test/validate_new_api.rb +408 -0
  80. data/test/{validate_test.rb → validate_old_api.rb} +59 -69
  81. data/test/validation/dry_validation_new_api.rb +836 -0
  82. data/test/validation/dry_validation_old_api.rb +772 -0
  83. data/test/validation/result_test.rb +77 -0
  84. data/test/validation_library_provided_test.rb +16 -0
  85. data/test/virtual_test.rb +47 -7
  86. data/test/writeable_test.rb +35 -6
  87. metadata +127 -56
  88. data/gemfiles/Gemfile.disposable-0.3 +0 -6
  89. data/lib/reform/contract/errors.rb +0 -43
  90. data/lib/reform/form/mongoid.rb +0 -37
  91. data/lib/reform/form/orm.rb +0 -26
  92. data/lib/reform/mongoid.rb +0 -4
  93. data/test/deprecation_test.rb +0 -27
  94. data/test/errors_test.rb +0 -165
  95. data/test/inherit_test.rb +0 -119
  96. data/test/readonly_test.rb +0 -14
  97. data/test/validation/dry_test.rb +0 -60
  98. data/test/validation/dry_validation_test.rb +0 -352
  99. data/test/validation/errors.yml +0 -4
@@ -1,34 +1,33 @@
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
- class SongForm < Reform::Form
8
+ class SongForm < TestForm
10
9
  property :name
11
10
  property :title
12
11
 
13
12
  validation do
14
- key(:name).required
13
+ 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
- form.title.must_equal nil
23
- form.name.must_equal nil
21
+ assert_nil form.title
22
+ form.name.must_be_nil
24
23
  end
25
24
 
26
25
  describe "and submitted values" do
27
26
  it "returns filled-out fields" do
28
27
  form.validate("name" => "Duran Duran")
29
28
 
30
- form.title.must_equal nil
31
- form.name.must_equal "Duran Duran"
29
+ assert_nil form.title
30
+ form.name.must_equal "Duran Duran"
32
31
  end
33
32
  end
34
33
  end
@@ -41,7 +40,7 @@ class ReformTest < Minitest::Spec
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")
@@ -63,21 +62,21 @@ class ReformTest < Minitest::Spec
63
62
  it "doesn't change model properties" do
64
63
  form.validate("name" => "Duran Duran")
65
64
 
66
- comp.name.must_equal nil # don't touch model, yet.
65
+ assert_nil comp.name # don't touch model, yet.
67
66
  end
68
67
 
69
68
  # TODO: test errors. test valid.
70
69
  describe "invalid input" do
71
- class ValidatingForm < Reform::Form
70
+ class ValidatingForm < TestForm
72
71
  property :name
73
72
  property :title
74
73
 
75
74
  validation do
76
- key(:name).required
77
- key(:title).required
75
+ required(:name).filled
76
+ required(:title).filled
78
77
  end
79
78
  end
80
- let (:form) { ValidatingForm.new(comp) }
79
+ let(:form) { ValidatingForm.new(comp) }
81
80
 
82
81
  it "returns false when invalid" do
83
82
  form.validate({}).must_equal false
@@ -85,29 +84,14 @@ class ReformTest < Minitest::Spec
85
84
 
86
85
  it "populates errors" do
87
86
  form.validate({})
88
- form.errors.messages.must_equal({:name=>["is missing"], :title=>["is missing"]})
87
+ form.errors.messages.must_equal({name: ["must be filled"], title: ["must be filled"]})
89
88
  end
90
89
  end
91
90
  end
92
91
 
93
- # FIXME: add this test to reform-rails.
94
- describe "#errors" do
95
- before { form.validate({})}
96
-
97
- it { form.errors.must_be_kind_of Reform::Contract::Errors }
98
-
99
- it { form.errors.messages.must_equal({}) }
100
-
101
- it do
102
- form.validate({"name"=>""})
103
- form.errors.messages.must_equal({:name=>["must be filled"]})
104
- end
105
- end
106
-
107
-
108
92
  describe "#save" do
109
- let (:comp) { OpenStruct.new }
110
- let (:form) { SongForm.new(comp) }
93
+ let(:comp) { OpenStruct.new }
94
+ let(:form) { SongForm.new(comp) }
111
95
 
112
96
  before { form.validate("name" => "Diesel Boy") }
113
97
 
@@ -115,7 +99,7 @@ class ReformTest < Minitest::Spec
115
99
  form.save
116
100
 
117
101
  comp.name.must_equal "Diesel Boy"
118
- comp.title.must_equal nil
102
+ assert_nil comp.title
119
103
  end
120
104
 
121
105
  describe "#save with block" do
@@ -126,42 +110,39 @@ class ReformTest < Minitest::Spec
126
110
  hash = map
127
111
  end
128
112
 
129
- hash.must_equal({"name"=>"Diesel Boy"})
113
+ hash.must_equal({"name" => "Diesel Boy", "title" => nil})
130
114
  end
131
115
  end
132
116
  end
133
117
 
134
-
135
118
  describe "#model" do
136
119
  it { form.model.must_equal comp }
137
120
  end
138
121
 
139
-
140
122
  describe "inheritance" do
141
123
  class HitForm < SongForm
142
124
  property :position
143
125
  validation do
144
- key(:position).required
126
+ required(:position).filled
145
127
  end
146
128
  end
147
129
 
148
- let (:form) { HitForm.new(OpenStruct.new()) }
130
+ let(:form) { HitForm.new(OpenStruct.new()) }
149
131
  it do
150
132
  form.validate({"title" => "The Body"})
151
133
  form.title.must_equal "The Body"
152
- form.position.must_equal nil
153
- form.errors.messages.must_equal({:name=>["is missing"], :position=>["is missing"]})
134
+ assert_nil form.position
135
+ form.errors.messages.must_equal({name: ["must be filled"], position: ["must be filled"]})
154
136
  end
155
137
  end
156
138
  end
157
139
 
158
-
159
140
  class OverridingAccessorsTest < BaseTest
160
- class SongForm < Reform::Form
141
+ class SongForm < TestForm
161
142
  property :title
162
143
 
163
144
  def title=(v) # used in #validate.
164
- super v*2
145
+ super v * 2
165
146
  end
166
147
 
167
148
  def title # used in #sync.
@@ -169,13 +150,12 @@ class OverridingAccessorsTest < BaseTest
169
150
  end
170
151
  end
171
152
 
172
- let (:song) { Song.new("Pray") }
153
+ let(:song) { Song.new("Pray") }
173
154
  subject { SongForm.new(song) }
174
155
 
175
156
  # override reader for presentation.
176
157
  it { subject.title.must_equal "pray" }
177
158
 
178
-
179
159
  describe "#save" do
180
160
  before { subject.validate("title" => "Hey Little World") }
181
161
 
@@ -198,9 +178,8 @@ class OverridingAccessorsTest < BaseTest
198
178
  end
199
179
  end
200
180
 
201
-
202
181
  class MethodInFormTest < MiniTest::Spec
203
- class AlbumForm < Reform::Form
182
+ class AlbumForm < TestForm
204
183
  property :title
205
184
 
206
185
  def title
@@ -217,7 +196,7 @@ class MethodInFormTest < MiniTest::Spec
217
196
  end
218
197
 
219
198
  # methods can be used instead of created accessors.
220
- subject { AlbumForm.new(OpenStruct.new(:hit => OpenStruct.new)) }
199
+ subject { AlbumForm.new(OpenStruct.new(hit: OpenStruct.new)) }
221
200
  it { subject.title.must_equal "The Suffer And The Witness" }
222
201
  it { subject.hit.title.must_equal "Drones" }
223
202
  end
@@ -0,0 +1,101 @@
1
+ require "test_helper"
2
+
3
+ class SaveTest < BaseTest
4
+ Song = Struct.new(:title, :album, :composer)
5
+ Album = Struct.new(:name, :songs, :artist)
6
+ Artist = Struct.new(:name)
7
+
8
+ class AlbumForm < TestForm
9
+ property :name
10
+ validation do
11
+ params { required(:name).filled }
12
+ end
13
+
14
+ collection :songs do
15
+ property :title
16
+ validation do
17
+ params { required(:title).filled }
18
+ end
19
+
20
+ property :composer do
21
+ property :name
22
+ validation do
23
+ params { required(:name).filled }
24
+ end
25
+ end
26
+ end
27
+
28
+ property :artist, save: false do
29
+ property :name
30
+ end
31
+ end
32
+
33
+ module Saveable
34
+ def save
35
+ @saved = true
36
+ end
37
+
38
+ def saved?
39
+ defined?(@saved) && @saved
40
+ end
41
+ end
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) }
48
+
49
+ let(:form) { AlbumForm.new(album) }
50
+
51
+ it do
52
+ form.validate("songs" => [{"title" => "Fixed"}])
53
+
54
+ form.save
55
+
56
+ album.saved?.must_equal true
57
+ album.songs[0].title.must_equal "Fixed"
58
+ album.songs[0].saved?.must_equal true
59
+ assert_nil album.artist.saved?
60
+ end
61
+
62
+ describe "#sync with block" do
63
+ it do
64
+ form = AlbumForm.new(Album.new("Greatest Hits"))
65
+
66
+ form.validate(name: nil) # nil-out the title.
67
+
68
+ nested_hash = nil
69
+ form.sync do |hash|
70
+ nested_hash = hash
71
+ end
72
+
73
+ nested_hash.must_equal({"name" => nil, "artist" => nil})
74
+ end
75
+ end
76
+ end
77
+
78
+ # class SaveWithDynamicOptionsTest < MiniTest::Spec
79
+ # Song = Struct.new(:id, :title, :length) do
80
+ # include Saveable
81
+ # end
82
+
83
+ # class SongForm < TestForm
84
+ # property :title#, save: false
85
+ # property :length, virtual: true
86
+ # end
87
+
88
+ # let(:song) { Song.new }
89
+ # let(:form) { SongForm.new(song) }
90
+
91
+ # # we have access to original input value and outside parameters.
92
+ # it "xxx" do
93
+ # form.validate("title" => "A Poor Man's Memory", "length" => 10)
94
+ # length_seconds = 120
95
+ # form.save(length: lambda { |value, options| form.model.id = "#{value}: #{length_seconds}" })
96
+
97
+ # song.title.must_equal "A Poor Man's Memory"
98
+ # assert_nil song.length
99
+ # song.id.must_equal "10: 120"
100
+ # end
101
+ # end
@@ -1,26 +1,26 @@
1
- require 'test_helper'
1
+ require "test_helper"
2
2
 
3
3
  class SaveTest < BaseTest
4
4
  Song = Struct.new(:title, :album, :composer)
5
5
  Album = Struct.new(:name, :songs, :artist)
6
6
  Artist = Struct.new(:name)
7
7
 
8
- class AlbumForm < Reform::Form
8
+ class AlbumForm < TestForm
9
9
  property :name
10
10
  validation do
11
- key(:name).required
11
+ required(:name).filled
12
12
  end
13
13
 
14
14
  collection :songs do
15
15
  property :title
16
16
  validation do
17
- key(:title).required
17
+ required(:title).filled
18
18
  end
19
19
 
20
20
  property :composer do
21
21
  property :name
22
22
  validation do
23
- key(:name).required
23
+ required(:name).filled
24
24
  end
25
25
  end
26
26
  end
@@ -36,19 +36,17 @@ class SaveTest < BaseTest
36
36
  end
37
37
 
38
38
  def saved?
39
- @saved
39
+ defined?(@saved) && @saved
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"}])
@@ -58,23 +56,37 @@ class SaveTest < BaseTest
58
56
  album.saved?.must_equal true
59
57
  album.songs[0].title.must_equal "Fixed"
60
58
  album.songs[0].saved?.must_equal true
61
- album.artist.saved?.must_equal nil
59
+ assert_nil album.artist.saved?
62
60
  end
63
- end
64
61
 
62
+ describe "#sync with block" do
63
+ it do
64
+ form = AlbumForm.new(Album.new("Greatest Hits"))
65
+
66
+ form.validate(name: nil) # nil-out the title.
67
+
68
+ nested_hash = nil
69
+ form.sync do |hash|
70
+ nested_hash = hash
71
+ end
72
+
73
+ nested_hash.must_equal({"name" => nil, "artist" => nil})
74
+ end
75
+ end
76
+ end
65
77
 
66
78
  # class SaveWithDynamicOptionsTest < MiniTest::Spec
67
79
  # Song = Struct.new(:id, :title, :length) do
68
80
  # include Saveable
69
81
  # end
70
82
 
71
- # class SongForm < Reform::Form
83
+ # class SongForm < TestForm
72
84
  # property :title#, save: false
73
85
  # property :length, virtual: true
74
86
  # end
75
87
 
76
- # let (:song) { Song.new }
77
- # let (:form) { SongForm.new(song) }
88
+ # let(:song) { Song.new }
89
+ # let(:form) { SongForm.new(song) }
78
90
 
79
91
  # # we have access to original input value and outside parameters.
80
92
  # it "xxx" do
@@ -83,7 +95,7 @@ end
83
95
  # form.save(length: lambda { |value, options| form.model.id = "#{value}: #{length_seconds}" })
84
96
 
85
97
  # song.title.must_equal "A Poor Man's Memory"
86
- # song.length.must_equal nil
98
+ # assert_nil song.length
87
99
  # song.id.must_equal "10: 120"
88
100
  # end
89
101
  # end
@@ -5,7 +5,7 @@ class SetupTest < MiniTest::Spec
5
5
  Album = Struct.new(:name, :songs, :artist)
6
6
  Artist = Struct.new(:name)
7
7
 
8
- class AlbumForm < Reform::Form
8
+ class AlbumForm < TestForm
9
9
  property :name
10
10
  collection :songs do
11
11
  property :title
@@ -20,20 +20,20 @@ class SetupTest < MiniTest::Spec
20
20
  end
21
21
  end
22
22
 
23
- let (:song) { Song.new("Broken") }
24
- let (:song_with_composer) { Song.new("Resist Stance", nil, composer) }
25
- let (:composer) { Artist.new("Greg Graffin") }
26
- let (:artist) { Artist.new("Bad Religion") }
23
+ let(:song) { Song.new("Broken") }
24
+ let(:song_with_composer) { Song.new("Resist Stance", nil, composer) }
25
+ let(:composer) { Artist.new("Greg Graffin") }
26
+ let(:artist) { Artist.new("Bad Religion") }
27
27
 
28
28
  describe "with nested objects" do
29
- let (:album) { Album.new("The Dissent Of Man", [song, song_with_composer], artist) }
29
+ let(:album) { Album.new("The Dissent Of Man", [song, song_with_composer], artist) }
30
30
 
31
31
  it do
32
32
  form = AlbumForm.new(album)
33
33
 
34
34
  form.name.must_equal "The Dissent Of Man"
35
35
  form.songs[0].title.must_equal "Broken"
36
- form.songs[0].composer.must_equal nil
36
+ assert_nil form.songs[0].composer
37
37
  form.songs[1].title.must_equal "Resist Stance"
38
38
  form.songs[1].composer.name.must_equal "Greg Graffin"
39
39
  form.artist.name.must_equal "Bad Religion"
@@ -45,4 +45,4 @@ class SetupTest < MiniTest::Spec
45
45
  form.artist.must_be_kind_of Reform::Form
46
46
  end
47
47
  end
48
- end
48
+ end