formtastic 3.0.0 → 5.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 (175) hide show
  1. checksums.yaml +5 -13
  2. data/.gitattributes +1 -0
  3. data/.github/workflows/test.yml +61 -0
  4. data/.gitignore +3 -2
  5. data/CHANGELOG.md +61 -0
  6. data/Gemfile.lock +140 -0
  7. data/MIT-LICENSE +1 -1
  8. data/{README.textile → README.md} +191 -168
  9. data/RELEASE_PROCESS +3 -1
  10. data/Rakefile +24 -8
  11. data/app/assets/stylesheets/formtastic.css +1 -1
  12. data/bin/appraisal +8 -0
  13. data/formtastic.gemspec +13 -17
  14. data/gemfiles/rails_6.0/Gemfile +5 -0
  15. data/gemfiles/rails_6.1/Gemfile +5 -0
  16. data/gemfiles/rails_7.0/Gemfile +5 -0
  17. data/gemfiles/rails_7.1/Gemfile +5 -0
  18. data/gemfiles/rails_edge/Gemfile +13 -0
  19. data/lib/formtastic/action_class_finder.rb +19 -0
  20. data/lib/formtastic/actions/base.rb +1 -0
  21. data/lib/formtastic/actions/button_action.rb +56 -53
  22. data/lib/formtastic/actions/buttonish.rb +1 -0
  23. data/lib/formtastic/actions/input_action.rb +60 -57
  24. data/lib/formtastic/actions/link_action.rb +69 -67
  25. data/lib/formtastic/actions.rb +7 -3
  26. data/lib/formtastic/deprecation.rb +6 -0
  27. data/lib/formtastic/engine.rb +4 -1
  28. data/lib/formtastic/form_builder.rb +32 -25
  29. data/lib/formtastic/helpers/action_helper.rb +22 -31
  30. data/lib/formtastic/helpers/actions_helper.rb +1 -0
  31. data/lib/formtastic/helpers/enum.rb +14 -0
  32. data/lib/formtastic/helpers/errors_helper.rb +3 -2
  33. data/lib/formtastic/helpers/fieldset_wrapper.rb +16 -11
  34. data/lib/formtastic/helpers/file_column_detection.rb +1 -0
  35. data/lib/formtastic/helpers/form_helper.rb +4 -3
  36. data/lib/formtastic/helpers/input_helper.rb +59 -80
  37. data/lib/formtastic/helpers/inputs_helper.rb +33 -27
  38. data/lib/formtastic/helpers/reflection.rb +5 -4
  39. data/lib/formtastic/helpers.rb +2 -2
  40. data/lib/formtastic/html_attributes.rb +13 -1
  41. data/lib/formtastic/i18n.rb +2 -1
  42. data/lib/formtastic/input_class_finder.rb +19 -0
  43. data/lib/formtastic/inputs/base/associations.rb +1 -0
  44. data/lib/formtastic/inputs/base/choices.rb +4 -3
  45. data/lib/formtastic/inputs/base/collections.rb +47 -11
  46. data/lib/formtastic/inputs/base/database.rb +8 -5
  47. data/lib/formtastic/inputs/base/datetime_pickerish.rb +1 -0
  48. data/lib/formtastic/inputs/base/errors.rb +7 -6
  49. data/lib/formtastic/inputs/base/fileish.rb +1 -0
  50. data/lib/formtastic/inputs/base/hints.rb +2 -1
  51. data/lib/formtastic/inputs/base/html.rb +12 -10
  52. data/lib/formtastic/inputs/base/labelling.rb +3 -2
  53. data/lib/formtastic/inputs/base/naming.rb +5 -4
  54. data/lib/formtastic/inputs/base/numeric.rb +1 -0
  55. data/lib/formtastic/inputs/base/options.rb +3 -3
  56. data/lib/formtastic/inputs/base/placeholder.rb +1 -0
  57. data/lib/formtastic/inputs/base/stringish.rb +1 -0
  58. data/lib/formtastic/inputs/base/timeish.rb +9 -4
  59. data/lib/formtastic/inputs/base/validations.rb +39 -12
  60. data/lib/formtastic/inputs/base/wrapping.rb +2 -3
  61. data/lib/formtastic/inputs/base.rb +17 -12
  62. data/lib/formtastic/inputs/boolean_input.rb +2 -1
  63. data/lib/formtastic/inputs/check_boxes_input.rb +16 -24
  64. data/lib/formtastic/inputs/color_input.rb +1 -1
  65. data/lib/formtastic/inputs/country_input.rb +4 -1
  66. data/lib/formtastic/inputs/datalist_input.rb +42 -0
  67. data/lib/formtastic/inputs/date_picker_input.rb +1 -0
  68. data/lib/formtastic/inputs/date_select_input.rb +1 -0
  69. data/lib/formtastic/inputs/datetime_picker_input.rb +1 -0
  70. data/lib/formtastic/inputs/datetime_select_input.rb +1 -0
  71. data/lib/formtastic/inputs/email_input.rb +1 -0
  72. data/lib/formtastic/inputs/file_input.rb +3 -2
  73. data/lib/formtastic/inputs/hidden_input.rb +3 -2
  74. data/lib/formtastic/inputs/number_input.rb +1 -0
  75. data/lib/formtastic/inputs/password_input.rb +1 -0
  76. data/lib/formtastic/inputs/phone_input.rb +1 -0
  77. data/lib/formtastic/inputs/radio_input.rb +26 -21
  78. data/lib/formtastic/inputs/range_input.rb +1 -0
  79. data/lib/formtastic/inputs/search_input.rb +1 -0
  80. data/lib/formtastic/inputs/select_input.rb +32 -10
  81. data/lib/formtastic/inputs/string_input.rb +1 -0
  82. data/lib/formtastic/inputs/text_input.rb +1 -0
  83. data/lib/formtastic/inputs/time_picker_input.rb +1 -0
  84. data/lib/formtastic/inputs/time_select_input.rb +1 -0
  85. data/lib/formtastic/inputs/time_zone_input.rb +17 -6
  86. data/lib/formtastic/inputs/url_input.rb +1 -0
  87. data/lib/formtastic/inputs.rb +33 -28
  88. data/lib/formtastic/localized_string.rb +2 -1
  89. data/lib/formtastic/localizer.rb +23 -24
  90. data/lib/formtastic/namespaced_class_finder.rb +98 -0
  91. data/lib/formtastic/version.rb +2 -1
  92. data/lib/formtastic.rb +19 -14
  93. data/lib/generators/formtastic/form/form_generator.rb +8 -2
  94. data/lib/generators/formtastic/input/input_generator.rb +47 -0
  95. data/lib/generators/formtastic/install/install_generator.rb +2 -0
  96. data/lib/generators/templates/formtastic.rb +29 -7
  97. data/lib/generators/templates/input.rb +19 -0
  98. data/sample/basic_inputs.html +1 -1
  99. data/script/integration-template.rb +73 -0
  100. data/script/integration.sh +19 -0
  101. data/spec/action_class_finder_spec.rb +13 -0
  102. data/spec/actions/button_action_spec.rb +21 -20
  103. data/spec/actions/generic_action_spec.rb +134 -133
  104. data/spec/actions/input_action_spec.rb +20 -19
  105. data/spec/actions/link_action_spec.rb +30 -29
  106. data/spec/builder/custom_builder_spec.rb +39 -22
  107. data/spec/builder/error_proc_spec.rb +6 -5
  108. data/spec/builder/semantic_fields_for_spec.rb +46 -45
  109. data/spec/fast_spec_helper.rb +13 -0
  110. data/spec/generators/formtastic/form/form_generator_spec.rb +33 -32
  111. data/spec/generators/formtastic/input/input_generator_spec.rb +125 -0
  112. data/spec/generators/formtastic/install/install_generator_spec.rb +10 -9
  113. data/spec/helpers/action_helper_spec.rb +70 -97
  114. data/spec/helpers/actions_helper_spec.rb +43 -42
  115. data/spec/helpers/form_helper_spec.rb +56 -39
  116. data/spec/helpers/input_helper_spec.rb +314 -255
  117. data/spec/helpers/inputs_helper_spec.rb +217 -202
  118. data/spec/helpers/reflection_helper_spec.rb +7 -6
  119. data/spec/helpers/semantic_errors_helper_spec.rb +26 -25
  120. data/spec/i18n_spec.rb +30 -29
  121. data/spec/input_class_finder_spec.rb +11 -0
  122. data/spec/inputs/base/collections_spec.rb +78 -0
  123. data/spec/inputs/base/validations_spec.rb +481 -0
  124. data/spec/inputs/boolean_input_spec.rb +73 -72
  125. data/spec/inputs/check_boxes_input_spec.rb +174 -123
  126. data/spec/inputs/color_input_spec.rb +53 -64
  127. data/spec/inputs/country_input_spec.rb +23 -22
  128. data/spec/inputs/custom_input_spec.rb +3 -6
  129. data/spec/inputs/datalist_input_spec.rb +62 -0
  130. data/spec/inputs/date_picker_input_spec.rb +114 -113
  131. data/spec/inputs/date_select_input_spec.rb +76 -61
  132. data/spec/inputs/datetime_picker_input_spec.rb +123 -122
  133. data/spec/inputs/datetime_select_input_spec.rb +85 -68
  134. data/spec/inputs/email_input_spec.rb +17 -16
  135. data/spec/inputs/file_input_spec.rb +18 -17
  136. data/spec/inputs/hidden_input_spec.rb +32 -31
  137. data/spec/inputs/include_blank_spec.rb +10 -9
  138. data/spec/inputs/label_spec.rb +36 -31
  139. data/spec/inputs/number_input_spec.rb +212 -211
  140. data/spec/inputs/password_input_spec.rb +17 -16
  141. data/spec/inputs/phone_input_spec.rb +17 -16
  142. data/spec/inputs/placeholder_spec.rb +18 -17
  143. data/spec/inputs/radio_input_spec.rb +92 -65
  144. data/spec/inputs/range_input_spec.rb +136 -135
  145. data/spec/inputs/readonly_spec.rb +51 -0
  146. data/spec/inputs/search_input_spec.rb +16 -15
  147. data/spec/inputs/select_input_spec.rb +209 -102
  148. data/spec/inputs/string_input_spec.rb +51 -50
  149. data/spec/inputs/text_input_spec.rb +34 -33
  150. data/spec/inputs/time_picker_input_spec.rb +115 -114
  151. data/spec/inputs/time_select_input_spec.rb +84 -70
  152. data/spec/inputs/time_zone_input_spec.rb +58 -31
  153. data/spec/inputs/url_input_spec.rb +17 -16
  154. data/spec/inputs/with_options_spec.rb +9 -8
  155. data/spec/localizer_spec.rb +18 -17
  156. data/spec/namespaced_class_finder_spec.rb +91 -0
  157. data/spec/schema.rb +22 -0
  158. data/spec/spec_helper.rb +180 -249
  159. data/spec/support/custom_macros.rb +128 -98
  160. data/spec/support/deprecation.rb +2 -1
  161. data/spec/support/shared_examples.rb +13 -0
  162. data/spec/support/specialized_class_finder_shared_example.rb +28 -0
  163. data/spec/support/test_environment.rb +25 -10
  164. metadata +95 -136
  165. data/.travis.yml +0 -28
  166. data/Appraisals +0 -25
  167. data/CHANGELOG +0 -27
  168. data/gemfiles/rails_3.2.gemfile +0 -7
  169. data/gemfiles/rails_4.0.4.gemfile +0 -7
  170. data/gemfiles/rails_4.1.gemfile +0 -7
  171. data/gemfiles/rails_4.gemfile +0 -7
  172. data/gemfiles/rails_edge.gemfile +0 -10
  173. data/lib/formtastic/util.rb +0 -53
  174. data/spec/support/deferred_garbage_collection.rb +0 -21
  175. data/spec/util_spec.rb +0 -52
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Helpers
3
4
 
