reform 2.2.4 → 2.3.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +13 -7
  3. data/CHANGES.md +26 -4
  4. data/CONTRIBUTING.md +31 -0
  5. data/Gemfile +1 -12
  6. data/ISSUE_TEMPLATE.md +25 -0
  7. data/LICENSE.txt +1 -1
  8. data/README.md +3 -3
  9. data/lib/reform.rb +1 -0
  10. data/lib/reform/contract.rb +1 -11
  11. data/lib/reform/contract/validate.rb +49 -23
  12. data/lib/reform/errors.rb +49 -0
  13. data/lib/reform/form.rb +20 -5
  14. data/lib/reform/form/dry.rb +57 -29
  15. data/lib/reform/form/populator.rb +2 -16
  16. data/lib/reform/form/prepopulate.rb +1 -1
  17. data/lib/reform/form/validate.rb +10 -2
  18. data/lib/reform/result.rb +63 -0
  19. data/lib/reform/validation.rb +19 -13
  20. data/lib/reform/validation/groups.rb +11 -25
  21. data/lib/reform/version.rb +1 -1
  22. data/reform.gemspec +7 -6
  23. data/test/benchmarking.rb +39 -5
  24. data/test/call_test.rb +1 -1
  25. data/test/changed_test.rb +1 -1
  26. data/test/coercion_test.rb +2 -2
  27. data/test/composition_test.rb +47 -9
  28. data/test/contract_test.rb +5 -5
  29. data/test/default_test.rb +1 -1
  30. data/test/deserialize_test.rb +3 -3
  31. data/test/errors_test.rb +36 -21
  32. data/test/feature_test.rb +1 -1
  33. data/test/fixtures/dry_error_messages.yml +70 -23
  34. data/test/form_option_test.rb +3 -3
  35. data/test/form_test.rb +3 -3
  36. data/test/from_test.rb +2 -2
  37. data/test/inherit_test.rb +44 -51
  38. data/test/module_test.rb +12 -12
  39. data/test/parse_option_test.rb +40 -0
  40. data/test/parse_pipeline_test.rb +2 -2
  41. data/test/populate_test.rb +59 -19
  42. data/test/populator_skip_test.rb +9 -8
  43. data/test/prepopulator_test.rb +3 -3
  44. data/test/readable_test.rb +2 -2
  45. data/test/readonly_test.rb +1 -1
  46. data/test/reform_test.rb +16 -31
  47. data/test/save_test.rb +23 -8
  48. data/test/setup_test.rb +2 -2
  49. data/test/skip_if_test.rb +4 -4
  50. data/test/skip_setter_and_getter_test.rb +1 -1
  51. data/test/test_helper.rb +13 -10
  52. data/test/validate_test.rb +18 -18
  53. data/test/validation/dry_validation_test.rb +430 -117
  54. data/test/validation/result_test.rb +79 -0
  55. data/test/validation_library_provided_test.rb +16 -0
  56. data/test/virtual_test.rb +1 -1
  57. data/test/writeable_test.rb +31 -2
  58. metadata +42 -23
  59. data/gemfiles/Gemfile.disposable-0.3 +0 -6
  60. data/lib/reform/contract/errors.rb +0 -43
  61. data/lib/reform/form/mongoid.rb +0 -37
  62. data/lib/reform/form/orm.rb +0 -26
  63. data/lib/reform/mongoid.rb +0 -4
  64. data/test/deprecation_test.rb +0 -27
  65. data/test/validation/dry_test.rb +0 -60
  66. data/test/validation/errors.yml +0 -4
@@ -9,7 +9,7 @@ class ModuleInclusionTest < MiniTest::Spec
9
9
  property :title
10
10
 
11
11
  validation do
12
- key(:title).required
12
+ required(:title).filled
13
13
  end
14
14
 
15
15
  def id # gets mixed into Form, too.
@@ -22,7 +22,7 @@ class ModuleInclusionTest < MiniTest::Spec
22
22
  end
23
23
 
24
24
  validation do
25
- key(:band).required
25
+ required(:band).filled
26
26
  end
27
27
 
28
28
  include Dry::Types.module # allows using Types::* in module.
@@ -36,22 +36,22 @@ class ModuleInclusionTest < MiniTest::Spec
36
36
  collection :airplays do
37
37
  property :station
38
38
  validation do
39
- key(:station).required
39
+ required(:station).filled
40
40
  end
