reform 0.2.7 → 1.0.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 +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
|