active_form 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +2 -0
- data/History.txt +4 -0
- data/License.txt +20 -0
- data/MIT-LICENSE +19 -0
- data/Manifest.txt +181 -0
- data/README.txt +1 -0
- data/Rakefile +4 -0
- data/config/hoe.rb +71 -0
- data/config/requirements.rb +17 -0
- data/init.rb +5 -0
- data/lib/active_form.rb +86 -0
- data/lib/active_form/core_extensions.rb +179 -0
- data/lib/active_form/definition.rb +72 -0
- data/lib/active_form/element.rb +50 -0
- data/lib/active_form/elements/base.rb +67 -0
- data/lib/active_form/elements/builder.rb +103 -0
- data/lib/active_form/elements/button.rb +15 -0
- data/lib/active_form/elements/checkbox.rb +18 -0
- data/lib/active_form/elements/file.rb +15 -0
- data/lib/active_form/elements/hidden.rb +9 -0
- data/lib/active_form/elements/image.rb +7 -0
- data/lib/active_form/elements/input.rb +23 -0
- data/lib/active_form/elements/password.rb +12 -0
- data/lib/active_form/elements/radio.rb +18 -0
- data/lib/active_form/elements/reset.rb +15 -0
- data/lib/active_form/elements/section.rb +37 -0
- data/lib/active_form/elements/select.rb +42 -0
- data/lib/active_form/elements/submit.rb +19 -0
- data/lib/active_form/elements/text.rb +9 -0
- data/lib/active_form/elements/textarea.rb +18 -0
- data/lib/active_form/errors.rb +73 -0
- data/lib/active_form/facets/checkbox_group.rb +11 -0
- data/lib/active_form/facets/collection_input.rb +96 -0
- data/lib/active_form/facets/radio_group.rb +11 -0
- data/lib/active_form/facets/select_date.rb +10 -0
- data/lib/active_form/facets/select_datetime.rb +10 -0
- data/lib/active_form/facets/select_day.rb +17 -0
- data/lib/active_form/facets/select_from_model.rb +60 -0
- data/lib/active_form/facets/select_hour.rb +17 -0
- data/lib/active_form/facets/select_minute.rb +17 -0
- data/lib/active_form/facets/select_month.rb +46 -0
- data/lib/active_form/facets/select_numeric_range.rb +59 -0
- data/lib/active_form/facets/select_second.rb +17 -0
- data/lib/active_form/facets/select_time.rb +10 -0
- data/lib/active_form/facets/select_timebased.rb +145 -0
- data/lib/active_form/facets/select_weekday.rb +46 -0
- data/lib/active_form/facets/select_year.rb +24 -0
- data/lib/active_form/mixins/attribute_methods.rb +33 -0
- data/lib/active_form/mixins/casting.rb +152 -0
- data/lib/active_form/mixins/collection_element_methods.rb +204 -0
- data/lib/active_form/mixins/common_methods.rb +350 -0
- data/lib/active_form/mixins/container_methods.rb +417 -0
- data/lib/active_form/mixins/css_methods.rb +70 -0
- data/lib/active_form/mixins/element_methods.rb +314 -0
- data/lib/active_form/mixins/javascript_methods.rb +99 -0
- data/lib/active_form/mixins/loader_methods.rb +72 -0
- data/lib/active_form/mixins/option_element_methods.rb +61 -0
- data/lib/active_form/mixins/validation_methods.rb +253 -0
- data/lib/active_form/validator.rb +47 -0
- data/lib/active_form/validators/alpha.rb +9 -0
- data/lib/active_form/validators/alphanum.rb +9 -0
- data/lib/active_form/validators/base.rb +132 -0
- data/lib/active_form/validators/digits.rb +9 -0
- data/lib/active_form/validators/email.rb +29 -0
- data/lib/active_form/validators/format.rb +15 -0
- data/lib/active_form/validators/length_range.rb +24 -0
- data/lib/active_form/validators/number.rb +9 -0
- data/lib/active_form/validators/option_count.rb +24 -0
- data/lib/active_form/validators/proc.rb +22 -0
- data/lib/active_form/validators/required.rb +9 -0
- data/lib/active_form/validators/set.rb +21 -0
- data/lib/active_form/validators/time_range.rb +30 -0
- data/lib/active_form/validators/uri.rb +54 -0
- data/lib/active_form/version.rb +9 -0
- data/lib/active_form/views/fieldsets.rb +34 -0
- data/lib/active_form/views/plain.rb +32 -0
- data/lib/active_form/views/tables.rb +28 -0
- data/lib/active_form/widget.rb +43 -0
- data/lib/active_form/widgets/base.rb +28 -0
- data/lib/loob/uri_validator.rb +140 -0
- data/lib/rails/acts_as_dropdown/MIT-LICENSE +20 -0
- data/lib/rails/acts_as_dropdown/acts_as_dropdown.rb +119 -0
- data/lib/rails/acts_as_dropdown/dropdown.rb +27 -0
- data/lib/rails/controller_helper.rb +19 -0
- data/lib/rails/model/loader.rb +206 -0
- data/lib/rails/view_helper.rb +20 -0
- data/samples/html/css/style.css +175 -0
- data/samples/html/js/effects.js +958 -0
- data/samples/html/js/prototype.js +2006 -0
- data/samples/html/js/validation.js +228 -0
- data/samples/html/plain_view.html +77 -0
- data/samples/html/sample001.html +158 -0
- data/samples/html/sample002.html +76 -0
- data/samples/html/sample003.html +34 -0
- data/samples/layouts/default.rhtml +18 -0
- data/samples/layouts/plain.rhtml +21 -0
- data/samples/layouts/sample003.rhtml +25 -0
- data/samples/plain_view.rb +58 -0
- data/samples/sample001.rb +111 -0
- data/samples/sample002.rb +92 -0
- data/samples/sample003.rb +30 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/script/txt2html +74 -0
- data/setup.rb +1585 -0
- data/tasks/deployment.rake +34 -0
- data/tasks/environment.rake +7 -0
- data/tasks/website.rake +17 -0
- data/test/elements/test_base_element.rb +159 -0
- data/test/elements/test_builder_element.rb +81 -0
- data/test/elements/test_button_element.rb +12 -0
- data/test/elements/test_checkbox_element.rb +233 -0
- data/test/elements/test_file_element.rb +20 -0
- data/test/elements/test_hidden_element.rb +11 -0
- data/test/elements/test_image_element.rb +11 -0
- data/test/elements/test_input_element.rb +39 -0
- data/test/elements/test_password_element.rb +16 -0
- data/test/elements/test_radio_element.rb +197 -0
- data/test/elements/test_reset_element.rb +12 -0
- data/test/elements/test_section_element.rb +162 -0
- data/test/elements/test_select_element.rb +364 -0
- data/test/elements/test_submit_element.rb +12 -0
- data/test/elements/test_text_element.rb +91 -0
- data/test/elements/test_textarea_element.rb +23 -0
- data/test/facets/test_checkbox_group_element.rb +388 -0
- data/test/facets/test_radio_group_element.rb +312 -0
- data/test/facets/test_select_date_element.rb +211 -0
- data/test/facets/test_select_datetime_element.rb +56 -0
- data/test/facets/test_select_day_element.rb +37 -0
- data/test/facets/test_select_from_model_element.rb +86 -0
- data/test/facets/test_select_hour_element.rb +37 -0
- data/test/facets/test_select_minute_element.rb +52 -0
- data/test/facets/test_select_month_element.rb +79 -0
- data/test/facets/test_select_numeric_range_element.rb +14 -0
- data/test/facets/test_select_second_element.rb +48 -0
- data/test/facets/test_select_time_element.rb +148 -0
- data/test/facets/test_select_weekday_element.rb +71 -0
- data/test/facets/test_select_year_element.rb +73 -0
- data/test/fixtures/author.rb +11 -0
- data/test/fixtures/authors.yml +0 -0
- data/test/fixtures/book.rb +14 -0
- data/test/fixtures/books.yml +8 -0
- data/test/fixtures/categories.yml +0 -0
- data/test/fixtures/categorization.rb +2 -0
- data/test/fixtures/categorizations.yml +0 -0
- data/test/fixtures/category.rb +8 -0
- data/test/fixtures/publisher.rb +5 -0
- data/test/fixtures/publishers.yml +10 -0
- data/test/fixtures/schema.rb +43 -0
- data/test/rails/test_model_autodefinition.rb +121 -0
- data/test/rails/test_model_loader.rb +61 -0
- data/test/resources/elements/chunky.rb +11 -0
- data/test/resources/forms/demo.rb +5 -0
- data/test/resources/models/book.rb +8 -0
- data/test/resources/models/register_publisher.rb +5 -0
- data/test/resources/sections/demo.rb +6 -0
- data/test/resources/sections/foo.rb +6 -0
- data/test/resources/validators/foo.rb +9 -0
- data/test/resources/widgets/custom.rb +13 -0
- data/test/test_active_form.rb +1050 -0
- data/test/test_casting.rb +124 -0
- data/test/test_definition.rb +68 -0
- data/test/test_definition_class.rb +118 -0
- data/test/test_element_class.rb +201 -0
- data/test/test_helper.rb +74 -0
- data/test/test_javascript.rb +234 -0
- data/test/test_load_definition.rb +68 -0
- data/test/test_load_element.rb +35 -0
- data/test/test_load_section_element.rb +43 -0
- data/test/test_load_validator.rb +31 -0
- data/test/test_load_widget.rb +93 -0
- data/test/test_validation.rb +722 -0
- data/test/validators/test_validates_as_alpha.rb +26 -0
- data/test/validators/test_validates_as_alphanum.rb +26 -0
- data/test/validators/test_validates_as_digits.rb +26 -0
- data/test/validators/test_validates_as_email.rb +29 -0
- data/test/validators/test_validates_as_number.rb +26 -0
- data/test/validators/test_validates_as_uri.rb +128 -0
- data/test/validators/test_validates_with_format.rb +26 -0
- data/test/validators/test_validates_within_length_range.rb +46 -0
- data/test/validators/test_validates_within_set.rb +46 -0
- data/test/validators/test_validates_within_time_range.rb +53 -0
- metadata +307 -0
- metadata.gz.sig +4 -0
@@ -0,0 +1,140 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
module Loob
|
5
|
+
class UriValidator
|
6
|
+
|
7
|
+
# possible error statuses:
|
8
|
+
# :invalid_scheme
|
9
|
+
# :invalid_host
|
10
|
+
# :invalid_content_type
|
11
|
+
# :invalid_format
|
12
|
+
# :moved_permanently
|
13
|
+
# :not_accessible
|
14
|
+
|
15
|
+
DEFAULT_HTTP_OK_CODES = [
|
16
|
+
Net::HTTPMovedPermanently,
|
17
|
+
Net::HTTPOK,
|
18
|
+
Net::HTTPCreated,
|
19
|
+
Net::HTTPAccepted,
|
20
|
+
Net::HTTPNonAuthoritativeInformation,
|
21
|
+
Net::HTTPPartialContent,
|
22
|
+
Net::HTTPFound,
|
23
|
+
Net::HTTPTemporaryRedirect,
|
24
|
+
Net::HTTPSeeOther
|
25
|
+
].freeze
|
26
|
+
|
27
|
+
attr_accessor :response_codes, :content_types, :schemes, :hosts, :port
|
28
|
+
attr_reader :error
|
29
|
+
|
30
|
+
class << self
|
31
|
+
|
32
|
+
def valid_uri?(str, options = {})
|
33
|
+
self.new(options).valid_uri?(str)
|
34
|
+
end
|
35
|
+
alias :valid_url? :valid_uri?
|
36
|
+
|
37
|
+
def valid_domain?(str, options = {})
|
38
|
+
self.new(options).valid_domain?(str)
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
def initialize(options = {})
|
44
|
+
self.schemes = options[:schemes] || []
|
45
|
+
self.hosts = options[:hosts] || []
|
46
|
+
self.content_types = options[:content_types] || []
|
47
|
+
self.response_codes = options[:response_codes] || DEFAULT_HTTP_OK_CODES
|
48
|
+
self.port = options[:port] || 80
|
49
|
+
end
|
50
|
+
|
51
|
+
[:response_codes, :content_types, :schemes, :hosts].each do |setter|
|
52
|
+
self.module_eval("def #{setter}=(args); @#{setter} = [*args]; end")
|
53
|
+
end
|
54
|
+
|
55
|
+
def valid_uri?(str)
|
56
|
+
reset_error!
|
57
|
+
begin
|
58
|
+
moved_retry ||= false
|
59
|
+
not_allowed_retry ||= false
|
60
|
+
|
61
|
+
uri = URI.parse(str)
|
62
|
+
uri.path = '/' if uri.path.length < 1
|
63
|
+
return set_error(:invalid_scheme) unless validate_scheme(uri)
|
64
|
+
return set_error(:invalid_host) unless validate_host(uri)
|
65
|
+
|
66
|
+
http = Net::HTTP.new(uri.host, (uri.scheme == 'https') ? 443 : port)
|
67
|
+
if uri.scheme == 'https'
|
68
|
+
http.use_ssl = true
|
69
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
70
|
+
end
|
71
|
+
response = not_allowed_retry ? http.request_get(uri.path) {|r|} : http.request_head(uri.path)
|
72
|
+
raise unless validate_response_code(response)
|
73
|
+
return set_error(:invalid_content_type) unless validate_content_type(response)
|
74
|
+
rescue URI::InvalidURIError
|
75
|
+
return set_error(:invalid_format)
|
76
|
+
rescue
|
77
|
+
if response.is_a?(Net::HTTPMovedPermanently)
|
78
|
+
unless moved_retry
|
79
|
+
moved_retry = true
|
80
|
+
str += '/' # In case webserver is just adding a /
|
81
|
+
retry
|
82
|
+
else
|
83
|
+
return set_error(:moved_permanently)
|
84
|
+
end
|
85
|
+
elsif response.is_a?(Net::HTTPMethodNotAllowed)
|
86
|
+
unless not_allowed_retry # Retry with a GET
|
87
|
+
not_allowed_retry = true
|
88
|
+
retry
|
89
|
+
else
|
90
|
+
return set_error(:not_accessible)
|
91
|
+
end
|
92
|
+
else
|
93
|
+
return set_error(:not_accessible)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
return true
|
97
|
+
end
|
98
|
+
alias :valid_url? :valid_uri?
|
99
|
+
|
100
|
+
def valid_domain?(str)
|
101
|
+
begin
|
102
|
+
uri = URI.parse(str)
|
103
|
+
return valid_uri?("#{uri.scheme || 'http'}://#{uri.host || str}/")
|
104
|
+
rescue URI::InvalidURIError, URI::NameError
|
105
|
+
return set_error(:invalid_format)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
def reset_error!
|
112
|
+
@error = nil
|
113
|
+
end
|
114
|
+
|
115
|
+
def set_error(code)
|
116
|
+
@error = code
|
117
|
+
false
|
118
|
+
end
|
119
|
+
|
120
|
+
def validate_scheme(uri)
|
121
|
+
return true if schemes.empty?
|
122
|
+
schemes.find { |match| match.kind_of?(Regexp) ? uri.scheme =~ match : uri.scheme.index(match) }
|
123
|
+
end
|
124
|
+
|
125
|
+
def validate_host(uri)
|
126
|
+
return true if hosts.empty?
|
127
|
+
hosts.find { |match| match.kind_of?(Regexp) ? uri.host =~ match : uri.host.index(match) }
|
128
|
+
end
|
129
|
+
|
130
|
+
def validate_response_code(response)
|
131
|
+
response_codes.include?(response.class)
|
132
|
+
end
|
133
|
+
|
134
|
+
def validate_content_type(response)
|
135
|
+
return true if content_types.empty?
|
136
|
+
content_types.find { |match| match.kind_of?(Regexp) ? response['content-type'] =~ match : response['content-type'].index(match) }
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2006 DeLynn Berry
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -0,0 +1,119 @@
|
|
1
|
+
# Copyright (c) 2006 DeLynn Berry
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
# a copy of this software and associated documentation files (the
|
5
|
+
# "Software"), to deal in the Software without restriction, including
|
6
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
# the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be
|
12
|
+
# included in all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
22
|
+
module ActiveRecord #:nodoc:
|
23
|
+
module Acts #:nodoc:
|
24
|
+
# Specify this act if you want to your model be used easily with the <tt>select</tt> form helper. By default the
|
25
|
+
# plugin assumes you want to use the <tt>id</tt> attribute for the option's value and the <tt>name</tt> attribute for
|
26
|
+
# the option's text.
|
27
|
+
#
|
28
|
+
# Example:
|
29
|
+
#
|
30
|
+
# class State < ActiveRecord::Base
|
31
|
+
# acts_as_dropdown
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# State.to_dropdown # => [["Alabama", 1], ["Alaska", 2], ["Arizona", 3], ["California", 4], ["Colorado", 5]]
|
35
|
+
#
|
36
|
+
# The <tt>value</tt>, <tt>text</tt>, <tt>conditions</tt>, and <tt>order</tt> can also be altered from the default configuration
|
37
|
+
# by using the options hash.
|
38
|
+
#
|
39
|
+
# Example:
|
40
|
+
#
|
41
|
+
# class State < ActiveRecord::Base
|
42
|
+
# acts_as_dropdown :text => "abbreviation", :conditions => "id < 4"
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# State.to_dropdown # => [["AL", 1], ["AK", 2], ["AZ", 3], ["CA", 4]]
|
46
|
+
#
|
47
|
+
# The class method <tt>to_dropdown</tt> can also alter the default class configuration using the same options hash.
|
48
|
+
#
|
49
|
+
# Example:
|
50
|
+
#
|
51
|
+
# class State < ActiveRecord::Base
|
52
|
+
# acts_as_dropdown :text => "abbreviation", :conditions => "id < 4"
|
53
|
+
# end
|
54
|
+
#
|
55
|
+
# State.to_dropdown :text => "name", :conditions => nil # => [["Alabama", 1], ["Alaska", 2], ["Arizona", 3], ["California", 4], ["Colorado", 5]]
|
56
|
+
#
|
57
|
+
# See ActiveRecord::Acts::Dropdown::ClassMethods#acts_as_dropdown for configuration options
|
58
|
+
module Dropdown
|
59
|
+
def self.included(base) # :nodoc:
|
60
|
+
base.extend ClassMethods
|
61
|
+
end
|
62
|
+
|
63
|
+
module ClassMethods
|
64
|
+
# == Configuration options
|
65
|
+
#
|
66
|
+
# * <tt>text</tt> - This is the class attribute that will be used as the text/label for the option tag (defaults to name).
|
67
|
+
# * <tt>value</tt> - This is the class attribute that will be used to fill in the option's value parameter (defaults to id).
|
68
|
+
# * <tt>conditions</tt> - This is the conditions string that will be used when executing the <tt>find</tt> method on the object (defaults to nil).
|
69
|
+
# * <tt>order</tt> - This is the order string that will be used when executing the <tt>find</tt> method on the object (defaults to the <tt>value</tt> attribute).
|
70
|
+
def acts_as_dropdown(options = {})
|
71
|
+
cattr_accessor :dropdown_text_attr, :dropdown_value_attr, :conditions_string, :order_string
|
72
|
+
|
73
|
+
self.dropdown_text_attr = options[:text] || "name"
|
74
|
+
self.dropdown_value_attr = options[:value] || "id"
|
75
|
+
self.conditions_string = options[:conditions] || nil
|
76
|
+
self.order_string = options[:order] || self.dropdown_value_attr.to_s
|
77
|
+
end
|
78
|
+
|
79
|
+
# === Example:
|
80
|
+
#
|
81
|
+
# class State < ActiveRecord::Base
|
82
|
+
# acts_as_dropdown
|
83
|
+
# end
|
84
|
+
#
|
85
|
+
# >> State.to_dropdown
|
86
|
+
# => [["Alabama", 1], ["Alaska", 2], ["Arizona", 3], ["California", 4], ["Colorado", 5]]
|
87
|
+
#
|
88
|
+
# The <tt>value</tt>, <tt>text</tt>, <tt>conditions</tt>, and <tt>order</tt> can also be altered from the default configuration
|
89
|
+
# by using the options hash.
|
90
|
+
#
|
91
|
+
# === Example:
|
92
|
+
#
|
93
|
+
# class State < ActiveRecord::Base
|
94
|
+
# acts_as_dropdown :text => "abbreviation", :conditions => "id < 4"
|
95
|
+
# end
|
96
|
+
#
|
97
|
+
# >> State.to_dropdown
|
98
|
+
# => [["AL", 1], ["AK", 2], ["AZ", 3], ["CA", 4]]
|
99
|
+
#
|
100
|
+
# The class method <tt>to_dropdown</tt> can also alter the default class configuration using the same options hash.
|
101
|
+
#
|
102
|
+
# === Example:
|
103
|
+
#
|
104
|
+
# class State < ActiveRecord::Base
|
105
|
+
# acts_as_dropdown :text => "abbreviation", :conditions => "id < 4"
|
106
|
+
# end
|
107
|
+
#
|
108
|
+
# >> State.to_dropdown :text => "name", :conditions => nil
|
109
|
+
# => [["Alabama", 1], ["Alaska", 2], ["Arizona", 3], ["California", 4], ["Colorado", 5]]
|
110
|
+
def to_dropdown(options = {})
|
111
|
+
acts_as_dropdown(options) unless options.empty?
|
112
|
+
find(:all, :conditions => self.conditions_string, :order => self.order_string).to_dropdown(self.dropdown_text_attr.to_sym, self.dropdown_value_attr.to_sym)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
ActiveRecord::Base.send(:include, ActiveRecord::Acts::Dropdown)
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module ActiveSupport #:nodoc:
|
2
|
+
module CoreExtensions #:nodoc:
|
3
|
+
module Array #:nodoc:
|
4
|
+
module Dropdown
|
5
|
+
# Collects the contents of the array and creates a new array that can be easily used
|
6
|
+
# with the <tt>select</tt> form helper method.
|
7
|
+
#
|
8
|
+
# == Options
|
9
|
+
# * <tt>text</tt> - This is the attribute that will be used as the text/label for the option tag (defaults to name).
|
10
|
+
# * <tt>value</tt> - This is the attribute that will be used to fill in the option's value parameter (defaults to id).
|
11
|
+
#
|
12
|
+
# === Example
|
13
|
+
#
|
14
|
+
# >> @states = State.find(:all, :order => "id")
|
15
|
+
# >> @states.to_dropdown
|
16
|
+
# => [["Alabama", 1], ["Alaska", 2], ["Arizona", 3], ["California", 4], ["Colorado", 5]]
|
17
|
+
def to_dropdown(text = "name", value = "id")
|
18
|
+
self.collect { |x| [x.send(text.to_sym), x.send(value.to_sym)] }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Array #:nodoc:
|
26
|
+
include ActiveSupport::CoreExtensions::Array::Dropdown
|
27
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module ActiveForm
|
2
|
+
module ControllerHelper
|
3
|
+
|
4
|
+
def create_active_form(*args, &block)
|
5
|
+
form = ActiveForm::compose(*args, &block)
|
6
|
+
form.update_from_params(params)
|
7
|
+
instance_variable_set("@#{form.name}", form)
|
8
|
+
end
|
9
|
+
|
10
|
+
def build_active_form(definition_name, *args, &block)
|
11
|
+
form = ActiveForm::Definition::build(definition_name, *args, &block)
|
12
|
+
form.update_from_params(params)
|
13
|
+
instance_variable_set("@#{form.name}", form)
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
ActionController::Base.send(:include, ActiveForm::ControllerHelper) if Object.const_defined?('ActionController')
|
@@ -0,0 +1,206 @@
|
|
1
|
+
module ActiveForm
|
2
|
+
class Definition
|
3
|
+
|
4
|
+
attr_accessor :model_instance
|
5
|
+
|
6
|
+
def clear_validations!
|
7
|
+
super
|
8
|
+
model_instance.errors.clear if model_instance.respond_to?(:errors)
|
9
|
+
end
|
10
|
+
|
11
|
+
def validate_element
|
12
|
+
super
|
13
|
+
code_lookup = ActiveRecord::Errors.default_error_messages.invert
|
14
|
+
if model_instance.respond_to?(:valid?) && model_instance.respond_to?(:errors)
|
15
|
+
unless model_instance.valid?
|
16
|
+
errors_on_base = [*model.instance.errors.on_base] rescue []
|
17
|
+
errors_on_base.each { |msg| self.errors.add(msg, 'ar_base') unless msg.blank? }
|
18
|
+
model_instance.errors.each do |attr, msg|
|
19
|
+
if elem = self[attr]
|
20
|
+
code = code_lookup.key?(msg) ? code_lookup[msg] : 'elem'
|
21
|
+
msg = "%s: #{msg}" unless code == 'elem'
|
22
|
+
elem.errors.add(elem.format_message(msg), "ar_#{code}")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module ActiveForm
|
33
|
+
module Model
|
34
|
+
|
35
|
+
def self.load(*args, &block)
|
36
|
+
custom_type = args.first.kind_of?(Symbol) ? args.shift : nil
|
37
|
+
instance = args.shift
|
38
|
+
definition_name = "#{instance.class.name.underscore}"
|
39
|
+
definitions = case instance
|
40
|
+
when ActiveRecord::Base then
|
41
|
+
if instance.new_record?
|
42
|
+
["create_#{definition_name}", definition_name]
|
43
|
+
else
|
44
|
+
["update_#{definition_name}", definition_name]
|
45
|
+
end
|
46
|
+
else
|
47
|
+
return nil
|
48
|
+
end
|
49
|
+
args.unshift("#{definition_name}_form") if args.empty? || args.first.kind_of?(Hash)
|
50
|
+
definitions.unshift("#{custom_type}_#{definition_name}") unless custom_type.nil?
|
51
|
+
if name = definitions.detect { |name| ActiveForm::get(name) }
|
52
|
+
Definition.new.build(name, instance, *args, &block)
|
53
|
+
else
|
54
|
+
AutoDefinition.new.build(definition_name, instance, *args, &block)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.load!(*args, &block)
|
59
|
+
form = load(*args, &block)
|
60
|
+
form.submit_element
|
61
|
+
form
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.build(*args, &block)
|
65
|
+
instance = args.shift
|
66
|
+
return nil unless instance.kind_of?(ActiveRecord::Base)
|
67
|
+
definition_name = "#{instance.class.name.underscore}"
|
68
|
+
args.unshift("#{definition_name}_form") if args.empty? || args.first.kind_of?(Hash)
|
69
|
+
AutoDefinition.new.build(definition_name, instance, *args, &block)
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.build!(*args, &block)
|
73
|
+
form = build(*args, &block)
|
74
|
+
form.submit_element
|
75
|
+
form
|
76
|
+
end
|
77
|
+
|
78
|
+
class Definition
|
79
|
+
|
80
|
+
def initialize
|
81
|
+
@now = Time.now
|
82
|
+
end
|
83
|
+
|
84
|
+
def build(name, instance, *args, &block)
|
85
|
+
form = ActiveForm::build(name, *args, &block)
|
86
|
+
pk_column = instance.column_for_attribute(instance.class.primary_key)
|
87
|
+
form.insert_element_at_top(self.class.primary_key_column(pk_column)) if !pk_column.nil? && form[instance.class.primary_key.to_sym].nil?
|
88
|
+
instance.class.columns.each { |column| (elem = form[column.name]) ? elem.type_cast = column.type : nil }
|
89
|
+
form = instance.new_record? ? prepare_new_record(form, instance) : prepare_record(form, instance)
|
90
|
+
assign_validation(form, instance)
|
91
|
+
form
|
92
|
+
end
|
93
|
+
|
94
|
+
def assign_validation(form, instance)
|
95
|
+
# TODO reflect_on_validations here
|
96
|
+
end
|
97
|
+
|
98
|
+
def prepare_new_record(form, instance)
|
99
|
+
prepare_record(form, instance)
|
100
|
+
form.get_elements_of_type(:select_date, :select_time, :select_datetime).each { |elem| elem.value = @now if elem.blank? }
|
101
|
+
instance.class.columns.each { |column| ((elem = form[column.name]) && !column.default.blank?) ? elem.type_cast = column.default : nil }
|
102
|
+
form
|
103
|
+
end
|
104
|
+
|
105
|
+
def prepare_record(form, instance)
|
106
|
+
form.update_values(ActiveForm::Values.new(instance.attributes))
|
107
|
+
form.model_instance = instance
|
108
|
+
form
|
109
|
+
end
|
110
|
+
|
111
|
+
def column_to_element(column, options = {})
|
112
|
+
options[:default] = column.default unless column.default.blank?
|
113
|
+
|
114
|
+
element = self.class.respond_to?("#{column.type}_column") ? self.class.send("#{column.type}_column", column, options) : self.class.string_column(column, options)
|
115
|
+
element.label = column.human_name
|
116
|
+
element
|
117
|
+
end
|
118
|
+
|
119
|
+
def association_column_to_element(assoc, column, options = {})
|
120
|
+
options[:default] = column.default unless column.default.blank?
|
121
|
+
|
122
|
+
element = self.class.respond_to?("#{assoc.macro}_column") ? self.class.send("#{assoc.macro}_column", assoc, column, options) : self.class.integer_column(column, options)
|
123
|
+
element.label = column.human_name
|
124
|
+
element
|
125
|
+
end
|
126
|
+
|
127
|
+
def associations_lookup(instance)
|
128
|
+
instance.class.reflect_on_all_associations.inject({}) do |lookup, assoc|
|
129
|
+
if assoc.macro == :belongs_to
|
130
|
+
lookup[assoc.primary_key_name] = assoc
|
131
|
+
end
|
132
|
+
lookup
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
class << self
|
137
|
+
|
138
|
+
def belongs_to_column(assoc, column, options = {})
|
139
|
+
if assoc.klass.respond_to?(:dropdown_text_attr)
|
140
|
+
ActiveForm::Element::build(:select_from_model, column.name, options.merge(:type_cast => :integer, :model => assoc.klass.to_s, :to_dropdown => true))
|
141
|
+
else
|
142
|
+
integer_column(column, options)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def primary_key_column(column, options = {})
|
147
|
+
ActiveForm::Element::build(:hidden, column.name, options.merge(:type_cast => :integer))
|
148
|
+
end
|
149
|
+
|
150
|
+
def string_column(column, options = {})
|
151
|
+
type = column.name =~ /password/i ? :password : :text
|
152
|
+
ActiveForm::Element::build(type, column.name, options.merge(:type_cast => :string))
|
153
|
+
end
|
154
|
+
|
155
|
+
def text_column(column, options = {})
|
156
|
+
ActiveForm::Element::build(:textarea, column.name, options.merge(:type_cast => :text))
|
157
|
+
end
|
158
|
+
|
159
|
+
def integer_column(column, options = {})
|
160
|
+
ActiveForm::Element::build(:text, column.name, options.merge(:type_cast => :integer))
|
161
|
+
end
|
162
|
+
alias :float_column :integer_column
|
163
|
+
|
164
|
+
def date_column(column, options = {})
|
165
|
+
ActiveForm::Element::build(:select_date, column.name, options.merge(:type_cast => :date))
|
166
|
+
end
|
167
|
+
|
168
|
+
def datetime_column(column, options = {})
|
169
|
+
ActiveForm::Element::build(:select_datetime, column.name, options.merge(:type_cast => :time))
|
170
|
+
end
|
171
|
+
alias :timestamp_column :datetime_column
|
172
|
+
|
173
|
+
def boolean_column(column, options = {})
|
174
|
+
ActiveForm::Element::build(:text, column.name, options.merge(:type_cast => :boolean))
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
|
179
|
+
end
|
180
|
+
|
181
|
+
class AutoDefinition < Definition
|
182
|
+
|
183
|
+
def build(name, instance, *args, &block)
|
184
|
+
assoc_lookup = associations_lookup(instance)
|
185
|
+
assoc_lookup_keys = assoc_lookup.keys
|
186
|
+
|
187
|
+
form = ActiveForm::compose(*args)
|
188
|
+
instance.class.columns.each do |column|
|
189
|
+
if column.primary
|
190
|
+
form << self.class.primary_key_column(column)
|
191
|
+
elsif column.name =~ /(_id)$/ && assoc_lookup_keys.include?(column.name)
|
192
|
+
form << association_column_to_element(assoc_lookup[column.name], column)
|
193
|
+
else
|
194
|
+
form << column_to_element(column)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
form.instance_eval(&block) if block_given?
|
198
|
+
form = instance.new_record? ? prepare_new_record(form, instance) : prepare_record(form, instance)
|
199
|
+
assign_validation(form, instance)
|
200
|
+
form
|
201
|
+
end
|
202
|
+
|
203
|
+
end
|
204
|
+
|
205
|
+
end
|
206
|
+
end
|