reform 2.3.0.rc1 → 2.3.0.rc2

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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.rubocop.yml +30 -0
  4. data/.rubocop_todo.yml +460 -0
  5. data/.travis.yml +26 -11
  6. data/CHANGES.md +25 -2
  7. data/Gemfile +6 -3
  8. data/ISSUE_TEMPLATE.md +1 -1
  9. data/README.md +2 -4
  10. data/Rakefile +18 -9
  11. data/lib/reform/contract.rb +7 -7
  12. data/lib/reform/contract/custom_error.rb +41 -0
  13. data/lib/reform/contract/validate.rb +9 -5
  14. data/lib/reform/errors.rb +27 -15
  15. data/lib/reform/form.rb +22 -11
  16. data/lib/reform/form/call.rb +1 -1
  17. data/lib/reform/form/composition.rb +2 -2
  18. data/lib/reform/form/dry.rb +10 -86
  19. data/lib/reform/form/dry/input_hash.rb +37 -0
  20. data/lib/reform/form/dry/new_api.rb +58 -0
  21. data/lib/reform/form/dry/old_api.rb +61 -0
  22. data/lib/reform/form/populator.rb +9 -11
  23. data/lib/reform/form/prepopulate.rb +3 -2
  24. data/lib/reform/form/validate.rb +19 -12
  25. data/lib/reform/result.rb +36 -9
  26. data/lib/reform/validation.rb +10 -8
  27. data/lib/reform/validation/groups.rb +2 -3
  28. data/lib/reform/version.rb +1 -1
  29. data/reform.gemspec +10 -9
  30. data/test/benchmarking.rb +10 -11
  31. data/test/call_new_api.rb +23 -0
  32. data/test/{call_test.rb → call_old_api.rb} +3 -3
  33. data/test/changed_test.rb +7 -7
  34. data/test/coercion_test.rb +50 -18
  35. data/test/composition_new_api.rb +186 -0
  36. data/test/{composition_test.rb → composition_old_api.rb} +23 -26
  37. data/test/contract/custom_error_test.rb +55 -0
  38. data/test/contract_new_api.rb +77 -0
  39. data/test/{contract_test.rb → contract_old_api.rb} +8 -8
  40. data/test/default_test.rb +1 -1
  41. data/test/deserialize_test.rb +8 -11
  42. data/test/errors_new_api.rb +225 -0
  43. data/test/errors_old_api.rb +230 -0
  44. data/test/feature_test.rb +7 -9
  45. data/test/fixtures/dry_error_messages.yml +5 -2
  46. data/test/fixtures/dry_new_api_error_messages.yml +104 -0
  47. data/test/form_new_api.rb +57 -0
  48. data/test/{form_test.rb → form_old_api.rb} +2 -2
  49. data/test/form_option_new_api.rb +24 -0
  50. data/test/{form_option_test.rb → form_option_old_api.rb} +1 -1
  51. data/test/from_test.rb +8 -12
  52. data/test/inherit_new_api.rb +105 -0
  53. data/test/{inherit_test.rb → inherit_old_api.rb} +10 -17
  54. data/test/module_new_api.rb +137 -0
  55. data/test/{module_test.rb → module_old_api.rb} +19 -15
  56. data/test/parse_option_test.rb +5 -5
  57. data/test/parse_pipeline_test.rb +2 -2
  58. data/test/populate_new_api.rb +304 -0
  59. data/test/{populate_test.rb → populate_old_api.rb} +28 -34
  60. data/test/populator_skip_test.rb +1 -2
  61. data/test/prepopulator_test.rb +5 -6
  62. data/test/read_only_test.rb +12 -1
  63. data/test/readable_test.rb +5 -5
  64. data/test/reform_new_api.rb +204 -0
  65. data/test/{reform_test.rb → reform_old_api.rb} +17 -23
  66. data/test/save_new_api.rb +101 -0
  67. data/test/{save_test.rb → save_old_api.rb} +10 -13
  68. data/test/setup_test.rb +6 -6
  69. data/test/{skip_if_test.rb → skip_if_new_api.rb} +20 -9
  70. data/test/skip_if_old_api.rb +92 -0
  71. data/test/skip_setter_and_getter_test.rb +2 -3
  72. data/test/test_helper.rb +13 -5
  73. data/test/validate_new_api.rb +408 -0
  74. data/test/{validate_test.rb → validate_old_api.rb} +43 -53
  75. data/test/validation/dry_validation_new_api.rb +826 -0
  76. data/test/validation/{dry_validation_test.rb → dry_validation_old_api.rb} +223 -116
  77. data/test/validation/result_test.rb +20 -22
  78. data/test/validation_library_provided_test.rb +3 -3
  79. data/test/virtual_test.rb +46 -6
  80. data/test/writeable_test.rb +7 -7
  81. metadata +101 -51
  82. data/test/errors_test.rb +0 -180
  83. 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
