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.
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