@@ -47,10 +48,6 @@ module Formtastic
47
48
  include Formtastic::Helpers::FieldsetWrapper
48
49
  include Formtastic::LocalizedString
49
50
 
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
51
  # {#inputs} creates an input fieldset and ol tag wrapping for use around a set of inputs. It can be
55
52
  # called either with a block (in which you can do the usual Rails form stuff, HTML, ERB, etc),
56
53
  # or with a list of fields (accepting all default arguments and options). These two examples
@@ -136,7 +133,7 @@ module Formtastic
136
133
  # <% end %>
137
134
  # <% end %>
138
135
  #
139
- # {#inputs} also provides a DSL similar to `fields_for` / `semantic_fields_for` to reduce the
136
+ # {#inputs} also provides a DSL similar to `fields_for` / `semantic_fields_for` to reduce the
140
137
  # lines of code a little:
141
138
  #
142
139
  # <% semantic_form_for @user do |f| %>
@@ -162,7 +159,7 @@ module Formtastic
162
159
  # All options except `:name`, `:title` and `:for` will be passed down to the fieldset as HTML
163
160
  # attributes (id, class, style, etc).
164
161
  #
165
- # When nesting `inputs()` inside another `inputs()` block, the nested content will
162
+ # When nesting `inputs()` inside another `inputs()` block, the nested content will
166
163
  # automatically be wrapped in an `<li>` tag to preserve the HTML validity (a `<fieldset>`
