freeform 0.0.2 → 0.0.3.rc1
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.
- data/README.md +58 -6
- data/lib/freeform/form/nested.rb +1 -1
- data/lib/freeform/form/property.rb +6 -0
- data/lib/freeform/form/{persistence.rb → validation.rb} +2 -25
- data/lib/freeform/form.rb +47 -0
- data/lib/freeform/version.rb +1 -1
- data/lib/freeform.rb +2 -11
- data/spec/acceptance_spec.rb +309 -0
- data/spec/form/nested_spec.rb +13 -1
- data/spec/form/{persistence_spec.rb → validation_spec.rb} +13 -96
- metadata +10 -8
data/README.md
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
# FreeForm
|
2
2
|
|
3
|
-
FreeForm is a gem designed to give you total control over form objects, allowing you to map form objects to domain objects in any way that you see fit.
|
3
|
+
FreeForm is a gem designed to give you total control over form objects, allowing you to map form objects to domain objects in any way that you see fit. The primary benefits are:
|
4
|
+
* Decoupling form objects from domain models
|
5
|
+
* Allowing form-specific validations, while respecting model validations
|
6
|
+
* Simply composing multi-model forms
|
7
|
+
* Removing the ugliness of `accepts_nested_attributes_for`
|
4
8
|
|
5
|
-
|
6
|
-
|
7
|
-
FreeForm is designed primarily with Rails in mind, but it should work on any Ruby framework.
|
8
|
-
|
9
|
-
FreeForm is compatible with (as far as I know) most form gems, including simpleform, formbuilder, and Ryan Bate's nested_form gem.
|
9
|
+
FreeForm is designed primarily with Rails in mind, but it should work on any Ruby framework. FreeForm is compatible with most form gems, including simpleform, formbuilder, and Ryan Bate's nested_form gem.
|
10
10
|
|
11
11
|
## Installation
|
12
12
|
|
@@ -107,6 +107,58 @@ end
|
|
107
107
|
user = User.new
|
108
108
|
RegistrationForm.new(:user => user, :address => user.build_address)
|
109
109
|
```
|
110
|
+
## Assigning Parameters
|
111
|
+
|
112
|
+
Forms can be populated either directly through accessors (e.g. `form.street = "123 Main St."`), or using the `assign_params()` or `fill()` methods.
|
113
|
+
|
114
|
+
```ruby
|
115
|
+
class RegistrationForm < FreeForm::Form
|
116
|
+
form_models :user, :address
|
117
|
+
|
118
|
+
property :username, :on => :user
|
119
|
+
property :street, :on => :address
|
120
|
+
end
|
121
|
+
|
122
|
+
form = RegistrationForm.new(:user => User.new, :address => Address.new)
|
123
|
+
form.assign_params({ :username => "myusername", :street => "1600 Pennsylvania Ave." })
|
124
|
+
# fill() is just an alias for assign_params
|
125
|
+
form.fill({ :username => "myusername", :street => "1600 Pennsylvania Ave." })
|
126
|
+
```
|
127
|
+
|
128
|
+
## Saving & Marking For Destruction
|
129
|
+
|
130
|
+
Calling `form.save` will validate the form, then make a save call to each model defined in the form (and each nested form). A call to `save!` behaves the same way.
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
class RegistrationForm < FreeForm::Form
|
134
|
+
form_models :user, :address
|
135
|
+
|
136
|
+
property :username, :on => :user
|
137
|
+
property :street, :on => :address
|
138
|
+
end
|
139
|
+
|
140
|
+
form = RegistrationForm.new(:user => User.new, :address => Address.new)
|
141
|
+
form.fill({ :username => "myusername", :street => "1600 Pennsylvania Ave." })
|
142
|
+
form.save # SAVES the User and Address models
|
143
|
+
```
|
144
|
+
|
145
|
+
Additionally, each model has a method you can add called `allow_destroy_on_save`. This method adds an accessor called `_destroy` (also aliased as `marked_for_destruction`) that can be set. If this is set, each form in the model will receive a `destroy` call on save.
|
146
|
+
|
147
|
+
```ruby
|
148
|
+
class RegistrationForm < FreeForm::Form
|
149
|
+
form_models :user, :address
|
150
|
+
allow_destroy_on_save
|
151
|
+
|
152
|
+
property :username, :on => :user
|
153
|
+
property :street, :on => :address
|
154
|
+
end
|
155
|
+
|
156
|
+
form = RegistrationForm.new(:user => User.new, :address => Address.new)
|
157
|
+
form.fill({ :username => "myusername", :street => "1600 Pennsylvania Ave.", :_destroy => "1" })
|
158
|
+
form.save # DESTROYS the User and Address models
|
159
|
+
```
|
160
|
+
This method is necessary if you use the nested_form gem for dynamically creating/deleting models.
|
161
|
+
|
110
162
|
## Form Validations
|
111
163
|
|
112
164
|
FreeForm handles validations wherever you define them. If you want to check model validations, simply specify that option in your form definition
|
data/lib/freeform/form/nested.rb
CHANGED
@@ -64,7 +64,7 @@ module FreeForm
|
|
64
64
|
form_class = self.class.nested_forms[:"#{attribute}"]
|
65
65
|
|
66
66
|
# Set default intializer if none provided
|
67
|
-
initializer ||= send("#{attribute}_form_initializer")
|
67
|
+
initializer ||= send("#{attribute}_form_initializer").call
|
68
68
|
|
69
69
|
# Build new model
|
70
70
|
form_model = form_class.new(initializer)
|
@@ -33,6 +33,12 @@ module FreeForm
|
|
33
33
|
@ignored_blank_params ||= []
|
34
34
|
end
|
35
35
|
|
36
|
+
def allow_destroy_on_save
|
37
|
+
# Define _destroy method for marked-for-destruction handling
|
38
|
+
attr_accessor :_destroy
|
39
|
+
alias_method :marked_for_destruction, :_destroy
|
40
|
+
end
|
41
|
+
|
36
42
|
def property(attribute, options={})
|
37
43
|
if options[:on]
|
38
44
|
def_delegator options[:on], attribute
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module FreeForm
|
2
|
-
module
|
2
|
+
module Validation
|
3
3
|
def self.included(base)
|
4
4
|
base.extend(ClassMethods)
|
5
5
|
end
|
@@ -9,30 +9,7 @@ module FreeForm
|
|
9
9
|
validate :model_validity
|
10
10
|
end
|
11
11
|
end
|
12
|
-
|
13
|
-
def save
|
14
|
-
return false unless valid?
|
15
|
-
self.class.models.each do |form_model|
|
16
|
-
if send(form_model).is_a?(Array)
|
17
|
-
send(form_model).each { |model| return model.save }
|
18
|
-
else
|
19
|
-
return false unless send(form_model).save
|
20
|
-
end
|
21
|
-
end
|
22
|
-
return true
|
23
|
-
end
|
24
|
-
|
25
|
-
def save!
|
26
|
-
raise StandardError, "form invalid." unless valid?
|
27
|
-
self.class.models.each do |form_model|
|
28
|
-
if send(form_model).is_a?(Array)
|
29
|
-
send(form_model).each { |model| model.save! }
|
30
|
-
else
|
31
|
-
send(form_model).save!
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
12
|
+
|
36
13
|
protected
|
37
14
|
def model_validity
|
38
15
|
model_validity = true
|
data/lib/freeform/form.rb
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
require 'forwardable'
|
2
2
|
require 'ostruct'
|
3
3
|
require 'active_model'
|
4
|
+
require 'freeform/form/form_input_key'
|
5
|
+
require 'freeform/form/nested'
|
6
|
+
require 'freeform/form/property'
|
7
|
+
require 'freeform/form/validation'
|
4
8
|
|
5
9
|
module FreeForm
|
6
10
|
class Form
|
@@ -9,6 +13,10 @@ module FreeForm
|
|
9
13
|
extend ActiveModel::Callbacks
|
10
14
|
include ActiveModel::Conversion
|
11
15
|
include ActiveModel::Validations
|
16
|
+
include FreeForm::FormInputKey
|
17
|
+
include FreeForm::Nested
|
18
|
+
include FreeForm::Property
|
19
|
+
include FreeForm::Validation
|
12
20
|
|
13
21
|
# Instance Methods
|
14
22
|
#----------------------------------------------------------------------------
|
@@ -20,5 +28,44 @@ module FreeForm
|
|
20
28
|
def initialize(h={})
|
21
29
|
h.each {|k,v| send("#{k}=",v)}
|
22
30
|
end
|
31
|
+
|
32
|
+
def save
|
33
|
+
return false unless valid?
|
34
|
+
self.class.models.each do |form_model|
|
35
|
+
if send(form_model).is_a?(Array)
|
36
|
+
send(form_model).each { |model| return model.save }
|
37
|
+
else
|
38
|
+
if marked_for_destruction?
|
39
|
+
send(form_model).destroy
|
40
|
+
else
|
41
|
+
return false unless send(form_model).save
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
return true
|
46
|
+
end
|
47
|
+
|
48
|
+
def save!
|
49
|
+
raise StandardError, "form invalid." unless valid?
|
50
|
+
self.class.models.each do |form_model|
|
51
|
+
if send(form_model).is_a?(Array)
|
52
|
+
send(form_model).each { |model| model.save! }
|
53
|
+
else
|
54
|
+
if marked_for_destruction?
|
55
|
+
send(form_model).destroy
|
56
|
+
else
|
57
|
+
send(form_model).save!
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def marked_for_destruction?
|
64
|
+
if self.respond_to?(:_destroy)
|
65
|
+
return self._destroy
|
66
|
+
else
|
67
|
+
return false
|
68
|
+
end
|
69
|
+
end
|
23
70
|
end
|
24
71
|
end
|
data/lib/freeform/version.rb
CHANGED
data/lib/freeform.rb
CHANGED
@@ -1,14 +1,5 @@
|
|
1
1
|
require 'freeform/version'
|
2
2
|
require 'freeform/form'
|
3
|
-
#require 'reform/form/composition'
|
4
|
-
#require 'reform/form/active_model'
|
5
3
|
|
6
|
-
if defined?(Rails) #
|
7
|
-
|
8
|
-
end
|
9
|
-
|
10
|
-
=begin
|
11
|
-
module Freeform
|
12
|
-
# Your code goes here...
|
13
|
-
end
|
14
|
-
=end
|
4
|
+
if defined?(Rails) # Where to include Rails specific modules...
|
5
|
+
end
|
@@ -0,0 +1,309 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FreeForm::Form do
|
4
|
+
let(:user_class) do
|
5
|
+
Class.new(Module) do
|
6
|
+
include ActiveModel::Validations
|
7
|
+
def self.model_name; ActiveModel::Name.new(self, nil, "user") end
|
8
|
+
|
9
|
+
attr_accessor :username
|
10
|
+
attr_accessor :email
|
11
|
+
validates :username, :presence => true
|
12
|
+
|
13
|
+
def save; return valid? end
|
14
|
+
alias_method :save!, :save
|
15
|
+
def destroy; return true end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
let(:address_class) do
|
20
|
+
Class.new(Module) do
|
21
|
+
include ActiveModel::Validations
|
22
|
+
def self.model_name; ActiveModel::Name.new(self, nil, "address") end
|
23
|
+
|
24
|
+
attr_accessor :street
|
25
|
+
attr_accessor :city
|
26
|
+
attr_accessor :state
|
27
|
+
validates :street, :presence => true
|
28
|
+
validates :city, :presence => true
|
29
|
+
validates :state, :presence => true
|
30
|
+
|
31
|
+
def save; return valid? end
|
32
|
+
alias_method :save!, :save
|
33
|
+
def destroy; return true end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
let(:phone_class) do
|
38
|
+
Class.new(Module) do
|
39
|
+
include ActiveModel::Validations
|
40
|
+
def self.model_name; ActiveModel::Name.new(self, nil, "phone") end
|
41
|
+
|
42
|
+
attr_accessor :area_code
|
43
|
+
attr_accessor :number
|
44
|
+
validates :number, :presence => true
|
45
|
+
|
46
|
+
def save; return valid? end
|
47
|
+
alias_method :save!, :save
|
48
|
+
def destroy; return true end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
let(:form_class) do
|
53
|
+
klass = Class.new(FreeForm::Form) do
|
54
|
+
form_input_key :user
|
55
|
+
form_models :user, :address
|
56
|
+
validate_models
|
57
|
+
allow_destroy_on_save
|
58
|
+
|
59
|
+
property :username, :on => :user
|
60
|
+
property :email, :on => :user
|
61
|
+
property :street, :on => :address
|
62
|
+
property :city, :on => :address
|
63
|
+
property :state, :on => :address
|
64
|
+
|
65
|
+
has_many :phone_numbers do
|
66
|
+
form_model :phone
|
67
|
+
validate_models
|
68
|
+
allow_destroy_on_save
|
69
|
+
|
70
|
+
property :area_code, :on => :phone
|
71
|
+
property :number, :on => :phone
|
72
|
+
end
|
73
|
+
end
|
74
|
+
# This wrapper just avoids CONST warnings
|
75
|
+
v, $VERBOSE = $VERBOSE, nil
|
76
|
+
Module.const_set("AcceptanceForm", klass)
|
77
|
+
$VERBOSE = v
|
78
|
+
klass
|
79
|
+
end
|
80
|
+
|
81
|
+
let(:form) do
|
82
|
+
form_model = form_class.new(
|
83
|
+
:user => user_class.new,
|
84
|
+
:address => address_class.new,
|
85
|
+
:phone_numbers_form_initializer => lambda { { :phone => phone_class.new } }
|
86
|
+
)
|
87
|
+
form_model.build_phone_number
|
88
|
+
form_model
|
89
|
+
end
|
90
|
+
|
91
|
+
describe "form initialization", :initialization => true do
|
92
|
+
it "initializes with user model" do
|
93
|
+
form.user.should be_a(user_class)
|
94
|
+
end
|
95
|
+
|
96
|
+
it "initializes with address model" do
|
97
|
+
form.address.should be_a(address_class)
|
98
|
+
end
|
99
|
+
|
100
|
+
it "initializes with phone model" do
|
101
|
+
form.phone_numbers.first.phone.should be_a(phone_class)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe "building nested models", :nested_models => true do
|
106
|
+
it "can build multiple phone_numbers" do
|
107
|
+
form.phone_numbers.count.should eq(1)
|
108
|
+
form.build_phone_number
|
109
|
+
form.build_phone_number
|
110
|
+
form.phone_numbers.count.should eq(3)
|
111
|
+
end
|
112
|
+
|
113
|
+
it "build new models for each phone numbers" do
|
114
|
+
form.build_phone_number
|
115
|
+
phone_1 = form.phone_numbers.first.phone
|
116
|
+
phone_2 = form.phone_numbers.last.phone
|
117
|
+
phone_1.should_not eq(phone_2)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe "assigning parameters", :assign_params => true do
|
122
|
+
let(:attributes) do {
|
123
|
+
:username => "dummyuser",
|
124
|
+
:email => "test@email.com",
|
125
|
+
:street => "1 Maple St.",
|
126
|
+
:city => "Portland",
|
127
|
+
:state => "Oregon",
|
128
|
+
:phone_numbers_attributes => {
|
129
|
+
"0" => {
|
130
|
+
:area_code => "555",
|
131
|
+
:number => "123-4567"
|
132
|
+
},
|
133
|
+
"1" => {
|
134
|
+
:area_code => "202",
|
135
|
+
:number => "876-5432"
|
136
|
+
}
|
137
|
+
} }
|
138
|
+
end
|
139
|
+
|
140
|
+
before(:each) do
|
141
|
+
form.fill(attributes)
|
142
|
+
end
|
143
|
+
|
144
|
+
it "assigns username" do
|
145
|
+
form.username.should eq("dummyuser")
|
146
|
+
end
|
147
|
+
|
148
|
+
it "assigns street" do
|
149
|
+
form.street.should eq("1 Maple St.")
|
150
|
+
end
|
151
|
+
|
152
|
+
it "assigns phone number area code" do
|
153
|
+
form.phone_numbers.first.phone.area_code.should eq("555")
|
154
|
+
end
|
155
|
+
|
156
|
+
it "builds new phone number automatically" do
|
157
|
+
form.phone_numbers.count.should eq(2)
|
158
|
+
end
|
159
|
+
|
160
|
+
it "assigns second phone number area code" do
|
161
|
+
form.phone_numbers.last.phone.area_code.should eq("202")
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
describe "validations", :validations => true do
|
166
|
+
context "with invalid attributes" do
|
167
|
+
let(:attributes) do {
|
168
|
+
:username => "dummyuser",
|
169
|
+
:email => "test@email.com",
|
170
|
+
:street => nil,
|
171
|
+
:city => "Portland",
|
172
|
+
:state => "Oregon",
|
173
|
+
:phone_numbers_attributes => {
|
174
|
+
"0" => {
|
175
|
+
:number => "123-4567"
|
176
|
+
},
|
177
|
+
"1" => {
|
178
|
+
:area_code => "202"
|
179
|
+
}
|
180
|
+
} }
|
181
|
+
end
|
182
|
+
|
183
|
+
before(:each) do
|
184
|
+
form.fill(attributes)
|
185
|
+
form.valid?
|
186
|
+
end
|
187
|
+
|
188
|
+
it "should be invalid" do
|
189
|
+
form.should_not be_valid
|
190
|
+
end
|
191
|
+
|
192
|
+
it "should have errors on street" do
|
193
|
+
form.errors[:street].should eq(["can't be blank"])
|
194
|
+
end
|
195
|
+
|
196
|
+
it "should have errors on first phone number's area code" do
|
197
|
+
form.phone_numbers.first.errors[:area_code].should be_empty
|
198
|
+
end
|
199
|
+
|
200
|
+
it "should have errors on first phone number's number" do
|
201
|
+
form.phone_numbers.last.errors[:number].should eq(["can't be blank"])
|
202
|
+
end
|
203
|
+
|
204
|
+
it "should return false on 'save'" do
|
205
|
+
form.save.should be_false
|
206
|
+
end
|
207
|
+
|
208
|
+
it "should raise error on 'save!'" do
|
209
|
+
expect{ form.save!.should be_false }.to raise_error
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
context "with valid attributes" do
|
214
|
+
let(:attributes) do {
|
215
|
+
:username => "dummyuser",
|
216
|
+
:email => "test@email.com",
|
217
|
+
:street => "1 Maple St.",
|
218
|
+
:city => "Portland",
|
219
|
+
:state => "Oregon",
|
220
|
+
:phone_numbers_attributes => {
|
221
|
+
"0" => {
|
222
|
+
:area_code => "555",
|
223
|
+
:number => "123-4567"
|
224
|
+
},
|
225
|
+
"1" => {
|
226
|
+
:area_code => "202",
|
227
|
+
:number => "876-5432"
|
228
|
+
}
|
229
|
+
} }
|
230
|
+
end
|
231
|
+
|
232
|
+
before(:each) do
|
233
|
+
form.fill(attributes)
|
234
|
+
end
|
235
|
+
|
236
|
+
it "should be valid" do
|
237
|
+
form.should be_valid
|
238
|
+
end
|
239
|
+
|
240
|
+
it "should return true on 'save', and call save on other models" do
|
241
|
+
form.user.should_receive(:save).and_return(true)
|
242
|
+
form.address.should_receive(:save).and_return(true)
|
243
|
+
form.phone_numbers.first.phone.should_receive(:save).and_return(true)
|
244
|
+
form.save
|
245
|
+
end
|
246
|
+
|
247
|
+
it "should return true on 'save!', and call save! on other models" do
|
248
|
+
form.user.should_receive(:save!).and_return(true)
|
249
|
+
form.address.should_receive(:save!).and_return(true)
|
250
|
+
form.phone_numbers.first.phone.should_receive(:save!).and_return(true)
|
251
|
+
form.save!
|
252
|
+
end
|
253
|
+
|
254
|
+
describe "destroying on save", :destroy_on_save => true do
|
255
|
+
describe "save" do
|
256
|
+
it "destroys models on save if set" do
|
257
|
+
form._destroy = true
|
258
|
+
form.user.should_receive(:destroy).and_return(true)
|
259
|
+
form.address.should_receive(:destroy).and_return(true)
|
260
|
+
form.phone_numbers.first.phone.should_receive(:save).and_return(true)
|
261
|
+
form.save
|
262
|
+
end
|
263
|
+
|
264
|
+
it "destroys models on save if set through attribute" do
|
265
|
+
form.fill({:_destroy => "1"})
|
266
|
+
form.user.should_receive(:destroy).and_return(true)
|
267
|
+
form.address.should_receive(:destroy).and_return(true)
|
268
|
+
form.phone_numbers.first.phone.should_receive(:save).and_return(true)
|
269
|
+
form.save
|
270
|
+
end
|
271
|
+
|
272
|
+
it "destroys nested models on save if set" do
|
273
|
+
form.phone_numbers.first._destroy = true
|
274
|
+
form.user.should_receive(:save).and_return(true)
|
275
|
+
form.address.should_receive(:save).and_return(true)
|
276
|
+
form.phone_numbers.first.phone.should_receive(:destroy).and_return(true)
|
277
|
+
form.save
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
describe "save!" do
|
282
|
+
it "destroys models on save! if set" do
|
283
|
+
form._destroy = true
|
284
|
+
form.user.should_receive(:destroy).and_return(true)
|
285
|
+
form.address.should_receive(:destroy).and_return(true)
|
286
|
+
form.phone_numbers.first.phone.should_receive(:save!).and_return(true)
|
287
|
+
form.save!
|
288
|
+
end
|
289
|
+
|
290
|
+
it "destroys models on save! if set" do
|
291
|
+
form.fill({:_destroy => "1"})
|
292
|
+
form.user.should_receive(:destroy).and_return(true)
|
293
|
+
form.address.should_receive(:destroy).and_return(true)
|
294
|
+
form.phone_numbers.first.phone.should_receive(:save!).and_return(true)
|
295
|
+
form.save!
|
296
|
+
end
|
297
|
+
|
298
|
+
it "destroys nested models on save! if set" do
|
299
|
+
form.phone_numbers.first._destroy = true
|
300
|
+
form.user.should_receive(:save!).and_return(true)
|
301
|
+
form.address.should_receive(:save!).and_return(true)
|
302
|
+
form.phone_numbers.first.phone.should_receive(:destroy).and_return(true)
|
303
|
+
form.save!
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|
309
|
+
end
|
data/spec/form/nested_spec.rb
CHANGED
@@ -28,7 +28,9 @@ describe FreeForm::Nested do
|
|
28
28
|
end
|
29
29
|
|
30
30
|
let(:form) do
|
31
|
-
form_class.new(
|
31
|
+
form_model = form_class.new(
|
32
|
+
:mailing_address_form_initializer => lambda { { :address => OpenStruct.new } }
|
33
|
+
)
|
32
34
|
end
|
33
35
|
|
34
36
|
describe "form class" do
|
@@ -49,6 +51,16 @@ describe FreeForm::Nested do
|
|
49
51
|
form.mailing_addresses.first.should be_a(Module::DummyForm::MailingAddressesForm)
|
50
52
|
end
|
51
53
|
|
54
|
+
it "builds unique models" do
|
55
|
+
# Using module here, since they initialize uniquely
|
56
|
+
form.mailing_address_form_initializer = lambda { { :address => Module.new } }
|
57
|
+
form.build_mailing_address
|
58
|
+
form.build_mailing_address
|
59
|
+
address_1 = form.mailing_addresses.first.address
|
60
|
+
address_2 = form.mailing_addresses.last.address
|
61
|
+
address_1.should_not eq(address_2)
|
62
|
+
end
|
63
|
+
|
52
64
|
it "allows nested_forms to be built with custom initializers" do
|
53
65
|
form.build_mailing_address(:address => OpenStruct.new(:street => "1600 Pennsylvania Ave."))
|
54
66
|
form.mailing_addresses.first.street.should eq("1600 Pennsylvania Ave.")
|
@@ -1,9 +1,9 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'freeform/form/property'
|
3
3
|
require 'freeform/form/nested'
|
4
|
-
require 'freeform/form/
|
4
|
+
require 'freeform/form/validation'
|
5
5
|
|
6
|
-
describe FreeForm::
|
6
|
+
describe FreeForm::Validation do
|
7
7
|
let(:user) do
|
8
8
|
model = Class.new(Module) do
|
9
9
|
include ActiveModel::Validations
|
@@ -34,13 +34,13 @@ describe FreeForm::Persistence do
|
|
34
34
|
end
|
35
35
|
|
36
36
|
describe "validation", :validation => true do
|
37
|
-
context "without model validation" do
|
37
|
+
context "without model validation", :no_model_validation => true do
|
38
38
|
let(:form_class) do
|
39
39
|
klass = Class.new(Module) do
|
40
40
|
include ActiveModel::Validations
|
41
41
|
include FreeForm::Property
|
42
42
|
include FreeForm::Nested
|
43
|
-
include FreeForm::
|
43
|
+
include FreeForm::Validation
|
44
44
|
|
45
45
|
form_model :user
|
46
46
|
property :username, :on => :user
|
@@ -64,7 +64,9 @@ describe FreeForm::Persistence do
|
|
64
64
|
end
|
65
65
|
|
66
66
|
let(:form) do
|
67
|
-
test_form = form_class.new(
|
67
|
+
test_form = form_class.new(
|
68
|
+
:user => user,
|
69
|
+
:address_form_initializer => lambda { { :address => address } } )
|
68
70
|
test_form.build_address
|
69
71
|
test_form
|
70
72
|
end
|
@@ -84,13 +86,13 @@ describe FreeForm::Persistence do
|
|
84
86
|
end
|
85
87
|
end
|
86
88
|
|
87
|
-
context "with model validation" do
|
89
|
+
context "with model validation", :model_validation => true do
|
88
90
|
let(:form_class) do
|
89
91
|
klass = Class.new(Module) do
|
90
92
|
include ActiveModel::Validations
|
91
93
|
include FreeForm::Property
|
92
94
|
include FreeForm::Nested
|
93
|
-
include FreeForm::
|
95
|
+
include FreeForm::Validation
|
94
96
|
validate_models
|
95
97
|
|
96
98
|
form_model :user
|
@@ -102,7 +104,7 @@ describe FreeForm::Persistence do
|
|
102
104
|
include ActiveModel::Validations
|
103
105
|
include FreeForm::Property
|
104
106
|
include FreeForm::Nested
|
105
|
-
include FreeForm::
|
107
|
+
include FreeForm::Validation
|
106
108
|
validate_models
|
107
109
|
declared_model :address
|
108
110
|
|
@@ -122,7 +124,9 @@ describe FreeForm::Persistence do
|
|
122
124
|
end
|
123
125
|
|
124
126
|
let(:form) do
|
125
|
-
test_form = form_class.new(
|
127
|
+
test_form = form_class.new(
|
128
|
+
:user => user,
|
129
|
+
:address_form_initializer => lambda { { :address => address } } )
|
126
130
|
test_form.build_address
|
127
131
|
test_form
|
128
132
|
end
|
@@ -147,91 +151,4 @@ describe FreeForm::Persistence do
|
|
147
151
|
end
|
148
152
|
end
|
149
153
|
end
|
150
|
-
|
151
|
-
describe "saving", :saving => true do
|
152
|
-
let(:form_class) do
|
153
|
-
klass = Class.new(Module) do
|
154
|
-
include ActiveModel::Validations
|
155
|
-
include FreeForm::Property
|
156
|
-
include FreeForm::Nested
|
157
|
-
include FreeForm::Persistence
|
158
|
-
validate_models
|
159
|
-
|
160
|
-
form_model :user
|
161
|
-
property :username, :on => :user
|
162
|
-
property :form_property
|
163
|
-
validates :form_property, :presence => true
|
164
|
-
|
165
|
-
nested_form :addresses do
|
166
|
-
include ActiveModel::Validations
|
167
|
-
include FreeForm::Property
|
168
|
-
include FreeForm::Nested
|
169
|
-
include FreeForm::Persistence
|
170
|
-
validate_models
|
171
|
-
declared_model :address
|
172
|
-
|
173
|
-
property :street, :on => :address
|
174
|
-
property :city, :on => :address
|
175
|
-
end
|
176
|
-
|
177
|
-
def initialize(h={})
|
178
|
-
h.each {|k,v| send("#{k}=",v)}
|
179
|
-
end
|
180
|
-
end
|
181
|
-
# This wrapper just avoids CONST warnings
|
182
|
-
v, $VERBOSE = $VERBOSE, nil
|
183
|
-
Module.const_set("DummyForm", klass)
|
184
|
-
$VERBOSE = v
|
185
|
-
klass
|
186
|
-
end
|
187
|
-
|
188
|
-
let(:form) do
|
189
|
-
test_form = form_class.new( :user => user, :address_form_initializer => { :address => address } )
|
190
|
-
test_form.build_address
|
191
|
-
test_form
|
192
|
-
end
|
193
|
-
|
194
|
-
context "form is invalid" do
|
195
|
-
it "should be invalid" do
|
196
|
-
form.should_not be_valid
|
197
|
-
end
|
198
|
-
|
199
|
-
it "should return false on 'save'" do
|
200
|
-
form.save.should be_false
|
201
|
-
end
|
202
|
-
|
203
|
-
it "should raise error on 'save!'" do
|
204
|
-
expect{ form.save!.should be_false }.to raise_error
|
205
|
-
end
|
206
|
-
end
|
207
|
-
|
208
|
-
context "form is valid" do
|
209
|
-
before(:each) do
|
210
|
-
form.fill({
|
211
|
-
:username => "myusername",
|
212
|
-
:form_property => "present",
|
213
|
-
:addresses_attributes => { "0" => {
|
214
|
-
:street => "123 Main St.",
|
215
|
-
:city => "Springfield"
|
216
|
-
} }
|
217
|
-
})
|
218
|
-
end
|
219
|
-
|
220
|
-
it "should be valid" do
|
221
|
-
form.should be_valid
|
222
|
-
end
|
223
|
-
|
224
|
-
it "should return true on 'save', and call save on other models" do
|
225
|
-
user.should_receive(:save).and_return(true)
|
226
|
-
address.should_receive(:save).and_return(true)
|
227
|
-
form.save.should be_true
|
228
|
-
end
|
229
|
-
|
230
|
-
it "should return true on 'save!', and call save! on other models" do
|
231
|
-
user.should_receive(:save!).and_return(true)
|
232
|
-
address.should_receive(:save!).and_return(true)
|
233
|
-
form.save!.should be_true
|
234
|
-
end
|
235
|
-
end
|
236
|
-
end
|
237
154
|
end
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: freeform
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.0.3.rc1
|
5
|
+
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- brycesenz
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-11-
|
12
|
+
date: 2013-11-15 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activemodel
|
@@ -94,13 +94,14 @@ files:
|
|
94
94
|
- lib/freeform/form.rb
|
95
95
|
- lib/freeform/form/form_input_key.rb
|
96
96
|
- lib/freeform/form/nested.rb
|
97
|
-
- lib/freeform/form/persistence.rb
|
98
97
|
- lib/freeform/form/property.rb
|
98
|
+
- lib/freeform/form/validation.rb
|
99
99
|
- lib/freeform/version.rb
|
100
|
+
- spec/acceptance_spec.rb
|
100
101
|
- spec/form/form_input_key_spec.rb
|
101
102
|
- spec/form/nested_spec.rb
|
102
|
-
- spec/form/persistence_spec.rb
|
103
103
|
- spec/form/property_spec.rb
|
104
|
+
- spec/form/validation_spec.rb
|
104
105
|
- spec/spec_helper.rb
|
105
106
|
homepage: https://github.com/brycesenz/freeform
|
106
107
|
licenses:
|
@@ -118,9 +119,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
118
119
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
119
120
|
none: false
|
120
121
|
requirements:
|
121
|
-
- - ! '
|
122
|
+
- - ! '>'
|
122
123
|
- !ruby/object:Gem::Version
|
123
|
-
version:
|
124
|
+
version: 1.3.1
|
124
125
|
requirements: []
|
125
126
|
rubyforge_project:
|
126
127
|
rubygems_version: 1.8.25
|
@@ -129,8 +130,9 @@ specification_version: 3
|
|
129
130
|
summary: FreeForm lets you compose forms however you like, explicitly guiding how
|
130
131
|
each field maps back to your domain models. No more accepting nested attributes!
|
131
132
|
test_files:
|
133
|
+
- spec/acceptance_spec.rb
|
132
134
|
- spec/form/form_input_key_spec.rb
|
133
135
|
- spec/form/nested_spec.rb
|
134
|
-
- spec/form/persistence_spec.rb
|
135
136
|
- spec/form/property_spec.rb
|
137
|
+
- spec/form/validation_spec.rb
|
136
138
|
- spec/spec_helper.rb
|