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
@@ -1,16 +1,18 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module Formtastic
4
+ # @private
4
5
  module I18n
5
6
 
6
7
  DEFAULT_SCOPE = [:formtastic].freeze
7
8
  DEFAULT_VALUES = YAML.load_file(File.expand_path("../../locale/en.yml", __FILE__))["en"]["formtastic"].freeze
8
9
  SCOPES = [
9
10
  '%{model}.%{nested_model}.%{action}.%{attribute}',
10
- '%{model}.%{action}.%{attribute}',
11
11
  '%{model}.%{nested_model}.%{attribute}',
12
- '%{model}.%{attribute}',
12
+ '%{nested_model}.%{action}.%{attribute}',
13
13
  '%{nested_model}.%{attribute}',
14
+ '%{model}.%{action}.%{attribute}',
15
+ '%{model}.%{attribute}',
14
16
  '%{attribute}'
15
17
  ]
16
18
 
@@ -0,0 +1,18 @@
1
+ module Formtastic
2
+
3
+ # Uses the {Formtastic::NamespacedClassFinder} to look up input class names.
4
+ #
5
+ # See {Formtastic::FormBuilder#namespaced_input_class} for details.
6
+ #
7
+ class InputClassFinder < NamespacedClassFinder
8
+
9
+ # @param builder [FormBuilder]
10
+ def initialize(builder)
11
+ super builder.input_namespaces
12
+ end
13
+
14
+ def class_name(as)
15
+ "#{super}Input"
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,39 @@
1
+ module Formtastic
2
+ module Inputs
3
+ extend ActiveSupport::Autoload
4
+
5
+ autoload :Base
6
+ autoload :Basic
7
+ autoload :BooleanInput
8
+ autoload :CheckBoxesInput
9
+ autoload :ColorInput
10
+ autoload :CountryInput
11
+ autoload :DatalistInput
12
+ autoload :DateInput
13
+ autoload :DatePickerInput
14
+ autoload :DatetimePickerInput
15
+ autoload :DateSelectInput
16
+ autoload :DatetimeInput
17
+ autoload :DatetimeSelectInput
18
+ autoload :EmailInput
19
+ autoload :FileInput
20
+ autoload :HiddenInput
21
+ autoload :NumberInput
22
+ autoload :NumericInput
23
+ autoload :PasswordInput
24
+ autoload :PhoneInput
25
+ autoload :RadioInput
26
+ autoload :RangeInput
27
+ autoload :SearchInput
28
+ autoload :SelectInput
29
+ autoload :StringInput
30
+ autoload :TextInput
31
+ autoload :TimeInput
32
+ autoload :TimePickerInput
33
+ autoload :TimeSelectInput
34
+ autoload :TimeZoneInput
35
+ autoload :Timeish
36
+ autoload :UrlInput
37
+ end
38
+ end
39
+
@@ -0,0 +1,76 @@
1
+ module Formtastic
2
+ module Inputs
3
+ module Base
4
+
5
+ attr_accessor :builder, :template, :object, :object_name, :method, :options
6
+
7
+ def initialize(builder, template, object, object_name, method, options)
8
+ @builder = builder
9
+ @template = template
10
+ @object = object
11
+ @object_name = object_name
12
+ @method = method
13
+ @options = options.dup
14
+
15
+ # Deprecate :member_label and :member_value, remove v4.0
16
+ member_deprecation_message = "passing an Array of label/value pairs like [['Justin', 2], ['Kate', 3]] into :collection directly (consider building the array in your model using Model.pluck)"
17
+ warn_deprecated_option!(:member_label, member_deprecation_message)
18
+ warn_deprecated_option!(:member_value, member_deprecation_message)
19
+ end
20
+
21
+ # Usefull for deprecating options.
22
+ def warn_and_correct_option!(old_option_name, new_option_name)
23
+ if options.key?(old_option_name)
24
+ ::ActiveSupport::Deprecation.warn("The :#{old_option_name} option is deprecated in favour of :#{new_option_name} and will be removed from Formtastic in the next version", caller(6))
25
+ options[new_option_name] = options.delete(old_option_name)
26
+ end
27
+ end
28
+
29
+ # Usefull for deprecating options.
30
+ def warn_deprecated_option!(old_option_name, instructions)
31
+ if options.key?(old_option_name)
32
+ ::ActiveSupport::Deprecation.warn("The :#{old_option_name} option is deprecated in favour of `#{instructions}`. :#{old_option_name} will be removed in the next version", caller(6))
33
+ end
34
+ end
35
+
36
+ # Usefull for raising an error on previously supported option.
37
+ def removed_option!(old_option_name)
38
+ raise ArgumentError, ":#{old_option_name} is no longer available" if options.key?(old_option_name)
39
+ end
40
+
41
+ extend ActiveSupport::Autoload
42
+
43
+ autoload :DatetimePickerish
44
+ autoload :Associations
45
+ autoload :Collections
46
+ autoload :Choices
47
+ autoload :Database
48
+ autoload :Errors
49
+ autoload :Fileish
50
+ autoload :Hints
51
+ autoload :Html
52
+ autoload :Labelling
53
+ autoload :Naming
54
+ autoload :Numeric
55
+ autoload :Options
56
+ autoload :Placeholder
57
+ autoload :Stringish
58
+ autoload :Timeish
59
+ autoload :Validations
60
+ autoload :Wrapping
61
+
62
+ include Html
63
+ include Options
64
+ include Database
65
+ include Errors
66
+ include Hints
67
+ include Naming
68
+ include Validations
69
+ include Fileish
70
+ include Associations
71
+ include Labelling
72
+ include Wrapping
73
+
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,31 @@
1
+ module Formtastic
2
+ module Inputs
3
+ module Base
4
+ module Associations
5
+ include Formtastic::Helpers::Reflection
6
+
7
+ # :belongs_to, etc
8
+ def association
9
+ @association ||= association_macro_for_method(method)
10
+ end
11
+
12
+ def reflection
13
+ @reflection ||= reflection_for(method)
14
+ end
15
+
16
+ def belongs_to?
17
+ association == :belongs_to
18
+ end
19
+
20
+ def has_many?
21
+ association == :has_many
22
+ end
23
+
24
+ def association_primary_key
25
+ association_primary_key_for_method(method)
26
+ end
27
+
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,108 @@
1
+ module Formtastic
2
+ module Inputs
3
+ module Base
4
+ module Choices
5
+
6
+ def choices_wrapping(&block)
7
+ template.content_tag(:fieldset,
8
+ template.capture(&block),
9
+ choices_wrapping_html_options
10
+ )
11
+ end
12
+
13
+ def choices_wrapping_html_options
14
+ { :class => "choices" }
15
+ end
16
+
17
+ def choices_group_wrapping(&block)
18
+ template.content_tag(:ol,
19
+ template.capture(&block),
20
+ choices_group_wrapping_html_options
21
+ )
22
+ end
23
+
24
+ def choices_group_wrapping_html_options
25
+ { :class => "choices-group" }
26
+ end
27
+
28
+ def choice_wrapping(html_options, &block)
29
+ template.content_tag(:li,
30
+ template.capture(&block),
31
+ html_options
32
+ )
33
+ end
34
+
35
+ def choice_wrapping_html_options(choice)
36
+ classes = ['choice']
37
+ classes << "#{sanitized_method_name.singularize}_#{choice_html_safe_value(choice)}" if value_as_class?
38
+
39
+ { :class => classes.join(" ") }
40
+ end
41
+
42
+ def choice_html(choice)
43
+ raise "choice_html() needs to be implemented when including Formtastic::Inputs::Base::Choices"
44
+ end
45
+
46
+ def choice_label(choice)
47
+ if choice.is_a?(Array)
48
+ choice.first
49
+ else
50
+ choice
51
+ end.to_s
52
+ end
53
+
54
+ def choice_value(choice)
55
+ choice.is_a?(Array) ? choice[1] : choice
56
+ end
57
+
58
+ def choice_html_options(choice)
59
+ custom_choice_html_options(choice).merge(default_choice_html_options(choice))
60
+ end
61
+
62
+ def default_choice_html_options(choice)
63
+ { :id => choice_input_dom_id(choice) }
64
+ end
65
+
66
+ def custom_choice_html_options(choice)
67
+ (choice.is_a?(Array) && choice.size > 2) ? choice[-1] : {}
68
+ end
69
+
70
+ def choice_html_safe_value(choice)
71
+ choice_value(choice).to_s.gsub(/\s/, '_').gsub(/[^\w-]/, '').downcase
72
+ end
73
+
74
+ def choice_input_dom_id(choice)
75
+ [
76
+ builder.dom_id_namespace,
77
+ sanitized_object_name,
78
+ builder.options[:index],
79
+ association_primary_key || method,
80
+ choice_html_safe_value(choice)
81
+ ].compact.reject { |i| i.blank? }.join("_")
82
+ end
83
+
84
+ def value_as_class?
85
+ options[:value_as_class]
86
+ end
87
+
88
+ def legend_html
89
+ if render_label?
90
+ template.content_tag(:legend,
91
+ template.content_tag(:label, label_text),
92
+ label_html_options.merge(:class => "label")
93
+ )
94
+ else
95
+ "".html_safe
96
+ end
97
+ end
98
+
99
+ # Override to remove the for attribute since this isn't associated with any element, as it's
100
+ # nested inside the legend.
101
+ def label_html_options
102
+ super.merge(:for => nil)
103
+ end
104
+
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,159 @@
1
+ module Formtastic
2
+ module Inputs
3
+ module Base
4
+ module Collections
5
+
6
+ def label_method
7
+ @label_method ||= (label_method_from_options || label_and_value_method.first)
8
+ end
9
+
10
+ def label_method_from_options
11
+ options[:member_label]
12
+ end
13
+
14
+ def value_method
15
+ @value_method ||= (value_method_from_options || label_and_value_method[-1])
16
+ end
17
+
18
+ def value_method_from_options
19
+ options[:member_value]
20
+ end
21
+
22
+ def label_and_value_method
23
+ @label_and_value_method ||= label_and_value_method_from_collection(raw_collection)
24
+ end
25
+
26
+ def label_and_value_method_from_collection(_collection)
27
+ sample = _collection.first || _collection[-1]
28
+
29
+ case sample
30
+ when Array
31
+ label, value = :first, :last
32
+ when Integer
33
+ label, value = :to_s, :to_i
34
+ when Symbol, String, NilClass
35
+ label, value = :to_s, :to_s
36
+ end
37
+
38
+ # Order of preference: user supplied method, class defaults, auto-detect
39
+ label ||= builder.collection_label_methods.find { |m| sample.respond_to?(m) }
40
+ value ||= builder.collection_value_methods.find { |m| sample.respond_to?(m) }
41
+
42
+ [label, value]
43
+ end
44
+
45
+ def raw_collection
46
+ @raw_collection ||= (collection_from_options || collection_from_enum || collection_from_association || collection_for_boolean)
47
+ end
48
+
49
+ def collection
50
+ # Return if we have a plain string
51
+ return raw_collection if raw_collection.is_a?(String)
52
+
53
+ # Return if we have an Array of strings, integers or arrays
54
+ return raw_collection if (raw_collection.instance_of?(Array) || raw_collection.instance_of?(Range)) &&
55
+ ([Array, String].include?(raw_collection.first.class) || raw_collection.first.is_a?(Integer)) &&
56
+ !(options.include?(:member_label) || options.include?(:member_value))
57
+
58
+ raw_collection.map { |o| [send_or_call(label_method, o), send_or_call(value_method, o)] }
59
+ end
60
+
61
+ def collection_from_options
62
+ items = options[:collection]
63
+ case items
64
+ when Hash
65
+ items.to_a
66
+ when Range
67
+ items.to_a.collect{ |c| [c.to_s, c] }
68
+ else
69
+ items
70
+ end
71
+ end
72
+
73
+ def collection_from_association
74
+ if reflection
75
+ if reflection.respond_to?(:options)
76
+ raise PolymorphicInputWithoutCollectionError.new(
77
+ "A collection must be supplied for #{method} input. Collections cannot be guessed for polymorphic associations."
78
+ ) if reflection.options[:polymorphic] == true
79
+ end
80
+
81
+ conditions_from_reflection = (reflection.respond_to?(:options) && reflection.options[:conditions]) || {}
82
+ conditions_from_reflection = conditions_from_reflection.call if conditions_from_reflection.is_a?(Proc)
83
+
84
+ scope_conditions = conditions_from_reflection.empty? ? nil : {:conditions => conditions_from_reflection}
85
+ where_conditions = (scope_conditions && scope_conditions[:conditions]) || {}
86
+
87
+ if Util.rails3?
88
+ reflection.klass.scoped(scope_conditions).where({}) # where is uneccessary, but keeps the stubbing simpler while we support rails3
89
+ else
90
+ reflection.klass.where(where_conditions)
91
+ end
92
+ end
93
+ end
94
+
95
+ # Assuming the following model:
96
+ #
97
+ # class Post < ActiveRecord::Base
98
+ # enum :status => [ :active, :archived ]
99
+ # end
100
+ #
101
+ # We would end up with a collection like this:
102
+ #
103
+ # [["Active", "active"], ["Archived", "archived"]
104
+ #
105
+ # The first element in each array uses String#humanize, but I18n
106
+ # translations are available too. Set them with the following structure.
107
+ #
108
+ # en:
109
+ # activerecord:
110
+ # attributes:
111
+ # post:
112
+ # statuses:
113
+ # active: Custom Active Label Here
114
+ # archived: Custom Archived Label Here
115
+ def collection_from_enum
116
+ if collection_from_enum?
117
+ method_name = method.to_s
118
+
119
+ enum_options_hash = object.defined_enums[method_name]
120
+ enum_options_hash.map do |name, value|
121
+ key = "activerecord.attributes.#{object_name}.#{method_name.pluralize}.#{name}"
122
+ label = ::I18n.translate(key, :default => name.humanize)
123
+ [label, name]
124
+ end
125
+ end
126
+ end
127
+
128
+ def collection_from_enum?
129
+ object.respond_to?(:defined_enums) && object.defined_enums.has_key?(method.to_s)
130
+ end
131
+
132
+ def collection_for_boolean
133
+ true_text = options[:true] || Formtastic::I18n.t(:yes)
134
+ false_text = options[:false] || Formtastic::I18n.t(:no)
135
+
136
+ # TODO options[:value_as_class] = true unless options.key?(:value_as_class)
137
+
138
+ [ [true_text, true], [false_text, false] ]
139
+ end
140
+
141
+ def send_or_call(duck, object)
142
+ if duck.respond_to?(:call)
143
+ duck.call(object)
144
+ elsif object.respond_to? duck.to_sym
145
+ object.send(duck)
146
+ end
147
+ end
148
+
149
+ # Avoids an issue where `send_or_call` can be a String and duck can be something simple like
150
+ # `:first`, which obviously String responds to.
151
+ def send_or_call_or_object(duck, object)
152
+ return object if object.is_a?(String) || object.is_a?(Integer) || object.is_a?(Symbol) # TODO what about other classes etc?
153
+ send_or_call(duck, object)
154
+ end
155
+
156
+ end
157
+ end
158
+ end
159
+ end