formtastic 3.1.2 → 4.0.0

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 (135) hide show
  1. checksums.yaml +7 -0
  2. data/.gitattributes +1 -0
  3. data/.github/workflows/test.yml +61 -0
  4. data/.gitignore +3 -2
  5. data/CHANGELOG.md +52 -0
  6. data/Gemfile.lock +105 -0
  7. data/MIT-LICENSE +1 -1
  8. data/{README.textile → README.md} +178 -167
  9. data/RELEASE_PROCESS +3 -1
  10. data/Rakefile +20 -1
  11. data/app/assets/stylesheets/formtastic.css +1 -1
  12. data/bin/appraisal +8 -0
  13. data/formtastic.gemspec +10 -16
  14. data/gemfiles/rails_5.2/Gemfile +5 -0
  15. data/gemfiles/rails_6.0/Gemfile +5 -0
  16. data/gemfiles/rails_6.1/Gemfile +5 -0
  17. data/gemfiles/rails_edge/Gemfile +13 -0
  18. data/lib/formtastic.rb +9 -11
  19. data/lib/formtastic/actions.rb +6 -3
  20. data/lib/formtastic/deprecation.rb +1 -38
  21. data/lib/formtastic/engine.rb +3 -1
  22. data/lib/formtastic/form_builder.rb +11 -24
  23. data/lib/formtastic/helpers.rb +1 -1
  24. data/lib/formtastic/helpers/action_helper.rb +1 -48
  25. data/lib/formtastic/helpers/enum.rb +13 -0
  26. data/lib/formtastic/helpers/errors_helper.rb +2 -2
  27. data/lib/formtastic/helpers/fieldset_wrapper.rb +13 -9
  28. data/lib/formtastic/helpers/form_helper.rb +1 -1
  29. data/lib/formtastic/helpers/input_helper.rb +23 -77
  30. data/lib/formtastic/helpers/inputs_helper.rb +27 -22
  31. data/lib/formtastic/i18n.rb +1 -1
  32. data/lib/formtastic/inputs.rb +32 -29
  33. data/lib/formtastic/inputs/base/choices.rb +1 -1
  34. data/lib/formtastic/inputs/base/collections.rb +43 -10
  35. data/lib/formtastic/inputs/base/database.rb +7 -2
  36. data/lib/formtastic/inputs/base/errors.rb +4 -4
  37. data/lib/formtastic/inputs/base/hints.rb +1 -1
  38. data/lib/formtastic/inputs/base/html.rb +7 -6
  39. data/lib/formtastic/inputs/base/naming.rb +4 -4
  40. data/lib/formtastic/inputs/base/options.rb +2 -3
  41. data/lib/formtastic/inputs/base/timeish.rb +5 -1
  42. data/lib/formtastic/inputs/base/validations.rb +38 -12
  43. data/lib/formtastic/inputs/check_boxes_input.rb +13 -5
  44. data/lib/formtastic/inputs/color_input.rb +0 -1
  45. data/lib/formtastic/inputs/country_input.rb +3 -1
  46. data/lib/formtastic/inputs/radio_input.rb +20 -0
  47. data/lib/formtastic/inputs/select_input.rb +29 -1
  48. data/lib/formtastic/inputs/time_zone_input.rb +16 -6
  49. data/lib/formtastic/localizer.rb +20 -22
  50. data/lib/formtastic/namespaced_class_finder.rb +1 -1
  51. data/lib/formtastic/version.rb +1 -1
  52. data/lib/generators/formtastic/form/form_generator.rb +1 -1
  53. data/lib/generators/formtastic/input/input_generator.rb +46 -0
  54. data/lib/generators/templates/formtastic.rb +14 -13
  55. data/lib/generators/templates/input.rb +19 -0
  56. data/sample/basic_inputs.html +1 -1
  57. data/script/integration-template.rb +74 -0
  58. data/script/integration.sh +19 -0
  59. data/spec/action_class_finder_spec.rb +1 -1
  60. data/spec/actions/button_action_spec.rb +8 -8
  61. data/spec/actions/generic_action_spec.rb +60 -60
  62. data/spec/actions/input_action_spec.rb +7 -7
  63. data/spec/actions/link_action_spec.rb +10 -10
  64. data/spec/builder/custom_builder_spec.rb +37 -21
  65. data/spec/builder/error_proc_spec.rb +4 -4
  66. data/spec/builder/semantic_fields_for_spec.rb +27 -27
  67. data/spec/fast_spec_helper.rb +12 -0
  68. data/spec/generators/formtastic/form/form_generator_spec.rb +25 -25
  69. data/spec/generators/formtastic/input/input_generator_spec.rb +124 -0
  70. data/spec/generators/formtastic/install/install_generator_spec.rb +9 -9
  71. data/spec/helpers/action_helper_spec.rb +328 -10
  72. data/spec/helpers/actions_helper_spec.rb +17 -17
  73. data/spec/helpers/form_helper_spec.rb +37 -37
  74. data/spec/helpers/input_helper_spec.rb +975 -2
  75. data/spec/helpers/inputs_helper_spec.rb +120 -105
  76. data/spec/helpers/reflection_helper_spec.rb +3 -3
  77. data/spec/helpers/semantic_errors_helper_spec.rb +22 -22
  78. data/spec/i18n_spec.rb +26 -26
  79. data/spec/input_class_finder_spec.rb +1 -1
  80. data/spec/inputs/base/collections_spec.rb +76 -0
  81. data/spec/inputs/base/validations_spec.rb +480 -0
  82. data/spec/inputs/boolean_input_spec.rb +55 -55
  83. data/spec/inputs/check_boxes_input_spec.rb +155 -108
  84. data/spec/inputs/color_input_spec.rb +51 -63
  85. data/spec/inputs/country_input_spec.rb +20 -20
  86. data/spec/inputs/custom_input_spec.rb +2 -6
  87. data/spec/inputs/datalist_input_spec.rb +1 -1
  88. data/spec/inputs/date_picker_input_spec.rb +42 -42
  89. data/spec/inputs/date_select_input_spec.rb +51 -37
  90. data/spec/inputs/datetime_picker_input_spec.rb +46 -46
  91. data/spec/inputs/datetime_select_input_spec.rb +53 -37
  92. data/spec/inputs/email_input_spec.rb +5 -5
  93. data/spec/inputs/file_input_spec.rb +6 -6
  94. data/spec/inputs/hidden_input_spec.rb +18 -18
  95. data/spec/inputs/include_blank_spec.rb +8 -8
  96. data/spec/inputs/label_spec.rb +20 -20
  97. data/spec/inputs/number_input_spec.rb +112 -112
  98. data/spec/inputs/password_input_spec.rb +5 -5
  99. data/spec/inputs/phone_input_spec.rb +5 -5
  100. data/spec/inputs/placeholder_spec.rb +5 -5
  101. data/spec/inputs/radio_input_spec.rb +84 -58
  102. data/spec/inputs/range_input_spec.rb +66 -66
  103. data/spec/inputs/readonly_spec.rb +50 -0
  104. data/spec/inputs/search_input_spec.rb +5 -5
  105. data/spec/inputs/select_input_spec.rb +149 -93
  106. data/spec/inputs/string_input_spec.rb +23 -23
  107. data/spec/inputs/text_input_spec.rb +16 -16
  108. data/spec/inputs/time_picker_input_spec.rb +43 -43
  109. data/spec/inputs/time_select_input_spec.rb +67 -54
  110. data/spec/inputs/time_zone_input_spec.rb +54 -28
  111. data/spec/inputs/url_input_spec.rb +5 -5
  112. data/spec/inputs/with_options_spec.rb +7 -7
  113. data/spec/localizer_spec.rb +17 -17
  114. data/spec/namespaced_class_finder_spec.rb +2 -2
  115. data/spec/schema.rb +21 -0
  116. data/spec/spec_helper.rb +165 -253
  117. data/spec/support/custom_macros.rb +72 -75
  118. data/spec/support/shared_examples.rb +0 -1232
  119. data/spec/support/test_environment.rb +23 -9
  120. metadata +69 -176
  121. data/.travis.yml +0 -29
  122. data/Appraisals +0 -29
  123. data/CHANGELOG +0 -31
  124. data/DEPRECATIONS +0 -49
  125. data/gemfiles/rails_3.2.gemfile +0 -7
  126. data/gemfiles/rails_4.0.4.gemfile +0 -7
  127. data/gemfiles/rails_4.1.gemfile +0 -7
  128. data/gemfiles/rails_4.2.gemfile +0 -7
  129. data/gemfiles/rails_4.gemfile +0 -7
  130. data/gemfiles/rails_edge.gemfile +0 -10
  131. data/lib/formtastic/util.rb +0 -57
  132. data/spec/helpers/namespaced_action_helper_spec.rb +0 -43
  133. data/spec/helpers/namespaced_input_helper_spec.rb +0 -36
  134. data/spec/support/deferred_garbage_collection.rb +0 -21
  135. data/spec/util_spec.rb +0 -66