41
41
  end
42
42
  validation do
43
- key(:airplays).required
43
+ required(:airplays).filled
44
44
  end
45
45
  end
46
46
 
47
47
 
48
48
  # test:
49
49
  # by including BandPropertyForm into multiple classes we assure that options hashes don't get messed up by AM:V.
50
- class HitForm < Reform::Form
50
+ class HitForm < TestForm
51
51
  include BandPropertyForm
52
52
  end
53
53
 
54
- class SongForm < Reform::Form
54
+ class SongForm < TestForm
55
55
  include Coercion
56
56
  property :title
57
57
 
@@ -72,7 +72,7 @@ class ModuleInclusionTest < MiniTest::Spec
72
72
  it do
73
73
  form = SongForm.new(OpenStruct.new)
74
74
  form.validate({})
75
- form.errors.messages.must_equal({:band=>["is missing"]})
75
+ form.errors.messages.must_equal({:band=>["must be filled"]})
76
76
  end
77
77
 
78
78
  # coercion works
@@ -90,18 +90,18 @@ class ModuleInclusionTest < MiniTest::Spec
90
90
 
91
91
  property :name
92
92
  validation do
93
- key(:name).required
93
+ required(:name).filled
94
94
  end
95
95
  end
96
96
 
97
- class AlbumForm < Reform::Form
97
+ class AlbumForm < TestForm
98
98
  include AlbumFormModule
99
99
 
100
100
  # pp heritage
101
101
  property :band, :inherit => true do
102
102
  property :label
103
103
  validation do
104
- key(:label).required
104
+ required(:label).filled
105
105
  end
106
106
  end
107
107
  end
@@ -109,7 +109,7 @@ class ModuleInclusionTest < MiniTest::Spec
109
109
  it do
110
110
  form = AlbumForm.new(OpenStruct.new(:band => OpenStruct.new))
111
111
  form.validate({"band" => {}})
112
- form.errors.messages.must_equal({:band=>["must be filled"], :"band.title"=>["is missing"], :"band.label"=>["is missing"], :name=>["is missing"]})
112
+ form.errors.messages.must_equal({:"band.title"=>["must be filled"], :"band.label"=>["must be filled"], :name=>["must be filled"]})
113
113
  end
114
114
 
115
115
 
@@ -127,7 +127,7 @@ class ModuleInclusionTest < MiniTest::Spec
127
127
  end
128
128
  end
129
129
 
130
- class IncludingSongForm < Reform::Form
130
+ class IncludingSongForm < TestForm
131
131
  include SongModule
132
132
  end
133
133
 
@@ -0,0 +1,40 @@
1
+ require 'test_helper'
2
+
3
+ class ParseOptionTest < MiniTest::Spec
4
+ Comment = Struct.new(:content, :user)
5
+ User = Struct.new(:name)
6
+
7
+ class CommentForm < TestForm
8
+ property :content
9
+ property :user, parse: false
10
+ end
11
+
12
+ let (:current_user) { User.new("Peter") }
13
+ let (:form) { CommentForm.new(Comment.new, user: current_user) }
14
+
15
+ it do
16
+ form.user.must_equal current_user
17
+
18
+ lorem = "Lorem ipsum dolor sit amet..."
19
+ form.validate("content" => lorem, "user" => "not the current user")
20
+
21
+ form.content.must_equal lorem
22
+ form.user.must_equal current_user
23
+ end
24
+
25
+ describe "using ':parse' option doesn't override other ':deserialize' options" do
26
+ class ArticleCommentForm < TestForm
27
+ property :content
28
+ property :article, deserializer: { instance: "Instance" }
29
+ property :user, parse: false, deserializer: { instance: "Instance" }
30
+ end
31
+
32
+ it do
33
+ ArticleCommentForm.definitions.get(:user)[:deserializer][:writeable].must_equal false
34
+ ArticleCommentForm.definitions.get(:user)[:deserializer][:instance].must_equal "Instance"
35
+
36
+ ArticleCommentForm.definitions.get(:article)[:deserializer][:writeable].must_equal true
37
+ ArticleCommentForm.definitions.get(:article)[:deserializer][:instance].must_equal "Instance"
38
+ end
39
+ end
40
+ end
@@ -3,8 +3,8 @@ require "test_helper"
3
3
  class ParsePipelineTest < MiniTest::Spec
