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