@@ -163,7 +163,7 @@ module Formtastic
163
163
  class_names << @@default_form_class
164
164
  model_class_name = case record_or_name_or_array
165
165
  when String, Symbol then record_or_name_or_array.to_s # :post => "post"
166
- when Array then options[:as] || singularizer.call(record_or_name_or_array.last.class) # [@post, @comment] # => "comment"
166
+ when Array then options[:as] || singularizer.call(record_or_name_or_array[-1].class) # [@post, @comment] # => "comment"
167
167
  else options[:as] || singularizer.call(record_or_name_or_array.class) # @post => "post"
168
168
  end
169
169
  class_names << @@default_form_model_class_proc.call(model_class_name)
@@ -36,10 +36,8 @@ module Formtastic
36
36
  # @see Formtastic::Helpers::InputsHelper#inputs
37
37
  # @see Formtastic::Helpers::FormHelper#semantic_form_for
38
38
  module InputHelper
39
- INPUT_CLASS_DEPRECATION = 'configure Formtastic::FormBuilder.input_class_finder instead (upgrade guide on wiki: http://bit.ly/1F9QtKc )'.freeze
40
- private_constant(:INPUT_CLASS_DEPRECATION)
41
-
42
39
  include Formtastic::Helpers::Reflection
40
+ include Formtastic::Helpers::Enum
43
41
  include Formtastic::Helpers::FileColumnDetection