167
164
  # cannot be a direct descendant of an `<ol>`.
168
165
  #
@@ -183,12 +180,12 @@ module Formtastic
183
180
  # <% end %>
184
181
  #
185
182
  # @example Quick form: Skip one or more fields
186
- # <%= f.inputs, :except => [:featured, :something_for_admin_only] %>
187
- # <%= f.inputs, :except => :featured %>
183
+ # <%= f.inputs :except => [:featured, :something_for_admin_only] %>
184
+ # <%= f.inputs :except => :featured %>
188
185
  #
189
186
  # @example Short hand: Render inputs for a named set of attributes and simple associations on the model, with all default arguments and options
190
187
  # <% semantic_form_for @post do |form| %>
191
- # <%= f.inputs, :title, :body, :user, :categories %>
188
+ # <%= f.inputs :title, :body, :user, :categories %>
192
189
  # <% end %>
193
190
  #
194
191
  # @example Block: Render inputs for attributes and simple associations with full control over arguments and options
@@ -219,7 +216,7 @@ module Formtastic
219
216
  # <%= f.input :title ... %>
220
217
  # <%= f.input :body ... %>
221
218
  # <% end %>
222
- # <%= f.inputs do :name => 'Advanced options:' do %>
219
+ # <%= f.inputs :name => 'Advanced options:' do %>
223
220
  # <%= f.input :user ... %>
