formtastic 3.1.3 → 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 (179) 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 +61 -0
  6. data/Gemfile.lock +140 -0
  7. data/MIT-LICENSE +1 -1
  8. data/{README.textile → README.md} +183 -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 +12 -16
  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 +1 -0
  20. data/lib/formtastic/actions/base.rb +1 -0
  21. data/lib/formtastic/actions/button_action.rb +1 -0
  22. data/lib/formtastic/actions/buttonish.rb +1 -0
  23. data/lib/formtastic/actions/input_action.rb +1 -0
  24. data/lib/formtastic/actions/link_action.rb +1 -0
  25. data/lib/formtastic/actions.rb +7 -3
  26. data/lib/formtastic/deprecation.rb +2 -38
  27. data/lib/formtastic/engine.rb +4 -1
  28. data/lib/formtastic/form_builder.rb +12 -24
  29. data/lib/formtastic/helpers/action_helper.rb +2 -48
  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 +14 -9
  34. data/lib/formtastic/helpers/file_column_detection.rb +1 -0
  35. data/lib/formtastic/helpers/form_helper.rb +2 -1
  36. data/lib/formtastic/helpers/input_helper.rb +20 -76
  37. data/lib/formtastic/helpers/inputs_helper.rb +29 -23
  38. data/lib/formtastic/helpers/reflection.rb +1 -0
  39. data/lib/formtastic/helpers.rb +2 -2
  40. data/lib/formtastic/html_attributes.rb +1 -0
  41. data/lib/formtastic/i18n.rb +2 -1
  42. data/lib/formtastic/input_class_finder.rb +1 -0
  43. data/lib/formtastic/inputs/base/associations.rb +1 -0
  44. data/lib/formtastic/inputs/base/choices.rb +3 -2
  45. data/lib/formtastic/inputs/base/collections.rb +46 -10
  46. data/lib/formtastic/inputs/base/database.rb +5 -7
  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 +9 -7
  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 +1 -0
  61. data/lib/formtastic/inputs/base.rb +3 -2
  62. data/lib/formtastic/inputs/boolean_input.rb +2 -1
  63. data/lib/formtastic/inputs/check_boxes_input.rb +15 -6
  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 +1 -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 +1 -0
  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 +21 -0
  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 +30 -1
  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 -29
  88. data/lib/formtastic/localized_string.rb +1 -0
  89. data/lib/formtastic/localizer.rb +21 -22
  90. data/lib/formtastic/namespaced_class_finder.rb +8 -9
  91. data/lib/formtastic/version.rb +2 -1
  92. data/lib/formtastic.rb +10 -11
  93. data/lib/generators/formtastic/form/form_generator.rb +2 -1
  94. data/lib/generators/formtastic/input/input_generator.rb +47 -0
  95. data/lib/generators/formtastic/install/install_generator.rb +1 -0
  96. data/lib/generators/templates/formtastic.rb +15 -13
  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 +2 -1
  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 +329 -10
  114. data/spec/helpers/actions_helper_spec.rb +43 -42
  115. data/spec/helpers/form_helper_spec.rb +45 -38
  116. data/spec/helpers/input_helper_spec.rb +976 -2
  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 +2 -1
  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 +169 -121
  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 +3 -2
  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 +26 -25
  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 +18 -6
  157. data/spec/schema.rb +22 -0
  158. data/spec/spec_helper.rb +172 -260
  159. data/spec/support/custom_macros.rb +74 -76
  160. data/spec/support/deprecation.rb +2 -1
  161. data/spec/support/shared_examples.rb +2 -1233
  162. data/spec/support/specialized_class_finder_shared_example.rb +1 -0
  163. data/spec/support/test_environment.rb +24 -9
  164. metadata +78 -170
  165. data/.travis.yml +0 -29
  166. data/Appraisals +0 -29
  167. data/CHANGELOG +0 -39
  168. data/DEPRECATIONS +0 -49
  169. data/gemfiles/rails_3.2.gemfile +0 -7
  170. data/gemfiles/rails_4.0.4.gemfile +0 -7
  171. data/gemfiles/rails_4.1.gemfile +0 -7
  172. data/gemfiles/rails_4.2.gemfile +0 -7
  173. data/gemfiles/rails_4.gemfile +0 -7
  174. data/gemfiles/rails_edge.gemfile +0 -10
  175. data/lib/formtastic/util.rb +0 -57
  176. data/spec/helpers/namespaced_action_helper_spec.rb +0 -43
  177. data/spec/helpers/namespaced_input_helper_spec.rb +0 -36
  178. data/spec/support/deferred_garbage_collection.rb +0 -21
  179. data/spec/util_spec.rb +0 -66
@@ -1,4 +1,5 @@
1
1
  # -*- coding: utf-8 -*-