4
4
  Album = Struct.new(:name)
5
5
 
6
- class AlbumForm < Reform::Form
7
- property :name, deserializer: { parse_pipeline: ->(input, options) { Representable::Pipeline[->(input, options) { options[:represented].name = input.inspect }] } }
6
+ class AlbumForm < TestForm
7
+ property :name, deserializer: { parse_pipeline: ->(input, options) { Representable::Pipeline[->(ipt, opts) { opts[:represented].name = ipt.inspect }] } }
8
8
  end
9
9
 
10
10
  it "allows passing :parse_pipeline directly" do
@@ -5,27 +5,25 @@ class PopulatorTest < 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, populator: ->(options) { self.name = options[:fragment].reverse }
10
10
  validation do
11
- key(:name).required
11
+ required(:name).filled
12
12
  end
13
13
 
14
14
  collection :songs,
15
- populator: ->(options) {
16
- fragment, collection, index = options[:fragment], options[:model], options[:index]
17
-
18
- (item = collection[index]) ? item : collection.insert(index, Song.new) } do
15
+ populator: ->(fragment:, model:, index:, **) {
16
+ (item = model[index]) ? item : model.insert(index, Song.new) } do
19
17
 
20
18
  property :title
21
19
  validation do
22
- key(:title).required
20
+ required(:title).filled
23
21
  end
24
22
 
25
23
  property :composer, populator: ->(options) { options[:model] || self.composer= Artist.new } do
26
24
  property :name
27
25
  validation do
28
- key(:name).required
26
+ required(:name).filled
29
27
  end
30
28
  end
31
29
  end
@@ -107,7 +105,7 @@ end
107
105
  class PopulateWithMethodTest < Minitest::Spec
108
106
  Album = Struct.new(:title)
109
107
 
110
- class AlbumForm < Reform::Form
108
+ class AlbumForm < TestForm
111
109
  property :title, populator: :title!
112
110
 
113
111
  def title!(options)
@@ -118,9 +116,51 @@ class PopulateWithMethodTest < Minitest::Spec
118
116
  let (:form) { AlbumForm.new(Album.new) }
119
117
 
120
118
  it "runs populator method" do
121
- form.validate(
122
- "title" => "override me!"
123
- )
119
+ form.validate("title" => "override me!")
120
+
121
+ form.title.must_equal "!em edirrevo"
122
+ end
123
+ end
124
+
125
+ class PopulateWithCallableTest < Minitest::Spec
126
+ Album = Struct.new(:title)
127
+
128
+ class TitlePopulator
129
+ include Uber::Callable
130
+
131
+ def call(form, options)
132
+ form.title = options[:fragment].reverse
133
+ end
134
+ end
135
+
136
+ class AlbumForm < TestForm
137
+ property :title, populator: TitlePopulator.new
138
+ end
139
+
140
+ let (:form) { AlbumForm.new(Album.new) }
141
+
142
+ it "runs populator method" do
143
+ form.validate("title" => "override me!")
144
+
145
+ form.title.must_equal "!em edirrevo"
146
+ end
147
+ end
148
+
149
+ class PopulateWithProcTest < Minitest::Spec
150
+ Album = Struct.new(:title)
151
+
152
+ TitlePopulator = ->(options) do
153
+ options[:represented].title = options[:fragment].reverse
154
+ end
155
+
156
+ class AlbumForm < TestForm
157
+ property :title, populator: TitlePopulator
158
+ end
159
+
160
+ let (:form) { AlbumForm.new(Album.new) }
161
+
162
+ it "runs populator method" do
163
+ form.validate("title" => "override me!")
124
164
 
125
165
  form.title.must_equal "!em edirrevo"
126
166
  end
@@ -139,7 +179,7 @@ class PopulateIfEmptyTest < MiniTest::Spec
139
179
 
140
180
 
141
181
 
142
- class AlbumForm < Reform::Form
182
+ class AlbumForm < TestForm
143
183
  property :name
144
184
 
145
185
  collection :songs,
@@ -147,18 +187,18 @@ class PopulateIfEmptyTest < MiniTest::Spec
147
187
 
148
188
  property :title
149
189
  validation do
150
- key(:title).required
190
+ required(:title).filled
151
191
  end
152
192
 
153
193
  property :composer, populate_if_empty: :populate_composer! do # lambda works, too. in form context.
