reform 2.2.4 → 2.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +5 -1
  3. data/.rubocop.yml +30 -0
  4. data/.rubocop_todo.yml +460 -0
  5. data/.travis.yml +11 -6
  6. data/Appraisals +8 -0
  7. data/CHANGES.md +54 -4
  8. data/CONTRIBUTING.md +31 -0
  9. data/Gemfile +2 -16
  10. data/ISSUE_TEMPLATE.md +25 -0
  11. data/LICENSE.txt +1 -1
  12. data/README.md +5 -7
  13. data/Rakefile +18 -9
  14. data/gemfiles/0.13.0.gemfile +8 -0
  15. data/gemfiles/1.5.0.gemfile +9 -0
  16. data/lib/reform.rb +1 -0
  17. data/lib/reform/contract.rb +7 -17
  18. data/lib/reform/contract/custom_error.rb +41 -0
  19. data/lib/reform/contract/validate.rb +53 -23
  20. data/lib/reform/errors.rb +61 -0
  21. data/lib/reform/form.rb +36 -10
  22. data/lib/reform/form/call.rb +1 -1
  23. data/lib/reform/form/composition.rb +2 -2
  24. data/lib/reform/form/dry.rb +10 -58
  25. data/lib/reform/form/dry/input_hash.rb +37 -0
  26. data/lib/reform/form/dry/new_api.rb +46 -0
  27. data/lib/reform/form/dry/old_api.rb +61 -0
  28. data/lib/reform/form/populator.rb +11 -27
  29. data/lib/reform/form/prepopulate.rb +4 -3
  30. data/lib/reform/form/validate.rb +28 -13
  31. data/lib/reform/result.rb +90 -0
  32. data/lib/reform/validation.rb +19 -11
  33. data/lib/reform/validation/groups.rb +12 -27
  34. data/lib/reform/version.rb +1 -1
  35. data/reform.gemspec +15 -13
  36. data/test/benchmarking.rb +39 -6
  37. data/test/call_new_api.rb +23 -0
  38. data/test/{call_test.rb → call_old_api.rb} +4 -4
  39. data/test/changed_test.rb +8 -8
  40. data/test/coercion_test.rb +51 -19
  41. data/test/composition_new_api.rb +186 -0
  42. data/test/{composition_test.rb → composition_old_api.rb} +66 -31
  43. data/test/contract/custom_error_test.rb +55 -0
  44. data/test/contract_new_api.rb +77 -0
  45. data/test/{contract_test.rb → contract_old_api.rb} +13 -13
  46. data/test/default_test.rb +2 -2
  47. data/test/deserialize_test.rb +11 -14
  48. data/test/errors_new_api.rb +225 -0
  49. data/test/errors_old_api.rb +230 -0
  50. data/test/feature_test.rb +8 -10
  51. data/test/fixtures/dry_error_messages.yml +73 -23
  52. data/test/fixtures/dry_new_api_error_messages.yml +104 -0
  53. data/test/form_new_api.rb +57 -0
  54. data/test/{form_test.rb → form_old_api.rb} +5 -5
  55. data/test/form_option_new_api.rb +24 -0
  56. data/test/{form_option_test.rb → form_option_old_api.rb} +4 -4
  57. data/test/from_test.rb +9 -13
  58. data/test/inherit_new_api.rb +105 -0
  59. data/test/inherit_old_api.rb +105 -0
  60. data/test/{module_test.rb → module_new_api.rb} +20 -25
  61. data/test/module_old_api.rb +146 -0
  62. data/test/parse_option_test.rb +40 -0
  63. data/test/parse_pipeline_test.rb +3 -3
  64. data/test/populate_new_api.rb +304 -0
  65. data/test/{populate_test.rb → populate_old_api.rb} +83 -49
  66. data/test/populator_skip_test.rb +9 -9
  67. data/test/prepopulator_test.rb +8 -9
  68. data/test/read_only_test.rb +12 -1
  69. data/test/readable_test.rb +7 -7
  70. data/test/reform_new_api.rb +204 -0
  71. data/test/{reform_test.rb → reform_old_api.rb} +30 -51
  72. data/test/save_new_api.rb +101 -0
  73. data/test/{save_test.rb → save_old_api.rb} +32 -20
  74. data/test/setup_test.rb +8 -8
  75. data/test/{skip_if_test.rb → skip_if_new_api.rb} +23 -12
  76. data/test/skip_if_old_api.rb +92 -0
  77. data/test/skip_setter_and_getter_test.rb +3 -4
  78. data/test/test_helper.rb +25 -14
  79. data/test/validate_new_api.rb +408 -0
  80. data/test/{validate_test.rb → validate_old_api.rb} +59 -69
  81. data/test/validation/dry_validation_new_api.rb +836 -0
  82. data/test/validation/dry_validation_old_api.rb +772 -0
  83. data/test/validation/result_test.rb +77 -0
  84. data/test/validation_library_provided_test.rb +16 -0
  85. data/test/virtual_test.rb +47 -7
  86. data/test/writeable_test.rb +35 -6
  87. metadata +127 -56
  88. data/gemfiles/Gemfile.disposable-0.3 +0 -6
  89. data/lib/reform/contract/errors.rb +0 -43
  90. data/lib/reform/form/mongoid.rb +0 -37
  91. data/lib/reform/form/orm.rb +0 -26
  92. data/lib/reform/mongoid.rb +0 -4
  93. data/test/deprecation_test.rb +0 -27
  94. data/test/errors_test.rb +0 -165
  95. data/test/inherit_test.rb +0 -119
  96. data/test/readonly_test.rb +0 -14
  97. data/test/validation/dry_test.rb +0 -60
  98. data/test/validation/dry_validation_test.rb +0 -352
  99. data/test/validation/errors.yml +0 -4