44
42
 
45
43
  # Returns a chunk of HTML markup for a given `method` on the form object, wrapped in
@@ -134,6 +132,7 @@ module Formtastic
134
132
  #
135
133
  # @option options :input_html [Hash]
136
134
  # Override or add to the HTML attributes to be passed down to the `<input>` tag
135
+ # (If you use attr_readonly method in your model, formtastic will automatically set those attributes's input readonly)
137
136
  #
138
137
  # @option options :wrapper_html [Hash]
139
138
  # Override or add to the HTML attributes to be passed down to the wrapping `<li>` tag
@@ -235,7 +234,7 @@ module Formtastic
235
234
  options = options.dup # Allow options to be shared without being tainted by Formtastic
236
235
  options[:as] ||= default_input_type(method, options)
237
236
 
238
- klass = input_class(options[:as])
237
+ klass = namespaced_input_class(options[:as])
239
238
 
240
239
  klass.new(self, template, @object, @object_name, method, options).to_html
241
240
  end
@@ -259,7 +258,8 @@ module Formtastic
259
258
  return :file if is_file?(method, options)
260
259
  end
261
260
 
262
- if column = column_for(method)
261
+ column = column_for(method)
262
+ if column && column.type
263
263
  # Special cases where the column type doesn't map to an input method.
264
264
  case column.type
265
265
  when :string
@@ -273,6 +273,7 @@ module Formtastic
273
273
  return :color if method.to_s =~ /color/
274
274
  when :integer
275
275
  return :select if reflection_for(method)
276
+ return :select if enum_for(method)
276
277
  return :number
277
278
  when :float, :decimal
278
279
  return :number
@@ -282,6 +283,10 @@ module Formtastic
282
283
  return :time_select
283
284
  when :date
284
285
  return :date_select
286
+ when :hstore, :json, :jsonb
287
+ return :text
288
+ when :citext, :inet
289
+ return :string
285
290
  end
286
291
 
287
292
  # Try look for hints in options hash. Quite common senario: Enum keys stored as string in the database.
@@ -296,12 +301,20 @@ module Formtastic
296
301
  end
297
302
 