@@ -26,4 +25,4 @@ class PopulatorSkipTest < MiniTest::Spec
26
25
  assert_nil form.songs[0].title
27
26
  form.songs[1].title.must_equal "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]
@@ -78,7 +78,6 @@ class PrepopulateWithoutConfiguration < MiniTest::Spec
78
78
  it { subject.songs.size.must_equal 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)
@@ -109,4 +108,4 @@ class ManualPrepopulatorOverridingTest < MiniTest::Spec
109
108
  form.hit.model.must_equal Song.new("Potemkin City Limits")
110
109
  form.hit.title.must_equal "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 { form.readonly?(:artist).must_equal false }
13
+ it { form.readonly?(:title).must_equal true }
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,8 +7,8 @@ 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.
@@ -25,6 +25,6 @@ class ReadableTest < MiniTest::Spec
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,10 +1,9 @@
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
@@ -16,11 +15,11 @@ class ReformTest < Minitest::Spec
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
+ form.name.must_be_nil
24
23
  end
25
24
 
26
25
  describe "and submitted values" do
@@ -28,7 +27,7 @@ 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
+ 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")
@@ -77,7 +76,7 @@ class ReformTest < Minitest::Spec
77
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,14 +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=>["must be filled"], :title=>["must be filled"]})
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
92
  describe "#save" do
94
- let (:comp) { OpenStruct.new }
95
- let (:form) { SongForm.new(comp) }
93
+ let(:comp) { OpenStruct.new }
94
+ let(:form) { SongForm.new(comp) }
96
95
 
97
96
  before { form.validate("name" => "Diesel Boy") }
98
97
 
@@ -111,17 +110,15 @@ class ReformTest < Minitest::Spec
111
110
  hash = map
112
111
  end
113
112
 
114
- hash.must_equal({"name"=>"Diesel Boy", "title" => nil})
113
+ hash.must_equal({"name" => "Diesel Boy", "title" => nil})
115
114
  end
116
115
  end
117
116
  end
118
117
 
119
-
120
118
  describe "#model" do
121
119
  it { form.model.must_equal comp }
122
120
  end
123
121
 
124
-
125
122
  describe "inheritance" do
126
123
  class HitForm < SongForm
127
124
  property :position
@@ -130,23 +127,22 @@ class ReformTest < Minitest::Spec
130
127
  end
131
128
  end
132
129
 
133
- let (:form) { HitForm.new(OpenStruct.new()) }
130
+ let(:form) { HitForm.new(OpenStruct.new()) }
134
131
  it do
135
132
  form.validate({"title" => "The Body"})
136
133
  form.title.must_equal "The Body"
137
134
  assert_nil form.position
138
- form.errors.messages.must_equal({:name=>["must be filled"], :position=>["must be filled"]})
135
+ form.errors.messages.must_equal({name: ["must be filled"], position: ["must be filled"]})
139
136
  end
140
137
  end
141
138
  end
142
139
 
143
-
144
140
  class OverridingAccessorsTest < BaseTest
145
141
  class SongForm < TestForm
146
142
  property :title
147
143
 
148
144
  def title=(v) # used in #validate.
149
- super v*2
145
+ super v * 2
150
146
  end
151
147
 
152
148
  def title # used in #sync.
@@ -154,13 +150,12 @@ class OverridingAccessorsTest < BaseTest
154
150
  end
155
151
  end
156
152
 
157
- let (:song) { Song.new("Pray") }
153
+ let(:song) { Song.new("Pray") }
158
154
  subject { SongForm.new(song) }
159
155
 
160
156
  # override reader for presentation.
161
157
  it { subject.title.must_equal "pray" }
162
158
 
163
-
164
159
  describe "#save" do
165
160
  before { subject.validate("title" => "Hey Little World") }
166
161
 
@@ -183,7 +178,6 @@ class OverridingAccessorsTest < BaseTest
183
178
  end
184
179
  end
185
180
 
186
-
187
181
  class MethodInFormTest < MiniTest::Spec
188
182
  class AlbumForm < TestForm
189
183
  property :title
@@ -202,7 +196,7 @@ class MethodInFormTest < MiniTest::Spec
202
196
  end
203
197
 
204
198
  # methods can be used instead of created accessors.
205
- subject { AlbumForm.new(OpenStruct.new(:hit => OpenStruct.new)) }
199
+ subject { AlbumForm.new(OpenStruct.new(hit: OpenStruct.new)) }
206
200
  it { subject.title.must_equal "The Suffer And The Witness" }
207
201
  it { subject.hit.title.must_equal "Drones" }
208
202
  end