224
221
  # <%= f.input :categories ... %>
225
222
  # <% end %>
@@ -283,7 +280,7 @@ module Formtastic
283
280
  def inputs(*args, &block)
284
281
  wrap_it = @already_in_an_inputs_block ? true : false
285
282
  @already_in_an_inputs_block = true
286
-
283
+
287
284
  title = field_set_title_from_args(*args)
288
285
  html_options = args.extract_options!
289
286
  html_options[:class] ||= "inputs"
@@ -303,21 +300,21 @@ module Formtastic
303
300
  field_set_and_list_wrapping(*((args << html_options) << contents))
304
301
  end
305
302
  end
306
-
303
+
307
304
  out = template.content_tag(:li, out, :class => "input") if wrap_it
308
305
  @already_in_an_inputs_block = wrap_it
309
306
  out
310
307
  end
311
308
 
312
309
  protected
313
-
310
+
314
311
  def default_columns_for_object
315
312
  cols = association_columns(:belongs_to)
316
313
  cols += content_columns
317
- cols -= SKIPPED_COLUMNS
314
+ cols -= skipped_columns
318
315
  cols.compact
319
316
  end
320
-
317
+
321
318
  def fieldset_contents_from_column_list(columns)
322
319
  columns.collect do |method|
323
320
  if @object
@@ -328,22 +325,22 @@ module Formtastic
328
325
  elsif @object.class.respond_to?(:associations)
329
326
  if (@object.class.associations[method.to_sym] && @object.class.associations[method.to_sym].options[:polymorphic] == true)
330
327
  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
328
+ end
329
+ end
333
330
  end
334
331
  input(method.to_sym)
335
332
  end
336
333
  end
337
-
334
+
338
335
  # Collects association columns (relation columns) for the current form object class. Skips
339
336
  # polymorphic associations because we can't guess which class to use for an automatically
340
337
  # generated input.
341
- def association_columns(*by_associations) #:nodoc:
338
+ def association_columns(*by_associations) # @private
342
339
  if @object.present? && @object.class.respond_to?(:reflections)
343
340
  @object.class.reflections.collect do |name, association_reflection|
344
341
  if by_associations.present?
345
342
  if by_associations.include?(association_reflection.macro) && association_reflection.options[:polymorphic] != true
346
- name
343
+ name
347
344
  end
348
345
  else
349
346
  name
@@ -354,12 +351,21 @@ module Formtastic
354
351
  end
355
352
  end
356
353
 
354
+ # Collects all foreign key columns
355
+ def foreign_key_columns # @private
356
+ if @object.present? && @object.class.respond_to?(:reflect_on_all_associations)
357
+ @object.class.reflect_on_all_associations(:belongs_to).map{ |reflection| reflection.foreign_key.to_sym }
358
+ else
359
+ []
360
+ end
361
+ end
362
+
357
363
  # Collects content columns (non-relation columns) for the current form object class.
