active_form 0.0.1
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.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
|