298
303
  # Get a column object for a specified attribute method - if possible.
304
+ # @return [ActiveModel::Type::Value, #type] in case of rails 5 attributes api
305
+ # @return [ActiveRecord::ConnectionAdapters::Column] in case of rails 4
299
306
  def column_for(method) # @private
300
- if @object.respond_to?(:column_for_attribute)
301
- # Remove deprecation wrapper & review after Rails 5.0 ships
302
- ActiveSupport::Deprecation.silence do
303
- @object.column_for_attribute(method)
304
- end
307
+ case
308
+ when @object.class.respond_to?(:type_for_attribute)
309
+ @object.class.type_for_attribute(method.to_s)
310
+ when @object.class.respond_to?(:column_for_attribute)
311
+ @object.class.column_for_attribute(method)
312
+ when @object.respond_to?(:column_for_attribute)
313
+ # Remove deprecation wrapper & review after Rails 5.0 ships
314
+ ActiveSupport::Deprecation.silence do
315
+ @object.column_for_attribute(method)
316
+ end
317
+ else nil
305
318
  end
306
319
  end
307
320
 
@@ -331,73 +344,6 @@ module Formtastic
331
344
  rescue Formtastic::InputClassFinder::NotFoundError
332
345
  raise Formtastic::UnknownInputError, "Unable to find input #{$!.message}"
333
346
  end
334
-
335
- # @api private
336
- # @deprecated Use {#namespaced_input_class} instead.
337
- def input_class(as)
338
- return namespaced_input_class(as) if input_class_finder
339
-
340
- input_class_deprecation_warning(__method__)
341
-
342
- @input_classes_cache ||= {}
343
- @input_classes_cache[as] ||= begin
344
- config = Rails.application.config
345
- use_const_defined = config.respond_to?(:eager_load) ? config.eager_load : config.cache_classes
346
- use_const_defined ? input_class_with_const_defined(as) : input_class_by_trying(as)
347
- end
348
- end
349
-
350
- # @api private
351
- # @deprecated Use {InputClassFinder#find} instead.
352
- # prevent exceptions in production environment for better performance
353
- def input_class_with_const_defined(as)
354
- input_class_name = custom_input_class_name(as)
355
-
356
- if ::Object.const_defined?(input_class_name)
357
- input_class_name.constantize
358
- elsif Formtastic::Inputs.const_defined?(input_class_name)
359
- standard_input_class_name(as).constantize
360
- else
361
- raise Formtastic::UnknownInputError, "Unable to find input class #{input_class_name}"
362
- end
363
- end
364
-
365
- # @api private
366
- # @deprecated Use {InputClassFinder#find} instead.
367
- # use auto-loading in development environment
368
- def input_class_by_trying(as)
369
- begin
370
- custom_input_class_name(as).constantize
371
- rescue NameError
372
- standard_input_class_name(as).constantize
373
- end
374
- rescue NameError
375
- raise Formtastic::UnknownInputError, "Unable to find input class for #{as}"
376
- end
377
-
378
- # @api private
379
- # @deprecated Use {InputClassFinder#class_name} instead.
380
- # :as => :string # => StringInput
381
- def custom_input_class_name(as)
382
- input_class_deprecation_warning(__method__)
383
- "#{as.to_s.camelize}Input"
384
- end
385
-
386
- # @api private
387
- # @deprecated Use {InputClassFinder#class_name} instead.
388
- # :as => :string # => {Formtastic::Inputs::StringInput}
389
- def standard_input_class_name(as)
390
- input_class_deprecation_warning(__method__)
391
- "Formtastic::Inputs::#{as.to_s.camelize}Input"
392
- end
393
-
394
- private
395
-
396
- def input_class_deprecation_warning(method)
397
- @input_class_deprecation_warned ||=
398
- Formtastic.deprecation.deprecation_warning(method, INPUT_CLASS_DEPRECATION, caller(2))
399
- end
400
-
401
347
  end
402
348
  end
403
349
  end
@@ -47,10 +47,6 @@ module Formtastic
47
47
  include Formtastic::Helpers::FieldsetWrapper
48
48
  include Formtastic::LocalizedString
49
49
 