358
- def content_columns #:nodoc:
364
+ def content_columns # @private
359
365
  # TODO: NameError is raised by Inflector.constantize. Consider checking if it exists instead.
360
366
  begin klass = model_name.constantize; rescue NameError; return [] end
361
367
  return [] unless klass.respond_to?(:content_columns)
362
- klass.content_columns.collect { |c| c.name.to_sym }.compact
368
+ klass.content_columns.collect { |c| c.name.to_sym }.compact - foreign_key_columns
363
369
  end
364
370
 
365
371
  # Deals with :for option when it's supplied to inputs methods. Additional
@@ -367,20 +373,20 @@ module Formtastic
367
373
  # key.
368
374
  #
369
375
  # It should raise an error if a block with arity zero is given.
370
- def inputs_for_nested_attributes(*args, &block) #:nodoc:
376
+ def inputs_for_nested_attributes(*args, &block) # @private
371
377
  options = args.extract_options!
372
378
  args << options.merge!(:parent => { :builder => self, :for => options[:for] })
373
379
 
374
380
  fields_for_block = if block_given?
375
- raise ArgumentError, 'You gave :for option with a block to inputs method, ' <<
381
+ raise ArgumentError, 'You gave :for option with a block to inputs method, ' +
376
382
  'but the block does not accept any argument.' if block.arity <= 0
377
383
  lambda do |f|
378
384
  contents = f.inputs(*args) do
379
385
  if block.arity == 1 # for backwards compatibility with REE & Ruby 1.8.x
380
- block.call(f)
386
+ yield(f)
381
387
  else
382
388
  index = parent_child_index(options[:parent]) if options[:parent]
383
- block.call(f, index)
389
+ yield(f, index)
384
390
  end
385
391
  end
386
392
  template.concat(contents)
@@ -396,7 +402,7 @@ module Formtastic
396
402
  fields_for(*fields_for_args, &fields_for_block)
397
403
  end
398
404
 
399
- def field_set_title_from_args(*args) #:nodoc:
405
+ def field_set_title_from_args(*args) # @private
400
406
  options = args.extract_options!
401
407
  options[:name] ||= options.delete(:title)
402
408
  title = options[:name]
@@ -1,23 +1,24 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Helpers
3
4
  # @private
4
5
  module Reflection
5
6
  # If an association method is passed in (f.input :author) try to find the
6
7
  # reflection object.
7
- def reflection_for(method) #:nodoc:
8
+ def reflection_for(method) # @private
8
9
  if @object.class.respond_to?(:reflect_on_association)
9
- @object.class.reflect_on_association(method)
10
+ @object.class.reflect_on_association(method)
10
11
  elsif @object.class.respond_to?(:associations) # MongoMapper uses the 'associations(method)' instead
11
12
  @object.class.associations[method]
12
13
  end
13
14
  end
14
15
 
15
- def association_macro_for_method(method) #:nodoc:
16
+ def association_macro_for_method(method) # @private
16
17
  reflection = reflection_for(method)
17
18
  reflection.macro if reflection
18
19
  end
19
20
 
20
- def association_primary_key_for_method(method) #:nodoc:
21
+ def association_primary_key_for_method(method) # @private
21
22
  reflection = reflection_for(method)
22
23
  if reflection
23
24
  case association_macro_for_method(method)
@@ -1,6 +1,6 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Helpers
3
- autoload :ButtonsHelper, 'formtastic/helpers/buttons_helper'
4
4
  autoload :ActionHelper, 'formtastic/helpers/action_helper'
5
5
  autoload :ActionsHelper, 'formtastic/helpers/actions_helper'
6
6
  autoload :ErrorsHelper, 'formtastic/helpers/errors_helper'
@@ -9,8 +9,8 @@ module Formtastic
9
9
  autoload :FormHelper, 'formtastic/helpers/form_helper'
10
10
  autoload :InputHelper, 'formtastic/helpers/input_helper'
11
11
  autoload :InputsHelper, 'formtastic/helpers/inputs_helper'
12
- autoload :LabelHelper, 'formtastic/helpers/label_helper'
13
12
  autoload :Reflection, 'formtastic/helpers/reflection'
13
+ autoload :Enum, 'formtastic/helpers/enum'
14
14
  end
15
15
  end