@@ -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
19
-
15
+ populator: ->(fragment:, model:, index:, **) {
16
+ (item = model[index]) ? item : model.insert(index, Song.new)
17
+ } do
20
18
  property :title
21
19
  validation do
22
- key(:title).required
20
+ required(:title).filled
23
21
  end
24
22
 
25
- property :composer, populator: ->(options) { options[:model] || self.composer= Artist.new } do
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
@@ -37,13 +35,13 @@ class PopulatorTest < MiniTest::Spec
37
35
  end
38
36
  end
39
37
 
40
- let (:song) { Song.new("Broken") }
41
- let (:song_with_composer) { Song.new("Resist Stance", nil, composer) }
42
- let (:composer) { Artist.new("Greg Graffin") }
43
- let (:artist) { Artist.new("Bad Religion") }
44
- let (:album) { Album.new("The Dissent Of Man", [song, song_with_composer], artist) }
38
+ let(:song) { Song.new("Broken") }
39
+ let(:song_with_composer) { Song.new("Resist Stance", nil, composer) }
40
+ let(:composer) { Artist.new("Greg Graffin") }
41
+ let(:artist) { Artist.new("Bad Religion") }
42
+ let(:album) { Album.new("The Dissent Of Man", [song, song_with_composer], artist) }
45
43
 
46
- let (:form) { AlbumForm.new(album) }
44
+ let(:form) { AlbumForm.new(album) }
47
45
 
48
46
  it "runs populator on scalar" do
