reform 2.3.0.rc1 → 2.3.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
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