reform 2.2.4 → 2.3.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +5 -1
  3. data/.travis.yml +11 -6
  4. data/Appraisals +8 -0
  5. data/CHANGES.md +57 -4
  6. data/CONTRIBUTING.md +31 -0
  7. data/Gemfile +2 -16
  8. data/ISSUE_TEMPLATE.md +25 -0
  9. data/LICENSE.txt +1 -1
  10. data/README.md +5 -7
  11. data/Rakefile +16 -9
  12. data/gemfiles/0.13.0.gemfile +8 -0
  13. data/gemfiles/1.5.0.gemfile +9 -0
  14. data/lib/reform.rb +1 -0
  15. data/lib/reform/contract.rb +7 -17
  16. data/lib/reform/contract/custom_error.rb +41 -0
  17. data/lib/reform/contract/validate.rb +53 -23
  18. data/lib/reform/errors.rb +61 -0
  19. data/lib/reform/form.rb +36 -10
  20. data/lib/reform/form/call.rb +1 -1
  21. data/lib/reform/form/composition.rb +2 -2
  22. data/lib/reform/form/dry.rb +10 -58
  23. data/lib/reform/form/dry/input_hash.rb +37 -0
  24. data/lib/reform/form/dry/new_api.rb +45 -0
  25. data/lib/reform/form/dry/old_api.rb +61 -0
  26. data/lib/reform/form/populator.rb +11 -27
  27. data/lib/reform/form/prepopulate.rb +4 -3
  28. data/lib/reform/form/validate.rb +28 -13
  29. data/lib/reform/result.rb +90 -0
  30. data/lib/reform/validation.rb +19 -11
  31. data/lib/reform/validation/groups.rb +12 -27
  32. data/lib/reform/version.rb +1 -1
  33. data/reform.gemspec +14 -13
  34. data/test/benchmarking.rb +39 -6
  35. data/test/call_new_api.rb +23 -0
  36. data/test/call_old_api.rb +23 -0
  37. data/test/changed_test.rb +14 -14
  38. data/test/coercion_test.rb +57 -25
  39. data/test/composition_new_api.rb +186 -0
  40. data/test/composition_old_api.rb +184 -0
  41. data/test/contract/custom_error_test.rb +55 -0
  42. data/test/contract_new_api.rb +77 -0
  43. data/test/contract_old_api.rb +77 -0
  44. data/test/default_test.rb +4 -4
  45. data/test/deserialize_test.rb +17 -20
  46. data/test/errors_new_api.rb +225 -0
  47. data/test/errors_old_api.rb +230 -0
  48. data/test/feature_test.rb +10 -12
  49. data/test/fixtures/dry_error_messages.yml +73 -23
  50. data/test/fixtures/dry_new_api_error_messages.yml +104 -0
  51. data/test/form_new_api.rb +57 -0
  52. data/test/{form_test.rb → form_old_api.rb} +8 -8
  53. data/test/form_option_new_api.rb +24 -0
  54. data/test/{form_option_test.rb → form_option_old_api.rb} +5 -5
  55. data/test/from_test.rb +18 -22
  56. data/test/inherit_new_api.rb +105 -0
  57. data/test/inherit_old_api.rb +105 -0
  58. data/test/{module_test.rb → module_new_api.rb} +26 -31
  59. data/test/module_old_api.rb +146 -0
  60. data/test/parse_option_test.rb +40 -0
  61. data/test/parse_pipeline_test.rb +4 -4
  62. data/test/populate_new_api.rb +304 -0
  63. data/test/populate_old_api.rb +304 -0
  64. data/test/populator_skip_test.rb +11 -11
  65. data/test/prepopulator_test.rb +23 -24
  66. data/test/read_only_test.rb +12 -1
  67. data/test/readable_test.rb +9 -9
  68. data/test/reform_new_api.rb +204 -0
  69. data/test/{reform_test.rb → reform_old_api.rb} +44 -65
  70. data/test/save_new_api.rb +101 -0
  71. data/test/save_old_api.rb +101 -0
  72. data/test/setup_test.rb +17 -17
  73. data/test/skip_if_new_api.rb +85 -0
  74. data/test/skip_if_old_api.rb +92 -0
  75. data/test/skip_setter_and_getter_test.rb +9 -10
  76. data/test/test_helper.rb +25 -14
  77. data/test/validate_new_api.rb +453 -0
  78. data/test/{validate_test.rb → validate_old_api.rb} +121 -131
  79. data/test/validation/dry_validation_new_api.rb +835 -0
  80. data/test/validation/dry_validation_old_api.rb +772 -0
  81. data/test/validation/result_test.rb +77 -0
  82. data/test/validation_library_provided_test.rb +16 -0
  83. data/test/virtual_test.rb +47 -7
  84. data/test/writeable_test.rb +38 -9
  85. metadata +111 -56
  86. data/gemfiles/Gemfile.disposable-0.3 +0 -6
  87. data/lib/reform/contract/errors.rb +0 -43
  88. data/lib/reform/form/mongoid.rb +0 -37
  89. data/lib/reform/form/orm.rb +0 -26
  90. data/lib/reform/mongoid.rb +0 -4
  91. data/test/call_test.rb +0 -23
  92. data/test/composition_test.rb +0 -149
  93. data/test/contract_test.rb +0 -77
  94. data/test/deprecation_test.rb +0 -27
  95. data/test/errors_test.rb +0 -165
  96. data/test/inherit_test.rb +0 -119
  97. data/test/populate_test.rb +0 -270
  98. data/test/readonly_test.rb +0 -14
  99. data/test/save_test.rb +0 -89
  100. data/test/skip_if_test.rb +0 -74
  101. data/test/validation/dry_test.rb +0 -60
  102. data/test/validation/dry_validation_test.rb +0 -352
  103. data/test/validation/errors.yml +0 -4
