formtastic 1.2.4 → 3.1.5

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 (189) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +46 -0
  5. data/.yardopts +1 -0
  6. data/Appraisals +43 -0
  7. data/CHANGELOG +54 -0
  8. data/DEPRECATIONS +52 -0
  9. data/Gemfile +3 -0
  10. data/README.md +629 -0
  11. data/RELEASE_PROCESS +6 -0
  12. data/Rakefile +35 -0
  13. data/app/assets/stylesheets/formtastic.css +289 -0
  14. data/app/assets/stylesheets/formtastic_ie6.css +33 -0
  15. data/app/assets/stylesheets/formtastic_ie7.css +23 -0
  16. data/formtastic.gemspec +42 -0
  17. data/gemfiles/rails_3.2.gemfile +9 -0
  18. data/gemfiles/rails_4.0.4.gemfile +8 -0
  19. data/gemfiles/rails_4.1.gemfile +8 -0
  20. data/gemfiles/rails_4.2.gemfile +8 -0
  21. data/gemfiles/rails_4.gemfile +8 -0
  22. data/gemfiles/rails_5.0.gemfile +8 -0
  23. data/gemfiles/rails_edge.gemfile +15 -0
  24. data/lib/formtastic.rb +40 -1945
  25. data/lib/formtastic/action_class_finder.rb +18 -0
  26. data/lib/formtastic/actions.rb +11 -0
  27. data/lib/formtastic/actions/base.rb +156 -0
  28. data/lib/formtastic/actions/button_action.rb +67 -0
  29. data/lib/formtastic/actions/buttonish.rb +17 -0
  30. data/lib/formtastic/actions/input_action.rb +70 -0
  31. data/lib/formtastic/actions/link_action.rb +88 -0
  32. data/lib/formtastic/deprecation.rb +42 -0
  33. data/lib/formtastic/engine.rb +11 -0
  34. data/lib/formtastic/form_builder.rb +124 -0
  35. data/lib/formtastic/helpers.rb +16 -0
  36. data/lib/formtastic/helpers/action_helper.rb +162 -0
  37. data/lib/formtastic/helpers/actions_helper.rb +168 -0
  38. data/lib/formtastic/helpers/enum.rb +13 -0
  39. data/lib/formtastic/helpers/errors_helper.rb +81 -0
  40. data/lib/formtastic/helpers/fieldset_wrapper.rb +80 -0
  41. data/lib/formtastic/helpers/file_column_detection.rb +16 -0
  42. data/lib/formtastic/helpers/form_helper.rb +203 -0
  43. data/lib/formtastic/helpers/input_helper.rb +407 -0
  44. data/lib/formtastic/helpers/inputs_helper.rb +411 -0
  45. data/lib/formtastic/helpers/reflection.rb +37 -0
  46. data/lib/formtastic/html_attributes.rb +32 -0
  47. data/lib/formtastic/i18n.rb +4 -2
  48. data/lib/formtastic/input_class_finder.rb +18 -0
  49. data/lib/formtastic/inputs.rb +39 -0
  50. data/lib/formtastic/inputs/base.rb +76 -0
  51. data/lib/formtastic/inputs/base/associations.rb +31 -0
  52. data/lib/formtastic/inputs/base/choices.rb +108 -0
  53. data/lib/formtastic/inputs/base/collections.rb +159 -0
  54. data/lib/formtastic/inputs/base/database.rb +22 -0
  55. data/lib/formtastic/inputs/base/datetime_pickerish.rb +85 -0
  56. data/lib/formtastic/inputs/base/errors.rb +58 -0
  57. data/lib/formtastic/inputs/base/fileish.rb +23 -0
  58. data/lib/formtastic/inputs/base/hints.rb +31 -0
  59. data/lib/formtastic/inputs/base/html.rb +53 -0
  60. data/lib/formtastic/inputs/base/labelling.rb +52 -0
  61. data/lib/formtastic/inputs/base/naming.rb +42 -0
  62. data/lib/formtastic/inputs/base/numeric.rb +50 -0
  63. data/lib/formtastic/inputs/base/options.rb +17 -0
  64. data/lib/formtastic/inputs/base/placeholder.rb +17 -0
  65. data/lib/formtastic/inputs/base/stringish.rb +38 -0
  66. data/lib/formtastic/inputs/base/timeish.rb +241 -0
  67. data/lib/formtastic/inputs/base/validations.rb +215 -0
  68. data/lib/formtastic/inputs/base/wrapping.rb +50 -0
  69. data/lib/formtastic/inputs/boolean_input.rb +118 -0
  70. data/lib/formtastic/inputs/check_boxes_input.rb +197 -0
  71. data/lib/formtastic/inputs/color_input.rb +42 -0
  72. data/lib/formtastic/inputs/country_input.rb +86 -0
  73. data/lib/formtastic/inputs/datalist_input.rb +41 -0
  74. data/lib/formtastic/inputs/date_picker_input.rb +93 -0
  75. data/lib/formtastic/inputs/date_select_input.rb +34 -0
  76. data/lib/formtastic/inputs/datetime_picker_input.rb +103 -0
  77. data/lib/formtastic/inputs/datetime_select_input.rb +12 -0
  78. data/lib/formtastic/inputs/email_input.rb +41 -0
  79. data/lib/formtastic/inputs/file_input.rb +42 -0
  80. data/lib/formtastic/inputs/hidden_input.rb +62 -0
  81. data/lib/formtastic/inputs/number_input.rb +88 -0
  82. data/lib/formtastic/inputs/password_input.rb +41 -0
  83. data/lib/formtastic/inputs/phone_input.rb +42 -0
  84. data/lib/formtastic/inputs/radio_input.rb +163 -0
  85. data/lib/formtastic/inputs/range_input.rb +95 -0
  86. data/lib/formtastic/inputs/search_input.rb +41 -0
  87. data/lib/formtastic/inputs/select_input.rb +235 -0
  88. data/lib/formtastic/inputs/string_input.rb +36 -0
  89. data/lib/formtastic/inputs/text_input.rb +48 -0
  90. data/lib/formtastic/inputs/time_picker_input.rb +99 -0
  91. data/lib/formtastic/inputs/time_select_input.rb +38 -0
  92. data/lib/formtastic/inputs/time_zone_input.rb +58 -0
  93. data/lib/formtastic/inputs/url_input.rb +41 -0
  94. data/lib/formtastic/localized_string.rb +17 -0
  95. data/lib/formtastic/localizer.rb +152 -0
  96. data/lib/formtastic/namespaced_class_finder.rb +99 -0
  97. data/lib/formtastic/util.rb +35 -16
  98. data/lib/formtastic/version.rb +3 -0
  99. data/lib/generators/formtastic/form/form_generator.rb +64 -37
  100. data/lib/generators/formtastic/input/input_generator.rb +46 -0
  101. data/lib/generators/formtastic/install/install_generator.rb +13 -5
  102. data/lib/generators/templates/_form.html.erb +10 -4
  103. data/lib/generators/templates/_form.html.haml +8 -4
  104. data/lib/generators/templates/_form.html.slim +8 -0
  105. data/lib/generators/templates/formtastic.rb +77 -44
  106. data/lib/generators/templates/input.rb +19 -0
  107. data/lib/locale/en.yml +3 -0
  108. data/sample/basic_inputs.html +224 -0
  109. data/sample/config.ru +69 -0
  110. data/sample/index.html +14 -0
  111. data/spec/action_class_finder_spec.rb +12 -0
  112. data/spec/actions/button_action_spec.rb +63 -0
  113. data/spec/actions/generic_action_spec.rb +521 -0
  114. data/spec/actions/input_action_spec.rb +59 -0
  115. data/spec/actions/link_action_spec.rb +92 -0
  116. data/spec/builder/custom_builder_spec.rb +116 -0
  117. data/spec/builder/error_proc_spec.rb +27 -0
  118. data/spec/builder/semantic_fields_for_spec.rb +142 -0
  119. data/spec/fast_spec_helper.rb +12 -0
  120. data/spec/generators/formtastic/form/form_generator_spec.rb +131 -0
  121. data/spec/generators/formtastic/input/input_generator_spec.rb +124 -0
  122. data/spec/generators/formtastic/install/install_generator_spec.rb +47 -0
  123. data/spec/helpers/action_helper_spec.rb +19 -0
  124. data/spec/helpers/actions_helper_spec.rb +143 -0
  125. data/spec/helpers/form_helper_spec.rb +218 -0
  126. data/spec/helpers/input_helper_spec.rb +6 -0
  127. data/spec/helpers/inputs_helper_spec.rb +655 -0
  128. data/spec/helpers/namespaced_action_helper_spec.rb +43 -0
  129. data/spec/helpers/namespaced_input_helper_spec.rb +36 -0
  130. data/spec/helpers/reflection_helper_spec.rb +32 -0
  131. data/spec/helpers/semantic_errors_helper_spec.rb +112 -0
  132. data/spec/i18n_spec.rb +210 -0
  133. data/spec/input_class_finder_spec.rb +10 -0
  134. data/spec/inputs/base/collections_spec.rb +76 -0
  135. data/spec/inputs/base/validations_spec.rb +342 -0
  136. data/spec/inputs/boolean_input_spec.rb +254 -0
  137. data/spec/inputs/check_boxes_input_spec.rb +546 -0
  138. data/spec/inputs/color_input_spec.rb +97 -0
  139. data/spec/inputs/country_input_spec.rb +133 -0
  140. data/spec/inputs/custom_input_spec.rb +55 -0
  141. data/spec/inputs/datalist_input_spec.rb +61 -0
  142. data/spec/inputs/date_picker_input_spec.rb +449 -0
  143. data/spec/inputs/date_select_input_spec.rb +235 -0
  144. data/spec/inputs/datetime_picker_input_spec.rb +490 -0
  145. data/spec/inputs/datetime_select_input_spec.rb +193 -0
  146. data/spec/inputs/email_input_spec.rb +85 -0
  147. data/spec/inputs/file_input_spec.rb +89 -0
  148. data/spec/inputs/hidden_input_spec.rb +135 -0
  149. data/spec/inputs/include_blank_spec.rb +78 -0
  150. data/spec/inputs/label_spec.rb +149 -0
  151. data/spec/inputs/number_input_spec.rb +815 -0
  152. data/spec/inputs/password_input_spec.rb +99 -0
  153. data/spec/inputs/phone_input_spec.rb +85 -0
  154. data/spec/inputs/placeholder_spec.rb +71 -0
  155. data/spec/inputs/radio_input_spec.rb +328 -0
  156. data/spec/inputs/range_input_spec.rb +505 -0
  157. data/spec/inputs/readonly_spec.rb +50 -0
  158. data/spec/inputs/search_input_spec.rb +84 -0
  159. data/spec/inputs/select_input_spec.rb +615 -0
  160. data/spec/inputs/string_input_spec.rb +260 -0
  161. data/spec/inputs/text_input_spec.rb +187 -0
  162. data/spec/inputs/time_picker_input_spec.rb +455 -0
  163. data/spec/inputs/time_select_input_spec.rb +248 -0
  164. data/spec/inputs/time_zone_input_spec.rb +143 -0
  165. data/spec/inputs/url_input_spec.rb +85 -0
  166. data/spec/inputs/with_options_spec.rb +43 -0
  167. data/spec/localizer_spec.rb +130 -0
  168. data/spec/namespaced_class_finder_spec.rb +79 -0
  169. data/spec/spec.opts +2 -0
  170. data/spec/spec_helper.rb +525 -0
  171. data/spec/support/custom_macros.rb +564 -0
  172. data/spec/support/deprecation.rb +6 -0
  173. data/spec/support/shared_examples.rb +1313 -0
  174. data/spec/support/specialized_class_finder_shared_example.rb +27 -0
  175. data/spec/support/test_environment.rb +31 -0
  176. data/spec/util_spec.rb +66 -0
  177. metadata +434 -161
  178. data/README.textile +0 -682
  179. data/generators/form/USAGE +0 -16
  180. data/generators/form/form_generator.rb +0 -111
  181. data/generators/formtastic/formtastic_generator.rb +0 -26
  182. data/init.rb +0 -5
  183. data/lib/formtastic/layout_helper.rb +0 -12
  184. data/lib/formtastic/railtie.rb +0 -14
  185. data/lib/generators/templates/formtastic.css +0 -145
  186. data/lib/generators/templates/formtastic_changes.css +0 -14
  187. data/lib/generators/templates/rails2/_form.html.erb +0 -5
  188. data/lib/generators/templates/rails2/_form.html.haml +0 -4
  189. data/rails/init.rb +0 -2