50
- # Which columns to skip when automatically rendering a form without any fields specified.
51
- SKIPPED_COLUMNS = [:created_at, :updated_at, :created_on, :updated_on, :lock_version, :version]
52
-
53
-
54
50
  # {#inputs} creates an input fieldset and ol tag wrapping for use around a set of inputs. It can be
55
51
  # called either with a block (in which you can do the usual Rails form stuff, HTML, ERB, etc),
56
52
  # or with a list of fields (accepting all default arguments and options). These two examples
@@ -136,7 +132,7 @@ module Formtastic
136
132
  # <% end %>
137
133
  # <% end %>
138
134
  #
139
- # {#inputs} also provides a DSL similar to `fields_for` / `semantic_fields_for` to reduce the
135
+ # {#inputs} also provides a DSL similar to `fields_for` / `semantic_fields_for` to reduce the
140
136
  # lines of code a little:
141
137
  #
142
138
  # <% semantic_form_for @user do |f| %>
@@ -162,7 +158,7 @@ module Formtastic
162
158
  # All options except `:name`, `:title` and `:for` will be passed down to the fieldset as HTML
163
159
  # attributes (id, class, style, etc).
164
160
  #
165
- # When nesting `inputs()` inside another `inputs()` block, the nested content will
161
+ # When nesting `inputs()` inside another `inputs()` block, the nested content will
166
162
  # automatically be wrapped in an `<li>` tag to preserve the HTML validity (a `<fieldset>`
167
163
  # cannot be a direct descendant of an `<ol>`.
168
164
  #
@@ -183,12 +179,12 @@ module Formtastic
183
179
  # <% end %>
184
180
  #
185
181
  # @example Quick form: Skip one or more fields
186
- # <%= f.inputs, :except => [:featured, :something_for_admin_only] %>
187
- # <%= f.inputs, :except => :featured %>
182
+ # <%= f.inputs :except => [:featured, :something_for_admin_only] %>
183
+ # <%= f.inputs :except => :featured %>
188
184
  #
189
185
  # @example Short hand: Render inputs for a named set of attributes and simple associations on the model, with all default arguments and options
190
186
  # <% semantic_form_for @post do |form| %>
191
- # <%= f.inputs, :title, :body, :user, :categories %>
187
+ # <%= f.inputs :title, :body, :user, :categories %>
192
188
  # <% end %>
193
189
  #
194
190
  # @example Block: Render inputs for attributes and simple associations with full control over arguments and options
@@ -219,7 +215,7 @@ module Formtastic
219
215
  # <%= f.input :title ... %>
220
216
  # <%= f.input :body ... %>
221
217
  # <% end %>
222
- # <%= f.inputs do :name => 'Advanced options:' do %>
218
+ # <%= f.inputs :name => 'Advanced options:' do %>
223
219
  # <%= f.input :user ... %>
224
220
  # <%= f.input :categories ... %>
225
221
  # <% end %>
@@ -283,7 +279,7 @@ module Formtastic
283
279
  def inputs(*args, &block)
284
280
  wrap_it = @already_in_an_inputs_block ? true : false
285
281
  @already_in_an_inputs_block = true
286
-
282
+
287
283
  title = field_set_title_from_args(*args)
288
284
  html_options = args.extract_options!
289
285
  html_options[:class] ||= "inputs"
@@ -303,21 +299,21 @@ module Formtastic
303
299
  field_set_and_list_wrapping(*((args << html_options) << contents))
304
300
  end
305
301
  end
306
-
302
+
307
303
  out = template.content_tag(:li, out, :class => "input") if wrap_it
308
304
  @already_in_an_inputs_block = wrap_it
309
305
  out
310
306
  end
311
307
 
312
308
  protected
313
-
309
+
314
310
  def default_columns_for_object
315
311
  cols = association_columns(:belongs_to)
316
312
  cols += content_columns
317
- cols -= SKIPPED_COLUMNS
313
+ cols -= skipped_columns
318
314
  cols.compact
319
315
  end
320
-
316
+
321
317
  def fieldset_contents_from_column_list(columns)
322
318
  columns.collect do |method|
323
319
  if @object
@@ -328,13 +324,13 @@ module Formtastic
328
324
  elsif @object.class.respond_to?(:associations)
