simple_form 3.0.1 → 3.1.1
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 +4 -4
- data/CHANGELOG.md +75 -25
- data/MIT-LICENSE +1 -1
- data/README.md +251 -93
- data/lib/generators/simple_form/install_generator.rb +3 -3
- data/lib/generators/simple_form/templates/README +3 -4
- data/lib/generators/simple_form/templates/config/initializers/simple_form.rb +29 -9
- data/lib/generators/simple_form/templates/config/initializers/simple_form_bootstrap.rb +128 -24
- data/lib/generators/simple_form/templates/config/initializers/simple_form_foundation.rb +86 -5
- data/lib/generators/simple_form/templates/config/locales/simple_form.en.yml +7 -2
- data/lib/simple_form/action_view_extensions/builder.rb +1 -0
- data/lib/simple_form/action_view_extensions/form_helper.rb +6 -2
- data/lib/simple_form/components/errors.rb +28 -2
- data/lib/simple_form/components/hints.rb +2 -2
- data/lib/simple_form/components/html5.rb +5 -5
- data/lib/simple_form/components/label_input.rb +20 -2
- data/lib/simple_form/components/labels.rb +11 -7
- data/lib/simple_form/components/maxlength.rb +1 -1
- data/lib/simple_form/components/min_max.rb +1 -1
- data/lib/simple_form/components/pattern.rb +1 -1
- data/lib/simple_form/components/placeholders.rb +2 -2
- data/lib/simple_form/components/readonly.rb +1 -1
- data/lib/simple_form/form_builder.rb +127 -76
- data/lib/simple_form/helpers.rb +5 -5
- data/lib/simple_form/inputs/base.rb +33 -11
- data/lib/simple_form/inputs/block_input.rb +1 -1
- data/lib/simple_form/inputs/boolean_input.rb +28 -15
- data/lib/simple_form/inputs/collection_input.rb +30 -9
- data/lib/simple_form/inputs/collection_radio_buttons_input.rb +7 -12
- data/lib/simple_form/inputs/collection_select_input.rb +4 -2
- data/lib/simple_form/inputs/date_time_input.rb +23 -9
- data/lib/simple_form/inputs/file_input.rb +4 -2
- data/lib/simple_form/inputs/grouped_collection_select_input.rb +15 -3
- data/lib/simple_form/inputs/hidden_input.rb +4 -2
- data/lib/simple_form/inputs/numeric_input.rb +5 -4
- data/lib/simple_form/inputs/password_input.rb +4 -2
- data/lib/simple_form/inputs/priority_input.rb +4 -2
- data/lib/simple_form/inputs/range_input.rb +1 -1
- data/lib/simple_form/inputs/string_input.rb +4 -2
- data/lib/simple_form/inputs/text_input.rb +4 -2
- data/lib/simple_form/railtie.rb +7 -0
- data/lib/simple_form/tags.rb +6 -0
- data/lib/simple_form/version.rb +1 -1
- data/lib/simple_form/wrappers/builder.rb +6 -6
- data/lib/simple_form/wrappers/leaf.rb +28 -0
- data/lib/simple_form/wrappers/many.rb +6 -6
- data/lib/simple_form/wrappers/root.rb +1 -1
- data/lib/simple_form/wrappers/single.rb +5 -3
- data/lib/simple_form/wrappers.rb +1 -0
- data/lib/simple_form.rb +59 -8
- data/test/action_view_extensions/builder_test.rb +36 -36
- data/test/action_view_extensions/form_helper_test.rb +33 -14
- data/test/components/label_test.rb +37 -37
- data/test/form_builder/association_test.rb +52 -35
- data/test/form_builder/button_test.rb +10 -10
- data/test/form_builder/error_notification_test.rb +1 -1
- data/test/form_builder/error_test.rb +144 -24
- data/test/form_builder/general_test.rb +107 -71
- data/test/form_builder/hint_test.rb +18 -18
- data/test/form_builder/input_field_test.rb +86 -56
- data/test/form_builder/label_test.rb +55 -13
- data/test/form_builder/wrapper_test.rb +136 -20
- data/test/generators/simple_form_generator_test.rb +4 -4
- data/test/inputs/boolean_input_test.rb +62 -6
- data/test/inputs/collection_check_boxes_input_test.rb +85 -17
- data/test/inputs/collection_radio_buttons_input_test.rb +134 -36
- data/test/inputs/collection_select_input_test.rb +146 -41
- data/test/inputs/datetime_input_test.rb +120 -48
- data/test/inputs/disabled_test.rb +15 -15
- data/test/inputs/discovery_test.rb +56 -6
- data/test/inputs/file_input_test.rb +2 -2
- data/test/inputs/general_test.rb +20 -20
- data/test/inputs/grouped_collection_select_input_test.rb +44 -8
- data/test/inputs/hidden_input_test.rb +4 -4
- data/test/inputs/numeric_input_test.rb +43 -43
- data/test/inputs/priority_input_test.rb +13 -13
- data/test/inputs/readonly_test.rb +19 -19
- data/test/inputs/required_test.rb +25 -13
- data/test/inputs/string_input_test.rb +50 -30
- data/test/inputs/text_input_test.rb +14 -7
- data/test/simple_form_test.rb +8 -0
- data/test/support/discovery_inputs.rb +32 -2
- data/test/support/misc_helpers.rb +84 -5
- data/test/support/models.rb +60 -24
- data/test/test_helper.rb +11 -1
- metadata +22 -33
|
@@ -8,8 +8,8 @@ module SimpleForm
|
|
|
8
8
|
|
|
9
9
|
# When action is create or update, we still should use new and edit
|
|
10
10
|
ACTIONS = {
|
|
11
|
-
create
|
|
12
|
-
update
|
|
11
|
+
'create' => 'new',
|
|
12
|
+
'update' => 'edit'
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
ATTRIBUTE_COMPONENTS = [:html5, :min_max, :maxlength, :placeholder, :pattern, :readonly]
|
|
@@ -17,19 +17,20 @@ module SimpleForm
|
|
|
17
17
|
extend MapType
|
|
18
18
|
include SimpleForm::Inputs
|
|
19
19
|
|
|
20
|
-
map_type :text,
|
|
21
|
-
map_type :file,
|
|
22
|
-
map_type :string, :email, :search, :tel, :url, to: SimpleForm::Inputs::StringInput
|
|
23
|
-
map_type :password,
|
|
24
|
-
map_type :integer, :decimal, :float,
|
|
25
|
-
map_type :range,
|
|
26
|
-
map_type :check_boxes,
|
|
27
|
-
map_type :radio_buttons,
|
|
28
|
-
map_type :select,
|
|
29
|
-
map_type :grouped_select,
|
|
30
|
-
map_type :date, :time, :datetime,
|
|
31
|
-
map_type :country, :time_zone,
|
|
32
|
-
map_type :boolean,
|
|
20
|
+
map_type :text, to: SimpleForm::Inputs::TextInput
|
|
21
|
+
map_type :file, to: SimpleForm::Inputs::FileInput
|
|
22
|
+
map_type :string, :email, :search, :tel, :url, :uuid, to: SimpleForm::Inputs::StringInput
|
|
23
|
+
map_type :password, to: SimpleForm::Inputs::PasswordInput
|
|
24
|
+
map_type :integer, :decimal, :float, to: SimpleForm::Inputs::NumericInput
|
|
25
|
+
map_type :range, to: SimpleForm::Inputs::RangeInput
|
|
26
|
+
map_type :check_boxes, to: SimpleForm::Inputs::CollectionCheckBoxesInput
|
|
27
|
+
map_type :radio_buttons, to: SimpleForm::Inputs::CollectionRadioButtonsInput
|
|
28
|
+
map_type :select, to: SimpleForm::Inputs::CollectionSelectInput
|
|
29
|
+
map_type :grouped_select, to: SimpleForm::Inputs::GroupedCollectionSelectInput
|
|
30
|
+
map_type :date, :time, :datetime, to: SimpleForm::Inputs::DateTimeInput
|
|
31
|
+
map_type :country, :time_zone, to: SimpleForm::Inputs::PriorityInput
|
|
32
|
+
map_type :boolean, to: SimpleForm::Inputs::BooleanInput
|
|
33
|
+
map_type :hidden, to: SimpleForm::Inputs::HiddenInput
|
|
33
34
|
|
|
34
35
|
def self.discovery_cache
|
|
35
36
|
@discovery_cache ||= {}
|
|
@@ -106,18 +107,13 @@ module SimpleForm
|
|
|
106
107
|
# Some inputs, as :time_zone and :country accepts a :priority option. If none is
|
|
107
108
|
# given SimpleForm.time_zone_priority and SimpleForm.country_priority are used respectively.
|
|
108
109
|
#
|
|
109
|
-
def input(attribute_name, options={}, &block)
|
|
110
|
+
def input(attribute_name, options = {}, &block)
|
|
110
111
|
options = @defaults.deep_dup.deep_merge(options) if @defaults
|
|
111
|
-
input = find_input(attribute_name, options, &block)
|
|
112
112
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
name.respond_to?(:render) ? name : SimpleForm.wrapper(name)
|
|
116
|
-
else
|
|
117
|
-
wrapper
|
|
118
|
-
end
|
|
113
|
+
input = find_input(attribute_name, options, &block)
|
|
114
|
+
wrapper = find_wrapper(input.input_type, options)
|
|
119
115
|
|
|
120
|
-
|
|
116
|
+
wrapper.render input
|
|
121
117
|
end
|
|
122
118
|
alias :attribute :input
|
|
123
119
|
|
|
@@ -135,12 +131,18 @@ module SimpleForm
|
|
|
135
131
|
# <input class="string required" id="user_name" maxlength="100"
|
|
136
132
|
# name="user[name]" type="text" value="Carlos" />
|
|
137
133
|
#
|
|
138
|
-
def input_field(attribute_name, options={})
|
|
134
|
+
def input_field(attribute_name, options = {})
|
|
135
|
+
components = (wrapper.components.map(&:namespace) & ATTRIBUTE_COMPONENTS)
|
|
136
|
+
|
|
139
137
|
options = options.dup
|
|
140
|
-
options[:input_html] = options.except(:as, :collection, :label_method, :value_method, *
|
|
138
|
+
options[:input_html] = options.except(:as, :boolean_style, :collection, :label_method, :value_method, *components)
|
|
141
139
|
options = @defaults.deep_dup.deep_merge(options) if @defaults
|
|
142
140
|
|
|
143
|
-
|
|
141
|
+
input = find_input(attribute_name, options)
|
|
142
|
+
wrapper = find_wrapper(input.input_type, options)
|
|
143
|
+
components = components.concat([:input]).map { |component| SimpleForm::Wrappers::Leaf.new(component) }
|
|
144
|
+
|
|
145
|
+
SimpleForm::Wrappers::Root.new(components, wrapper.options.merge(wrapper: false)).render input
|
|
144
146
|
end
|
|
145
147
|
|
|
146
148
|
# Helper for dealing with association selects/radios, generating the
|
|
@@ -171,7 +173,7 @@ module SimpleForm
|
|
|
171
173
|
#
|
|
172
174
|
# Please note that the association helper is currently only tested with Active Record. Depending on the ORM you are using your mileage may vary.
|
|
173
175
|
#
|
|
174
|
-
def association(association, options={}, &block)
|
|
176
|
+
def association(association, options = {}, &block)
|
|
175
177
|
options = options.dup
|
|
176
178
|
|
|
177
179
|
return simple_fields_for(*[association,
|
|
@@ -183,31 +185,9 @@ module SimpleForm
|
|
|
183
185
|
raise "Association #{association.inspect} not found" unless reflection
|
|
184
186
|
|
|
185
187
|
options[:as] ||= :select
|
|
186
|
-
options[:collection] ||= options
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
reflection.klass.where(conditions).order(reflection.options[:order])
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
attribute = case reflection.macro
|
|
193
|
-
when :belongs_to
|
|
194
|
-
(reflection.respond_to?(:options) && reflection.options[:foreign_key]) || :"#{reflection.name}_id"
|
|
195
|
-
when :has_one
|
|
196
|
-
raise ArgumentError, ":has_one associations are not supported by f.association"
|
|
197
|
-
else
|
|
198
|
-
if options[:as] == :select
|
|
199
|
-
html_options = options[:input_html] ||= {}
|
|
200
|
-
html_options[:multiple] = true unless html_options.key?(:multiple)
|
|
201
|
-
end
|
|
202
|
-
|
|
203
|
-
# Force the association to be preloaded for performance.
|
|
204
|
-
if options[:preload] != false && object.respond_to?(association)
|
|
205
|
-
target = object.send(association)
|
|
206
|
-
target.to_a if target.respond_to?(:to_a)
|
|
207
|
-
end
|
|
208
|
-
|
|
209
|
-
:"#{reflection.name.to_s.singularize}_ids"
|
|
210
|
-
end
|
|
188
|
+
options[:collection] ||= fetch_association_collection(reflection, options)
|
|
189
|
+
|
|
190
|
+
attribute = build_association_attribute(reflection, association, options)
|
|
211
191
|
|
|
212
192
|
input(attribute, options.merge(reflection: reflection))
|
|
213
193
|
end
|
|
@@ -227,8 +207,8 @@ module SimpleForm
|
|
|
227
207
|
options = args.extract_options!.dup
|
|
228
208
|
options[:class] = [SimpleForm.button_class, options[:class]].compact
|
|
229
209
|
args << options
|
|
230
|
-
if respond_to?("#{type}_button")
|
|
231
|
-
send("#{type}_button", *args, &block)
|
|
210
|
+
if respond_to?(:"#{type}_button")
|
|
211
|
+
send(:"#{type}_button", *args, &block)
|
|
232
212
|
else
|
|
233
213
|
send(type, *args, &block)
|
|
234
214
|
end
|
|
@@ -242,7 +222,7 @@ module SimpleForm
|
|
|
242
222
|
# f.error :name
|
|
243
223
|
# f.error :name, id: "cool_error"
|
|
244
224
|
#
|
|
245
|
-
def error(attribute_name, options={})
|
|
225
|
+
def error(attribute_name, options = {})
|
|
246
226
|
options = options.dup
|
|
247
227
|
|
|
248
228
|
options[:error_html] = options.except(:error_tag, :error_prefix, :error_method)
|
|
@@ -259,7 +239,7 @@ module SimpleForm
|
|
|
259
239
|
#
|
|
260
240
|
# f.full_error :token #=> <span class="error">Token is invalid</span>
|
|
261
241
|
#
|
|
262
|
-
def full_error(attribute_name, options={})
|
|
242
|
+
def full_error(attribute_name, options = {})
|
|
263
243
|
options = options.dup
|
|
264
244
|
|
|
265
245
|
options[:error_prefix] ||= if object.class.respond_to?(:human_attribute_name)
|
|
@@ -281,7 +261,7 @@ module SimpleForm
|
|
|
281
261
|
# f.hint :name, id: "cool_hint"
|
|
282
262
|
# f.hint "Don't forget to accept this"
|
|
283
263
|
#
|
|
284
|
-
def hint(attribute_name, options={})
|
|
264
|
+
def hint(attribute_name, options = {})
|
|
285
265
|
options = options.dup
|
|
286
266
|
|
|
287
267
|
options[:hint_html] = options.except(:hint_tag, :hint)
|
|
@@ -332,7 +312,7 @@ module SimpleForm
|
|
|
332
312
|
# f.error_notification message: 'Something went wrong'
|
|
333
313
|
# f.error_notification id: 'user_error_message', class: 'form_error'
|
|
334
314
|
#
|
|
335
|
-
def error_notification(options={})
|
|
315
|
+
def error_notification(options = {})
|
|
336
316
|
SimpleForm::ErrorNotification.new(self, options).render
|
|
337
317
|
end
|
|
338
318
|
|
|
@@ -464,15 +444,56 @@ module SimpleForm
|
|
|
464
444
|
@lookup_action ||= begin
|
|
465
445
|
action = template.controller && template.controller.action_name
|
|
466
446
|
return unless action
|
|
467
|
-
action = action.
|
|
447
|
+
action = action.to_s
|
|
468
448
|
ACTIONS[action] || action
|
|
469
449
|
end
|
|
470
450
|
end
|
|
471
451
|
|
|
472
452
|
private
|
|
473
453
|
|
|
454
|
+
def fetch_association_collection(reflection, options)
|
|
455
|
+
options.fetch(:collection) do
|
|
456
|
+
relation = reflection.klass.all
|
|
457
|
+
|
|
458
|
+
if reflection.respond_to?(:scope) && reflection.scope
|
|
459
|
+
relation = reflection.klass.instance_exec(&reflection.scope)
|
|
460
|
+
else
|
|
461
|
+
order = reflection.options[:order]
|
|
462
|
+
conditions = reflection.options[:conditions]
|
|
463
|
+
conditions = object.instance_exec(&conditions) if conditions.respond_to?(:call)
|
|
464
|
+
|
|
465
|
+
relation = relation.where(conditions)
|
|
466
|
+
relation = relation.order(order) if relation.respond_to?(:order)
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
relation
|
|
470
|
+
end
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
def build_association_attribute(reflection, association, options)
|
|
474
|
+
case reflection.macro
|
|
475
|
+
when :belongs_to
|
|
476
|
+
(reflection.respond_to?(:options) && reflection.options[:foreign_key]) || :"#{reflection.name}_id"
|
|
477
|
+
when :has_one
|
|
478
|
+
raise ArgumentError, ":has_one associations are not supported by f.association"
|
|
479
|
+
else
|
|
480
|
+
if options[:as] == :select
|
|
481
|
+
html_options = options[:input_html] ||= {}
|
|
482
|
+
html_options[:multiple] = true unless html_options.key?(:multiple)
|
|
483
|
+
end
|
|
484
|
+
|
|
485
|
+
# Force the association to be preloaded for performance.
|
|
486
|
+
if options[:preload] != false && object.respond_to?(association)
|
|
487
|
+
target = object.send(association)
|
|
488
|
+
target.to_a if target.respond_to?(:to_a)
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
:"#{reflection.name.to_s.singularize}_ids"
|
|
492
|
+
end
|
|
493
|
+
end
|
|
494
|
+
|
|
474
495
|
# Find an input based on the attribute name.
|
|
475
|
-
def find_input(attribute_name, options={}, &block)
|
|
496
|
+
def find_input(attribute_name, options = {}, &block)
|
|
476
497
|
column = find_attribute_column(attribute_name)
|
|
477
498
|
input_type = default_input_type(attribute_name, column, options)
|
|
478
499
|
|
|
@@ -486,10 +507,10 @@ module SimpleForm
|
|
|
486
507
|
# Attempt to guess the better input type given the defined options. By
|
|
487
508
|
# default alwayls fallback to the user :as option, or to a :select when a
|
|
488
509
|
# collection is given.
|
|
489
|
-
def default_input_type(attribute_name, column, options)
|
|
510
|
+
def default_input_type(attribute_name, column, options)
|
|
490
511
|
return options[:as].to_sym if options[:as]
|
|
491
|
-
return :select if options[:collection]
|
|
492
512
|
custom_type = find_custom_type(attribute_name.to_s) and return custom_type
|
|
513
|
+
return :select if options[:collection]
|
|
493
514
|
|
|
494
515
|
input_type = column.try(:type)
|
|
495
516
|
case input_type
|
|
@@ -511,24 +532,24 @@ module SimpleForm
|
|
|
511
532
|
end
|
|
512
533
|
end
|
|
513
534
|
|
|
514
|
-
def find_custom_type(attribute_name)
|
|
535
|
+
def find_custom_type(attribute_name)
|
|
515
536
|
SimpleForm.input_mappings.find { |match, type|
|
|
516
537
|
attribute_name =~ match
|
|
517
538
|
}.try(:last) if SimpleForm.input_mappings
|
|
518
539
|
end
|
|
519
540
|
|
|
520
|
-
def file_method?(attribute_name)
|
|
541
|
+
def file_method?(attribute_name)
|
|
521
542
|
file = @object.send(attribute_name) if @object.respond_to?(attribute_name)
|
|
522
543
|
file && SimpleForm.file_methods.any? { |m| file.respond_to?(m) }
|
|
523
544
|
end
|
|
524
545
|
|
|
525
|
-
def find_attribute_column(attribute_name)
|
|
526
|
-
if @object.respond_to?(:column_for_attribute)
|
|
546
|
+
def find_attribute_column(attribute_name)
|
|
547
|
+
if @object.respond_to?(:column_for_attribute) && @object.has_attribute?(attribute_name)
|
|
527
548
|
@object.column_for_attribute(attribute_name)
|
|
528
549
|
end
|
|
529
550
|
end
|
|
530
551
|
|
|
531
|
-
def find_association_reflection(association)
|
|
552
|
+
def find_association_reflection(association)
|
|
532
553
|
if @object.class.respond_to?(:reflect_on_association)
|
|
533
554
|
@object.class.reflect_on_association(association)
|
|
534
555
|
end
|
|
@@ -541,24 +562,42 @@ module SimpleForm
|
|
|
541
562
|
# b) Or use the found mapping
|
|
542
563
|
# 2) If not, fallbacks to #{input_type}Input
|
|
543
564
|
# 3) If not, fallbacks to SimpleForm::Inputs::#{input_type}Input
|
|
544
|
-
def find_mapping(input_type)
|
|
565
|
+
def find_mapping(input_type)
|
|
545
566
|
discovery_cache[input_type] ||=
|
|
546
567
|
if mapping = self.class.mappings[input_type]
|
|
547
568
|
mapping_override(mapping) || mapping
|
|
548
569
|
else
|
|
549
570
|
camelized = "#{input_type.to_s.camelize}Input"
|
|
550
|
-
|
|
571
|
+
attempt_mapping_with_custom_namespace(camelized) ||
|
|
572
|
+
attempt_mapping(camelized, Object) ||
|
|
573
|
+
attempt_mapping(camelized, self.class) ||
|
|
551
574
|
raise("No input found for #{input_type}")
|
|
552
575
|
end
|
|
553
576
|
end
|
|
554
577
|
|
|
555
|
-
|
|
556
|
-
|
|
578
|
+
# Attempts to find a wrapper mapping. It follows the following rules:
|
|
579
|
+
#
|
|
580
|
+
# 1) It tries to find a wrapper for the current form
|
|
581
|
+
# 2) If not, it tries to find a config
|
|
582
|
+
def find_wrapper_mapping(input_type)
|
|
583
|
+
if options[:wrapper_mappings] && options[:wrapper_mappings][input_type]
|
|
584
|
+
options[:wrapper_mappings][input_type]
|
|
585
|
+
else
|
|
586
|
+
SimpleForm.wrapper_mappings && SimpleForm.wrapper_mappings[input_type]
|
|
587
|
+
end
|
|
588
|
+
end
|
|
589
|
+
|
|
590
|
+
def find_wrapper(input_type, options)
|
|
591
|
+
if name = options[:wrapper] || find_wrapper_mapping(input_type)
|
|
592
|
+
name.respond_to?(:render) ? name : SimpleForm.wrapper(name)
|
|
593
|
+
else
|
|
594
|
+
wrapper
|
|
595
|
+
end
|
|
557
596
|
end
|
|
558
597
|
|
|
559
598
|
# If cache_discovery is enabled, use the class level cache that persists
|
|
560
599
|
# between requests, otherwise use the instance one.
|
|
561
|
-
def discovery_cache
|
|
600
|
+
def discovery_cache
|
|
562
601
|
if SimpleForm.cache_discovery
|
|
563
602
|
self.class.discovery_cache
|
|
564
603
|
else
|
|
@@ -566,14 +605,16 @@ module SimpleForm
|
|
|
566
605
|
end
|
|
567
606
|
end
|
|
568
607
|
|
|
569
|
-
def mapping_override(klass)
|
|
608
|
+
def mapping_override(klass)
|
|
570
609
|
name = klass.name
|
|
571
610
|
if name =~ /^SimpleForm::Inputs/
|
|
572
|
-
|
|
611
|
+
input_name = name.split("::").last
|
|
612
|
+
attempt_mapping_with_custom_namespace(input_name) ||
|
|
613
|
+
attempt_mapping(input_name, Object)
|
|
573
614
|
end
|
|
574
615
|
end
|
|
575
616
|
|
|
576
|
-
def attempt_mapping(mapping, at)
|
|
617
|
+
def attempt_mapping(mapping, at)
|
|
577
618
|
return if SimpleForm.inputs_discovery == false && at == Object
|
|
578
619
|
|
|
579
620
|
begin
|
|
@@ -582,5 +623,15 @@ module SimpleForm
|
|
|
582
623
|
raise if e.message !~ /#{mapping}$/
|
|
583
624
|
end
|
|
584
625
|
end
|
|
626
|
+
|
|
627
|
+
def attempt_mapping_with_custom_namespace(input_name)
|
|
628
|
+
SimpleForm.custom_inputs_namespaces.each do |namespace|
|
|
629
|
+
if (mapping = attempt_mapping(input_name, namespace.constantize))
|
|
630
|
+
return mapping
|
|
631
|
+
end
|
|
632
|
+
end
|
|
633
|
+
|
|
634
|
+
nil
|
|
635
|
+
end
|
|
585
636
|
end
|
|
586
637
|
end
|
data/lib/simple_form/helpers.rb
CHANGED
|
@@ -3,10 +3,10 @@ module SimpleForm
|
|
|
3
3
|
# For instance, disabled cannot be turned on automatically, it requires the
|
|
4
4
|
# user to explicitly pass the option disabled: true so it may work.
|
|
5
5
|
module Helpers
|
|
6
|
-
autoload :Autofocus,
|
|
7
|
-
autoload :Disabled,
|
|
8
|
-
autoload :Readonly,
|
|
9
|
-
autoload :Required,
|
|
10
|
-
autoload :Validators,
|
|
6
|
+
autoload :Autofocus, 'simple_form/helpers/autofocus'
|
|
7
|
+
autoload :Disabled, 'simple_form/helpers/disabled'
|
|
8
|
+
autoload :Readonly, 'simple_form/helpers/readonly'
|
|
9
|
+
autoload :Required, 'simple_form/helpers/required'
|
|
10
|
+
autoload :Validators, 'simple_form/helpers/validators'
|
|
11
11
|
end
|
|
12
12
|
end
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
require 'simple_form/i18n_cache'
|
|
2
2
|
require 'active_support/core_ext/string/output_safety'
|
|
3
|
+
require 'action_view/helpers'
|
|
3
4
|
|
|
4
5
|
module SimpleForm
|
|
5
6
|
module Inputs
|
|
6
7
|
class Base
|
|
7
8
|
include ERB::Util
|
|
9
|
+
include ActionView::Helpers::TranslationHelper
|
|
8
10
|
|
|
9
11
|
extend I18nCache
|
|
10
12
|
|
|
@@ -53,14 +55,14 @@ module SimpleForm
|
|
|
53
55
|
def initialize(builder, attribute_name, column, input_type, options = {})
|
|
54
56
|
super
|
|
55
57
|
|
|
56
|
-
options
|
|
57
|
-
@builder
|
|
58
|
-
@attribute_name
|
|
59
|
-
@column
|
|
60
|
-
@input_type
|
|
61
|
-
@reflection
|
|
62
|
-
@options
|
|
63
|
-
@required
|
|
58
|
+
options = options.dup
|
|
59
|
+
@builder = builder
|
|
60
|
+
@attribute_name = attribute_name
|
|
61
|
+
@column = column
|
|
62
|
+
@input_type = input_type
|
|
63
|
+
@reflection = options.delete(:reflection)
|
|
64
|
+
@options = options.reverse_merge!(self.class.default_options)
|
|
65
|
+
@required = calculate_required
|
|
64
66
|
|
|
65
67
|
# Notice that html_options_for receives a reference to input_html_classes.
|
|
66
68
|
# This means that classes added dynamically to input_html_classes will
|
|
@@ -79,7 +81,7 @@ module SimpleForm
|
|
|
79
81
|
end
|
|
80
82
|
end
|
|
81
83
|
|
|
82
|
-
def input
|
|
84
|
+
def input(wrapper_options = nil)
|
|
83
85
|
raise NotImplementedError
|
|
84
86
|
end
|
|
85
87
|
|
|
@@ -167,7 +169,7 @@ module SimpleForm
|
|
|
167
169
|
# email: 'E-mail.'
|
|
168
170
|
#
|
|
169
171
|
# Take a look at our locale example file.
|
|
170
|
-
def
|
|
172
|
+
def translate_from_namespace(namespace, default = '')
|
|
171
173
|
model_names = lookup_model_names.dup
|
|
172
174
|
lookups = []
|
|
173
175
|
|
|
@@ -176,13 +178,33 @@ module SimpleForm
|
|
|
176
178
|
model_names.shift
|
|
177
179
|
|
|
178
180
|
lookups << :"#{joined_model_names}.#{lookup_action}.#{reflection_or_attribute_name}"
|
|
181
|
+
lookups << :"#{joined_model_names}.#{lookup_action}.#{reflection_or_attribute_name}_html"
|
|
179
182
|
lookups << :"#{joined_model_names}.#{reflection_or_attribute_name}"
|
|
183
|
+
lookups << :"#{joined_model_names}.#{reflection_or_attribute_name}_html"
|
|
180
184
|
end
|
|
181
185
|
lookups << :"defaults.#{lookup_action}.#{reflection_or_attribute_name}"
|
|
186
|
+
lookups << :"defaults.#{lookup_action}.#{reflection_or_attribute_name}_html"
|
|
182
187
|
lookups << :"defaults.#{reflection_or_attribute_name}"
|
|
188
|
+
lookups << :"defaults.#{reflection_or_attribute_name}_html"
|
|
183
189
|
lookups << default
|
|
184
190
|
|
|
185
|
-
|
|
191
|
+
t(lookups.shift, scope: :"#{i18n_scope}.#{namespace}", default: lookups).presence
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def merge_wrapper_options(options, wrapper_options)
|
|
195
|
+
if wrapper_options
|
|
196
|
+
options.merge(wrapper_options) do |_, oldval, newval|
|
|
197
|
+
if Array === oldval
|
|
198
|
+
oldval + Array(newval)
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
else
|
|
202
|
+
options
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def i18n_scope
|
|
207
|
+
SimpleForm.i18n_scope
|
|
186
208
|
end
|
|
187
209
|
end
|
|
188
210
|
end
|
|
@@ -1,31 +1,36 @@
|
|
|
1
1
|
module SimpleForm
|
|
2
2
|
module Inputs
|
|
3
3
|
class BooleanInput < Base
|
|
4
|
-
def input
|
|
4
|
+
def input(wrapper_options = nil)
|
|
5
|
+
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
|
|
6
|
+
|
|
5
7
|
if nested_boolean_style?
|
|
6
8
|
build_hidden_field_for_checkbox +
|
|
7
|
-
template.label_tag(nil, class:
|
|
8
|
-
build_check_box_without_hidden_field +
|
|
9
|
+
template.label_tag(nil, class: SimpleForm.boolean_label_class) {
|
|
10
|
+
build_check_box_without_hidden_field(merged_input_options) +
|
|
11
|
+
inline_label
|
|
9
12
|
}
|
|
10
13
|
else
|
|
11
|
-
build_check_box
|
|
14
|
+
build_check_box(unchecked_value, merged_input_options)
|
|
12
15
|
end
|
|
13
16
|
end
|
|
14
17
|
|
|
15
|
-
def label_input
|
|
16
|
-
if options[:label] == false
|
|
17
|
-
input
|
|
18
|
+
def label_input(wrapper_options = nil)
|
|
19
|
+
if options[:label] == false || inline_label?
|
|
20
|
+
input(wrapper_options)
|
|
18
21
|
elsif nested_boolean_style?
|
|
19
22
|
html_options = label_html_options.dup
|
|
20
23
|
html_options[:class] ||= []
|
|
21
|
-
html_options[:class].push(
|
|
24
|
+
html_options[:class].push(SimpleForm.boolean_label_class) if SimpleForm.boolean_label_class
|
|
25
|
+
|
|
26
|
+
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
|
|
22
27
|
|
|
23
28
|
build_hidden_field_for_checkbox +
|
|
24
29
|
@builder.label(label_target, html_options) {
|
|
25
|
-
build_check_box_without_hidden_field + label_text
|
|
30
|
+
build_check_box_without_hidden_field(merged_input_options) + label_text
|
|
26
31
|
}
|
|
27
32
|
else
|
|
28
|
-
input + label
|
|
33
|
+
input(wrapper_options) + label(wrapper_options)
|
|
29
34
|
end
|
|
30
35
|
end
|
|
31
36
|
|
|
@@ -35,14 +40,14 @@ module SimpleForm
|
|
|
35
40
|
# reuse the method for nested boolean style, but with no unchecked value,
|
|
36
41
|
# which won't generate the hidden checkbox. This is the default functionality
|
|
37
42
|
# in Rails > 3.2.1, and is backported in SimpleForm AV helpers.
|
|
38
|
-
def build_check_box(unchecked_value
|
|
39
|
-
@builder.check_box(attribute_name,
|
|
43
|
+
def build_check_box(unchecked_value, options)
|
|
44
|
+
@builder.check_box(attribute_name, options, checked_value, unchecked_value)
|
|
40
45
|
end
|
|
41
46
|
|
|
42
47
|
# Build a checkbox without generating the hidden field. See
|
|
43
48
|
# #build_hidden_field_for_checkbox for more info.
|
|
44
|
-
def build_check_box_without_hidden_field
|
|
45
|
-
build_check_box(nil)
|
|
49
|
+
def build_check_box_without_hidden_field(options)
|
|
50
|
+
build_check_box(nil, options)
|
|
46
51
|
end
|
|
47
52
|
|
|
48
53
|
# Create a hidden field for the current checkbox, so we can simulate Rails
|
|
@@ -56,9 +61,17 @@ module SimpleForm
|
|
|
56
61
|
@builder.hidden_field(attribute_name, options)
|
|
57
62
|
end
|
|
58
63
|
|
|
64
|
+
def inline_label?
|
|
65
|
+
nested_boolean_style? && options[:inline_label]
|
|
66
|
+
end
|
|
67
|
+
|
|
59
68
|
def inline_label
|
|
60
69
|
inline_option = options[:inline_label]
|
|
61
|
-
|
|
70
|
+
|
|
71
|
+
if inline_option
|
|
72
|
+
label = inline_option == true ? label_text : html_escape(inline_option)
|
|
73
|
+
" #{label}".html_safe
|
|
74
|
+
end
|
|
62
75
|
end
|
|
63
76
|
|
|
64
77
|
# Booleans are not required by default because in most of the cases
|
|
@@ -12,14 +12,18 @@ module SimpleForm
|
|
|
12
12
|
end
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
def input
|
|
15
|
+
def input(wrapper_options = nil)
|
|
16
16
|
raise NotImplementedError,
|
|
17
17
|
"input should be implemented by classes inheriting from CollectionInput"
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
def input_options
|
|
21
21
|
options = super
|
|
22
|
+
|
|
22
23
|
options[:include_blank] = true unless skip_include_blank?
|
|
24
|
+
translate_option options, :prompt
|
|
25
|
+
translate_option options, :include_blank
|
|
26
|
+
|
|
23
27
|
options
|
|
24
28
|
end
|
|
25
29
|
|
|
@@ -33,7 +37,7 @@ module SimpleForm
|
|
|
33
37
|
end
|
|
34
38
|
|
|
35
39
|
def has_required?
|
|
36
|
-
super && (input_options[:include_blank] || multiple?)
|
|
40
|
+
super && (input_options[:include_blank] || input_options[:prompt] || multiple?)
|
|
37
41
|
end
|
|
38
42
|
|
|
39
43
|
# Check if :include_blank must be included by default.
|
|
@@ -70,13 +74,17 @@ module SimpleForm
|
|
|
70
74
|
elsif collection_includes_basic_objects?(collection_classes)
|
|
71
75
|
{ label: :to_s, value: :to_s }
|
|
72
76
|
else
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
{ label: SimpleForm.collection_label_methods.find { |m| sample.respond_to?(m) },
|
|
76
|
-
value: SimpleForm.collection_value_methods.find { |m| sample.respond_to?(m) } }
|
|
77
|
+
detect_method_from_class(collection_classes)
|
|
77
78
|
end
|
|
78
79
|
end
|
|
79
80
|
|
|
81
|
+
def detect_method_from_class(collection_classes)
|
|
82
|
+
sample = collection.first || collection.last
|
|
83
|
+
|
|
84
|
+
{ label: SimpleForm.collection_label_methods.find { |m| sample.respond_to?(m) },
|
|
85
|
+
value: SimpleForm.collection_value_methods.find { |m| sample.respond_to?(m) } }
|
|
86
|
+
end
|
|
87
|
+
|
|
80
88
|
def detect_collection_classes(some_collection = collection)
|
|
81
89
|
some_collection.map { |e| e.class }.uniq
|
|
82
90
|
end
|
|
@@ -88,14 +96,27 @@ module SimpleForm
|
|
|
88
96
|
end
|
|
89
97
|
|
|
90
98
|
def translate_collection
|
|
91
|
-
if translated_collection =
|
|
99
|
+
if translated_collection = translate_from_namespace(:options)
|
|
92
100
|
@collection = collection.map do |key|
|
|
93
|
-
|
|
101
|
+
html_key = "#{key}_html".to_sym
|
|
102
|
+
|
|
103
|
+
if translated_collection[html_key]
|
|
104
|
+
[translated_collection[html_key].html_safe || key, key.to_s]
|
|
105
|
+
else
|
|
106
|
+
[translated_collection[key] || key, key.to_s]
|
|
107
|
+
end
|
|
94
108
|
end
|
|
95
109
|
true
|
|
96
110
|
end
|
|
97
111
|
end
|
|
112
|
+
|
|
113
|
+
def translate_option(options, key)
|
|
114
|
+
if options[key] == :translate
|
|
115
|
+
namespace = key.to_s.pluralize
|
|
116
|
+
|
|
117
|
+
options[key] = translate_from_namespace(namespace, true)
|
|
118
|
+
end
|
|
119
|
+
end
|
|
98
120
|
end
|
|
99
121
|
end
|
|
100
122
|
end
|
|
101
|
-
|