2
+ # frozen_string_literal: true
2
3
  module Formtastic
3
4
  module Helpers
4
5
 
@@ -36,10 +37,8 @@ module Formtastic
36
37
  # @see Formtastic::Helpers::InputsHelper#inputs
37
38
  # @see Formtastic::Helpers::FormHelper#semantic_form_for
38
39
  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
40
  include Formtastic::Helpers::Reflection
41
+ include Formtastic::Helpers::Enum
43
42
  include Formtastic::Helpers::FileColumnDetection
44
43
 
45
44
  # Returns a chunk of HTML markup for a given `method` on the form object, wrapped in
@@ -134,6 +133,7 @@ module Formtastic
134
133
  #
135
134
  # @option options :input_html [Hash]
136
135
  # Override or add to the HTML attributes to be passed down to the `<input>` tag
136
+ # (If you use attr_readonly method in your model, formtastic will automatically set those attributes's input readonly)
137
137
  #
138
138
  # @option options :wrapper_html [Hash]
139
139
  # Override or add to the HTML attributes to be passed down to the wrapping `<li>` tag
@@ -235,7 +235,7 @@ module Formtastic
235
235
  options = options.dup # Allow options to be shared without being tainted by Formtastic
236
236
  options[:as] ||= default_input_type(method, options)
237
237
 
238
- klass = input_class(options[:as])
238
+ klass = namespaced_input_class(options[:as])
239
239
 
240
240
  klass.new(self, template, @object, @object_name, method, options).to_html
241
241
  end
@@ -259,7 +259,8 @@ module Formtastic
259
259
  return :file if is_file?(method, options)
260
260
  end
261
261
 
262
- if column = column_for(method)
262
+ column = column_for(method)
263
+ if column && column.type
263
264
  # Special cases where the column type doesn't map to an input method.
264
265
  case column.type
265
266
  when :string
@@ -273,6 +274,7 @@ module Formtastic
273
274
  return :color if method.to_s =~ /color/
274
275
  when :integer
275
276
  return :select if reflection_for(method)
277
+ return :select if enum_for(method)
276
278
  return :number
277
279
  when :float, :decimal
278
280
  return :number
@@ -282,6 +284,10 @@ module Formtastic
282
284
  return :time_select
283
285
  when :date
284
286
  return :date_select
287
+ when :hstore, :json, :jsonb
288
+ return :text
289
+ when :citext, :inet
290
+ return :string
285
291
  end
286
292
 
287
293
  # Try look for hints in options hash. Quite common senario: Enum keys stored as string in the database.
@@ -296,12 +302,17 @@ module Formtastic
296
302
  end
297
303
 
298
304
  # Get a column object for a specified attribute method - if possible.
305
+ # @return [ActiveModel::Type::Value, #type] in case of rails 5 attributes api
306
+ # @return [ActiveRecord::ConnectionAdapters::Column] in case of rails 4
299
307
  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
308
+ case
309
+ when @object.class.respond_to?(:type_for_attribute)
310
+ @object.class.type_for_attribute(method.to_s)
311
+ when @object.class.respond_to?(:column_for_attribute)
312
+ @object.class.column_for_attribute(method)
313
+ when @object.respond_to?(:column_for_attribute)
303
314
  @object.column_for_attribute(method)
304
- end
315
+ else nil
305
316
  end
306
317
  end
307
318
 
@@ -331,73 +342,6 @@ module Formtastic
331
342
  rescue Formtastic::InputClassFinder::NotFoundError
332
343
  raise Formtastic::UnknownInputError, "Unable to find input #{$!.message}"
333
344
  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
345
  end
402
346
  end
403
347
  end
@@ -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,13 +325,13 @@ 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.
@@ -343,7 +340,7 @@ module Formtastic
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
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
@@ -372,15 +378,15 @@ module Formtastic
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)
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  module Helpers
3
4
  # @private
@@ -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,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
  # @private
3
4
  module HtmlAttributes
@@ -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
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Formtastic
2
3
 
3
4
  # Uses the {Formtastic::NamespacedClassFinder} to look up input class names.
@@ -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)
@@ -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
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,21 +1,19 @@
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
8
  if object.respond_to?(:column_for_attribute)
8
- # Remove deprecation wrapper & review after Rails 5.0 ships
9
- ActiveSupport::Deprecation.silence do
10
- object.column_for_attribute(method)
11
- end
9
+ object.column_for_attribute(method)
12
10
  end
13
11
  end
14
-
12
+
15
13
  def column?
16
14
  !column.nil?
17
15
  end
18
-
16
+
19
17
  end
20
18
  end
21
19
  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,15 +18,16 @@ 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
33
  builder.dom_id_namespace,
@@ -34,7 +36,7 @@ module Formtastic
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