329
325
  if (@object.class.associations[method.to_sym] && @object.class.associations[method.to_sym].options[:polymorphic] == true)
330
326
  raise PolymorphicInputWithoutCollectionError.new("Please provide a collection for :#{method} input (you'll need to use block form syntax). Inputs for polymorphic associations can only be used when an explicit :collection is provided.")
331
- end
332
- end
327
+ end
328
+ end
333
329
  end
334
330
  input(method.to_sym)
335
331
  end
336
332
  end
337
-
333
+
338
334
  # Collects association columns (relation columns) for the current form object class. Skips
339
335
  # polymorphic associations because we can't guess which class to use for an automatically
340
336
  # generated input.
@@ -343,7 +339,7 @@ module Formtastic
343
339
  @object.class.reflections.collect do |name, association_reflection|
344
340
  if by_associations.present?
345
341
  if by_associations.include?(association_reflection.macro) && association_reflection.options[:polymorphic] != true
346
- name
342
+ name
347
343
  end
348
344
  else
349
345
  name
@@ -354,12 +350,21 @@ module Formtastic
354
350
  end
355
351
  end
356
352
 
353
+ # Collects all foreign key columns
354
+ def foreign_key_columns # @private
355
+ if @object.present? && @object.class.respond_to?(:reflect_on_all_associations)
356
+ @object.class.reflect_on_all_associations(:belongs_to).map{ |reflection| reflection.foreign_key.to_sym }
357
+ else
358
+ []
359
+ end
360
+ end
361
+
357
362
  # Collects content columns (non-relation columns) for the current form object class.
358
363
  def content_columns # @private
359
364
  # TODO: NameError is raised by Inflector.constantize. Consider checking if it exists instead.
360
365
  begin klass = model_name.constantize; rescue NameError; return [] end
361
366
  return [] unless klass.respond_to?(:content_columns)
362
- klass.content_columns.collect { |c| c.name.to_sym }.compact
367
+ klass.content_columns.collect { |c| c.name.to_sym }.compact - foreign_key_columns
363
368
  end
364
369
 
365
370
  # Deals with :for option when it's supplied to inputs methods. Additional
@@ -377,10 +382,10 @@ module Formtastic
377
382
  lambda do |f|
378
383
  contents = f.inputs(*args) do
379
384
  if block.arity == 1 # for backwards compatibility with REE & Ruby 1.8.x
380
- block.call(f)
385
+ yield(f)
381
386
  else
382
387
  index = parent_child_index(options[:parent]) if options[:parent]
383
- block.call(f, index)
388
+ yield(f, index)
384
389
  end
385
390
  end
386
391
  template.concat(contents)
@@ -23,7 +23,7 @@ module Formtastic
23
23
  options = args.extract_options!
24
24
  options.reverse_merge!(:default => DEFAULT_VALUES[key])
25
25
  options[:scope] = [DEFAULT_SCOPE, options[:scope]].flatten.compact
26
- ::I18n.translate(key, *(args << options))
26
+ ::I18n.translate(key, *args, **options)
27
27
  end
28
28
  alias :t :translate
29
29
 
@@ -4,36 +4,39 @@ module Formtastic
4
4
 
5
5
  autoload :Base
6
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
7
  autoload :Timeish
36
- autoload :UrlInput
8
+
9
+ eager_autoload do
10
+ autoload :BooleanInput
11
+ autoload :CheckBoxesInput
12
+ autoload :ColorInput
13
+ autoload :CountryInput
14
+ autoload :DatalistInput
15
+ autoload :DateInput
16
+ autoload :DatePickerInput
17
+ autoload :DatetimePickerInput
18
+ autoload :DateSelectInput
19
+ autoload :DatetimeInput
20
+ autoload :DatetimeSelectInput
21
+ autoload :EmailInput
22
+ autoload :FileInput
23
+ autoload :HiddenInput
24
+ autoload :NumberInput
25
+ autoload :NumericInput
26
+ autoload :PasswordInput
27
+ autoload :PhoneInput
28
+ autoload :RadioInput
29
+ autoload :RangeInput
30
+ autoload :SearchInput
31
+ autoload :SelectInput
32
+ autoload :StringInput
33
+ autoload :TextInput
34
+ autoload :TimeInput
35
+ autoload :TimePickerInput
36
+ autoload :TimeSelectInput
37
+ autoload :TimeZoneInput
38
+ autoload :UrlInput
39
+ end
37
40
  end