154
194
  property :name
155
195
  validation do
156
- key(:name).required
196
+ required(:name).filled
157
197
  end
158
198
  end
159
199
 
160
200
  private
161
- def populate_composer!(fragment, options)
201
+ def populate_composer!(options)
162
202
  Artist.new
163
203
  end
164
204
  end
@@ -222,7 +262,7 @@ class PopulateIfEmptyTest < MiniTest::Spec
222
262
  # test lambda block arguments.
223
263
  form.artist.model.args.to_s.must_equal "[{\"name\"=>\"From Autumn To Ashes\"}, nil]"
224
264
 
225
- album.artist.must_equal nil
265
+ assert_nil album.artist
226
266
  end
227
267
  end
228
268
 
@@ -237,7 +277,7 @@ class PopulateIfEmptyWithDeletionTest < MiniTest::Spec
237
277
  let (:album) { Album.new("The Dissent Of Man", [song, song2]) }
238
278
 
239
279
 
240
- class AlbumForm < Reform::Form
280
+ class AlbumForm < TestForm
241
281
  property :name
242
282
 
243
283
  collection :songs,
@@ -245,7 +285,7 @@ class PopulateIfEmptyWithDeletionTest < MiniTest::Spec
245
285
 
246
286
  property :title
247
287
  validation do
248
- key(:title).required
288
+ required(:title).filled
249
289
  end
250
290
  end
251
291
 
@@ -5,13 +5,14 @@ class PopulatorSkipTest < MiniTest::Spec
5
5
  Song = Struct.new(:title)
6
6
 
7
7
 
8
- class AlbumForm < Reform::Form
9
- collection :songs,
10
- populator: ->(options) {
11
- return skip! if options[:fragment][:title] == "Good"
12
- songs[options[:index]]
13
- } do
14
- property :title
8
+ class AlbumForm < TestForm
9
+ collection :songs, populator: :my_populator do
10
+ property :title
11
+ end
12
+
13
+ def my_populator(options)
14
+ return skip! if options[:fragment][:title] == "Good"
15
+ songs[options[:index]]
15
16
  end
16
17
  end
17
18
 
@@ -22,7 +23,7 @@ class PopulatorSkipTest < MiniTest::Spec
22
23
  form.validate(hash)
23
24
 
24
25
  form.songs.size.must_equal 2
25
- form.songs[0].title.must_equal nil
26
+ assert_nil form.songs[0].title
26
27
  form.songs[1].title.must_equal "Bad"
27
28
  end
28
29
  end
@@ -4,7 +4,7 @@ class PrepopulatorTest < MiniTest::Spec
4
4
  Song = Struct.new(:title, :band, :length)
5
5
  Band = Struct.new(:name)
6
6
 
7
- class AlbumForm < Reform::Form
7
+ class AlbumForm < TestForm
8
8
  property :title, prepopulator: ->(*){ self.title = "Another Day At Work" } # normal assignment.
9
9
  property :length
10
10
 
@@ -63,7 +63,7 @@ end
63
63
  class PrepopulateWithoutConfiguration < MiniTest::Spec
64
64
  Song = Struct.new(:title)
65
65
 
66
- class AlbumForm < Reform::Form
66
+ class AlbumForm < TestForm
67
67
  collection :songs do
68
68
  property :title
69
69
  end
@@ -83,7 +83,7 @@ class ManualPrepopulatorOverridingTest < MiniTest::Spec
83
83
  Song = Struct.new(:title, :band, :length)
84
84
  Band = Struct.new(:name)
85
85
 
86
- class AlbumForm < Reform::Form
86
+ class AlbumForm < TestForm
87
87
  property :title
88
88
  property :length
89
89
 
@@ -3,7 +3,7 @@ require 'test_helper'
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
 
@@ -11,7 +11,7 @@ class ReadableTest < MiniTest::Spec
11
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
 
@@ -1,7 +1,7 @@
1
1
  require "test_helper"
2
2
 
3
3
  class ReadonlyTest < MiniTest::Spec
4
- class SongForm < Reform::Form
4
+ class SongForm < TestForm
5
5
  property :artist
6
6
  property :title, writeable: false
7
7
  # TODO: what to do with virtual values?
@@ -6,12 +6,12 @@ class ReformTest < Minitest::Spec
6
6
 
7
7
  let (:form) { SongForm.new(comp) }
8
8
 
