reform 0.2.7 → 1.0.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 +18 -0
- data/Gemfile +2 -1
- data/README.md +196 -17
- data/TODO.md +14 -3
- data/database.sqlite3 +0 -0
- data/lib/reform.rb +9 -1
- data/lib/reform/composition.rb +41 -34
- data/lib/reform/contract.rb +109 -0
- data/lib/reform/contract/errors.rb +33 -0
- data/lib/reform/contract/setup.rb +44 -0
- data/lib/reform/contract/validate.rb +48 -0
- data/lib/reform/form.rb +13 -309
- data/lib/reform/form/active_model.rb +8 -5
- data/lib/reform/form/active_record.rb +30 -37
- data/lib/reform/form/coercion.rb +10 -11
- data/lib/reform/form/composition.rb +40 -50
- data/lib/reform/form/multi_parameter_attributes.rb +6 -1
- data/lib/reform/form/save.rb +61 -0
- data/lib/reform/form/sync.rb +60 -0
- data/lib/reform/form/validate.rb +104 -0
- data/lib/reform/form/virtual_attributes.rb +3 -5
- data/lib/reform/representer.rb +17 -3
- data/lib/reform/version.rb +1 -1
- data/reform.gemspec +2 -1
- data/test/active_model_test.rb +0 -92
- data/test/as_test.rb +75 -0
- data/test/coercion_test.rb +26 -8
- data/test/composition_test.rb +8 -8
- data/test/contract_test.rb +57 -0
- data/test/errors_test.rb +37 -10
- data/test/feature_test.rb +28 -0
- data/test/form_builder_test.rb +105 -0
- data/test/form_composition_test.rb +30 -13
- data/test/nested_form_test.rb +12 -18
- data/test/reform_test.rb +11 -6
- data/test/save_test.rb +81 -0
- data/test/setup_test.rb +38 -0
- data/test/sync_test.rb +39 -0
- data/test/test_helper.rb +36 -2
- data/test/validate_test.rb +191 -0
- metadata +42 -4
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class FeatureInheritanceTest < BaseTest
|
4
|
+
class AlbumForm < Reform::Form
|
5
|
+
# include Reform::Form::ActiveModel
|
6
|
+
# include Coercion
|
7
|
+
# include MultiParameterAttributes
|
8
|
+
|
9
|
+
property :band do
|
10
|
+
property :label do
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
subject { AlbumForm.new(Album.new(nil, nil, nil, Band.new(Label.new))) }
|
16
|
+
|
17
|
+
# it { subject.class.include?(Reform::Form::ActiveModel) }
|
18
|
+
# it { subject.class.include?(Reform::Form::Coercion) }
|
19
|
+
# it { subject.is_a?(Reform::Form::MultiParameterAttributes) }
|
20
|
+
|
21
|
+
# it { subject.band.class.include?(Reform::Form::ActiveModel) }
|
22
|
+
# it { subject.band.is_a?(Reform::Form::Coercion) }
|
23
|
+
# it { subject.band.is_a?(Reform::Form::MultiParameterAttributes) }
|
24
|
+
|
25
|
+
# it { subject.band.label.is_a?(Reform::Form::ActiveModel) }
|
26
|
+
# it { subject.band.label.is_a?(Reform::Form::Coercion) }
|
27
|
+
# it { subject.band.label.is_a?(Reform::Form::MultiParameterAttributes) }
|
28
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class FormBuilderCompatTest < BaseTest
|
4
|
+
let (:form_class) {
|
5
|
+
Class.new(Reform::Form) do
|
6
|
+
include Reform::Form::ActiveModel::FormBuilderMethods
|
7
|
+
|
8
|
+
property :artist do
|
9
|
+
property :name
|
10
|
+
validates :name, :presence => true
|
11
|
+
end
|
12
|
+
|
13
|
+
collection :songs do
|
14
|
+
property :title
|
15
|
+
property :release_date
|
16
|
+
validates :title, :presence => true
|
17
|
+
end
|
18
|
+
|
19
|
+
class LabelForm < Reform::Form
|
20
|
+
property :name
|
21
|
+
end
|
22
|
+
|
23
|
+
property :label, :form => LabelForm
|
24
|
+
|
25
|
+
property :band do
|
26
|
+
property :label do
|
27
|
+
property :name
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
}
|
32
|
+
|
33
|
+
let (:song) { OpenStruct.new }
|
34
|
+
let (:form) { form_class.new(OpenStruct.new(
|
35
|
+
:artist => Artist.new(:name => "Propagandhi"),
|
36
|
+
:songs => [song],
|
37
|
+
:label => OpenStruct.new,
|
38
|
+
|
39
|
+
:band => Band.new(Label.new)
|
40
|
+
)) }
|
41
|
+
|
42
|
+
it "respects _attributes params hash" do
|
43
|
+
form.validate("artist_attributes" => {"name" => "Blink 182"},
|
44
|
+
"songs_attributes" => {"0" => {"title" => "Damnit"}},
|
45
|
+
"band_attributes" => {"label_attributes" => {"name" => "Epitaph"}})
|
46
|
+
|
47
|
+
form.artist.name.must_equal "Blink 182"
|
48
|
+
form.songs.first.title.must_equal "Damnit"
|
49
|
+
form.band.label.name.must_equal "Epitaph"
|
50
|
+
end
|
51
|
+
|
52
|
+
it "allows nested collection and property to be missing" do
|
53
|
+
form.validate({})
|
54
|
+
|
55
|
+
form.artist.name.must_equal "Propagandhi"
|
56
|
+
|
57
|
+
form.songs.size.must_equal 1
|
58
|
+
form.songs[0].model.must_equal song # this is a weird test.
|
59
|
+
end
|
60
|
+
|
61
|
+
it "defines _attributes= setter so Rails' FB works properly" do
|
62
|
+
form.must_respond_to("artist_attributes=")
|
63
|
+
form.must_respond_to("songs_attributes=")
|
64
|
+
form.must_respond_to("label_attributes=")
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "deconstructed date parameters" do
|
68
|
+
let(:form_attributes) do
|
69
|
+
{
|
70
|
+
"artist_attributes" => {"name" => "Blink 182"},
|
71
|
+
"songs_attributes" => {"0" => {"title" => "Damnit", "release_date(1i)" => release_year,
|
72
|
+
"release_date(2i)" => release_month, "release_date(3i)" => release_day}}
|
73
|
+
}
|
74
|
+
end
|
75
|
+
let(:release_year) { "1997" }
|
76
|
+
let(:release_month) { "9" }
|
77
|
+
let(:release_day) { "27" }
|
78
|
+
|
79
|
+
describe "with valid parameters" do
|
80
|
+
it "creates a date" do
|
81
|
+
form.validate(form_attributes)
|
82
|
+
|
83
|
+
form.songs.first.release_date.must_equal Date.new(1997, 9, 27)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
%w(year month day).each do |date_attr|
|
88
|
+
describe "when the #{date_attr} is missing" do
|
89
|
+
let(:"release_#{date_attr}") { "" }
|
90
|
+
|
91
|
+
it "rejects the date" do
|
92
|
+
form.validate(form_attributes)
|
93
|
+
|
94
|
+
form.songs.first.release_date.must_be_nil
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
it "returns flat errors hash" do
|
101
|
+
form.validate("artist_attributes" => {"name" => ""},
|
102
|
+
"songs_attributes" => {"0" => {"title" => ""}})
|
103
|
+
form.errors.messages.must_equal(:"artist.name" => ["can't be blank"], :"songs.title" => ["can't be blank"])
|
104
|
+
end
|
105
|
+
end
|
@@ -1,26 +1,36 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
3
|
class FormCompositionTest < MiniTest::Spec
|
4
|
-
|
4
|
+
Song = Struct.new(:id, :title)
|
5
|
+
Requester = Struct.new(:id, :name)
|
6
|
+
|
7
|
+
class RequestForm < Reform::Form
|
5
8
|
include Composition
|
6
9
|
|
7
|
-
property :
|
8
|
-
|
10
|
+
property :name, :on => :requester
|
11
|
+
property :requester_id, :on => :requester, :as => :id
|
12
|
+
properties [:title, :id], :on => :song
|
13
|
+
# property :channel # FIXME: what about the "main model"?
|
14
|
+
property :channel, :empty => true, :on => :song
|
9
15
|
|
10
|
-
validates :name, :title, :
|
16
|
+
validates :name, :title, :channel, :presence => true
|
11
17
|
end
|
12
18
|
|
13
|
-
let (:form) {
|
14
|
-
let (:song) {
|
15
|
-
let (:
|
19
|
+
let (:form) { RequestForm.new(:song => song, :requester => requester) }
|
20
|
+
let (:song) { Song.new(1, "Rio") }
|
21
|
+
let (:requester) { Requester.new(2, "Duran Duran") }
|
16
22
|
|
17
23
|
|
18
24
|
# delegation form -> composition works
|
19
|
-
it { form.
|
20
|
-
it { form.
|
25
|
+
it { form.id.must_equal 1 }
|
26
|
+
it { form.title.must_equal "Rio" }
|
27
|
+
it { form.name.must_equal "Duran Duran" }
|
28
|
+
it { form.requester_id.must_equal 2 }
|
29
|
+
it { form.channel.must_equal nil }
|
30
|
+
|
21
31
|
# delegation form -> composed models (e.g. when saving this can be handy)
|
22
32
|
it { form.song.must_equal song }
|
23
|
-
it { form.
|
33
|
+
it { form.requester.must_equal requester }
|
24
34
|
|
25
35
|
|
26
36
|
it "creates Composition for you" do
|
@@ -40,21 +50,28 @@ class FormCompositionTest < MiniTest::Spec
|
|
40
50
|
end
|
41
51
|
|
42
52
|
it "provides nested symbolized hash as second block argument" do
|
53
|
+
form.validate("title" => "Greyhound", "name" => "Frenzal Rhomb", "channel" => "JJJ")
|
54
|
+
|
43
55
|
hash = {}
|
44
56
|
|
45
57
|
form.save do |data, map|
|
46
58
|
hash = map
|
47
59
|
end
|
48
60
|
|
49
|
-
hash.must_equal({:song=>{:title=>"
|
61
|
+
hash.must_equal({:song=>{:title=>"Greyhound", :id=>1, :channel => "JJJ"}, :requester=>{:name=>"Frenzal Rhomb", :id=>2}})
|
50
62
|
end
|
51
63
|
|
52
|
-
it "pushes data to models when no block passed" do
|
64
|
+
it "pushes data to models and calls #save when no block passed" do
|
65
|
+
song.extend(Saveable)
|
66
|
+
requester.extend(Saveable)
|
67
|
+
|
53
68
|
form.validate("title" => "Greyhound", "name" => "Frenzal Rhomb")
|
54
69
|
form.save
|
55
70
|
|
56
|
-
|
71
|
+
requester.name.must_equal "Frenzal Rhomb"
|
72
|
+
requester.saved?.must_equal true
|
57
73
|
song.title.must_equal "Greyhound"
|
74
|
+
song.saved?.must_equal true
|
58
75
|
end
|
59
76
|
end
|
60
77
|
end
|
data/test/nested_form_test.rb
CHANGED
@@ -37,14 +37,18 @@ class NestedFormTest < MiniTest::Spec
|
|
37
37
|
let (:songs) { [OpenStruct.new(:title => "Calling")] }
|
38
38
|
let (:form) { AlbumForm.new(album) }
|
39
39
|
|
40
|
-
it "responds to #
|
41
|
-
|
40
|
+
it "responds to #save" do
|
41
|
+
hsh = nil
|
42
|
+
form.save do |f, nested|
|
43
|
+
hsh = nested
|
44
|
+
end
|
45
|
+
hsh.must_equal({"hit"=>{"title"=>"Downtown"}, "title" => "Blackhawks Over Los Angeles", "songs"=>[{"title"=>"Calling"}]})
|
42
46
|
end
|
43
47
|
|
44
48
|
|
45
49
|
it "creates nested forms" do
|
46
50
|
form.hit.must_be_kind_of Reform::Form
|
47
|
-
form.songs.must_be_kind_of
|
51
|
+
form.songs.must_be_kind_of Array
|
48
52
|
end
|
49
53
|
|
50
54
|
describe "#initialize" do
|
@@ -70,19 +74,7 @@ class NestedFormTest < MiniTest::Spec
|
|
70
74
|
# end
|
71
75
|
end
|
72
76
|
|
73
|
-
|
74
|
-
subject { Class.new(Reform::Form) do
|
75
|
-
# collection :songs, :validates => {:length => {:minimum => 1}}
|
76
|
-
collection :songs, :cardinality => {:minimum => 1} do
|
77
|
-
property :title
|
78
|
-
end
|
79
|
-
end.new(OpenStruct.new) }
|
80
|
-
|
81
|
-
it "whatxxx" do
|
82
|
-
subject.validate({}).must_equal false
|
83
|
-
subject.errors.messages.must_equal(:"songs.size"=>["songs size is 0 but must be {:minimum=>1}"])
|
84
|
-
end
|
85
|
-
end
|
77
|
+
|
86
78
|
end
|
87
79
|
|
88
80
|
|
@@ -194,14 +186,16 @@ class NestedFormTest < MiniTest::Spec
|
|
194
186
|
form.hit.title.must_equal "Downtown"
|
195
187
|
end
|
196
188
|
|
197
|
-
it
|
189
|
+
it ("xxx") {
|
190
|
+
form.validate({"hit" => {"title" => ""}})
|
191
|
+
form.hit.title.must_equal ""
|
198
192
|
form.errors[:"hit.title"].must_equal(["can't be blank"])
|
199
193
|
}
|
200
194
|
end
|
201
195
|
|
202
196
|
class UnitTest < self
|
203
197
|
it "keeps Forms for form collection" do
|
204
|
-
form.send(:fields).songs.must_be_kind_of
|
198
|
+
form.send(:fields).songs.must_be_kind_of Array
|
205
199
|
end
|
206
200
|
|
207
201
|
describe "#validate" do
|
data/test/reform_test.rb
CHANGED
@@ -31,13 +31,13 @@ end
|
|
31
31
|
class FieldsTest < MiniTest::Spec
|
32
32
|
describe "#new" do
|
33
33
|
it "accepts list of properties" do
|
34
|
-
fields = Reform::Fields.new([:name, :title])
|
34
|
+
fields = Reform::Contract::Fields.new([:name, :title])
|
35
35
|
fields.name.must_equal nil
|
36
36
|
fields.title.must_equal nil
|
37
37
|
end
|
38
38
|
|
39
39
|
it "accepts list of properties and values" do
|
40
|
-
fields = Reform::Fields.new(["name", "title"], "title" => "The Body")
|
40
|
+
fields = Reform::Contract::Fields.new(["name", "title"], "title" => "The Body")
|
41
41
|
fields.name.must_equal nil
|
42
42
|
fields.title.must_equal "The Body"
|
43
43
|
end
|
@@ -58,11 +58,14 @@ class ReformTest < ReformSpec
|
|
58
58
|
|
59
59
|
|
60
60
|
describe "::properties" do
|
61
|
-
|
61
|
+
subject do
|
62
62
|
Class.new(Reform::Form) do
|
63
63
|
properties [:name, :title]
|
64
|
-
end.new(comp)
|
64
|
+
end.new(comp)
|
65
65
|
end
|
66
|
+
|
67
|
+
it { subject.name.must_equal "Duran Duran" }
|
68
|
+
it { subject.title.must_equal "Rio" }
|
66
69
|
end
|
67
70
|
|
68
71
|
class SongForm < Reform::Form
|
@@ -163,6 +166,8 @@ class ReformTest < ReformSpec
|
|
163
166
|
end
|
164
167
|
|
165
168
|
describe "#errors" do
|
169
|
+
before { form.validate({})}
|
170
|
+
|
166
171
|
it { form.errors.must_be_kind_of Reform::Form::Errors }
|
167
172
|
|
168
173
|
it { form.errors.messages.must_equal({}) }
|
@@ -252,7 +257,7 @@ class EmptyAttributesTest < MiniTest::Spec
|
|
252
257
|
form.password.must_equal "123"
|
253
258
|
form.password_confirmation.must_equal "321"
|
254
259
|
|
255
|
-
form.
|
260
|
+
form.sync
|
256
261
|
cred.password.must_equal "123"
|
257
262
|
|
258
263
|
hash = {}
|
@@ -280,7 +285,7 @@ class ReadonlyAttributesTest < MiniTest::Spec
|
|
280
285
|
form.country.must_equal "Germany"
|
281
286
|
|
282
287
|
|
283
|
-
form.
|
288
|
+
form.sync
|
284
289
|
loc.country.must_equal "Australia" # the writer wasn't called.
|
285
290
|
|
286
291
|
hash = {}
|
data/test/save_test.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class SaveTest < BaseTest
|
4
|
+
let (:params) {
|
5
|
+
{
|
6
|
+
"title" => "Best Of",
|
7
|
+
"hit" => {"title" => "Roxanne"},
|
8
|
+
"songs" => [{"title" => "Fallout"}, {"title" => "Roxanne"}],
|
9
|
+
:band => {:label => {:name => "Polydor"}}
|
10
|
+
}
|
11
|
+
}
|
12
|
+
|
13
|
+
let (:album) { Album.new(nil, hit, [song1, song2], band) }
|
14
|
+
let (:hit) { Song.new }
|
15
|
+
let (:song1) { Song.new }
|
16
|
+
let (:song2) { Song.new }
|
17
|
+
let (:band) { Band.new(label) }
|
18
|
+
let (:label) { Label.new }
|
19
|
+
|
20
|
+
subject { ErrorsTest::AlbumForm.new(album) }
|
21
|
+
|
22
|
+
before do
|
23
|
+
[album, hit, song1, song2, band, label].each { |mdl| mdl.extend(Saveable) }
|
24
|
+
|
25
|
+
subject.validate(params)
|
26
|
+
subject.save
|
27
|
+
end
|
28
|
+
|
29
|
+
# synced?
|
30
|
+
it { album.title.must_equal "Best Of" }
|
31
|
+
it { hit.title.must_equal "Roxanne" }
|
32
|
+
it { song1.title.must_equal "Fallout" }
|
33
|
+
it { song2.title.must_equal "Roxanne" }
|
34
|
+
it { label.name.must_equal "Polydor" }
|
35
|
+
|
36
|
+
# saved?
|
37
|
+
it { album.saved?.must_equal true }
|
38
|
+
it { hit.saved?.must_equal true }
|
39
|
+
it { song1.saved?.must_equal true }
|
40
|
+
it { song1.saved?.must_equal true }
|
41
|
+
it { band.saved?.must_equal true }
|
42
|
+
it { label.saved?.must_equal true }
|
43
|
+
|
44
|
+
|
45
|
+
describe "save: false" do
|
46
|
+
let (:form) {
|
47
|
+
Class.new(Reform::Form) do
|
48
|
+
property :hit do
|
49
|
+
property :title
|
50
|
+
end
|
51
|
+
|
52
|
+
collection :songs, :save => false do
|
53
|
+
property :title
|
54
|
+
end
|
55
|
+
|
56
|
+
property :band do # yepp, people do crazy stuff like that.
|
57
|
+
property :label, :save => false do
|
58
|
+
property :name
|
59
|
+
end
|
60
|
+
# TODO: make band a required object.
|
61
|
+
end
|
62
|
+
end
|
63
|
+
}
|
64
|
+
|
65
|
+
subject { form.new(album) }
|
66
|
+
|
67
|
+
# synced?
|
68
|
+
it { hit.title.must_equal "Roxanne" }
|
69
|
+
it { song1.title.must_equal "Fallout" }
|
70
|
+
it { song2.title.must_equal "Roxanne" }
|
71
|
+
it { label.name.must_equal "Polydor" }
|
72
|
+
|
73
|
+
# saved?
|
74
|
+
it { album.saved?.must_equal true }
|
75
|
+
it { hit.saved?.must_equal true }
|
76
|
+
it { song1.saved?.must_equal nil }
|
77
|
+
it { song1.saved?.must_equal nil }
|
78
|
+
it { band.saved?.must_equal true }
|
79
|
+
it { label.saved?.must_equal nil }
|
80
|
+
end
|
81
|
+
end
|
data/test/setup_test.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class SetupTest < BaseTest
|
4
|
+
describe "populated" do
|
5
|
+
subject { AlbumForm.new(Album.new("Best Of", hit, [Song.new("Fallout"), Song.new("Roxanne")])) }
|
6
|
+
|
7
|
+
it { subject.title.must_equal "Best Of" }
|
8
|
+
|
9
|
+
|
10
|
+
it { subject.hit.must_be_kind_of Reform::Form }
|
11
|
+
it { subject.hit.title.must_equal "Roxanne" }
|
12
|
+
|
13
|
+
it { subject.songs.must_be_kind_of Array }
|
14
|
+
it { subject.songs.size.must_equal 2 }
|
15
|
+
|
16
|
+
it { subject.songs[0].must_be_kind_of Reform::Form }
|
17
|
+
it { subject.songs[0].title.must_equal "Fallout" }
|
18
|
+
|
19
|
+
it { subject.songs[1].must_be_kind_of Reform::Form }
|
20
|
+
it { subject.songs[1].title.must_equal "Roxanne" }
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
describe "empty" do
|
25
|
+
subject { AlbumForm.new(Album.new) }
|
26
|
+
|
27
|
+
it { subject.title.must_equal nil }
|
28
|
+
|
29
|
+
|
30
|
+
# TODO: discuss and implement.
|
31
|
+
# it { subject.hit.must_be_kind_of Reform::Form }
|
32
|
+
# it { subject.hit.title.must_equal nil }
|
33
|
+
|
34
|
+
|
35
|
+
# it { subject.songs.must_be_kind_of Reform::Form::Forms }
|
36
|
+
# it { subject.songs.size.must_equal 0 }
|
37
|
+
end
|
38
|
+
end
|