reform 1.0.4 → 1.1.0
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.
- checksums.yaml +4 -4
- data/CHANGES.md +13 -0
- data/Gemfile +1 -1
- data/README.md +61 -13
- data/database.sqlite3 +0 -0
- data/lib/reform/composition.rb +1 -0
- data/lib/reform/contract.rb +24 -15
- data/lib/reform/form.rb +5 -0
- data/lib/reform/form/active_model.rb +5 -3
- data/lib/reform/form/active_model/model_validations.rb +98 -0
- data/lib/reform/form/active_record.rb +8 -1
- data/lib/reform/form/coercion.rb +4 -4
- data/lib/reform/form/composition.rb +2 -19
- data/lib/reform/form/module.rb +27 -0
- data/lib/reform/form/multi_parameter_attributes.rb +22 -2
- data/lib/reform/form/save.rb +18 -5
- data/lib/reform/form/scalar.rb +52 -0
- data/lib/reform/form/sync.rb +5 -6
- data/lib/reform/form/validate.rb +40 -57
- data/lib/reform/rails.rb +3 -3
- data/lib/reform/representer.rb +14 -7
- data/lib/reform/version.rb +1 -1
- data/reform.gemspec +6 -4
- data/test/active_model_test.rb +4 -23
- data/test/active_record_test.rb +188 -42
- data/test/as_test.rb +1 -1
- data/test/builder_test.rb +32 -0
- data/test/coercion_test.rb +56 -28
- data/test/deserialize_test.rb +40 -0
- data/test/form_builder_test.rb +1 -1
- data/test/form_composition_test.rb +50 -22
- data/test/inherit_test.rb +79 -0
- data/test/model_validations_test.rb +82 -0
- data/test/nested_form_test.rb +2 -13
- data/test/reform_test.rb +106 -7
- data/test/scalar_test.rb +167 -0
- data/test/test_helper.rb +10 -0
- data/test/validate_test.rb +42 -2
- metadata +37 -12
- data/test/setup_test.rb +0 -68
data/test/scalar_test.rb
ADDED
@@ -0,0 +1,167 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
|
4
|
+
class SelfNestedTest < BaseTest
|
5
|
+
class Form < Reform::Form
|
6
|
+
property :title do
|
7
|
+
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
let (:song) { Song.new("Crash And Burn") }
|
12
|
+
it do
|
13
|
+
Form.new(song)
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
it do
|
18
|
+
form = Form.new(song)
|
19
|
+
|
20
|
+
form.title = Class.new(Reform::Form) do
|
21
|
+
@form_name = "ficken"
|
22
|
+
def self.name # needed by ActiveModel::Validation and I18N.
|
23
|
+
@form_name
|
24
|
+
end
|
25
|
+
|
26
|
+
validates :model, :length => {:minimum => 10}
|
27
|
+
|
28
|
+
|
29
|
+
def update!(object)
|
30
|
+
model.replace(object)
|
31
|
+
end
|
32
|
+
|
33
|
+
end.new("Crash And Burn") # gets initialized with string (or image object, or whatever).
|
34
|
+
|
35
|
+
form.validate({"title" => "Teaser"})
|
36
|
+
|
37
|
+
form.errors.messages.must_equal({:"title.model"=>["is too short (minimum is 10 characters)"]})
|
38
|
+
|
39
|
+
|
40
|
+
# validation only kicks in when value present
|
41
|
+
form = Form.new(song)
|
42
|
+
form.validate({})
|
43
|
+
form.errors.messages.must_equal({})
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
class ImageForm < Reform::Form
|
48
|
+
# property :image, populate_if_empty: lambda { |object, args| object } do
|
49
|
+
property :image, :scalar => true do
|
50
|
+
validates :size, numericality: { less_than: 10 }
|
51
|
+
validates :length, numericality: { greater_than: 1 } # TODO: make better validators and remove AM::Validators at some point.
|
52
|
+
|
53
|
+
# FIXME: does that only work with representable 2.0?
|
54
|
+
# def size; model.size; end
|
55
|
+
# def type; model.class.to_s; end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
AlbumCover = Struct.new(:image) do
|
60
|
+
include Saveable
|
61
|
+
end
|
62
|
+
|
63
|
+
# no image in params AND model.
|
64
|
+
it do
|
65
|
+
form = ImageForm.new(AlbumCover.new(nil))
|
66
|
+
|
67
|
+
|
68
|
+
form.validate({})
|
69
|
+
form.errors.messages.must_equal({})
|
70
|
+
end
|
71
|
+
|
72
|
+
# no image in params but in model.
|
73
|
+
it do
|
74
|
+
skip
|
75
|
+
|
76
|
+
# TODO: implement validations that only run when requested (only_validate_params: true)
|
77
|
+
form = ImageForm.new(AlbumCover.new("i don't know how i got here but i'm invalid"))
|
78
|
+
|
79
|
+
|
80
|
+
form.validate({})
|
81
|
+
form.errors.messages.must_equal({})
|
82
|
+
end
|
83
|
+
|
84
|
+
# image in params but NOT in model.
|
85
|
+
it do
|
86
|
+
form = ImageForm.new(AlbumCover.new(nil))
|
87
|
+
|
88
|
+
form.validate({"image" => "I'm OK!"})
|
89
|
+
puts form.inspect
|
90
|
+
form.errors.messages.must_equal({})
|
91
|
+
form.image.scalar.must_equal "I'm OK!"
|
92
|
+
end
|
93
|
+
|
94
|
+
# OK image, image existent.
|
95
|
+
it "hello" do
|
96
|
+
form = ImageForm.new(AlbumCover.new("nil"))
|
97
|
+
|
98
|
+
form.image.model.must_equal "nil"
|
99
|
+
|
100
|
+
form.validate({"image" => "I'm OK!"})
|
101
|
+
form.errors.messages.must_equal({})
|
102
|
+
end
|
103
|
+
|
104
|
+
# invalid image, image existent.
|
105
|
+
it "xx"do
|
106
|
+
form = ImageForm.new(AlbumCover.new("nil"))
|
107
|
+
|
108
|
+
form.validate({"image" => "I'm too long, is that a problem?"})
|
109
|
+
form.errors.messages.must_equal({:"image.size"=>["must be less than 10"]})
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
|
114
|
+
# validate string only if it's in params.
|
115
|
+
class StringForm < Reform::Form
|
116
|
+
property :image, :scalar => true do # creates "empty" form
|
117
|
+
validates :length => {:minimum => 10}
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
|
122
|
+
# validates when present.
|
123
|
+
# invalid
|
124
|
+
it do
|
125
|
+
form = StringForm.new(AlbumCover.new(nil))
|
126
|
+
form.validate("image" => "0x123").must_equal false
|
127
|
+
form.image.scalar.must_equal("0x123")
|
128
|
+
# TODO: errors, save
|
129
|
+
|
130
|
+
form.errors.messages.must_equal({:"image.scalar"=>["is too short (minimum is 10 characters)"]})
|
131
|
+
end
|
132
|
+
|
133
|
+
# valid
|
134
|
+
it "xxx" do
|
135
|
+
cover = AlbumCover.new(nil)
|
136
|
+
|
137
|
+
form = StringForm.new(cover)
|
138
|
+
form.validate("image" => "0x123456789").must_equal true
|
139
|
+
|
140
|
+
form.image.scalar.must_equal("0x123456789")
|
141
|
+
cover.image.must_equal nil
|
142
|
+
|
143
|
+
# errors
|
144
|
+
form.errors.messages.must_equal({})
|
145
|
+
|
146
|
+
# sync
|
147
|
+
form.sync
|
148
|
+
form.image.scalar.must_equal("0x123456789")
|
149
|
+
cover.image.must_equal "0x123456789" # that already writes it back.
|
150
|
+
|
151
|
+
# save
|
152
|
+
form.save
|
153
|
+
cover.image.must_equal "0x123456789" # #save writes back to model.
|
154
|
+
|
155
|
+
form.save do |hash|
|
156
|
+
hash.must_equal("image"=>"0x123456789")
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# does not validate when absent (that's the whole point of this directive).
|
161
|
+
it do
|
162
|
+
form = StringForm.new(AlbumCover.new(nil))
|
163
|
+
form.validate({}).must_equal true
|
164
|
+
end
|
165
|
+
|
166
|
+
# DISCUSS: when AlbumCover.new("Hello").validate({}), does that fail?
|
167
|
+
end
|
data/test/test_helper.rb
CHANGED
@@ -9,11 +9,21 @@ end
|
|
9
9
|
require 'active_record'
|
10
10
|
class Artist < ActiveRecord::Base
|
11
11
|
end
|
12
|
+
|
13
|
+
class Song < ActiveRecord::Base
|
14
|
+
belongs_to :artist
|
15
|
+
end
|
16
|
+
|
17
|
+
class Album < ActiveRecord::Base
|
18
|
+
has_many :songs
|
19
|
+
end
|
20
|
+
|
12
21
|
ActiveRecord::Base.establish_connection(
|
13
22
|
:adapter => "sqlite3",
|
14
23
|
:database => "#{Dir.pwd}/database.sqlite3"
|
15
24
|
)
|
16
25
|
|
26
|
+
|
17
27
|
#Artist.delete_all
|
18
28
|
|
19
29
|
class BaseTest < MiniTest::Spec
|
data/test/validate_test.rb
CHANGED
@@ -85,6 +85,34 @@ class ValidateTest < BaseTest
|
|
85
85
|
it { subject.songs[1].title.must_equal "Roxanne" }
|
86
86
|
end
|
87
87
|
|
88
|
+
|
89
|
+
# not sure if we should catch that in Reform or rather do that in disposable. this is https://github.com/apotonick/reform/pull/104
|
90
|
+
# describe ":populator with :empty" do
|
91
|
+
# let (:form) {
|
92
|
+
# Class.new(Reform::Form) do
|
93
|
+
# collection :songs, :empty => true, :populator => lambda { |fragment, index, args|
|
94
|
+
# songs[index] = args.binding[:form].new(Song.new)
|
95
|
+
# } do
|
96
|
+
# property :title
|
97
|
+
# end
|
98
|
+
# end
|
99
|
+
# }
|
100
|
+
|
101
|
+
# let (:params) {
|
102
|
+
# {
|
103
|
+
# "songs" => [{"title" => "Fallout"}, {"title" => "Roxanne"}]
|
104
|
+
# }
|
105
|
+
# }
|
106
|
+
|
107
|
+
# subject { form.new(Album.new("Hits", [], [])) }
|
108
|
+
|
109
|
+
# before { subject.validate(params) }
|
110
|
+
|
111
|
+
# it { subject.songs[0].title.must_equal "Fallout" }
|
112
|
+
# it { subject.songs[1].title.must_equal "Roxanne" }
|
113
|
+
# end
|
114
|
+
|
115
|
+
|
88
116
|
describe ":populate_if_empty, half-populated collection" do
|
89
117
|
let (:form) {
|
90
118
|
Class.new(Reform::Form) do
|
@@ -148,10 +176,22 @@ class ValidateTest < BaseTest
|
|
148
176
|
it { subject.songs[0].title.must_equal "Fallout" }
|
149
177
|
it { subject.songs[1].title.must_equal "Roxanne" }
|
150
178
|
|
151
|
-
|
152
|
-
it { album.
|
179
|
+
# population doesn't write to the model.
|
180
|
+
it { album.hit.must_equal nil }
|
181
|
+
it { album.songs.size.must_equal 0 }
|
153
182
|
|
154
183
|
it { subject.band.label.name.must_equal "Epitaph" }
|
184
|
+
|
185
|
+
|
186
|
+
describe "missing parameters" do
|
187
|
+
let (:params) {
|
188
|
+
{ }
|
189
|
+
}
|
190
|
+
|
191
|
+
before { subject.validate(params) }
|
192
|
+
|
193
|
+
it { subject.hit.must_equal nil }
|
194
|
+
end
|
155
195
|
end
|
156
196
|
|
157
197
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: reform
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Sutterer
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-
|
12
|
+
date: 2014-08-20 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: representable
|
@@ -17,42 +17,42 @@ dependencies:
|
|
17
17
|
requirements:
|
18
18
|
- - ~>
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version:
|
20
|
+
version: 2.0.3
|
21
21
|
type: :runtime
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
25
|
- - ~>
|
26
26
|
- !ruby/object:Gem::Version
|
27
|
-
version:
|
27
|
+
version: 2.0.3
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
29
|
name: disposable
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
31
31
|
requirements:
|
32
32
|
- - ~>
|
33
33
|
- !ruby/object:Gem::Version
|
34
|
-
version: 0.0.
|
34
|
+
version: 0.0.5
|
35
35
|
type: :runtime
|
36
36
|
prerelease: false
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
39
|
- - ~>
|
40
40
|
- !ruby/object:Gem::Version
|
41
|
-
version: 0.0.
|
41
|
+
version: 0.0.5
|
42
42
|
- !ruby/object:Gem::Dependency
|
43
43
|
name: uber
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
45
45
|
requirements:
|
46
46
|
- - ~>
|
47
47
|
- !ruby/object:Gem::Version
|
48
|
-
version: 0.0.
|
48
|
+
version: 0.0.8
|
49
49
|
type: :runtime
|
50
50
|
prerelease: false
|
51
51
|
version_requirements: !ruby/object:Gem::Requirement
|
52
52
|
requirements:
|
53
53
|
- - ~>
|
54
54
|
- !ruby/object:Gem::Version
|
55
|
-
version: 0.0.
|
55
|
+
version: 0.0.8
|
56
56
|
- !ruby/object:Gem::Dependency
|
57
57
|
name: activemodel
|
58
58
|
requirement: !ruby/object:Gem::Requirement
|
@@ -87,14 +87,14 @@ dependencies:
|
|
87
87
|
requirements:
|
88
88
|
- - '>='
|
89
89
|
- !ruby/object:Gem::Version
|
90
|
-
version:
|
90
|
+
version: '0'
|
91
91
|
type: :development
|
92
92
|
prerelease: false
|
93
93
|
version_requirements: !ruby/object:Gem::Requirement
|
94
94
|
requirements:
|
95
95
|
- - '>='
|
96
96
|
- !ruby/object:Gem::Version
|
97
|
-
version:
|
97
|
+
version: '0'
|
98
98
|
- !ruby/object:Gem::Dependency
|
99
99
|
name: minitest
|
100
100
|
requirement: !ruby/object:Gem::Requirement
|
@@ -165,6 +165,20 @@ dependencies:
|
|
165
165
|
- - '>='
|
166
166
|
- !ruby/object:Gem::Version
|
167
167
|
version: '0'
|
168
|
+
- !ruby/object:Gem::Dependency
|
169
|
+
name: actionpack
|
170
|
+
requirement: !ruby/object:Gem::Requirement
|
171
|
+
requirements:
|
172
|
+
- - '>='
|
173
|
+
- !ruby/object:Gem::Version
|
174
|
+
version: '0'
|
175
|
+
type: :development
|
176
|
+
prerelease: false
|
177
|
+
version_requirements: !ruby/object:Gem::Requirement
|
178
|
+
requirements:
|
179
|
+
- - '>='
|
180
|
+
- !ruby/object:Gem::Version
|
181
|
+
version: '0'
|
168
182
|
description: Freeing your AR models from form logic.
|
169
183
|
email:
|
170
184
|
- apotonick@gmail.com
|
@@ -195,11 +209,14 @@ files:
|
|
195
209
|
- lib/reform/contract/validate.rb
|
196
210
|
- lib/reform/form.rb
|
197
211
|
- lib/reform/form/active_model.rb
|
212
|
+
- lib/reform/form/active_model/model_validations.rb
|
198
213
|
- lib/reform/form/active_record.rb
|
199
214
|
- lib/reform/form/coercion.rb
|
200
215
|
- lib/reform/form/composition.rb
|
216
|
+
- lib/reform/form/module.rb
|
201
217
|
- lib/reform/form/multi_parameter_attributes.rb
|
202
218
|
- lib/reform/form/save.rb
|
219
|
+
- lib/reform/form/scalar.rb
|
203
220
|
- lib/reform/form/sync.rb
|
204
221
|
- lib/reform/form/validate.rb
|
205
222
|
- lib/reform/form/virtual_attributes.rb
|
@@ -210,9 +227,11 @@ files:
|
|
210
227
|
- test/active_model_test.rb
|
211
228
|
- test/active_record_test.rb
|
212
229
|
- test/as_test.rb
|
230
|
+
- test/builder_test.rb
|
213
231
|
- test/coercion_test.rb
|
214
232
|
- test/composition_test.rb
|
215
233
|
- test/contract_test.rb
|
234
|
+
- test/deserialize_test.rb
|
216
235
|
- test/dummy/Rakefile
|
217
236
|
- test/dummy/app/controllers/albums_controller.rb
|
218
237
|
- test/dummy/app/controllers/application_controller.rb
|
@@ -240,11 +259,13 @@ files:
|
|
240
259
|
- test/feature_test.rb
|
241
260
|
- test/form_builder_test.rb
|
242
261
|
- test/form_composition_test.rb
|
262
|
+
- test/inherit_test.rb
|
263
|
+
- test/model_validations_test.rb
|
243
264
|
- test/nested_form_test.rb
|
244
265
|
- test/rails/integration_test.rb
|
245
266
|
- test/reform_test.rb
|
246
267
|
- test/save_test.rb
|
247
|
-
- test/
|
268
|
+
- test/scalar_test.rb
|
248
269
|
- test/sync_test.rb
|
249
270
|
- test/test_helper.rb
|
250
271
|
- test/validate_test.rb
|
@@ -277,9 +298,11 @@ test_files:
|
|
277
298
|
- test/active_model_test.rb
|
278
299
|
- test/active_record_test.rb
|
279
300
|
- test/as_test.rb
|
301
|
+
- test/builder_test.rb
|
280
302
|
- test/coercion_test.rb
|
281
303
|
- test/composition_test.rb
|
282
304
|
- test/contract_test.rb
|
305
|
+
- test/deserialize_test.rb
|
283
306
|
- test/dummy/Rakefile
|
284
307
|
- test/dummy/app/controllers/albums_controller.rb
|
285
308
|
- test/dummy/app/controllers/application_controller.rb
|
@@ -307,11 +330,13 @@ test_files:
|
|
307
330
|
- test/feature_test.rb
|
308
331
|
- test/form_builder_test.rb
|
309
332
|
- test/form_composition_test.rb
|
333
|
+
- test/inherit_test.rb
|
334
|
+
- test/model_validations_test.rb
|
310
335
|
- test/nested_form_test.rb
|
311
336
|
- test/rails/integration_test.rb
|
312
337
|
- test/reform_test.rb
|
313
338
|
- test/save_test.rb
|
314
|
-
- test/
|
339
|
+
- test/scalar_test.rb
|
315
340
|
- test/sync_test.rb
|
316
341
|
- test/test_helper.rb
|
317
342
|
- test/validate_test.rb
|
data/test/setup_test.rb
DELETED
@@ -1,68 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
class SetupTest < BaseTest
|
4
|
-
class AlbumForm < Reform::Form
|
5
|
-
property :title
|
6
|
-
|
7
|
-
property :hit do
|
8
|
-
property :title
|
9
|
-
validates :title, :presence => true
|
10
|
-
end
|
11
|
-
|
12
|
-
collection :songs do
|
13
|
-
property :title
|
14
|
-
validates :title, :presence => true
|
15
|
-
|
16
|
-
property :length do
|
17
|
-
property :minutes
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
property :band do # yepp, people do crazy stuff like that.
|
22
|
-
property :label do
|
23
|
-
property :name
|
24
|
-
validates :name, :presence => true
|
25
|
-
end
|
26
|
-
# TODO: make band a required object.
|
27
|
-
end
|
28
|
-
|
29
|
-
validates :title, :presence => true
|
30
|
-
end
|
31
|
-
|
32
|
-
|
33
|
-
describe "populated" do
|
34
|
-
subject { AlbumForm.new(Album.new("Best Of", hit, [Song.new("Fallout", Length.new(2,3)), Song.new("Roxanne")])) }
|
35
|
-
|
36
|
-
it { subject.title.must_equal "Best Of" }
|
37
|
-
|
38
|
-
|
39
|
-
it { subject.hit.must_be_kind_of Reform::Form }
|
40
|
-
it { subject.hit.title.must_equal "Roxanne" }
|
41
|
-
|
42
|
-
it { subject.songs.must_be_kind_of Array }
|
43
|
-
it { subject.songs.size.must_equal 2 }
|
44
|
-
|
45
|
-
it { subject.songs[0].must_be_kind_of Reform::Form }
|
46
|
-
it { subject.songs[0].title.must_equal "Fallout" }
|
47
|
-
it { subject.songs[0].length.minutes.must_equal 2 }
|
48
|
-
|
49
|
-
it { subject.songs[1].must_be_kind_of Reform::Form }
|
50
|
-
it { subject.songs[1].title.must_equal "Roxanne" }
|
51
|
-
end
|
52
|
-
|
53
|
-
|
54
|
-
describe "empty" do
|
55
|
-
subject { AlbumForm.new(Album.new) }
|
56
|
-
|
57
|
-
it { subject.title.must_equal nil }
|
58
|
-
|
59
|
-
|
60
|
-
# TODO: discuss and implement.
|
61
|
-
# it { subject.hit.must_be_kind_of Reform::Form }
|
62
|
-
# it { subject.hit.title.must_equal nil }
|
63
|
-
|
64
|
-
|
65
|
-
# it { subject.songs.must_be_kind_of Reform::Form::Forms }
|
66
|
-
# it { subject.songs.size.must_equal 0 }
|
67
|
-
end
|
68
|
-
end
|