16
16
 
@@ -1,6 +1,18 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  # @private
3
4
  module HtmlAttributes
5
+ # Returns a namespace passed by option or inherited from parent builders / class configuration
6
+ def dom_id_namespace
7
+ namespace = options[:custom_namespace]
8
+ parent = options[:parent_builder]
9
+
10
+ case
11
+ when namespace then namespace
12
+ when parent && parent != self then parent.dom_id_namespace
13
+ else custom_namespace
14
+ end
15
+ end
4
16
 
5
17
  protected
6
18
 
@@ -18,4 +30,4 @@ module Formtastic
18
30
  end
19
31
 
20
32
  end
21
- end
33
+ end
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module Formtastic
4
5
  # @private
@@ -23,7 +24,7 @@ module Formtastic
23
24
  options = args.extract_options!
24
25
  options.reverse_merge!(:default => DEFAULT_VALUES[key])
25
26
  options[:scope] = [DEFAULT_SCOPE, options[:scope]].flatten.compact
26
- ::I18n.translate(key, *(args << options))
27
+ ::I18n.translate(key, *args, **options)
27
28
  end
28
29
  alias :t :translate
29
30
 
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+ module Formtastic
3
+
4
+ # Uses the {Formtastic::NamespacedClassFinder} to look up input class names.
5
+ #
6
+ # See {Formtastic::FormBuilder#namespaced_input_class} for details.
7
+ #
8
+ class InputClassFinder < NamespacedClassFinder
9
+
10
+ # @param builder [FormBuilder]
11
+ def initialize(builder)
12
+ super builder.input_namespaces
13
+ end
14
+
15
+ def class_name(as)
16
+ "#{super}Input"
17
+ end
18
+ end
19
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Inputs
3
4
  module Base
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Inputs
3
4
  module Base
@@ -64,7 +65,7 @@ module Formtastic
64
65
  end
65
66
 
66
67
  def custom_choice_html_options(choice)
67
- (choice.is_a?(Array) && choice.size > 2) ? choice.last : {}
68
+ (choice.is_a?(Array) && choice.size > 2) ? choice[-1] : {}
68
69
  end
69
70
 
70
71
  def choice_html_safe_value(choice)
@@ -73,7 +74,7 @@ module Formtastic
73
74
 
74
75
  def choice_input_dom_id(choice)
75
76
  [
76
- builder.custom_namespace,
77
+ builder.dom_id_namespace,
77
78
  sanitized_object_name,
78
79
  builder.options[:index],
79
80
  association_primary_key || method,
@@ -92,7 +93,7 @@ module Formtastic
92
93
  label_html_options.merge(:class => "label")
93
94
  )
94
95
  else
95
- "".html_safe
96
+ +"".html_safe
96
97
  end
97
98
  end
98
99
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Inputs
3
4
  module Base
@@ -12,7 +13,7 @@ module Formtastic
12
13
  end
13
14
 
14
15
  def value_method
15
- @value_method ||= (value_method_from_options || label_and_value_method.last)
16
+ @value_method ||= (value_method_from_options || label_and_value_method[-1])
16
17
  end
17
18
 
18
19
  def value_method_from_options
@@ -24,7 +25,7 @@ module Formtastic
24
25
  end
25
26
 
26
27
  def label_and_value_method_from_collection(_collection)
27
- sample = _collection.first || _collection.last
28
+ sample = _collection.first || _collection[-1]
28
29
 
29
30
  case sample
30
31
  when Array
@@ -43,16 +44,16 @@ module Formtastic
43
44
  end
44
45
 
45
46
  def raw_collection
46
- @raw_collection ||= (collection_from_options || collection_from_association || collection_for_boolean)
47
+ @raw_collection ||= (collection_from_options || collection_from_enum || collection_from_association || collection_for_boolean)
47
48
  end
48
49
 
49
50
  def collection
50
51
  # Return if we have a plain string
51
- return raw_collection if raw_collection.instance_of?(String) || raw_collection.instance_of?(ActiveSupport::SafeBuffer)
52
+ return raw_collection if raw_collection.is_a?(String)
52
53
 
53
- # Return if we have an Array of strings, fixnums or arrays
54
+ # Return if we have an Array of strings, integers or arrays
54
55
  return raw_collection if (raw_collection.instance_of?(Array) || raw_collection.instance_of?(Range)) &&
