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.
- checksums.yaml +7 -0
- data/.gitattributes +1 -0
- data/.github/workflows/test.yml +61 -0
- data/.gitignore +3 -2
- data/CHANGELOG.md +61 -0
- data/Gemfile.lock +140 -0
- data/MIT-LICENSE +1 -1
- data/{README.textile → README.md} +183 -167
- data/RELEASE_PROCESS +3 -1
- data/Rakefile +20 -1
- data/app/assets/stylesheets/formtastic.css +1 -1
- data/bin/appraisal +8 -0
- data/formtastic.gemspec +12 -16
- data/gemfiles/rails_6.0/Gemfile +5 -0
- data/gemfiles/rails_6.1/Gemfile +5 -0
- data/gemfiles/rails_7.0/Gemfile +5 -0
- data/gemfiles/rails_7.1/Gemfile +5 -0
- data/gemfiles/rails_edge/Gemfile +13 -0
- data/lib/formtastic/action_class_finder.rb +1 -0
- data/lib/formtastic/actions/base.rb +1 -0
- data/lib/formtastic/actions/button_action.rb +1 -0
- data/lib/formtastic/actions/buttonish.rb +1 -0
- data/lib/formtastic/actions/input_action.rb +1 -0
- data/lib/formtastic/actions/link_action.rb +1 -0
- data/lib/formtastic/actions.rb +7 -3
- data/lib/formtastic/deprecation.rb +2 -38
- data/lib/formtastic/engine.rb +4 -1
- data/lib/formtastic/form_builder.rb +12 -24
- data/lib/formtastic/helpers/action_helper.rb +2 -48
- data/lib/formtastic/helpers/actions_helper.rb +1 -0
- data/lib/formtastic/helpers/enum.rb +14 -0
- data/lib/formtastic/helpers/errors_helper.rb +3 -2
- data/lib/formtastic/helpers/fieldset_wrapper.rb +14 -9
- data/lib/formtastic/helpers/file_column_detection.rb +1 -0
- data/lib/formtastic/helpers/form_helper.rb +2 -1
- data/lib/formtastic/helpers/input_helper.rb +20 -76
- data/lib/formtastic/helpers/inputs_helper.rb +29 -23
- data/lib/formtastic/helpers/reflection.rb +1 -0
- data/lib/formtastic/helpers.rb +2 -2
- data/lib/formtastic/html_attributes.rb +1 -0
- data/lib/formtastic/i18n.rb +2 -1
- data/lib/formtastic/input_class_finder.rb +1 -0
- data/lib/formtastic/inputs/base/associations.rb +1 -0
- data/lib/formtastic/inputs/base/choices.rb +3 -2
- data/lib/formtastic/inputs/base/collections.rb +46 -10
- data/lib/formtastic/inputs/base/database.rb +5 -7
- data/lib/formtastic/inputs/base/datetime_pickerish.rb +1 -0
- data/lib/formtastic/inputs/base/errors.rb +7 -6
- data/lib/formtastic/inputs/base/fileish.rb +1 -0
- data/lib/formtastic/inputs/base/hints.rb +2 -1
- data/lib/formtastic/inputs/base/html.rb +9 -7
- data/lib/formtastic/inputs/base/labelling.rb +3 -2
- data/lib/formtastic/inputs/base/naming.rb +5 -4
- data/lib/formtastic/inputs/base/numeric.rb +1 -0
- data/lib/formtastic/inputs/base/options.rb +3 -3
- data/lib/formtastic/inputs/base/placeholder.rb +1 -0
- data/lib/formtastic/inputs/base/stringish.rb +1 -0
- data/lib/formtastic/inputs/base/timeish.rb +9 -4
- data/lib/formtastic/inputs/base/validations.rb +39 -12
- data/lib/formtastic/inputs/base/wrapping.rb +1 -0
- data/lib/formtastic/inputs/base.rb +3 -2
- data/lib/formtastic/inputs/boolean_input.rb +2 -1
- data/lib/formtastic/inputs/check_boxes_input.rb +15 -6
- data/lib/formtastic/inputs/color_input.rb +1 -1
- data/lib/formtastic/inputs/country_input.rb +4 -1
- data/lib/formtastic/inputs/datalist_input.rb +1 -0
- data/lib/formtastic/inputs/date_picker_input.rb +1 -0
- data/lib/formtastic/inputs/date_select_input.rb +1 -0
- data/lib/formtastic/inputs/datetime_picker_input.rb +1 -0
- data/lib/formtastic/inputs/datetime_select_input.rb +1 -0
- data/lib/formtastic/inputs/email_input.rb +1 -0
- data/lib/formtastic/inputs/file_input.rb +1 -0
- data/lib/formtastic/inputs/hidden_input.rb +3 -2
- data/lib/formtastic/inputs/number_input.rb +1 -0
- data/lib/formtastic/inputs/password_input.rb +1 -0
- data/lib/formtastic/inputs/phone_input.rb +1 -0
- data/lib/formtastic/inputs/radio_input.rb +21 -0
- data/lib/formtastic/inputs/range_input.rb +1 -0
- data/lib/formtastic/inputs/search_input.rb +1 -0
- data/lib/formtastic/inputs/select_input.rb +30 -1
- data/lib/formtastic/inputs/string_input.rb +1 -0
- data/lib/formtastic/inputs/text_input.rb +1 -0
- data/lib/formtastic/inputs/time_picker_input.rb +1 -0
- data/lib/formtastic/inputs/time_select_input.rb +1 -0
- data/lib/formtastic/inputs/time_zone_input.rb +17 -6
- data/lib/formtastic/inputs/url_input.rb +1 -0
- data/lib/formtastic/inputs.rb +33 -29
- data/lib/formtastic/localized_string.rb +1 -0
- data/lib/formtastic/localizer.rb +21 -22
- data/lib/formtastic/namespaced_class_finder.rb +8 -9
- data/lib/formtastic/version.rb +2 -1
- data/lib/formtastic.rb +10 -11
- data/lib/generators/formtastic/form/form_generator.rb +2 -1
- data/lib/generators/formtastic/input/input_generator.rb +47 -0
- data/lib/generators/formtastic/install/install_generator.rb +1 -0
- data/lib/generators/templates/formtastic.rb +15 -13
- data/lib/generators/templates/input.rb +19 -0
- data/sample/basic_inputs.html +1 -1
- data/script/integration-template.rb +73 -0
- data/script/integration.sh +19 -0
- data/spec/action_class_finder_spec.rb +2 -1
- data/spec/actions/button_action_spec.rb +21 -20
- data/spec/actions/generic_action_spec.rb +134 -133
- data/spec/actions/input_action_spec.rb +20 -19
- data/spec/actions/link_action_spec.rb +30 -29
- data/spec/builder/custom_builder_spec.rb +39 -22
- data/spec/builder/error_proc_spec.rb +6 -5
- data/spec/builder/semantic_fields_for_spec.rb +46 -45
- data/spec/fast_spec_helper.rb +13 -0
- data/spec/generators/formtastic/form/form_generator_spec.rb +33 -32
- data/spec/generators/formtastic/input/input_generator_spec.rb +125 -0
- data/spec/generators/formtastic/install/install_generator_spec.rb +10 -9
- data/spec/helpers/action_helper_spec.rb +329 -10
- data/spec/helpers/actions_helper_spec.rb +43 -42
- data/spec/helpers/form_helper_spec.rb +45 -38
- data/spec/helpers/input_helper_spec.rb +976 -2
- data/spec/helpers/inputs_helper_spec.rb +217 -202
- data/spec/helpers/reflection_helper_spec.rb +7 -6
- data/spec/helpers/semantic_errors_helper_spec.rb +26 -25
- data/spec/i18n_spec.rb +30 -29
- data/spec/input_class_finder_spec.rb +2 -1
- data/spec/inputs/base/collections_spec.rb +78 -0
- data/spec/inputs/base/validations_spec.rb +481 -0
- data/spec/inputs/boolean_input_spec.rb +73 -72
- data/spec/inputs/check_boxes_input_spec.rb +169 -121
- data/spec/inputs/color_input_spec.rb +53 -64
- data/spec/inputs/country_input_spec.rb +23 -22
- data/spec/inputs/custom_input_spec.rb +3 -6
- data/spec/inputs/datalist_input_spec.rb +3 -2
- data/spec/inputs/date_picker_input_spec.rb +114 -113
- data/spec/inputs/date_select_input_spec.rb +76 -61
- data/spec/inputs/datetime_picker_input_spec.rb +123 -122
- data/spec/inputs/datetime_select_input_spec.rb +85 -68
- data/spec/inputs/email_input_spec.rb +17 -16
- data/spec/inputs/file_input_spec.rb +18 -17
- data/spec/inputs/hidden_input_spec.rb +32 -31
- data/spec/inputs/include_blank_spec.rb +10 -9
- data/spec/inputs/label_spec.rb +26 -25
- data/spec/inputs/number_input_spec.rb +212 -211
- data/spec/inputs/password_input_spec.rb +17 -16
- data/spec/inputs/phone_input_spec.rb +17 -16
- data/spec/inputs/placeholder_spec.rb +18 -17
- data/spec/inputs/radio_input_spec.rb +92 -65
- data/spec/inputs/range_input_spec.rb +136 -135
- data/spec/inputs/readonly_spec.rb +51 -0
- data/spec/inputs/search_input_spec.rb +16 -15
- data/spec/inputs/select_input_spec.rb +209 -102
- data/spec/inputs/string_input_spec.rb +51 -50
- data/spec/inputs/text_input_spec.rb +34 -33
- data/spec/inputs/time_picker_input_spec.rb +115 -114
- data/spec/inputs/time_select_input_spec.rb +84 -70
- data/spec/inputs/time_zone_input_spec.rb +58 -31
- data/spec/inputs/url_input_spec.rb +17 -16
- data/spec/inputs/with_options_spec.rb +9 -8
- data/spec/localizer_spec.rb +18 -17
- data/spec/namespaced_class_finder_spec.rb +18 -6
- data/spec/schema.rb +22 -0
- data/spec/spec_helper.rb +172 -260
- data/spec/support/custom_macros.rb +74 -76
- data/spec/support/deprecation.rb +2 -1
- data/spec/support/shared_examples.rb +2 -1233
- data/spec/support/specialized_class_finder_shared_example.rb +1 -0
- data/spec/support/test_environment.rb +24 -9
- metadata +78 -170
- data/.travis.yml +0 -29
- data/Appraisals +0 -29
- data/CHANGELOG +0 -39
- data/DEPRECATIONS +0 -49
- data/gemfiles/rails_3.2.gemfile +0 -7
- data/gemfiles/rails_4.0.4.gemfile +0 -7
- data/gemfiles/rails_4.1.gemfile +0 -7
- data/gemfiles/rails_4.2.gemfile +0 -7
- data/gemfiles/rails_4.gemfile +0 -7
- data/gemfiles/rails_edge.gemfile +0 -10
- data/lib/formtastic/util.rb +0 -57
- data/spec/helpers/namespaced_action_helper_spec.rb +0 -43
- data/spec/helpers/namespaced_input_helper_spec.rb +0 -36
- data/spec/support/deferred_garbage_collection.rb +0 -21
- 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 =
|
|
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
|
-
|
|
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
|
-
|
|
301
|
-
|
|
302
|
-
|
|
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
|
-
|
|
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
|
|
187
|
-
# <%= f.inputs
|
|
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
|
|
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
|
|
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 -=
|
|
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
|
-
|
|
386
|
+
yield(f)
|
|
381
387
|
else
|
|
382
388
|
index = parent_child_index(options[:parent]) if options[:parent]
|
|
383
|
-
|
|
389
|
+
yield(f, index)
|
|
384
390
|
end
|
|
385
391
|
end
|
|
386
392
|
template.concat(contents)
|
data/lib/formtastic/helpers.rb
CHANGED
|
@@ -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
|
|
data/lib/formtastic/i18n.rb
CHANGED
|
@@ -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, *
|
|
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
|
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
|
|
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
|
|
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
|
|
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,
|
|
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,
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
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,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,
|
|
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,
|
|
20
|
+
list_elements << template.content_tag(:li, error.html_safe)
|
|
20
21
|
end
|
|
21
|
-
template.content_tag(:ul,
|
|
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,
|
|
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
|
|
@@ -7,7 +8,7 @@ module Formtastic
|
|
|
7
8
|
if hint?
|
|
8
9
|
template.content_tag(
|
|
9
10
|
:p,
|
|
10
|
-
|
|
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
|