@@ -1,30 +1,30 @@
1
- require 'test_helper'
1
+ require "test_helper"
2
2
 
3
3
  class ReadableTest < MiniTest::Spec
4
4
  Credentials = Struct.new(:password)
5
5
 
6
- class PasswordForm < Reform::Form
6
+ class PasswordForm < TestForm
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
- form.password.must_equal nil # password not read.
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
+ _(form.password).must_equal "123"
19
19
 
20
20
  form.sync
21
- cred.password.must_equal "123" # password written.
21
+ _(cred.password).must_equal "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
+ _(hash).must_equal("password" => "123")
29
29
  }
30
- end
30
+ end
@@ -0,0 +1,204 @@
1
+ require "test_helper"
2
+
3
+ class ReformTest < Minitest::Spec
4
+ let(:comp) { OpenStruct.new(name: "Duran Duran", title: "Rio") }
5
+
6
+ let(:form) { SongForm.new(comp) }
7
+
8
+ class SongForm < TestForm
9
+ property :name
10
+ property :title
11
+
12
+ validation do
13
+ params { required(:name).filled }
14
+ end
15
+ end
16
+
17
+ describe "(new) form with empty models" do
18
+ let(:comp) { OpenStruct.new }
19
+
20
+ it "returns empty fields" do
21
+ assert_nil form.title
22
+ _(form.name).must_be_nil
23
+ end
24
+
25
+ describe "and submitted values" do
26
+ it "returns filled-out fields" do
27
+ form.validate("name" => "Duran Duran")
28
+
29
+ assert_nil form.title
30
+ _(form.name).must_equal "Duran Duran"
31
+ end
32
+ end
33
+ end
34
+
35
+ describe "(edit) form with existing models" do
36
+ it "returns filled-out fields" do
37
+ _(form.name).must_equal "Duran Duran"
38
+ _(form.title).must_equal "Rio"
39
+ end
40
+ end
41
+
42
+ describe "#validate" do
43
+ let(:comp) { OpenStruct.new }
44
+
45
+ it "ignores unmapped fields in input" do
46
+ form.validate("name" => "Duran Duran", :genre => "80s")
47
+ assert_raises NoMethodError do
48
+ form.genre
49
+ end
50
+ end
51
+
52
+ it "returns true when valid" do
53
+ _(form.validate("name" => "Duran Duran")).must_equal true
54
+ end
55
+
56
+ it "exposes input via property accessors" do
57
+ form.validate("name" => "Duran Duran")
58
+
59
+ _(form.name).must_equal "Duran Duran"
60
+ end
61
+
62
+ it "doesn't change model properties" do
63
+ form.validate("name" => "Duran Duran")
64
+
65
+ assert_nil comp.name # don't touch model, yet.
66
+ end
67
+
68
+ # TODO: test errors. test valid.
69
+ describe "invalid input" do
70
+ class ValidatingForm < TestForm
71
+ property :name
72
+ property :title
73
+
74
+ validation do
75
+ params do
76
+ required(:name).filled
77
+ required(:title).filled
78
+ end
79
+ end
80
+ end
81
+ let(:form) { ValidatingForm.new(comp) }
82
+
83
+ it "returns false when invalid" do
84
+ _(form.validate({})).must_equal false
85
+ end
86
+
87
+ it "populates errors" do
88
+ form.validate({})
89
+ _(form.errors.messages).must_equal(name: ["must be filled"], title: ["must be filled"])
90
+ end
91
+ end
92
+ end
93
+
94
+ describe "#save" do
95
+ let(:comp) { OpenStruct.new }
96
+ let(:form) { SongForm.new(comp) }
97
+
98
+ before { form.validate("name" => "Diesel Boy") }
99
+
100
+ it "xxpushes data to models" do
101
+ form.save
102
+
103
+ _(comp.name).must_equal "Diesel Boy"
104
+ assert_nil comp.title
105
+ end
106
+
107
+ describe "#save with block" do
108
+ it do
109
+ hash = {}
110
+
111
+ form.save do |map|
112
+ hash = map
113
+ end
114
+
115
+ _(hash).must_equal("name" => "Diesel Boy", "title" => nil)
116
+ end
117
+ end
118
+ end
119
+
120
+ describe "#model" do
121
+ it { _(form.model).must_equal comp }
122
+ end
123
+
124
+ describe "inheritance" do
125
+ class HitForm < SongForm
126
+ property :position
127
+ validation do
128
+ params { required(:position).filled }
129
+ end
130
+ end
131
+
132
+ let(:form) { HitForm.new(OpenStruct.new()) }
133
+ it do
134
+ form.validate("title" => "The Body")
135
+ _(form.title).must_equal "The Body"
136
+ assert_nil form.position
137
+ _(form.errors.messages).must_equal(name: ["must be filled"], position: ["must be filled"])
138
+ end
139
+ end
140
+ end
141
+
142
+ class OverridingAccessorsTest < BaseTest
143
+ class SongForm < TestForm
144
+ property :title
145
+
146
+ def title=(v) # used in #validate.
147
+ super v * 2
148
+ end
149
+
150
+ def title # used in #sync.
151
+ super.downcase
152
+ end
153
+ end
154
+
155
+ let(:song) { Song.new("Pray") }
156
+ subject { SongForm.new(song) }
157
+
158
+ # override reader for presentation.
159
+ it { _(subject.title).must_equal "pray" }
160
+
161
+ describe "#save" do
162
+ before { subject.validate("title" => "Hey Little World") }
163
+
164
+ # reader always used
165
+ it { _(subject.title).must_equal "hey little worldhey little world" }
166
+
167
+ # the reader is not used when saving/syncing.
168
+ it do
169
+ subject.save do |hash|
170
+ _(hash["title"]).must_equal "Hey Little WorldHey Little World"
171
+ end
172
+ end
173
+
174
+ # no reader or writer used when saving/syncing.
175
+ it do
176
+ song.extend(Saveable)
177
+ subject.save
178
+ _(song.title).must_equal "Hey Little WorldHey Little World"
179
+ end
180
+ end
181
+ end
182
+
183
+ class MethodInFormTest < MiniTest::Spec
184
+ class AlbumForm < TestForm
185
+ property :title
186
+
187
+ def title
188
+ "The Suffer And The Witness"
189
+ end
190
+
191
+ property :hit do
192
+ property :title
193
+
194
+ def title
195
+ "Drones"
196
+ end
197
+ end
198
+ end
199
+
200
+ # methods can be used instead of created accessors.
201
+ subject { AlbumForm.new(OpenStruct.new(hit: OpenStruct.new)) }
202
+ it { _(subject.title).must_equal "The Suffer And The Witness" }
203
+ it { _(subject.hit.title).must_equal "Drones" }
204
+ end
@@ -1,47 +1,46 @@
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
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
+ _(form.name).must_equal "Duran Duran"
38
+ _(form.title).must_equal "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,71 +50,56 @@ 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
+ _(form.validate("name" => "Duran Duran")).must_equal 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
+ _(form.name).must_equal "Duran Duran"
61
60
  end