9
- class SongForm < Reform::Form
9
+ class SongForm < TestForm
10
10
  property :name
11
11
  property :title
12
12
 
13
13
  validation do
14
- key(:name).required
14
+ required(:name).filled
15
15
  end
16
16
  end
17
17
 
@@ -19,7 +19,7 @@ class ReformTest < Minitest::Spec
19
19
  let (:comp) { OpenStruct.new }
20
20
 
21
21
  it "returns empty fields" do
22
- form.title.must_equal nil
22
+ assert_nil form.title
23
23
  form.name.must_equal nil
24
24
  end
25
25
 
@@ -27,7 +27,7 @@ class ReformTest < Minitest::Spec
27
27
  it "returns filled-out fields" do
28
28
  form.validate("name" => "Duran Duran")
29
29
 
30
- form.title.must_equal nil
30
+ assert_nil form.title
31
31
  form.name.must_equal "Duran Duran"
32
32
  end
33
33
  end
@@ -63,18 +63,18 @@ class ReformTest < Minitest::Spec
63
63
  it "doesn't change model properties" do
64
64
  form.validate("name" => "Duran Duran")
65
65
 
66
- comp.name.must_equal nil # don't touch model, yet.
66
+ assert_nil comp.name # don't touch model, yet.
67
67
  end
68
68
 
69
69
  # TODO: test errors. test valid.
70
70
  describe "invalid input" do
71
- class ValidatingForm < Reform::Form
71
+ class ValidatingForm < TestForm
72
72
  property :name
73
73
  property :title
74
74
 
75
75
  validation do
76
- key(:name).required
77
- key(:title).required
76
+ required(:name).filled
77
+ required(:title).filled
78
78
  end
79
79
  end
80
80
  let (:form) { ValidatingForm.new(comp) }
@@ -85,26 +85,11 @@ class ReformTest < Minitest::Spec
85
85
 
86
86
  it "populates errors" do
87
87
  form.validate({})
88
- form.errors.messages.must_equal({:name=>["is missing"], :title=>["is missing"]})
88
+ form.errors.messages.must_equal({:name=>["must be filled"], :title=>["must be filled"]})
89
89
  end
90
90
  end
91
91
  end
92
92
 
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
93
  describe "#save" do
109
94
  let (:comp) { OpenStruct.new }
110
95
  let (:form) { SongForm.new(comp) }
@@ -115,7 +100,7 @@ class ReformTest < Minitest::Spec
115
100
  form.save
116
101
 
117
102
  comp.name.must_equal "Diesel Boy"
118
- comp.title.must_equal nil
103
+ assert_nil comp.title
119
104
  end
120
105
 
121
106
  describe "#save with block" do
@@ -126,7 +111,7 @@ class ReformTest < Minitest::Spec
126
111
  hash = map
127
112
  end
128
113
 
129
- hash.must_equal({"name"=>"Diesel Boy"})
114
+ hash.must_equal({"name"=>"Diesel Boy", "title" => nil})
130
115
  end
131
116
  end
132
117
  end
@@ -141,7 +126,7 @@ class ReformTest < Minitest::Spec
141
126
  class HitForm < SongForm
142
127
  property :position
143
128
  validation do
144
- key(:position).required
129
+ required(:position).filled
145
130
  end
146
131
  end
147
132
 
@@ -149,15 +134,15 @@ class ReformTest < Minitest::Spec
149
134
  it do
150
135
  form.validate({"title" => "The Body"})
151
136
  form.title.must_equal "The Body"
152
- form.position.must_equal nil
153
- form.errors.messages.must_equal({:name=>["is missing"], :position=>["is missing"]})
137
+ assert_nil form.position
138
+ form.errors.messages.must_equal({:name=>["must be filled"], :position=>["must be filled"]})
154
139
  end
155
140
  end
156
141
  end
157
142
 
158
143
 
159
144
  class OverridingAccessorsTest < BaseTest
160
- class SongForm < Reform::Form
145
+ class SongForm < TestForm
161
146
  property :title
162
147
 
163
148
  def title=(v) # used in #validate.
@@ -200,7 +185,7 @@ end
200
185
 
201
186
 
202
187
  class MethodInFormTest < MiniTest::Spec
203
- class AlbumForm < Reform::Form
188
+ class AlbumForm < TestForm
204
189
  property :title
205
190
 
206
191
  def title