55
- [Array, Fixnum, String].include?(raw_collection.first.class) &&
56
+ ([Array, String].include?(raw_collection.first.class) || raw_collection.first.is_a?(Integer)) &&
56
57
  !(options.include?(:member_label) || options.include?(:member_value))
57
58
 
58
59
  raw_collection.map { |o| [send_or_call(label_method, o), send_or_call(value_method, o)] }
@@ -78,20 +79,55 @@ module Formtastic
78
79
  ) if reflection.options[:polymorphic] == true
79
80
  end
80
81
 
82
+ return reflection.klass.merge(reflection.scope) if reflection.scope
83
+
81
84
  conditions_from_reflection = (reflection.respond_to?(:options) && reflection.options[:conditions]) || {}
82
85
  conditions_from_reflection = conditions_from_reflection.call if conditions_from_reflection.is_a?(Proc)
83
86
 
84
87
  scope_conditions = conditions_from_reflection.empty? ? nil : {:conditions => conditions_from_reflection}
85
88
  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)
89
+
90
+ reflection.klass.where(where_conditions)
91
+ end
92
+ end
93
+
94
+ # Assuming the following model:
95
+ #
96
+ # class Post < ActiveRecord::Base
97
+ # enum :status => [ :active, :archived ]
98
+ # end
99
+ #
100
+ # We would end up with a collection like this:
101
+ #
102
+ # [["Active", "active"], ["Archived", "archived"]
103
+ #
104
+ # The first element in each array uses String#humanize, but I18n
105
+ # translations are available too. Set them with the following structure.
106
+ #
107
+ # en:
108
+ # activerecord:
109
+ # attributes:
110
+ # post:
111
+ # statuses:
112
+ # active: Custom Active Label Here
113
+ # archived: Custom Archived Label Here
114
+ def collection_from_enum
115
+ if collection_from_enum?
116
+ method_name = method.to_s
117
+
118
+ enum_options_hash = object.defined_enums[method_name]
119
+ enum_options_hash.map do |name, value|
120
+ key = "activerecord.attributes.#{object.model_name.i18n_key}.#{method_name.pluralize}.#{name}"
121
+ label = ::I18n.translate(key, :default => name.humanize)
122
+ [label, name]
91
123
  end
92
124
  end
93
125
  end
94
126
 
127
+ def collection_from_enum?
128
+ object.respond_to?(:defined_enums) && object.defined_enums.has_key?(method.to_s)
129
+ end
130
+
95
131
  def collection_for_boolean
96
132
  true_text = options[:true] || Formtastic::I18n.t(:yes)
97
133
  false_text = options[:false] || Formtastic::I18n.t(:no)
@@ -1,17 +1,20 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Inputs
3
4
  module Base
4
5
  module Database
5
-
6
+
6
7
  def column
7
- object.column_for_attribute(method) if object.respond_to?(:column_for_attribute)
8
+ if object.respond_to?(:column_for_attribute)
9
+ object.column_for_attribute(method)
10
+ end
8
11
  end
9
-
12
+
10
13
  def column?
11
14
  !column.nil?
12
15
  end
13
-
16
+
14
17
  end
15
18
  end
16
19
  end
17
- end
20
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Inputs
3
4
  module Base
@@ -1,33 +1,34 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Inputs
3
4
  module Base
4
5
  module Errors
5
6
 
6
7
  def error_html
7
- errors? ? send(:"error_#{builder.inline_errors}_html") : ""
8
+ errors? ? send(:"error_#{builder.inline_errors}_html") : +""
8
9
  end
9
10
 
10
11
  def error_sentence_html
11
12
  error_class = builder.default_inline_error_class
12
- template.content_tag(:p, Formtastic::Util.html_safe(errors.to_sentence.html_safe), :class => error_class)
13
+ template.content_tag(:p, errors.to_sentence, :class => error_class)
13
14
  end
14
15
 
15
16
  def error_list_html
16
17
  error_class = builder.default_error_list_class
17
18
  list_elements = []
18
19
  errors.each do |error|
19
- list_elements << template.content_tag(:li, Formtastic::Util.html_safe(error.html_safe))
20
+ list_elements << template.content_tag(:li, error.html_safe)
20
21
  end
21
- template.content_tag(:ul, Formtastic::Util.html_safe(list_elements.join("\n")), :class => error_class)
22
+ template.content_tag(:ul, list_elements.join("\n").html_safe, :class => error_class)
22
23
  end
23
24
 
24
25
  def error_first_html