62
61
 
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
- form.validate({}).must_equal false
82
+ _(form.validate({})).must_equal false
84
83
  end
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
 
114
98
  it "xxpushes data to models" do
115
99
  form.save
116
100
 
117
- comp.name.must_equal "Diesel Boy"
118
- comp.title.must_equal nil
101
+ _(comp.name).must_equal "Diesel Boy"
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
- it { form.model.must_equal comp }
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
- form.title.must_equal "The Body"
152
- form.position.must_equal nil
153
- form.errors.messages.must_equal({:name=>["is missing"], :position=>["is missing"]})
133
+ _(form.title).must_equal "The Body"
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,23 +150,22 @@ 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
- it { subject.title.must_equal "pray" }
177
-
157
+ it { _(subject.title).must_equal "pray" }
178
158
 
179
159
  describe "#save" do
180
160
  before { subject.validate("title" => "Hey Little World") }
181
161
 
182
162
  # reader always used
183
- it { subject.title.must_equal "hey little worldhey little world" }
163
+ it { _(subject.title).must_equal "hey little worldhey little world" }
184
164
 
185
165
  # the reader is not used when saving/syncing.
186
166
  it do
187
167
  subject.save do |hash|
188
- hash["title"].must_equal "Hey Little WorldHey Little World"
168
+ _(hash["title"]).must_equal "Hey Little WorldHey Little World"
189
169
  end
190
170
  end
191
171
 
@@ -193,14 +173,13 @@ class OverridingAccessorsTest < BaseTest
193
173
  it do
194
174
  song.extend(Saveable)
195
175
  subject.save
196
- song.title.must_equal "Hey Little WorldHey Little World"
176
+ _(song.title).must_equal "Hey Little WorldHey Little World"
197
177
  end
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)) }
221
- it { subject.title.must_equal "The Suffer And The Witness" }
222
- it { subject.hit.title.must_equal "Drones" }
199
+ subject { AlbumForm.new(OpenStruct.new(hit: OpenStruct.new)) }
200
+ it { _(subject.title).must_equal "The Suffer And The Witness" }
201
+ it { _(subject.hit.title).must_equal "Drones" }
223
202
  end