38
41
  end
39
42
 
@@ -64,7 +64,7 @@ module Formtastic
64
64
  end
65
65
 
66
66
  def custom_choice_html_options(choice)
67
- (choice.is_a?(Array) && choice.size > 2) ? choice.last : {}
67
+ (choice.is_a?(Array) && choice.size > 2) ? choice[-1] : {}
68
68
  end
69
69
 
70
70
  def choice_html_safe_value(choice)
@@ -12,7 +12,7 @@ module Formtastic
12
12
  end
13
13
 
14
14
  def value_method
15
- @value_method ||= (value_method_from_options || label_and_value_method.last)
15
+ @value_method ||= (value_method_from_options || label_and_value_method[-1])
16
16
  end
17
17
 
18
18
  def value_method_from_options
@@ -24,7 +24,7 @@ module Formtastic
24
24
  end
25
25
 
26
26
  def label_and_value_method_from_collection(_collection)
27
- sample = _collection.first || _collection.last
27
+ sample = _collection.first || _collection[-1]
28
28
 
29
29
  case sample
30
30
  when Array
@@ -43,16 +43,16 @@ module Formtastic
43
43
  end
44
44
 
45
45
  def raw_collection
46
- @raw_collection ||= (collection_from_options || collection_from_association || collection_for_boolean)
46
+ @raw_collection ||= (collection_from_options || collection_from_enum || collection_from_association || collection_for_boolean)
47
47
  end
48
48
 
49
49
  def collection
50
50
  # Return if we have a plain string
51
51
  return raw_collection if raw_collection.is_a?(String)
52
52
 
53
- # Return if we have an Array of strings, fixnums or arrays
53
+ # Return if we have an Array of strings, integers or arrays
54
54
  return raw_collection if (raw_collection.instance_of?(Array) || raw_collection.instance_of?(Range)) &&
55
- [Array, Fixnum, String].include?(raw_collection.first.class) &&
55
+ ([Array, String].include?(raw_collection.first.class) || raw_collection.first.is_a?(Integer)) &&
56
56
  !(options.include?(:member_label) || options.include?(:member_value))
57
57
 
58
58
  raw_collection.map { |o| [send_or_call(label_method, o), send_or_call(value_method, o)] }
@@ -83,15 +83,48 @@ module Formtastic
83
83
 
84
84
  scope_conditions = conditions_from_reflection.empty? ? nil : {:conditions => conditions_from_reflection}
85
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)
86
+
87
+ reflection.klass.where(where_conditions)
88
+ end
89
+ end
90
+
91
+ # Assuming the following model:
92
+ #
93
+ # class Post < ActiveRecord::Base
94
+ # enum :status => [ :active, :archived ]
95
+ # end
96
+ #
97
+ # We would end up with a collection like this:
98
+ #
99
+ # [["Active", "active"], ["Archived", "archived"]
100
+ #
101
+ # The first element in each array uses String#humanize, but I18n
102
+ # translations are available too. Set them with the following structure.
103
+ #
104
+ # en:
105
+ # activerecord:
106
+ # attributes:
107
+ # post:
108
+ # statuses:
109
+ # active: Custom Active Label Here
110
+ # archived: Custom Archived Label Here
111
+ def collection_from_enum
112
+ if collection_from_enum?
113
+ method_name = method.to_s
114
+
115
+ enum_options_hash = object.defined_enums[method_name]
116
+ enum_options_hash.map do |name, value|
117
+ key = "activerecord.attributes.#{object_name}.#{method_name.pluralize}.#{name}"
118
+ label = ::I18n.translate(key, :default => name.humanize)
119
+ [label, name]
91
120
  end
92
121
  end
93
122
  end
94
123
 
124
+ def collection_from_enum?
125
+ object.respond_to?(:defined_enums) && object.defined_enums.has_key?(method.to_s)
126
+ end
127
+
95
128
  def collection_for_boolean
96
129
  true_text = options[:true] || Formtastic::I18n.t(:yes)
97
130
  false_text = options[:false] || Formtastic::I18n.t(:no)