active_form 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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