@@ -0,0 +1,17 @@
1
+ module Formtastic
2
+ module Inputs
3
+ module Base
4
+ module Placeholder
5
+
6
+ def input_html_options
7
+ {:placeholder => placeholder_text}.merge(super)
8
+ end
9
+
10
+ def placeholder_text
11
+ localized_string(method, options[:placeholder], :placeholder)
12
+ end
13
+
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,38 @@
1
+ module Formtastic
2
+ module Inputs
3
+ module Base
4
+ module Stringish
5
+
6
+ # @abstract Override this method in your input class to describe how the input should render itself.
7
+ def to_html
8
+ input_wrapping do
9
+ label_html <<
10
+ builder.text_field(method, input_html_options)
11
+ end
12
+ end
13
+
14
+ # Overrides standard `input_html_options` to provide a `maxlength` and `size` attribute.
15
+ def input_html_options
16
+ {
17
+ :maxlength => maxlength,
18
+ :size => size
19
+ }.merge(super)
20
+ end
21
+
22
+ def size
23
+ builder.default_text_field_size
24
+ end
25
+
26
+ def maxlength
27
+ options[:input_html].try(:[], :maxlength) || limit
28
+ end
29
+
30
+ def wrapper_html_options
31
+ new_class = [super[:class], "stringish"].compact.join(" ")
32
+ super.merge(:class => new_class)
33
+ end
34
+
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,241 @@
1
+ module Formtastic
2
+ module Inputs
3
+ module Base
4
+ # Timeish inputs (`:date_select`, `:datetime_select`, `:time_select`) are similar to the Rails date and time
5
+ # helpers (`date_select`, `datetime_select`, `time_select`), rendering a series of `<select>`
6
+ # tags for each fragment (year, month, day, hour, minute, seconds). The fragments are then
7
+ # re-combined to a date by ActiveRecord through multi-parameter assignment.
8
+ #
9
+ # The mark-up produced by Rails is simple but far from ideal, with no way to label the
10
+ # individual fragments for accessibility, no fieldset to group the related fields, and no
11
+ # legend describing the group. Formtastic addresses this within the standard `<li>` wrapper
12
+ # with a `<fieldset>` with a `<legend>` as a label, followed by an ordered list (`<ol>`) of
13
+ # list items (`<li>`), one for each fragment (year, month, ...). Each `<li>` fragment contains
14
+ # a `<label>` (eg "Year") for the fragment, and a `<select>` containing `<option>`s (eg a
15
+ # range of years).
16
+ #
17
+ # In the supplied formtastic.css file, the resulting mark-up is styled to appear a lot like a
18
+ # standard Rails date time select by:
19
+ #
20
+ # * styling the legend to look like the other labels (to the left hand side of the selects)
21
+ # * floating the `<li>` fragments against each other as a single line
22
+ # * hiding the `<label>` of each fragment with `display:none`
23
+ #
24
+ # @example `:date_select` input with full form context and sample HTMl output
25
+ #
26
+ # <%= semantic_form_for(@post) do |f| %>
27
+ # <%= f.inputs do %>
28
+ # ...
29
+ # <%= f.input :publish_at, :as => :date_select %>
30
+ # <% end %>
31
+ # <% end %>
32
+ #
33
+ # <form...>
34
+ # <fieldset class="inputs">
35
+ # <ol>
36
+ # <li class="date">
37
+ # <fieldset class="fragments">
38
+ # <ol class="fragments-group">
39
+ # <li class="fragment">
40
+ # <label for="post_publish_at_1i">Year</label>
41
+ # <select id="post_publish_at_1i" name="post[publish_at_1i]">...</select>
42
+ # </li>
43
+ # <li class="fragment">
44
+ # <label for="post_publish_at_2i">Month</label>
45
+ # <select id="post_publish_at_2i" name="post[publish_at_2i]">...</select>
46
+ # </li>
47
+ # <li class="fragment">
48
+ # <label for="post_publish_at_3i">Day</label>
49
+ # <select id="post_publish_at_3i" name="post[publish_at_3i]">...</select>
50
+ # </li>
51
+ # </ol>
52
+ # </fieldset>
53
+ # </li>
54
+ # </ol>
55
+ # </fieldset>
56
+ # </form>
57
+ #
58
+ #
59
+ # @example `:time_select` input
60
+ # <%= f.input :publish_at, :as => :time_select %>
61
+ #
62
+ # @example `:datetime_select` input
63
+ # <%= f.input :publish_at, :as => :datetime_select %>
64
+ #
65
+ # @example Change the labels for each fragment
66
+ # <%= f.input :publish_at, :as => :date_select, :labels => { :year => "Y", :month => "M", :day => "D" } %>
67
+ #
68
+ # @example Suppress the labels for all fragments
69
+ # <%= f.input :publish_at, :as => :date_select, :labels => false %>
70
+ #
71
+ # @example Skip a fragment (defaults to 1, skips all following fragments)
72
+ # <%= f.input :publish_at, :as => :datetime_select, :discard_minute => true %>
73
+ # <%= f.input :publish_at, :as => :datetime_select, :discard_hour => true %>
74
+ # <%= f.input :publish_at, :as => :datetime_select, :discard_day => true %>
75
+ # <%= f.input :publish_at, :as => :datetime_select, :discard_month => true %>
76
+ # <%= f.input :publish_at, :as => :datetime_select, :discard_year => true %>
77
+ #
78
+ # @example Change the order
79
+ # <%= f.input :publish_at, :as => :date_select, :order => [:month, :day, :year] %>
80
+ #
81
+ # @example Include seconds with times (excluded by default)
82
+ # <%= f.input :publish_at, :as => :time_select, :include_seconds => true %>
83
+ #
84
+ # @example Specify if there should be a blank option at the start of each select or not. Note that, unlike select inputs, :include_blank does not accept a string value.
85
+ # <%= f.input :publish_at, :as => :time_select, :include_blank => true %>
86
+ # <%= f.input :publish_at, :as => :time_select, :include_blank => false %>
87
+ #
88
+ # @todo Document i18n
89
+ # @todo Check what other Rails options are supported (`start_year`, `end_year`, `use_month_numbers`, `use_short_month`, `add_month_numbers`, `prompt`), write tests for them, and otherwise support them
90
+ # @todo Could we take the rendering from Rails' helpers and inject better HTML in and around it rather than re-inventing the whee?
91
+ module Timeish
92
+
93
+ def to_html
94
+ input_wrapping do
95
+ fragments_wrapping do
96
+ hidden_fragments <<
97
+ fragments_label <<
98
+ template.content_tag(:ol,
99
+ fragments.map do |fragment|
100
+ fragment_wrapping do
101
+ fragment_label_html(fragment) <<
102
+ fragment_input_html(fragment)
103
+ end
104
+ end.join.html_safe, # TODO is this safe?
105
+ { :class => 'fragments-group' } # TODO refactor to fragments_group_wrapping
106
+ )
107
+ end
108
+ end
109
+ end
110
+
111
+ def fragments
112
+ date_fragments + time_fragments
113
+ end
114
+
115
+ def time_fragments
116
+ options[:include_seconds] ? [:hour, :minute, :second] : [:hour, :minute]
117
+ end
118
+
119
+ def date_fragments
120
+ options[:order] || i18n_date_fragments || default_date_fragments
121
+ end
122
+
123
+ def default_date_fragments
124
+ [:year, :month, :day]
125
+ end
126
+
127
+ def fragment_wrapping(&block)
128
+ template.content_tag(:li, template.capture(&block), fragment_wrapping_html_options)
129
+ end
130
+
131
+ def fragment_wrapping_html_options
132
+ { :class => 'fragment' }
133
+ end
134
+
135
+ def fragment_label(fragment)
136
+ labels_from_options = options.key?(:labels) ? options[:labels] : {}
137
+ if !labels_from_options
138
+ ''
139
+ elsif labels_from_options.key?(fragment)
140
+ labels_from_options[fragment]
141
+ else
142
+ ::I18n.t(fragment.to_s, :default => fragment.to_s.humanize, :scope => [:datetime, :prompts])
143
+ end
144
+ end
145
+
146
+ def fragment_id(fragment)
147
+ "#{input_html_options[:id]}_#{position(fragment)}i"
148
+ end
149
+
150
+ def fragment_name(fragment)
151
+ "#{method}(#{position(fragment)}i)"
152
+ end
153
+
154
+ def fragment_label_html(fragment)
155
+ text = fragment_label(fragment)
156
+ text.blank? ? "".html_safe : template.content_tag(:label, text, :for => fragment_id(fragment))
157
+ end
158
+
159
+ def value
160
+ object.send(method) if object && object.respond_to?(method)
161
+ end
162
+
163
+ def fragment_input_html(fragment)
164
+ opts = input_options.merge(:prefix => fragment_prefix, :field_name => fragment_name(fragment), :default => value, :include_blank => include_blank?)
165
+ template.send(:"select_#{fragment}", value, opts, input_html_options.merge(:id => fragment_id(fragment)))
166
+ end
167
+
168
+ def fragment_prefix
169
+ if builder.options.key?(:index)
170
+ object_name + "[#{builder.options[:index]}]"
171
+ else
172
+ object_name
173
+ end
174
+ end
175
+
176
+ # TODO extract to BlankOptions or similar -- Select uses similar code
177
+ def include_blank?
178
+ options.key?(:include_blank) ? options[:include_blank] : builder.include_blank_for_select_by_default
179
+ end
180
+
181
+ def positions
182
+ { :year => 1, :month => 2, :day => 3, :hour => 4, :minute => 5, :second => 6 }
183
+ end
184
+
185
+ def position(fragment)
186
+ positions[fragment]
187
+ end
188
+
189
+ def i18n_date_fragments
190
+ order = ::I18n.t(:order, :scope => [:date])
191
+ if order.is_a?(Array)
192
+ order.map &:to_sym
193
+ else
194
+ nil
195
+ end
196
+ end
197
+
198
+ def fragments_wrapping(&block)
199
+ template.content_tag(:fieldset,
200
+ template.capture(&block).html_safe,
201
+ fragments_wrapping_html_options
202
+ )
203
+ end
204
+
205
+ def fragments_wrapping_html_options
206
+ { :class => "fragments" }
207
+ end
208
+
209
+ def fragments_label
210
+ if render_label?
211
+ template.content_tag(:legend,
212
+ builder.label(method, label_text, :for => fragment_id(fragments.first)),
213
+ :class => "label"
214
+ )
215
+ else
216
+ "".html_safe
217
+ end
218
+ end
219
+
220
+ def fragments_inner_wrapping(&block)
221
+ template.content_tag(:ol,
222
+ template.capture(&block)
223
+ )
224
+ end
225
+
226
+ def hidden_fragments
227
+ "".html_safe
228
+ end
229
+
230
+ def hidden_field_name(fragment)
231
+ if builder.options.key?(:index)
232
+ "#{object_name}[#{builder.options[:index]}][#{fragment_name(fragment)}]"
233
+ else
234
+ "#{object_name}[#{fragment_name(fragment)}]"
235
+ end
236
+ end
237
+
238
+ end
239
+ end
240
+ end
241
+ end
@@ -0,0 +1,215 @@
1
+ module Formtastic
2
+ module Inputs
3
+ module Base
4
+ module Validations
5
+
6
+ class IndeterminableMinimumAttributeError < ArgumentError
7
+ def message
8
+ [
9
+ "A minimum value can not be determined when the validation uses :greater_than on a :decimal or :float column type.",
10
+ "Please alter the validation to use :greater_than_or_equal_to, or provide a value for this attribute explicitly with the :min option on input()."
11
+ ].join("\n")
12
+ end
13
+ end
14
+
15
+ class IndeterminableMaximumAttributeError < ArgumentError
16
+ def message
17
+ [
18
+ "A maximum value can not be determined when the validation uses :less_than on a :decimal or :float column type.",
19
+ "Please alter the validation to use :less_than_or_equal_to, or provide a value for this attribute explicitly with the :max option on input()."
20
+ ].join("\n")
21
+ end
22
+ end
23
+
24
+ def validations
25
+ @validations ||= if object && object.class.respond_to?(:validators_on)
26
+ object.class.validators_on(attributized_method_name).select do |validator|
27
+ validator_relevant?(validator)
28
+ end
29
+ else
30
+ nil
31
+ end
32
+ end
33
+
34
+ def validator_relevant?(validator)
35
+ return true unless validator.options.key?(:if) || validator.options.key?(:unless)
36
+ conditional = validator.options.key?(:if) ? validator.options[:if] : validator.options[:unless]
37
+
38
+ result = if conditional.respond_to?(:call) && conditional.arity > 0
39
+ conditional.call(object)
40
+ elsif conditional.respond_to?(:call) && conditional.arity == 0
41
+ object.instance_exec(&conditional)
42
+ elsif conditional.is_a?(::Symbol) && object.respond_to?(conditional)
43
+ object.send(conditional)
44
+ else
45
+ conditional
46
+ end
47
+
48
+ result = validator.options.key?(:unless) ? !result : !!result
49
+ not_required_through_negated_validation! if !result && [:presence, :inclusion, :length].include?(validator.kind)
50
+
51
+ result
52
+ end
53
+
54
+ def validation_limit
55
+ validation = validations? && validations.find do |validation|
56
+ validation.kind == :length
57
+ end
58
+ if validation
59
+ validation.options[:maximum] || (validation.options[:within].present? ? validation.options[:within].max : nil)
60
+ else
61
+ nil
62
+ end
63
+ end
64
+
65
+ # Prefer :greater_than_or_equal_to over :greater_than, for no particular reason.
66
+ def validation_min
67
+ validation = validations? && validations.find do |validation|
68
+ validation.kind == :numericality
69
+ end
70
+
71
+ if validation
72
+ # We can't determine an appropriate value for :greater_than with a float/decimal column
73
+ raise IndeterminableMinimumAttributeError if validation.options[:greater_than] && column? && [:float, :decimal].include?(column.type)
74
+
75
+ if validation.options[:greater_than_or_equal_to]
76
+ return (validation.options[:greater_than_or_equal_to].call(object)) if validation.options[:greater_than_or_equal_to].kind_of?(Proc)
77
+ return (validation.options[:greater_than_or_equal_to])
78
+ end
79
+
80
+ if validation.options[:greater_than]
81
+ return (validation.options[:greater_than].call(object) + 1) if validation.options[:greater_than].kind_of?(Proc)
82
+ return (validation.options[:greater_than] + 1)
83
+ end
84
+ end
85
+ end
86
+
87
+ # Prefer :less_than_or_equal_to over :less_than, for no particular reason.
88
+ def validation_max
89
+ validation = validations? && validations.find do |validation|
90
+ validation.kind == :numericality
91
+ end
92
+ if validation
93
+ # We can't determine an appropriate value for :greater_than with a float/decimal column
94
+ raise IndeterminableMaximumAttributeError if validation.options[:less_than] && column? && [:float, :decimal].include?(column.type)
95
+
96
+ if validation.options[:less_than_or_equal_to]
97
+ return (validation.options[:less_than_or_equal_to].call(object)) if validation.options[:less_than_or_equal_to].kind_of?(Proc)
98
+ return (validation.options[:less_than_or_equal_to])
99
+ end
100
+
101
+ if validation.options[:less_than]
102
+ return ((validation.options[:less_than].call(object)) - 1) if validation.options[:less_than].kind_of?(Proc)
103
+ return (validation.options[:less_than] - 1)
104
+ end
105
+ end
106
+ end
107
+
108
+ def validation_step
109
+ validation = validations? && validations.find do |validation|
110
+ validation.kind == :numericality
111
+ end
112
+ if validation
113
+ validation.options[:step] || (1 if validation_integer_only?)
114
+ else
115
+ nil
116
+ end
117
+ end
118
+
119
+ def validation_integer_only?
120
+ validation = validations? && validations.find do |validation|
121
+ validation.kind == :numericality
122
+ end
123
+ if validation
124
+ validation.options[:only_integer]
125
+ else
126
+ false
127
+ end
128
+ end
129
+
130
+ def validations?
131
+ validations != nil
132
+ end
133
+
134
+ def required?
135
+ return false if options[:required] == false
136
+ return true if options[:required] == true
137
+ return false if not_required_through_negated_validation?
138
+ if validations?
139
+ validations.any? { |validator|
140
+ if validator.options.key?(:on)
141
+ validator_on = Array(validator.options[:on])
142
+ next false if (validator_on.exclude?(:save)) && ((object.new_record? && validator_on.exclude?(:create)) || (!object.new_record? && validator_on.exclude?(:update)))
143
+ end
144
+ case validator.kind
145
+ when :presence
146
+ true
147
+ when :inclusion
148
+ validator.options[:allow_blank] != true
149
+ when :length
150
+ validator.options[:allow_blank] != true &&
151
+ validator.options[:minimum].to_i > 0 ||
152
+ validator.options[:within].try(:first).to_i > 0
153
+ else
154
+ false
155
+ end
156
+ }
157
+ else
158
+ return responds_to_global_required? && !!builder.all_fields_required_by_default
159
+ end
160
+ end
161
+
162
+ def required_attribute?
163
+ required? && builder.use_required_attribute
164
+ end
165
+
166
+ def not_required_through_negated_validation?
167
+ @not_required_through_negated_validation
168
+ end
169
+
170
+ def not_required_through_negated_validation!
171
+ @not_required_through_negated_validation = true
172
+ end
173
+
174
+ def responds_to_global_required?
175
+ true
176
+ end
177
+
178
+ def optional?
179
+ !required?
180
+ end
181
+
182
+ def autofocus?
183
+ opt_autofocus = options[:input_html] && options[:input_html][:autofocus]
184
+
185
+ !!opt_autofocus
186
+ end
187
+
188
+ def column_limit
189
+ column.limit if column? && column.respond_to?(:limit)
190
+ end
191
+
192
+ def limit
193
+ validation_limit || column_limit
194
+ end
195
+
196
+ def readonly?
197
+ readonly_from_options? || readonly_attribute?
198
+ end
199
+
200
+ def readonly_attribute?
201
+ object_class = self.object.class
202
+ object_class.respond_to?(:readonly_attributes) &&
203
+ self.object.persisted? &&
204
+ column.respond_to?(:name) &&
205
+ object_class.readonly_attributes.include?(column.name.to_s)
206
+ end
207
+
208
+ def readonly_from_options?
209
+ options[:input_html] && options[:input_html][:readonly]
210
+ end
211
+ end
212
+ end
213
+ end
214
+ end
215
+