freeform 0.0.4 → 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.
- data/.gitignore +8 -17
- data/Gemfile +1 -1
- data/Gemfile.lock +150 -0
- data/MIT-LICENSE +20 -0
- data/README.md +31 -43
- data/Rakefile +32 -1
- data/freeform.gemspec +22 -15
- data/lib/freeform/builder/builder_mixin.rb +111 -0
- data/lib/freeform/builder/builders.rb +32 -0
- data/lib/freeform/builder/engine.rb +14 -0
- data/lib/freeform/builder/view_helper.rb +56 -0
- data/lib/freeform/form/form_input_key.rb +1 -1
- data/lib/freeform/form/nested.rb +17 -55
- data/lib/freeform/form/property.rb +82 -42
- data/lib/freeform/form/validation.rb +38 -11
- data/lib/freeform/form.rb +24 -27
- data/lib/freeform/version.rb +1 -1
- data/lib/freeform.rb +4 -0
- data/lib/generators/freeform/install_generator.rb +17 -0
- data/lib/tasks/freeform_tasks.rake +4 -0
- data/spec/acceptance_spec.rb +261 -155
- data/spec/builder/builder_spec.rb +215 -0
- data/spec/builder/view_helper_spec.rb +97 -0
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/images/.keep +0 -0
- data/spec/dummy/app/assets/javascripts/application.js +5 -0
- data/spec/dummy/app/assets/javascripts/jquery.js +9404 -0
- data/spec/dummy/app/assets/javascripts/jquery_events_test.js +19 -0
- data/spec/dummy/app/assets/javascripts/projects.js +2 -0
- data/spec/dummy/app/assets/javascripts/prototype.js +6082 -0
- data/spec/dummy/app/assets/javascripts/prototype_events_test.js +20 -0
- data/spec/dummy/app/assets/stylesheets/application.css +7 -0
- data/spec/dummy/app/assets/stylesheets/companies.css +4 -0
- data/spec/dummy/app/assets/stylesheets/projects.css +4 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/controllers/companies_controller.rb +5 -0
- data/spec/dummy/app/controllers/projects_controller.rb +11 -0
- data/spec/dummy/app/forms/milestone_form.rb +4 -0
- data/spec/dummy/app/forms/project_form.rb +11 -0
- data/spec/dummy/app/forms/task_form.rb +10 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/.keep +0 -0
- data/spec/dummy/app/models/.keep +0 -0
- data/spec/dummy/app/models/company.rb +5 -0
- data/spec/dummy/app/models/milestone.rb +6 -0
- data/spec/dummy/app/models/project.rb +9 -0
- data/spec/dummy/app/models/project_task.rb +3 -0
- data/spec/dummy/app/models/task.rb +7 -0
- data/spec/dummy/app/views/companies/new.html.erb +16 -0
- data/spec/dummy/app/views/layouts/application.html.erb +19 -0
- data/spec/dummy/app/views/projects/new.html.erb +13 -0
- data/spec/dummy/app/views/projects/without_intermediate_inputs.html.erb +11 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/config/application.rb +35 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +29 -0
- data/spec/dummy/config/environments/production.rb +80 -0
- data/spec/dummy/config/environments/test.rb +36 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +13 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +5 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/db/migrate/20110710143903_initial_tables.rb +38 -0
- data/spec/dummy/db/schema.rb +43 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/lib/assets/.keep +0 -0
- data/spec/dummy/log/.keep +0 -0
- data/spec/dummy/log/development.log +0 -0
- data/spec/dummy/log/test.log +39736 -0
- data/spec/dummy/public/404.html +58 -0
- data/spec/dummy/public/422.html +58 -0
- data/spec/dummy/public/500.html +57 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/tmp/cache/assets/C54/850/sprockets%2Fd484f47085686f151320ff7248bb5105 +0 -0
- data/spec/dummy/tmp/cache/assets/C76/160/sprockets%2F49c594d143212b346d11f1ecf9358103 +0 -0
- data/spec/dummy/tmp/cache/assets/C99/4D0/sprockets%2F5e30a6b911437f1428dc32c3ae182123 +0 -0
- data/spec/dummy/tmp/cache/assets/C99/7D0/sprockets%2F79513e6956e0ee8624976e041fd5636d +0 -0
- data/spec/dummy/tmp/cache/assets/CAA/C90/sprockets%2F1e6c8ee1258009385ccf5b84015424b3 +0 -0
- data/spec/dummy/tmp/cache/assets/CB2/CB0/sprockets%2F11cc8d161d71a716dd36f16849d90870 +0 -0
- data/spec/dummy/tmp/cache/assets/CB7/7F0/sprockets%2Fac97b043470f6fcc925c352f16956643 +0 -0
- data/spec/dummy/tmp/cache/assets/CBA/680/sprockets%2F912d84e091e5f2190b1ab7926de19679 +0 -0
- data/spec/dummy/tmp/cache/assets/CC7/430/sprockets%2F48e99a3e16ba3e65749e009d82546f94 +0 -0
- data/spec/dummy/tmp/cache/assets/CD8/370/sprockets%2F357970feca3ac29060c1e3861e2c0953 +0 -0
- data/spec/dummy/tmp/cache/assets/CDB/8A0/sprockets%2Faed2a2575c376263c26e93b56b57051d +0 -0
- data/spec/dummy/tmp/cache/assets/CF1/E80/sprockets%2F7735295e1e3085f2e1dc29d59a45e8e6 +0 -0
- data/spec/dummy/tmp/cache/assets/D0F/E60/sprockets%2F1123d9a479fdf5c2b8415087e2a43ec6 +0 -0
- data/spec/dummy/tmp/cache/assets/D1F/A10/sprockets%2F60317e62cb324bfd9987e8da9636fd06 +0 -0
- data/spec/dummy/tmp/cache/assets/D25/E20/sprockets%2F29ba1a4a2b611731334f54abb9e5b5d1 +0 -0
- data/spec/dummy/tmp/cache/assets/D32/A10/sprockets%2F13fe41fee1fe35b49d145bcc06610705 +0 -0
- data/spec/dummy/tmp/cache/assets/D35/760/sprockets%2F49e26620c5c743ac4cdab7703f54db06 +0 -0
- data/spec/dummy/tmp/cache/assets/D49/870/sprockets%2F90896142645585acc8baf56ad57e3afb +0 -0
- data/spec/dummy/tmp/cache/assets/D4E/1B0/sprockets%2Ff7cbd26ba1d28d48de824f0e94586655 +0 -0
- data/spec/dummy/tmp/cache/assets/D5A/EA0/sprockets%2Fd771ace226fc8215a3572e0aa35bb0d6 +0 -0
- data/spec/dummy/tmp/cache/assets/D62/F00/sprockets%2F6ac03a007f26b1c18ed3d53498ad3eb8 +0 -0
- data/spec/dummy/tmp/cache/assets/D79/BC0/sprockets%2F85a1db977361cc5130fcefb4637ff67e +0 -0
- data/spec/dummy/tmp/cache/assets/D87/1F0/sprockets%2F61c9ceafb877fb740c67416ff6f782a9 +0 -0
- data/spec/dummy/tmp/cache/assets/DA8/900/sprockets%2Fde7a2b3987b89b9d9afb268ce4204a4a +0 -0
- data/spec/dummy/tmp/cache/assets/DAD/4F0/sprockets%2F765acd1bf68dc90f7d3e61da78b1e659 +0 -0
- data/spec/dummy/tmp/cache/assets/DDC/400/sprockets%2Fcffd775d018f68ce5dba1ee0d951a994 +0 -0
- data/spec/dummy/tmp/cache/assets/E03/3F0/sprockets%2F82b37ae9eccec44c1ef44cfdd07d8534 +0 -0
- data/spec/dummy/tmp/cache/assets/E04/890/sprockets%2F2f5173deea6c795b8fdde723bb4b63af +0 -0
- data/spec/dummy/tmp/cache/assets/E0A/CA0/sprockets%2F7fe72ac1c0db1a7e8e7f59cf25e8a39e +0 -0
- data/spec/dummy/tmp/cache/assets/E20/9B0/sprockets%2F2fa6cd86b1af7bda41f5b7921cdadf10 +0 -0
- data/spec/dummy/tmp/cache/assets/E3A/A60/sprockets%2F7c72c96cfc66454caf5fc8ca0fedd4f3 +0 -0
- data/spec/dummy/tmp/cache/assets/E54/400/sprockets%2Fd0cbc16cc37efcf9dc41f242b5dbbf81 +0 -0
- data/spec/dummy/tmp/cache/assets/EB7/AB0/sprockets%2F801d29a5debdbfbfb4eef14d70d9bcdb +0 -0
- data/spec/dummy/tmp/cache/assets/F48/5E0/sprockets%2F26cd6ffcffbebbe2fd6cd1a8f0c2debc +0 -0
- data/spec/form/nested_spec.rb +75 -61
- data/spec/form/property_spec.rb +167 -14
- data/spec/form/validation_spec.rb +46 -23
- data/spec/javascript_spec.rb +60 -0
- data/spec/persistence_spec.rb +608 -0
- data/spec/spec_helper.rb +18 -2
- data/vendor/assets/javascripts/jquery_freeform.js +120 -0
- data/vendor/assets/javascripts/jquery_nested_form.js +0 -0
- data/vendor/assets/javascripts/prototype_freeform.js +69 -0
- data/vendor/assets/javascripts/prototype_nested_form.js +0 -0
- metadata +341 -3
data/lib/freeform/form/nested.rb
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
require 'freeform/form/property'
|
|
2
|
-
|
|
3
1
|
module FreeForm
|
|
4
2
|
module Nested
|
|
5
3
|
def self.included(base)
|
|
@@ -9,55 +7,18 @@ module FreeForm
|
|
|
9
7
|
module ClassMethods
|
|
10
8
|
# Nested Forms
|
|
11
9
|
#------------------------------------------------------------------------
|
|
12
|
-
attr_accessor :nested_forms
|
|
10
|
+
attr_accessor :nested_forms
|
|
13
11
|
|
|
14
|
-
def nested_form(attribute, options={}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
unless initializer_method.nil?
|
|
19
|
-
define_singleton_method(:"#{initializer_method}") do
|
|
20
|
-
var = instance_variable_get(:"@#{initializer_method}")
|
|
21
|
-
var ||= {}
|
|
22
|
-
var
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
define_singleton_method(:"#{initializer_method}=") do |val|
|
|
26
|
-
instance_variable_set(:"@#{initializer_method}", val)
|
|
27
|
-
end
|
|
28
|
-
end
|
|
12
|
+
def nested_form(attribute, options={})
|
|
13
|
+
nested_form_class = options[:class]
|
|
14
|
+
@nested_forms ||= {}
|
|
15
|
+
@nested_forms.merge!({:"#{attribute}" => nested_form_class})
|
|
29
16
|
|
|
30
17
|
# Define an attr_accessor for the parent class to hold this attribute
|
|
31
18
|
declared_model(attribute)
|
|
32
19
|
|
|
33
|
-
# Define the new class, and set it up with a new name
|
|
34
|
-
nested_form_class = Class.new(FreeForm::Form) do
|
|
35
|
-
include FreeForm::Property
|
|
36
|
-
self.instance_eval(&block)
|
|
37
|
-
|
|
38
|
-
define_singleton_method(:default_initializer) do
|
|
39
|
-
unless initializer_method.nil?
|
|
40
|
-
method = parent_class.send(initializer_method)
|
|
41
|
-
if method.is_a? Proc
|
|
42
|
-
method.call
|
|
43
|
-
else
|
|
44
|
-
method
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def initialize(p={}, *args)
|
|
50
|
-
p = self.class.default_initializer.merge(p)
|
|
51
|
-
super(p, *args)
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
self.const_set("#{attribute.to_s.camelize}Form", nested_form_class)
|
|
55
|
-
|
|
56
|
-
@nested_forms ||= {}
|
|
57
|
-
@nested_forms.merge!({:"#{attribute}" => nested_form_class})
|
|
58
|
-
|
|
59
20
|
# Defined other methods
|
|
60
|
-
define_nested_model_methods(attribute, nested_form_class)
|
|
21
|
+
define_nested_model_methods(attribute, nested_form_class, options)
|
|
61
22
|
|
|
62
23
|
# Setup Handling for nested attributes
|
|
63
24
|
define_nested_attribute_methods(attribute, nested_form_class)
|
|
@@ -65,18 +26,10 @@ module FreeForm
|
|
|
65
26
|
alias_method :has_many, :nested_form
|
|
66
27
|
alias_method :has_one, :nested_form
|
|
67
28
|
|
|
68
|
-
# Supporting Methods
|
|
69
|
-
#------------------------------------------------------------------------
|
|
70
|
-
def reflect_on_association(key, *args)
|
|
71
|
-
reflection = OpenStruct.new
|
|
72
|
-
reflection.klass = self.nested_forms[key]
|
|
73
|
-
reflection
|
|
74
|
-
end
|
|
75
|
-
|
|
76
29
|
protected
|
|
77
30
|
# Defining Helper Methods For Models
|
|
78
31
|
#------------------------------------------------------------------------
|
|
79
|
-
def define_nested_model_methods(attribute, form_class)
|
|
32
|
+
def define_nested_model_methods(attribute, form_class, options={})
|
|
80
33
|
singularized_attribute = attribute.to_s.singularize.to_s
|
|
81
34
|
|
|
82
35
|
# Example: form.addresses will return all nested address forms
|
|
@@ -85,9 +38,18 @@ module FreeForm
|
|
|
85
38
|
end
|
|
86
39
|
|
|
87
40
|
# Example: form.build_addresses (optional custom initializer)
|
|
88
|
-
define_method(:"build_#{attribute}") do |initializer=
|
|
41
|
+
define_method(:"build_#{attribute}") do |initializer=nil|
|
|
42
|
+
# Builder object
|
|
43
|
+
parent_object = self
|
|
44
|
+
|
|
89
45
|
# Get correct class
|
|
90
46
|
form_class = self.class.nested_forms[:"#{attribute}"]
|
|
47
|
+
|
|
48
|
+
# Default Initializer
|
|
49
|
+
if options[:default_initializer]
|
|
50
|
+
initializer ||= instance_eval(&options[:default_initializer])
|
|
51
|
+
end
|
|
52
|
+
initializer ||= {}
|
|
91
53
|
|
|
92
54
|
# Build new model
|
|
93
55
|
form_model = form_class.new(initializer)
|
|
@@ -13,6 +13,10 @@ module FreeForm
|
|
|
13
13
|
@models ||= []
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
+
def child_models
|
|
17
|
+
@child_models ||= []
|
|
18
|
+
end
|
|
19
|
+
|
|
16
20
|
def declared_model(name, opts={})
|
|
17
21
|
@models ||= []
|
|
18
22
|
@models << name
|
|
@@ -27,73 +31,109 @@ module FreeForm
|
|
|
27
31
|
end
|
|
28
32
|
alias_method :form_models, :declared_models
|
|
29
33
|
|
|
34
|
+
def child_model(name, opts={}, &block)
|
|
35
|
+
@models ||= []
|
|
36
|
+
@models << name
|
|
37
|
+
@child_models ||= []
|
|
38
|
+
@child_models << name
|
|
39
|
+
attr_accessor name
|
|
40
|
+
define_method("initialize_#{name}") do
|
|
41
|
+
instance_variable_set("@#{name}", instance_eval(&block))
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
30
45
|
# Properties
|
|
31
46
|
#------------------------------------------------------------------------
|
|
32
|
-
def
|
|
33
|
-
|
|
47
|
+
def property_mappings
|
|
48
|
+
# Take the form of {:property => {:model => model, :field => field, :ignore_blank => false}}
|
|
49
|
+
@property_mappings ||= Hash.new
|
|
34
50
|
end
|
|
35
51
|
|
|
36
52
|
def allow_destroy_on_save
|
|
37
53
|
# Define _destroy method for marked-for-destruction handling
|
|
38
54
|
attr_accessor :_destroy
|
|
39
|
-
|
|
55
|
+
define_method(:_destroy=) do |value|
|
|
56
|
+
false_values = [nil, 0 , false, "0", "false"]
|
|
57
|
+
@_destroy = !(false_values.include?(value))
|
|
58
|
+
end
|
|
59
|
+
alias_method :marked_for_destruction?, :_destroy
|
|
60
|
+
define_method(:mark_for_destruction) do
|
|
61
|
+
@_destroy = true
|
|
62
|
+
end
|
|
40
63
|
end
|
|
41
64
|
|
|
42
65
|
def property(attribute, options={})
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
66
|
+
@property_mappings ||= Hash.new
|
|
67
|
+
model = options[:on] ? options [:on] : :self
|
|
68
|
+
field = options[:as] ? options[:as] : attribute.to_sym
|
|
69
|
+
ignore_blank = options[:ignore_blank] ? options[:ignore_blank] : false
|
|
70
|
+
@property_mappings.merge!({field => {:model => model, :field => attribute.to_sym, :ignore_blank => ignore_blank}})
|
|
71
|
+
|
|
72
|
+
if model == :self
|
|
47
73
|
attr_accessor attribute
|
|
74
|
+
else
|
|
75
|
+
define_method("#{field}") do
|
|
76
|
+
send("#{model}").send("#{attribute}")
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
define_method("#{field}=") do |value|
|
|
80
|
+
send("#{model}").send("#{attribute}=", value)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
attr_accessor :parent_form
|
|
87
|
+
|
|
88
|
+
class DateParamsFilter
|
|
89
|
+
def call(params)
|
|
90
|
+
date_attributes = {}
|
|
91
|
+
|
|
92
|
+
params.each do |attribute, value|
|
|
93
|
+
if value.is_a?(Hash)
|
|
94
|
+
call(value) # TODO: #validate should only handle local form params.
|
|
95
|
+
elsif matches = attribute.match(/^(\w+)\(.i\)$/)
|
|
96
|
+
date_attribute = matches[1]
|
|
97
|
+
date_attributes[date_attribute] = params_to_date(
|
|
98
|
+
params.delete("#{date_attribute}(1i)"),
|
|
99
|
+
params.delete("#{date_attribute}(2i)"),
|
|
100
|
+
params.delete("#{date_attribute}(3i)")
|
|
101
|
+
)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
params.merge!(date_attributes)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
private
|
|
108
|
+
def params_to_date(year, month, day)
|
|
109
|
+
day ||= 1 # FIXME: is that really what we want? test.
|
|
110
|
+
begin # TODO: test fails.
|
|
111
|
+
return Date.new(year.to_i, month.to_i, day.to_i)
|
|
112
|
+
rescue ArgumentError => e
|
|
113
|
+
return nil
|
|
48
114
|
end
|
|
49
|
-
@ignored_blank_params ||= []
|
|
50
|
-
@ignored_blank_params << attribute if options[:ignore_blank]
|
|
51
115
|
end
|
|
52
116
|
end
|
|
53
117
|
|
|
54
118
|
def assign_params(params)
|
|
55
|
-
|
|
119
|
+
DateParamsFilter.new.call(params)
|
|
56
120
|
params.each_pair do |attribute, value|
|
|
57
|
-
|
|
121
|
+
assign_attribute(attribute, value)
|
|
58
122
|
end
|
|
59
123
|
self
|
|
60
124
|
end
|
|
61
125
|
alias_method :assign_attributes, :assign_params
|
|
62
126
|
alias_method :populate, :assign_params
|
|
63
127
|
alias_method :fill, :assign_params
|
|
64
|
-
|
|
65
|
-
def ignore?(attribute, value)
|
|
66
|
-
ignored_if_blank = self.class.ignored_blank_params
|
|
67
|
-
return (ignored_if_blank.include?(attribute.to_sym) && value.blank?)
|
|
68
|
-
end
|
|
69
128
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
date_attributes = {}
|
|
74
|
-
params.each do |attribute, value|
|
|
75
|
-
if attribute.to_s.include?("(1i)") || attribute.to_s.include?("(2i)") || attribute.to_s.include?("(3i)")
|
|
76
|
-
date_attribute = attribute.to_s.gsub(/(\(.*\))/, "")
|
|
77
|
-
date_attributes[date_attribute.to_sym] = params_to_date(
|
|
78
|
-
params.delete("#{date_attribute}(1i)"),
|
|
79
|
-
params.delete("#{date_attribute}(2i)"),
|
|
80
|
-
params.delete("#{date_attribute}(3i)")
|
|
81
|
-
)
|
|
82
|
-
end
|
|
83
|
-
end
|
|
84
|
-
params.merge!(date_attributes)
|
|
129
|
+
private
|
|
130
|
+
def assign_attribute(attribute, value)
|
|
131
|
+
self.send :"#{attribute}=", value unless ignore?(attribute, value)
|
|
85
132
|
end
|
|
86
|
-
|
|
87
|
-
def
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
return Date.new(year.to_i, month.to_i, day.to_i)
|
|
91
|
-
rescue => e
|
|
92
|
-
return nil
|
|
93
|
-
end
|
|
133
|
+
|
|
134
|
+
def ignore?(attribute, value)
|
|
135
|
+
mapping = self.class.property_mappings[attribute.to_sym]
|
|
136
|
+
return (mapping[:ignore_blank] && value.blank?) unless mapping.nil?
|
|
94
137
|
end
|
|
95
138
|
end
|
|
96
|
-
|
|
97
|
-
#Instance Methods
|
|
98
|
-
#----------------------------------------------------------------------------
|
|
99
139
|
end
|
|
@@ -2,6 +2,7 @@ module FreeForm
|
|
|
2
2
|
module Validation
|
|
3
3
|
def self.included(base)
|
|
4
4
|
base.extend(ClassMethods)
|
|
5
|
+
extend FreeForm::Property
|
|
5
6
|
end
|
|
6
7
|
|
|
7
8
|
module ClassMethods
|
|
@@ -10,25 +11,51 @@ module FreeForm
|
|
|
10
11
|
end
|
|
11
12
|
end
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
protected
|
|
14
15
|
def model_validity
|
|
15
|
-
|
|
16
|
-
self.class.models.each do |form_model|
|
|
16
|
+
self.class.models.each do |form_model|
|
|
17
17
|
if send(form_model).is_a?(Array)
|
|
18
|
-
|
|
18
|
+
# If it's an array, we're dealing with nested forms
|
|
19
|
+
errors.add(:base, "has invalid nested forms") unless validate_nested_forms(send(form_model))
|
|
19
20
|
else
|
|
20
|
-
|
|
21
|
+
# Otherwise, validate the form object
|
|
22
|
+
validate_form_model(form_model)
|
|
21
23
|
end
|
|
22
24
|
end
|
|
23
|
-
return model_validity
|
|
24
25
|
end
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
private
|
|
28
|
+
def validate_nested_forms(form_array)
|
|
29
|
+
form_validity = true
|
|
30
|
+
form_array.each do |model|
|
|
31
|
+
form_validity = false unless model.valid?
|
|
30
32
|
end
|
|
31
|
-
|
|
33
|
+
form_validity
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def validate_form_model(form_model)
|
|
37
|
+
model = send(form_model)
|
|
38
|
+
append_errors(form_model)
|
|
39
|
+
return model.valid?
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def append_errors(form_model)
|
|
43
|
+
model = send(form_model)
|
|
44
|
+
model.valid?
|
|
45
|
+
model.errors.each do |error, message|
|
|
46
|
+
if find_form_field_from_model_field(form_model, error)
|
|
47
|
+
self.errors.add(find_form_field_from_model_field(form_model, error), message)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def find_form_field_from_model_field(model, field)
|
|
53
|
+
self.class.property_mappings.each_pair do |property, attributes|
|
|
54
|
+
if (attributes[:model] == model.to_sym) && (attributes[:field] == field.to_sym)
|
|
55
|
+
return property
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
return false
|
|
32
59
|
end
|
|
33
60
|
end
|
|
34
61
|
end
|
data/lib/freeform/form.rb
CHANGED
|
@@ -22,51 +22,48 @@ module FreeForm
|
|
|
22
22
|
# Instance Methods
|
|
23
23
|
#----------------------------------------------------------------------------
|
|
24
24
|
# Required for ActiveModel
|
|
25
|
-
def persisted
|
|
26
|
-
false
|
|
27
|
-
end
|
|
25
|
+
def persisted?; false end
|
|
28
26
|
|
|
29
27
|
def initialize(h={})
|
|
30
28
|
h.each {|k,v| send("#{k}=",v)}
|
|
29
|
+
initialize_child_models
|
|
31
30
|
end
|
|
32
31
|
|
|
33
32
|
def save
|
|
34
33
|
return false unless valid?
|
|
34
|
+
|
|
35
35
|
self.class.models.each do |form_model|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
else
|
|
39
|
-
if marked_for_destruction?
|
|
40
|
-
send(form_model).destroy
|
|
41
|
-
else
|
|
42
|
-
return false unless send(form_model).save
|
|
43
|
-
end
|
|
44
|
-
end
|
|
36
|
+
model = send(form_model)
|
|
37
|
+
model.is_a?(Array) ? model.each { |m| m.save } : save_or_destroy(model)
|
|
45
38
|
end
|
|
46
|
-
return true
|
|
47
39
|
end
|
|
48
40
|
|
|
49
41
|
def save!
|
|
50
42
|
raise FreeForm::FormInvalid, "form invalid." unless valid?
|
|
43
|
+
|
|
51
44
|
self.class.models.each do |form_model|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
else
|
|
55
|
-
if marked_for_destruction?
|
|
56
|
-
send(form_model).destroy
|
|
57
|
-
else
|
|
58
|
-
send(form_model).save!
|
|
59
|
-
end
|
|
60
|
-
end
|
|
45
|
+
model = send(form_model)
|
|
46
|
+
model.is_a?(Array) ? model.each { |m| m.save! } : save_or_destroy!(model)
|
|
61
47
|
end
|
|
62
48
|
end
|
|
63
49
|
|
|
50
|
+
private
|
|
51
|
+
def initialize_child_models
|
|
52
|
+
self.class.child_models.each do |c|
|
|
53
|
+
send("initialize_#{c}")
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def save_or_destroy(model)
|
|
58
|
+
marked_for_destruction? ? model.destroy : model.save
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def save_or_destroy!(model)
|
|
62
|
+
marked_for_destruction? ? model.destroy : model.save!
|
|
63
|
+
end
|
|
64
|
+
|
|
64
65
|
def marked_for_destruction?
|
|
65
|
-
|
|
66
|
-
return self._destroy
|
|
67
|
-
else
|
|
68
|
-
return false
|
|
69
|
-
end
|
|
66
|
+
respond_to?(:_destroy) ? _destroy : false
|
|
70
67
|
end
|
|
71
68
|
end
|
|
72
69
|
end
|
data/lib/freeform/version.rb
CHANGED
data/lib/freeform.rb
CHANGED
|
@@ -2,4 +2,8 @@ require 'freeform/version'
|
|
|
2
2
|
require 'freeform/form'
|
|
3
3
|
|
|
4
4
|
if defined?(Rails) # Where to include Rails specific modules...
|
|
5
|
+
require 'freeform/builder/builder_mixin'
|
|
6
|
+
require 'freeform/builder/builders'
|
|
7
|
+
require 'freeform/builder/view_helper'
|
|
8
|
+
require 'freeform/builder/engine'
|
|
5
9
|
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module FreeForm
|
|
2
|
+
module Generators
|
|
3
|
+
class InstallGenerator < Rails::Generators::Base
|
|
4
|
+
def self.source_root
|
|
5
|
+
File.expand_path('../../../../vendor/assets/javascripts', __FILE__)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def copy_jquery_file
|
|
9
|
+
if File.exists?('public/javascripts/prototype.js')
|
|
10
|
+
copy_file 'prototype_freeform.js', 'public/javascripts/freeform.js'
|
|
11
|
+
else
|
|
12
|
+
copy_file 'jquery_freeform.js', 'public/javascripts/freeform.js'
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|