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/active_record_test.rb
CHANGED
@@ -1,65 +1,81 @@
|
|
1
1
|
require 'test_helper'
|
2
|
+
require 'reform/active_record'
|
3
|
+
|
4
|
+
# ActiveRecord::Schema.define do
|
5
|
+
# create_table :artists do |table|
|
6
|
+
# table.column :name, :string
|
7
|
+
# table.timestamps
|
8
|
+
# end
|
9
|
+
# create_table :songs do |table|
|
10
|
+
# table.column :title, :string
|
11
|
+
# table.column :artist_id, :integer
|
12
|
+
# table.column :album_id, :integer
|
13
|
+
# table.timestamps
|
14
|
+
# end
|
15
|
+
# create_table :albums do |table|
|
16
|
+
# table.column :title, :string
|
17
|
+
# table.timestamps
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
# Artist.new(:name => "Racer X").save
|
21
|
+
|
2
22
|
|
3
23
|
class ActiveRecordTest < MiniTest::Spec
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
include Reform::Form::ActiveRecord
|
8
|
-
model :artist
|
24
|
+
class SongForm < Reform::Form
|
25
|
+
include Reform::Form::ActiveRecord
|
26
|
+
model :song
|
9
27
|
|
10
|
-
|
11
|
-
|
28
|
+
property :title
|
29
|
+
property :created_at
|
12
30
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
31
|
+
validates_uniqueness_of :title
|
32
|
+
validates :created_at, :presence => true # have another property to test if we mix up.
|
33
|
+
|
34
|
+
property :artist do
|
35
|
+
property :name
|
36
|
+
validates_uniqueness_of :name # this currently also tests if Form::AR is included as a feature.
|
37
|
+
end
|
17
38
|
end
|
18
39
|
|
19
|
-
|
40
|
+
let (:form) { SongForm.new(Song.new(:artist => Artist.new)) }
|
20
41
|
|
21
|
-
|
22
|
-
# ActiveRecord::Schema.define do
|
23
|
-
# create_table :artists do |table|
|
24
|
-
# table.column :name, :string
|
25
|
-
# table.timestamps
|
26
|
-
# end
|
27
|
-
# end
|
28
|
-
# Artist.new(:name => "Racer X").save
|
42
|
+
it { form.class.i18n_scope.must_equal :activerecord }
|
29
43
|
|
30
|
-
|
31
|
-
|
44
|
+
it "allows accessing the database" do
|
45
|
+
end
|
32
46
|
|
33
|
-
|
34
|
-
|
35
|
-
|
47
|
+
# uniqueness
|
48
|
+
it "is valid when name is unique" do
|
49
|
+
form.validate("artist" => {"name" => "Paul Gilbert"}, "title" => "The Gargoyle", "created_at" => "November 6, 1966").must_equal true
|
50
|
+
end
|
36
51
|
|
37
|
-
|
38
|
-
|
52
|
+
# nested object taken.
|
53
|
+
it "is invalid and shows error when taken" do
|
54
|
+
Song.delete_all
|
55
|
+
Artist.create(:name => "Racer X")
|
39
56
|
|
40
|
-
|
41
|
-
|
42
|
-
|
57
|
+
form.validate("artist" => {"name" => "Racer X"}, "title" => "Ghost Inside My Skin").must_equal false
|
58
|
+
form.errors.messages.must_equal({:"artist.name"=>["has already been taken"], :created_at => ["can't be blank"]})
|
59
|
+
end
|
43
60
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
61
|
+
it "works with Composition" do
|
62
|
+
form = Class.new(Reform::Form) do
|
63
|
+
include Reform::Form::ActiveRecord
|
64
|
+
include Reform::Form::Composition
|
48
65
|
|
49
|
-
|
50
|
-
|
51
|
-
|
66
|
+
property :name, :on => :artist
|
67
|
+
validates_uniqueness_of :name
|
68
|
+
end.new(:artist => Artist.new)
|
52
69
|
|
53
|
-
|
54
|
-
|
55
|
-
end
|
70
|
+
Artist.create(:name => "Bad Religion")
|
71
|
+
form.validate("name" => "Bad Religion").must_equal false
|
56
72
|
end
|
57
73
|
|
58
74
|
describe "#save" do
|
59
75
|
# TODO: test 1-n?
|
60
76
|
it "calls model.save" do
|
61
77
|
Artist.delete_all
|
62
|
-
form.validate("name" => "Bad Religion")
|
78
|
+
form.validate("artist" => {"name" => "Bad Religion"}, "title" => "Ghost Inside My Skin")
|
63
79
|
form.save
|
64
80
|
Artist.where(:name => "Bad Religion").size.must_equal 1
|
65
81
|
end
|
@@ -71,4 +87,134 @@ class ActiveRecordTest < MiniTest::Spec
|
|
71
87
|
Artist.where(:name => "Bad Religion").size.must_equal 0
|
72
88
|
end
|
73
89
|
end
|
74
|
-
end
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
class PopulateWithActiveRecordTest < MiniTest::Spec
|
94
|
+
class AlbumForm < Reform::Form
|
95
|
+
property :title
|
96
|
+
|
97
|
+
collection :songs, :populate_if_empty => Song do
|
98
|
+
property :title
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
let (:album) { Album.new(:songs => []) }
|
103
|
+
it do
|
104
|
+
form = AlbumForm.new(album)
|
105
|
+
|
106
|
+
form.validate("songs" => [{"title" => "Straight From The Jacket"}])
|
107
|
+
|
108
|
+
# form populated.
|
109
|
+
form.songs.size.must_equal 1
|
110
|
+
form.songs[0].model.must_be_kind_of Song
|
111
|
+
|
112
|
+
# model NOT populated.
|
113
|
+
album.songs.must_equal []
|
114
|
+
|
115
|
+
|
116
|
+
form.sync
|
117
|
+
|
118
|
+
# form populated.
|
119
|
+
form.songs.size.must_equal 1
|
120
|
+
form.songs[0].model.must_be_kind_of Song
|
121
|
+
|
122
|
+
# model also populated.
|
123
|
+
song = album.songs[0]
|
124
|
+
album.songs.must_equal [song]
|
125
|
+
song.title.must_equal "Straight From The Jacket"
|
126
|
+
|
127
|
+
|
128
|
+
if ActiveRecord::VERSION::STRING !~ /^3.0/
|
129
|
+
# saving saves association.
|
130
|
+
form.save
|
131
|
+
|
132
|
+
album.reload
|
133
|
+
song = album.songs[0]
|
134
|
+
album.songs.must_equal [song]
|
135
|
+
song.title.must_equal "Straight From The Jacket"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
describe "modifying 1., adding 2." do
|
141
|
+
let (:song) { Song.new(:title => "Part 2") }
|
142
|
+
let (:album) { Album.create.tap { |a| a.songs << song } }
|
143
|
+
|
144
|
+
it do
|
145
|
+
form = AlbumForm.new(album)
|
146
|
+
|
147
|
+
id = album.songs[0].id
|
148
|
+
assert id > 0
|
149
|
+
|
150
|
+
form.validate("songs" => [{"title" => "Part Two"}, {"title" => "Check For A Pulse"}])
|
151
|
+
|
152
|
+
# form populated.
|
153
|
+
form.songs.size.must_equal 2
|
154
|
+
form.songs[0].model.must_be_kind_of Song
|
155
|
+
form.songs[1].model.must_be_kind_of Song
|
156
|
+
|
157
|
+
# model NOT populated.
|
158
|
+
album.songs.must_equal [song]
|
159
|
+
|
160
|
+
|
161
|
+
form.sync
|
162
|
+
|
163
|
+
# form populated.
|
164
|
+
form.songs.size.must_equal 2
|
165
|
+
|
166
|
+
# model also populated.
|
167
|
+
album.songs.size.must_equal 2
|
168
|
+
|
169
|
+
# corrected title
|
170
|
+
album.songs[0].title.must_equal "Part Two"
|
171
|
+
# ..but same song.
|
172
|
+
album.songs[0].id.must_equal id
|
173
|
+
|
174
|
+
# and a new song.
|
175
|
+
album.songs[1].title.must_equal "Check For A Pulse"
|
176
|
+
album.songs[1].persisted?.must_equal true # TODO: with << strategy, this shouldn't be saved.
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
# it do
|
181
|
+
# a=Album.new
|
182
|
+
# a.songs << Song.new(title: "Old What's His Name") # Song does not get persisted.
|
183
|
+
|
184
|
+
# a.songs[1] = Song.new(title: "Permanent Rust")
|
185
|
+
|
186
|
+
# puts "@@@"
|
187
|
+
# puts a.songs.inspect
|
188
|
+
|
189
|
+
# puts "---"
|
190
|
+
# a.save
|
191
|
+
# puts a.songs.inspect
|
192
|
+
|
193
|
+
|
194
|
+
|
195
|
+
# b = a.songs.first
|
196
|
+
|
197
|
+
# a.songs = [Song.new(title:"Biomag")]
|
198
|
+
# puts "\\\\"
|
199
|
+
# a.save
|
200
|
+
# a.reload
|
201
|
+
# puts a.songs.inspect
|
202
|
+
|
203
|
+
# b.reload
|
204
|
+
# puts "#{b.inspect}, #{b.persisted?}"
|
205
|
+
|
206
|
+
|
207
|
+
# a.songs = [a.songs.first, Song.new(title: "Count Down")]
|
208
|
+
# b = a.songs.first
|
209
|
+
# puts ":::::"
|
210
|
+
# a.save
|
211
|
+
# a.reload
|
212
|
+
# puts a.songs.inspect
|
213
|
+
|
214
|
+
# b.reload
|
215
|
+
# puts "#{b.inspect}, #{b.persisted?}"
|
216
|
+
# end
|
217
|
+
end
|
218
|
+
|
219
|
+
|
220
|
+
|
data/test/as_test.rb
CHANGED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class BuilderTest < MiniTest::Spec
|
4
|
+
it do
|
5
|
+
Builder.new.checkboxes(:settings, :hash => {"play" => true, "released" => false}).must_equal %{<input name="yo" type="hidden" value="unchecked_value" /><input id="object_name_method" name="yo" type="checkbox" value="checked_value" />
|
6
|
+
<input name="yo" type="hidden" value="unchecked_value" /><input id="object_name_method" name="yo" type="checkbox" value="checked_value" />}
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
require 'action_view/helpers/capture_helper'
|
12
|
+
require 'action_view/helpers/tag_helper'
|
13
|
+
require 'action_view/helpers/url_helper'
|
14
|
+
require 'action_view/helpers/sanitize_helper'
|
15
|
+
require 'action_view/helpers/text_helper'
|
16
|
+
require 'action_view/helpers/form_tag_helper'
|
17
|
+
require 'action_view/helpers/form_helper'
|
18
|
+
|
19
|
+
class Builder
|
20
|
+
include ActionView::Helpers::CaptureHelper
|
21
|
+
include ActionView::Helpers::FormHelper
|
22
|
+
|
23
|
+
# {name: value}
|
24
|
+
def checkboxes(name, options)
|
25
|
+
# get property form.to_a ? to_builder_hash or something like that
|
26
|
+
options[:hash].collect do |id, value|
|
27
|
+
ActionView::Helpers::InstanceTag.new(:object_name, :method, self).to_check_box_tag({:name => "yo"}, :checked_value, :unchecked_value)
|
28
|
+
|
29
|
+
# check_box_tag(id, value, checked = false, {})
|
30
|
+
end.join("\n")
|
31
|
+
end
|
32
|
+
end
|
data/test/coercion_test.rb
CHANGED
@@ -2,43 +2,71 @@ require "test_helper"
|
|
2
2
|
require "reform/form/coercion"
|
3
3
|
|
4
4
|
class CoercionTest < BaseTest
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
class Irreversible < Virtus::Attribute
|
6
|
+
def coerce(value)
|
7
|
+
value*2
|
8
|
+
end
|
9
|
+
end
|
8
10
|
|
9
|
-
|
11
|
+
class Form < Reform::Form
|
12
|
+
include Coercion
|
10
13
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
14
|
+
property :released_at, :type => DateTime
|
15
|
+
|
16
|
+
property :hit do
|
17
|
+
# include Coercion
|
18
|
+
property :length, :type => Integer
|
19
|
+
property :good, :type => Virtus::Attribute::Boolean
|
20
|
+
end
|
15
21
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
22
|
+
property :band do
|
23
|
+
include Coercion
|
24
|
+
property :label do
|
25
|
+
include Coercion
|
26
|
+
property :value, :type => Irreversible
|
20
27
|
end
|
21
|
-
end
|
22
|
-
:released_at => "31/03/1981",
|
23
|
-
:hit => OpenStruct.new(:length => "312"),
|
24
|
-
:band => Band.new(OpenStruct.new(:value => "9999.99"))
|
25
|
-
))
|
28
|
+
end
|
26
29
|
end
|
27
30
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
it { subject.band.label.value.must_equal 9999.99 }
|
31
|
+
subject do
|
32
|
+
Form.new(album)
|
33
|
+
end
|
32
34
|
|
35
|
+
let (:album) {
|
36
|
+
OpenStruct.new(
|
37
|
+
:released_at => "31/03/1981",
|
38
|
+
:hit => OpenStruct.new(:length => "312"),
|
39
|
+
:band => Band.new(OpenStruct.new(:value => "9999.99"))
|
40
|
+
)
|
41
|
+
}
|
33
42
|
|
34
|
-
it
|
35
|
-
|
36
|
-
|
43
|
+
# it { subject.released_at.must_be_kind_of DateTime }
|
44
|
+
it { subject.released_at.must_equal "31/03/1981" } # NO coercion in setup.
|
45
|
+
it { subject.hit.length.must_equal "312" }
|
46
|
+
it { subject.band.label.value.must_equal "9999.99" }
|
37
47
|
|
38
|
-
property :id, :type => Integer
|
39
|
-
end.new(OpenStruct.new())
|
40
48
|
|
41
|
-
|
42
|
-
|
49
|
+
let (:params) {
|
50
|
+
{
|
51
|
+
:released_at => "30/03/1981",
|
52
|
+
:hit => {:length => "312"},
|
53
|
+
:band => {:label => {:value => "9999.99"}}
|
54
|
+
}
|
55
|
+
}
|
56
|
+
|
57
|
+
|
58
|
+
# validate
|
59
|
+
describe "#validate" do
|
60
|
+
before { subject.validate(params) }
|
61
|
+
|
62
|
+
it { subject.released_at.must_equal DateTime.parse("30/03/1981") }
|
63
|
+
it { subject.hit.length.must_equal 312 }
|
64
|
+
it { subject.hit.good.must_equal nil }
|
65
|
+
it { subject.band.label.value.must_equal "9999.999999.99" } # coercion happened once.
|
43
66
|
end
|
67
|
+
|
68
|
+
# save
|
69
|
+
|
70
|
+
|
71
|
+
|
44
72
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'representable/json'
|
3
|
+
|
4
|
+
class DeserializeTest < BaseTest
|
5
|
+
class AlbumContract < Reform::Form
|
6
|
+
self.representer_class.class_eval do
|
7
|
+
include Representable::JSON
|
8
|
+
end
|
9
|
+
def deserialize_method
|
10
|
+
:from_json
|
11
|
+
end
|
12
|
+
|
13
|
+
property :title
|
14
|
+
validates :title, :presence => true, :length => {:minimum => 3}
|
15
|
+
|
16
|
+
property :hit do
|
17
|
+
property :title
|
18
|
+
validates :title, :presence => true
|
19
|
+
end
|
20
|
+
|
21
|
+
property :band do # yepp, people do crazy stuff like that.
|
22
|
+
validates :label, :presence => true
|
23
|
+
|
24
|
+
property :label do
|
25
|
+
property :name
|
26
|
+
validates :name, :presence => true
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
let (:album) { Album.new(nil, Song.new, [Song.new, Song.new], Band.new(Label.new("Fat Wreck")) ) }
|
32
|
+
subject { AlbumContract.new(album) }
|
33
|
+
|
34
|
+
let (:json) { '{"hit":{"title":"Sacrifice"},"title":"Second Heat","songs":[{"title":"Heart Of A Lion"}],"band":{"label":{"name":"Fat Wreck"}}}' }
|
35
|
+
|
36
|
+
it {
|
37
|
+
subject.validate(json)
|
38
|
+
subject.band.label.name.must_equal "Fat Wreck"
|
39
|
+
}
|
40
|
+
end
|