25
26
  error_class = builder.default_inline_error_class
26
- template.content_tag(:p, Formtastic::Util.html_safe(errors.first.untaint), :class => error_class)
27
+ template.content_tag(:p, errors.first.untaint.html_safe, :class => error_class)
27
28
  end
28
29
 
29
30
  def error_none_html
30
- ""
31
+ +""
31
32
  end
32
33
 
33
34
  def errors?
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Inputs
3
4
  module Base
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Inputs
3
4
  module Base
@@ -7,7 +8,7 @@ module Formtastic
7
8
  if hint?
8
9
  template.content_tag(
9
10
  :p,
10
- Formtastic::Util.html_safe(hint_text),
11
+ hint_text.html_safe,
11
12
  :class => builder.default_hint_class
12
13
  )
13
14
  end
@@ -1,8 +1,9 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Inputs
3
4
  module Base
4
5
  module Html
5
-
6
+
6
7
  # Defines how the instance of an input should be rendered to a HTML string.
7
8
  #
8
9
  # @abstract Implement this method in your input class to describe how the input should render itself.
@@ -17,24 +18,25 @@ module Formtastic
17
18
  def to_html
18
19
  raise NotImplementedError
19
20
  end
20
-
21
+
21
22
  def input_html_options
22
- {
23
+ {
23
24
  :id => dom_id,
24
25
  :required => required_attribute?,
25
- :autofocus => autofocus?
26
+ :autofocus => autofocus?,
27
+ :readonly => readonly?
26
28
  }.merge(options[:input_html] || {})
27
29
  end
28
-
30
+
29
31
  def dom_id
30
32
  [
31
- builder.custom_namespace,
32
- sanitized_object_name,
33
- dom_index,
33
+ builder.dom_id_namespace,
34
+ sanitized_object_name,
35
+ dom_index,
34
36
  association_primary_key || sanitized_method_name
35
37
  ].reject { |x| x.blank? }.join('_')
36
38
  end
37
-
39
+
38
40
  def dom_index
39
41
  if builder.options.has_key?(:index)
40
42
  builder.options[:index]
@@ -42,7 +44,7 @@ module Formtastic
42
44
  # TODO there's no coverage for this case, not sure how to create a scenario for it
43
45
  builder.auto_index
44
46
  else
45
- ""
47
+ +""
46
48
  end
47
49
  end
48
50
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Inputs
3
4
  module Base
@@ -6,7 +7,7 @@ module Formtastic
6
7
  include Formtastic::LocalizedString
7
8
 
8
9
  def label_html
9
- render_label? ? builder.label(input_name, label_text, label_html_options) : "".html_safe
10
+ render_label? ? builder.label(input_name, label_text, label_html_options) : +"".html_safe
10
11
  end
11
12
 
12
13
  def label_html_options
@@ -49,4 +50,4 @@ module Formtastic
49
50
  end
50
51
  end
51
52
  end
52
- end
53
+ end
@@ -1,12 +1,13 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Inputs
3
4
  module Base
4
5
  module Naming
5
6
 
6
7
  def as
7
- self.class.name.split("::").last.underscore.gsub(/_input$/, '')
8
+ self.class.name.split("::")[-1].underscore.gsub(/_input$/, '')
8
9
  end
9
-
10
+
10
11
  def sanitized_object_name
11
12
  object_name.to_s.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "")
12
13
  end
@@ -18,7 +19,7 @@ module Formtastic
18
19
  def attributized_method_name
19
20
  method.to_s.gsub(/_id$/, '').to_sym
20
21
  end
21
-
22
+
22
23
  def humanized_method_name
23
24
  if builder.label_str_method != :humanize
24
25
  # Special case where label_str_method should trump the human_attribute_name
@@ -39,4 +40,4 @@ module Formtastic
39
40
  end
40
41
  end
41
42
  end
42
- end
43
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Inputs
3
4
  module Base
@@ -1,16 +1,16 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Inputs
3
4
  module Base
4
5
  module Options
5
-
6
+
6
7
  def input_options
7
8
  options.except(*formtastic_options)
8
9
  end
9
-
10
+
10
11
  def formtastic_options
11
12
  [:priority_countries, :priority_zones, :member_label, :member_value, :collection, :required, :label, :as, :hint, :input_html, :value_as_class, :class]
12
13
  end
13
-
14
14
  end
15
15
  end
16
16
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Inputs
3
4
  module Base
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Inputs
3
4
  module Base