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.
Files changed (184) hide show
  1. data.tar.gz.sig +2 -0
  2. data/History.txt +4 -0
  3. data/License.txt +20 -0
  4. data/MIT-LICENSE +19 -0
  5. data/Manifest.txt +181 -0
  6. data/README.txt +1 -0
  7. data/Rakefile +4 -0
  8. data/config/hoe.rb +71 -0
  9. data/config/requirements.rb +17 -0
  10. data/init.rb +5 -0
  11. data/lib/active_form.rb +86 -0
  12. data/lib/active_form/core_extensions.rb +179 -0
  13. data/lib/active_form/definition.rb +72 -0
  14. data/lib/active_form/element.rb +50 -0
  15. data/lib/active_form/elements/base.rb +67 -0
  16. data/lib/active_form/elements/builder.rb +103 -0
  17. data/lib/active_form/elements/button.rb +15 -0
  18. data/lib/active_form/elements/checkbox.rb +18 -0
  19. data/lib/active_form/elements/file.rb +15 -0
  20. data/lib/active_form/elements/hidden.rb +9 -0
  21. data/lib/active_form/elements/image.rb +7 -0
  22. data/lib/active_form/elements/input.rb +23 -0
  23. data/lib/active_form/elements/password.rb +12 -0
  24. data/lib/active_form/elements/radio.rb +18 -0
  25. data/lib/active_form/elements/reset.rb +15 -0
  26. data/lib/active_form/elements/section.rb +37 -0
  27. data/lib/active_form/elements/select.rb +42 -0
  28. data/lib/active_form/elements/submit.rb +19 -0
  29. data/lib/active_form/elements/text.rb +9 -0
  30. data/lib/active_form/elements/textarea.rb +18 -0
  31. data/lib/active_form/errors.rb +73 -0
  32. data/lib/active_form/facets/checkbox_group.rb +11 -0
  33. data/lib/active_form/facets/collection_input.rb +96 -0
  34. data/lib/active_form/facets/radio_group.rb +11 -0
  35. data/lib/active_form/facets/select_date.rb +10 -0
  36. data/lib/active_form/facets/select_datetime.rb +10 -0
  37. data/lib/active_form/facets/select_day.rb +17 -0
  38. data/lib/active_form/facets/select_from_model.rb +60 -0
  39. data/lib/active_form/facets/select_hour.rb +17 -0
  40. data/lib/active_form/facets/select_minute.rb +17 -0
  41. data/lib/active_form/facets/select_month.rb +46 -0
  42. data/lib/active_form/facets/select_numeric_range.rb +59 -0
  43. data/lib/active_form/facets/select_second.rb +17 -0
  44. data/lib/active_form/facets/select_time.rb +10 -0
  45. data/lib/active_form/facets/select_timebased.rb +145 -0
  46. data/lib/active_form/facets/select_weekday.rb +46 -0
  47. data/lib/active_form/facets/select_year.rb +24 -0
  48. data/lib/active_form/mixins/attribute_methods.rb +33 -0
  49. data/lib/active_form/mixins/casting.rb +152 -0
  50. data/lib/active_form/mixins/collection_element_methods.rb +204 -0
  51. data/lib/active_form/mixins/common_methods.rb +350 -0
  52. data/lib/active_form/mixins/container_methods.rb +417 -0
  53. data/lib/active_form/mixins/css_methods.rb +70 -0
  54. data/lib/active_form/mixins/element_methods.rb +314 -0
  55. data/lib/active_form/mixins/javascript_methods.rb +99 -0
  56. data/lib/active_form/mixins/loader_methods.rb +72 -0
  57. data/lib/active_form/mixins/option_element_methods.rb +61 -0
  58. data/lib/active_form/mixins/validation_methods.rb +253 -0
  59. data/lib/active_form/validator.rb +47 -0
  60. data/lib/active_form/validators/alpha.rb +9 -0
  61. data/lib/active_form/validators/alphanum.rb +9 -0
  62. data/lib/active_form/validators/base.rb +132 -0
  63. data/lib/active_form/validators/digits.rb +9 -0
  64. data/lib/active_form/validators/email.rb +29 -0
  65. data/lib/active_form/validators/format.rb +15 -0
  66. data/lib/active_form/validators/length_range.rb +24 -0
  67. data/lib/active_form/validators/number.rb +9 -0
  68. data/lib/active_form/validators/option_count.rb +24 -0
  69. data/lib/active_form/validators/proc.rb +22 -0
  70. data/lib/active_form/validators/required.rb +9 -0
  71. data/lib/active_form/validators/set.rb +21 -0
  72. data/lib/active_form/validators/time_range.rb +30 -0
  73. data/lib/active_form/validators/uri.rb +54 -0
  74. data/lib/active_form/version.rb +9 -0
  75. data/lib/active_form/views/fieldsets.rb +34 -0
  76. data/lib/active_form/views/plain.rb +32 -0
  77. data/lib/active_form/views/tables.rb +28 -0
  78. data/lib/active_form/widget.rb +43 -0
  79. data/lib/active_form/widgets/base.rb +28 -0
  80. data/lib/loob/uri_validator.rb +140 -0
  81. data/lib/rails/acts_as_dropdown/MIT-LICENSE +20 -0
  82. data/lib/rails/acts_as_dropdown/acts_as_dropdown.rb +119 -0
  83. data/lib/rails/acts_as_dropdown/dropdown.rb +27 -0
  84. data/lib/rails/controller_helper.rb +19 -0
  85. data/lib/rails/model/loader.rb +206 -0
  86. data/lib/rails/view_helper.rb +20 -0
  87. data/samples/html/css/style.css +175 -0
  88. data/samples/html/js/effects.js +958 -0
  89. data/samples/html/js/prototype.js +2006 -0
  90. data/samples/html/js/validation.js +228 -0
  91. data/samples/html/plain_view.html +77 -0
  92. data/samples/html/sample001.html +158 -0
  93. data/samples/html/sample002.html +76 -0
  94. data/samples/html/sample003.html +34 -0
  95. data/samples/layouts/default.rhtml +18 -0
  96. data/samples/layouts/plain.rhtml +21 -0
  97. data/samples/layouts/sample003.rhtml +25 -0
  98. data/samples/plain_view.rb +58 -0
  99. data/samples/sample001.rb +111 -0
  100. data/samples/sample002.rb +92 -0
  101. data/samples/sample003.rb +30 -0
  102. data/script/destroy +14 -0
  103. data/script/generate +14 -0
  104. data/script/txt2html +74 -0
  105. data/setup.rb +1585 -0
  106. data/tasks/deployment.rake +34 -0
  107. data/tasks/environment.rake +7 -0
  108. data/tasks/website.rake +17 -0
  109. data/test/elements/test_base_element.rb +159 -0
  110. data/test/elements/test_builder_element.rb +81 -0
  111. data/test/elements/test_button_element.rb +12 -0
  112. data/test/elements/test_checkbox_element.rb +233 -0
  113. data/test/elements/test_file_element.rb +20 -0
  114. data/test/elements/test_hidden_element.rb +11 -0
  115. data/test/elements/test_image_element.rb +11 -0
  116. data/test/elements/test_input_element.rb +39 -0
  117. data/test/elements/test_password_element.rb +16 -0
  118. data/test/elements/test_radio_element.rb +197 -0
  119. data/test/elements/test_reset_element.rb +12 -0
  120. data/test/elements/test_section_element.rb +162 -0
  121. data/test/elements/test_select_element.rb +364 -0
  122. data/test/elements/test_submit_element.rb +12 -0
  123. data/test/elements/test_text_element.rb +91 -0
  124. data/test/elements/test_textarea_element.rb +23 -0
  125. data/test/facets/test_checkbox_group_element.rb +388 -0
  126. data/test/facets/test_radio_group_element.rb +312 -0
  127. data/test/facets/test_select_date_element.rb +211 -0
  128. data/test/facets/test_select_datetime_element.rb +56 -0
  129. data/test/facets/test_select_day_element.rb +37 -0
  130. data/test/facets/test_select_from_model_element.rb +86 -0
  131. data/test/facets/test_select_hour_element.rb +37 -0
  132. data/test/facets/test_select_minute_element.rb +52 -0
  133. data/test/facets/test_select_month_element.rb +79 -0
  134. data/test/facets/test_select_numeric_range_element.rb +14 -0
  135. data/test/facets/test_select_second_element.rb +48 -0
  136. data/test/facets/test_select_time_element.rb +148 -0
  137. data/test/facets/test_select_weekday_element.rb +71 -0
  138. data/test/facets/test_select_year_element.rb +73 -0
  139. data/test/fixtures/author.rb +11 -0
  140. data/test/fixtures/authors.yml +0 -0
  141. data/test/fixtures/book.rb +14 -0
  142. data/test/fixtures/books.yml +8 -0
  143. data/test/fixtures/categories.yml +0 -0
  144. data/test/fixtures/categorization.rb +2 -0
  145. data/test/fixtures/categorizations.yml +0 -0
  146. data/test/fixtures/category.rb +8 -0
  147. data/test/fixtures/publisher.rb +5 -0
  148. data/test/fixtures/publishers.yml +10 -0
  149. data/test/fixtures/schema.rb +43 -0
  150. data/test/rails/test_model_autodefinition.rb +121 -0
  151. data/test/rails/test_model_loader.rb +61 -0
  152. data/test/resources/elements/chunky.rb +11 -0
  153. data/test/resources/forms/demo.rb +5 -0
  154. data/test/resources/models/book.rb +8 -0
  155. data/test/resources/models/register_publisher.rb +5 -0
  156. data/test/resources/sections/demo.rb +6 -0
  157. data/test/resources/sections/foo.rb +6 -0
  158. data/test/resources/validators/foo.rb +9 -0
  159. data/test/resources/widgets/custom.rb +13 -0
  160. data/test/test_active_form.rb +1050 -0
  161. data/test/test_casting.rb +124 -0
  162. data/test/test_definition.rb +68 -0
  163. data/test/test_definition_class.rb +118 -0
  164. data/test/test_element_class.rb +201 -0
  165. data/test/test_helper.rb +74 -0
  166. data/test/test_javascript.rb +234 -0
  167. data/test/test_load_definition.rb +68 -0
  168. data/test/test_load_element.rb +35 -0
  169. data/test/test_load_section_element.rb +43 -0
  170. data/test/test_load_validator.rb +31 -0
  171. data/test/test_load_widget.rb +93 -0
  172. data/test/test_validation.rb +722 -0
  173. data/test/validators/test_validates_as_alpha.rb +26 -0
  174. data/test/validators/test_validates_as_alphanum.rb +26 -0
  175. data/test/validators/test_validates_as_digits.rb +26 -0
  176. data/test/validators/test_validates_as_email.rb +29 -0
  177. data/test/validators/test_validates_as_number.rb +26 -0
  178. data/test/validators/test_validates_as_uri.rb +128 -0
  179. data/test/validators/test_validates_with_format.rb +26 -0
  180. data/test/validators/test_validates_within_length_range.rb +46 -0
  181. data/test/validators/test_validates_within_set.rb +46 -0
  182. data/test/validators/test_validates_within_time_range.rb +53 -0
  183. metadata +307 -0
  184. 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