49
47
  form.validate(
@@ -93,7 +91,6 @@ class PopulatorTest < MiniTest::Spec
93
91
  form.songs.size.must_equal 4
94
92
  form.artist.name.must_equal "Bad Religion"
95
93
 
96
-
97
94
  # model has not changed, yet.
98
95
  album.name.must_equal "The Dissent Of Man"
99
96
  album.songs[0].title.must_equal "Broken"
@@ -107,7 +104,7 @@ end
107
104
  class PopulateWithMethodTest < Minitest::Spec
108
105
  Album = Struct.new(:title)
109
106
 
110
- class AlbumForm < Reform::Form
107
+ class AlbumForm < TestForm
111
108
  property :title, populator: :title!
112
109
 
113
110
  def title!(options)
@@ -115,12 +112,54 @@ class PopulateWithMethodTest < Minitest::Spec
115
112
  end
116
113
  end
117
114
 
118
- let (:form) { AlbumForm.new(Album.new) }
115
+ let(:form) { AlbumForm.new(Album.new) }
119
116
 
120
117
  it "runs populator method" do
121
- form.validate(
122
- "title" => "override me!"
123
- )
118
+ form.validate("title" => "override me!")
119
+
120
+ form.title.must_equal "!em edirrevo"
121
+ end
122
+ end
123
+
124
+ class PopulateWithCallableTest < Minitest::Spec
125
+ Album = Struct.new(:title)
126
+
127
+ class TitlePopulator
128
+ include Uber::Callable
129
+
130
+ def call(form, options)
131
+ form.title = options[:fragment].reverse
132
+ end
133
+ end
134
+
135
+ class AlbumForm < TestForm
136
+ property :title, populator: TitlePopulator.new
137
+ end
138
+
139
+ let(:form) { AlbumForm.new(Album.new) }
140
+
141
+ it "runs populator method" do
142
+ form.validate("title" => "override me!")
143
+
144
+ form.title.must_equal "!em edirrevo"
145
+ end
146
+ end
147
+
148
+ class PopulateWithProcTest < Minitest::Spec
149
+ Album = Struct.new(:title)
150
+
151
+ TitlePopulator = ->(options) do
152
+ options[:represented].title = options[:fragment].reverse
153
+ end
154
+
155
+ class AlbumForm < TestForm
156
+ property :title, populator: TitlePopulator
157
+ end
158
+
159
+ let(:form) { AlbumForm.new(Album.new) }
160
+
161
+ it "runs populator method" do
162
+ form.validate("title" => "override me!")
124
163
 
125
164
  form.title.must_equal "!em edirrevo"
126
165
  end
@@ -131,15 +170,13 @@ class PopulateIfEmptyTest < MiniTest::Spec
131
170
  Album = Struct.new(:name, :songs, :artist)
132
171
  Artist = Struct.new(:name)
133
172
 
134
- let (:song) { Song.new("Broken") }
135
- let (:song_with_composer) { Song.new("Resist Stance", nil, composer) }
136
- let (:composer) { Artist.new("Greg Graffin") }
137
- let (:artist) { Artist.new("Bad Religion") }
138
- let (:album) { Album.new("The Dissent Of Man", [song, song_with_composer], artist) }
139
-
140
-
173
+ let(:song) { Song.new("Broken") }
174
+ let(:song_with_composer) { Song.new("Resist Stance", nil, composer) }
175
+ let(:composer) { Artist.new("Greg Graffin") }
176
+ let(:artist) { Artist.new("Bad Religion") }
177
+ let(:album) { Album.new("The Dissent Of Man", [song, song_with_composer], artist) }
141
178
 
142
- class AlbumForm < Reform::Form
179
+ class AlbumForm < TestForm
143
180
  property :name
144
181
 
145
182
  collection :songs,
@@ -147,42 +184,42 @@ class PopulateIfEmptyTest < MiniTest::Spec
147
184
 
148
185
  property :title
149
186
  validation do
150
- key(:title).required
187
+ required(:title).filled
151
188
  end
152
189
 
153
190
  property :composer, populate_if_empty: :populate_composer! do # lambda works, too. in form context.
154
191
  property :name
155
192
  validation do
156
- key(:name).required
193
+ required(:name).filled
157
194
  end
158
195
  end
159
196
 
160
197
  private
161
- def populate_composer!(fragment, options)
198
+ def populate_composer!(options)
162
199
  Artist.new
163
200
  end
164
201
  end
165
202
 
166
- property :artist, populate_if_empty: lambda { |args| create_artist(args[:fragment], args[:user_options]) } do # methods work, too.
203
+ property :artist, populate_if_empty: ->(args) { create_artist(args[:fragment], args[:user_options]) } do # methods work, too.
167
204
  property :name
168
205
  end
169
206
 
170
- private
207
+ private
171
208
  class Sting < Artist
172
209
  attr_accessor :args
173
210
  end
174
211
  def create_artist(input, user_options)
175
- Sting.new.tap { |artist| artist.args=([input, user_options].to_s) }
212
+ Sting.new.tap { |artist| artist.args = ([input, user_options].to_s) }
176
213
  end
177
214
  end
178
215
 
179
- let (:form) { AlbumForm.new(album) }
216
+ let(:form) { AlbumForm.new(album) }
180
217
 
181
218
  it do
182
219
  form.songs.size.must_equal 2
183
220
 
184
221
  form.validate(
185
- "songs" => [{"title" => "Fallout"}, {"title" => "Roxanne"},
222
+ "songs" => [{"title" => "Fallout"}, {"title" => "Roxanne"},
186
223
  {"title" => "Rime Of The Ancient Mariner"}, # new song.
187
224
  {"title" => "Re-Education", "composer" => {"name" => "Rise Against"}}], # new song with new composer.
188
225
  ).must_equal true
@@ -201,7 +238,6 @@ class PopulateIfEmptyTest < MiniTest::Spec
201
238
  form.songs.size.must_equal 4
202
239
  form.artist.name.must_equal "Bad Religion"
203
240
 
204
-
205
241
  # model has not changed, yet.
206
242
  album.name.must_equal "The Dissent Of Man"
207
243
  album.songs[0].title.must_equal "Broken"
@@ -222,22 +258,20 @@ class PopulateIfEmptyTest < MiniTest::Spec
222
258
  # test lambda block arguments.
223
259
  form.artist.model.args.to_s.must_equal "[{\"name\"=>\"From Autumn To Ashes\"}, nil]"
224
260
 
225
- album.artist.must_equal nil
261
+ assert_nil album.artist
226
262
  end
227
263
  end
228
264
 
229
-
230
265
  # delete songs while deserializing.
231
266
  class PopulateIfEmptyWithDeletionTest < MiniTest::Spec
232
267
  Song = Struct.new(:title, :album, :composer)
233
268
  Album = Struct.new(:name, :songs, :artist)
234
269
 
235
- let (:song) { Song.new("Broken") }
236
- let (:song2) { Song.new("Resist Stance") }
237
- let (:album) { Album.new("The Dissent Of Man", [song, song2]) }
238
-
270
+ let(:song) { Song.new("Broken") }
271
+ let(:song2) { Song.new("Resist Stance") }
272
+ let(:album) { Album.new("The Dissent Of Man", [song, song2]) }
239
273
 
240
- class AlbumForm < Reform::Form
274
+ class AlbumForm < TestForm
241
275
  property :name
242
276
 
243
277
  collection :songs,
@@ -245,7 +279,7 @@ class PopulateIfEmptyWithDeletionTest < MiniTest::Spec
245
279
 
246
280
  property :title
247
281
  validation do
248
- key(:title).required
282
+ required(:title).filled
249
283
  end
250
284
  end
251
285
 
@@ -255,11 +289,11 @@ class PopulateIfEmptyWithDeletionTest < MiniTest::Spec
255
289
  end
256
290
  end
257
291
 
258
- let (:form) { AlbumForm.new(album) }
292
+ let(:form) { AlbumForm.new(album) }
259
293
 
260
294
  it do
261
295
  form.validate(
262
- "songs" => [{"title" => "Broken, delete me!"}, {"title" => "Roxanne"}]
296
+ "songs" => [{"title" => "Broken, delete me!"}, {"title" => "Roxanne"}]
263
297
  ).must_equal true
264
298
 
265
299
  form.errors.messages.inspect.must_equal "{}"
@@ -4,14 +4,14 @@ class PopulatorSkipTest < MiniTest::Spec
4
4
  Album = Struct.new(:songs)
5
5
  Song = Struct.new(:title)
6
6
 
7
+ class AlbumForm < TestForm
8
+ collection :songs, populator: :my_populator do
9
+ property :title
10
+ end
7
11
 
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
12
+ def my_populator(options)
13
+ return skip! if options[:fragment][:title] == "Good"
14
+ songs[options[:index]]
15
15
  end
16
16
  end
17
17
 
@@ -22,7 +22,7 @@ class PopulatorSkipTest < MiniTest::Spec
22
22
  form.validate(hash)
23
23
 
24
24
  form.songs.size.must_equal 2
25
- form.songs[0].title.must_equal nil
25
+ assert_nil form.songs[0].title
26
26
  form.songs[1].title.must_equal "Bad"
27
27
  end
28
- 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
- class AlbumForm < Reform::Form
8
- property :title, prepopulator: ->(*){ self.title = "Another Day At Work" } # normal assignment.
7
+ class AlbumForm < TestForm
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]
@@ -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
@@ -78,12 +78,11 @@ 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)
85
84
 
86
- class AlbumForm < Reform::Form
85
+ class AlbumForm < TestForm
87
86
  property :title
88
87
  property :length
89
88
 
@@ -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,17 +1,17 @@
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
 
@@ -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