simple_form 3.0.4 → 3.1.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 +4 -4
- data/CHANGELOG.md +45 -45
- data/MIT-LICENSE +1 -1
- data/README.md +204 -81
- 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 +28 -7
- data/lib/generators/simple_form/templates/config/initializers/simple_form_bootstrap.rb +115 -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 +24 -4
- data/lib/simple_form/components/hints.rb +2 -2
- data/lib/simple_form/components/html5.rb +1 -1
- data/lib/simple_form/components/label_input.rb +20 -2
- data/lib/simple_form/components/labels.rb +10 -6
- 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 +122 -74
- data/lib/simple_form/helpers.rb +5 -5
- data/lib/simple_form/inputs/base.rb +34 -12
- 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 +6 -11
- data/lib/simple_form/inputs/collection_select_input.rb +4 -2
- data/lib/simple_form/inputs/date_time_input.rb +12 -2
- 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 +111 -33
- data/test/form_builder/general_test.rb +89 -64
- data/test/form_builder/hint_test.rb +18 -18
- data/test/form_builder/input_field_test.rb +40 -16
- data/test/form_builder/label_test.rb +45 -13
- data/test/form_builder/wrapper_test.rb +135 -19
- 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 +117 -50
- 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 +13 -13
- data/test/inputs/string_input_test.rb +50 -30
- data/test/inputs/text_input_test.rb +7 -7
- data/test/simple_form_test.rb +8 -0
- data/test/support/discovery_inputs.rb +32 -2
- data/test/support/misc_helpers.rb +71 -5
- data/test/support/models.rb +60 -24
- data/test/test_helper.rb +5 -1
- metadata +5 -5
|
@@ -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,19 @@ 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
33
|
|
|
34
34
|
def self.discovery_cache
|
|
35
35
|
@discovery_cache ||= {}
|
|
@@ -106,18 +106,13 @@ module SimpleForm
|
|
|
106
106
|
# Some inputs, as :time_zone and :country accepts a :priority option. If none is
|
|
107
107
|
# given SimpleForm.time_zone_priority and SimpleForm.country_priority are used respectively.
|
|
108
108
|
#
|
|
109
|
-
def input(attribute_name, options={}, &block)
|
|
109
|
+
def input(attribute_name, options = {}, &block)
|
|
110
110
|
options = @defaults.deep_dup.deep_merge(options) if @defaults
|
|
111
|
-
input = find_input(attribute_name, options, &block)
|
|
112
111
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
name.respond_to?(:render) ? name : SimpleForm.wrapper(name)
|
|
116
|
-
else
|
|
117
|
-
wrapper
|
|
118
|
-
end
|
|
112
|
+
input = find_input(attribute_name, options, &block)
|
|
113
|
+
wrapper = find_wrapper(input.input_type, options)
|
|
119
114
|
|
|
120
|
-
|
|
115
|
+
wrapper.render input
|
|
121
116
|
end
|
|
122
117
|
alias :attribute :input
|
|
123
118
|
|
|
@@ -135,14 +130,18 @@ module SimpleForm
|
|
|
135
130
|
# <input class="string required" id="user_name" maxlength="100"
|
|
136
131
|
# name="user[name]" type="text" value="Carlos" />
|
|
137
132
|
#
|
|
138
|
-
def input_field(attribute_name, options={})
|
|
139
|
-
components = (wrapper.components & ATTRIBUTE_COMPONENTS)
|
|
133
|
+
def input_field(attribute_name, options = {})
|
|
134
|
+
components = (wrapper.components.map(&:namespace) & ATTRIBUTE_COMPONENTS)
|
|
140
135
|
|
|
141
136
|
options = options.dup
|
|
142
|
-
options[:input_html] = options.except(:as, :collection, :label_method, :value_method, *components)
|
|
137
|
+
options[:input_html] = options.except(:as, :boolean_style, :collection, :label_method, :value_method, *components)
|
|
143
138
|
options = @defaults.deep_dup.deep_merge(options) if @defaults
|
|
144
139
|
|
|
145
|
-
|
|
140
|
+
input = find_input(attribute_name, options)
|
|
141
|
+
wrapper = find_wrapper(input.input_type, options)
|
|
142
|
+
components = components.concat([:input]).map { |component| SimpleForm::Wrappers::Leaf.new(component) }
|
|
143
|
+
|
|
144
|
+
SimpleForm::Wrappers::Root.new(components, wrapper.options.merge(wrapper: false)).render input
|
|
146
145
|
end
|
|
147
146
|
|
|
148
147
|
# Helper for dealing with association selects/radios, generating the
|
|
@@ -173,7 +172,7 @@ module SimpleForm
|
|
|
173
172
|
#
|
|
174
173
|
# Please note that the association helper is currently only tested with Active Record. Depending on the ORM you are using your mileage may vary.
|
|
175
174
|
#
|
|
176
|
-
def association(association, options={}, &block)
|
|
175
|
+
def association(association, options = {}, &block)
|
|
177
176
|
options = options.dup
|
|
178
177
|
|
|
179
178
|
return simple_fields_for(*[association,
|
|
@@ -185,31 +184,9 @@ module SimpleForm
|
|
|
185
184
|
raise "Association #{association.inspect} not found" unless reflection
|
|
186
185
|
|
|
187
186
|
options[:as] ||= :select
|
|
188
|
-
options[:collection] ||= options
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
reflection.klass.where(conditions).order(reflection.options[:order])
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
attribute = case reflection.macro
|
|
195
|
-
when :belongs_to
|
|
196
|
-
(reflection.respond_to?(:options) && reflection.options[:foreign_key]) || :"#{reflection.name}_id"
|
|
197
|
-
when :has_one
|
|
198
|
-
raise ArgumentError, ":has_one associations are not supported by f.association"
|
|
199
|
-
else
|
|
200
|
-
if options[:as] == :select
|
|
201
|
-
html_options = options[:input_html] ||= {}
|
|
202
|
-
html_options[:multiple] = true unless html_options.key?(:multiple)
|
|
203
|
-
end
|
|
204
|
-
|
|
205
|
-
# Force the association to be preloaded for performance.
|
|
206
|
-
if options[:preload] != false && object.respond_to?(association)
|
|
207
|
-
target = object.send(association)
|
|
208
|
-
target.to_a if target.respond_to?(:to_a)
|
|
209
|
-
end
|
|
210
|
-
|
|
211
|
-
:"#{reflection.name.to_s.singularize}_ids"
|
|
212
|
-
end
|
|
187
|
+
options[:collection] ||= fetch_association_collection(reflection, options)
|
|
188
|
+
|
|
189
|
+
attribute = build_association_attribute(reflection, association, options)
|
|
213
190
|
|
|
214
191
|
input(attribute, options.merge(reflection: reflection))
|
|
215
192
|
end
|
|
@@ -244,7 +221,7 @@ module SimpleForm
|
|
|
244
221
|
# f.error :name
|
|
245
222
|
# f.error :name, id: "cool_error"
|
|
246
223
|
#
|
|
247
|
-
def error(attribute_name, options={})
|
|
224
|
+
def error(attribute_name, options = {})
|
|
248
225
|
options = options.dup
|
|
249
226
|
|
|
250
227
|
options[:error_html] = options.except(:error_tag, :error_prefix, :error_method)
|
|
@@ -261,7 +238,7 @@ module SimpleForm
|
|
|
261
238
|
#
|
|
262
239
|
# f.full_error :token #=> <span class="error">Token is invalid</span>
|
|
263
240
|
#
|
|
264
|
-
def full_error(attribute_name, options={})
|
|
241
|
+
def full_error(attribute_name, options = {})
|
|
265
242
|
options = options.dup
|
|
266
243
|
|
|
267
244
|
options[:error_prefix] ||= if object.class.respond_to?(:human_attribute_name)
|
|
@@ -283,7 +260,7 @@ module SimpleForm
|
|
|
283
260
|
# f.hint :name, id: "cool_hint"
|
|
284
261
|
# f.hint "Don't forget to accept this"
|
|
285
262
|
#
|
|
286
|
-
def hint(attribute_name, options={})
|
|
263
|
+
def hint(attribute_name, options = {})
|
|
287
264
|
options = options.dup
|
|
288
265
|
|
|
289
266
|
options[:hint_html] = options.except(:hint_tag, :hint)
|
|
@@ -334,7 +311,7 @@ module SimpleForm
|
|
|
334
311
|
# f.error_notification message: 'Something went wrong'
|
|
335
312
|
# f.error_notification id: 'user_error_message', class: 'form_error'
|
|
336
313
|
#
|
|
337
|
-
def error_notification(options={})
|
|
314
|
+
def error_notification(options = {})
|
|
338
315
|
SimpleForm::ErrorNotification.new(self, options).render
|
|
339
316
|
end
|
|
340
317
|
|
|
@@ -466,15 +443,56 @@ module SimpleForm
|
|
|
466
443
|
@lookup_action ||= begin
|
|
467
444
|
action = template.controller && template.controller.action_name
|
|
468
445
|
return unless action
|
|
469
|
-
action = action.
|
|
446
|
+
action = action.to_s
|
|
470
447
|
ACTIONS[action] || action
|
|
471
448
|
end
|
|
472
449
|
end
|
|
473
450
|
|
|
474
451
|
private
|
|
475
452
|
|
|
453
|
+
def fetch_association_collection(reflection, options)
|
|
454
|
+
options.fetch(:collection) do
|
|
455
|
+
relation = reflection.klass.all
|
|
456
|
+
|
|
457
|
+
if reflection.respond_to?(:scope) && reflection.scope
|
|
458
|
+
relation = reflection.klass.instance_exec(&reflection.scope)
|
|
459
|
+
else
|
|
460
|
+
order = reflection.options[:order]
|
|
461
|
+
conditions = reflection.options[:conditions]
|
|
462
|
+
conditions = object.instance_exec(&conditions) if conditions.respond_to?(:call)
|
|
463
|
+
|
|
464
|
+
relation = relation.where(conditions)
|
|
465
|
+
relation = relation.order(order) if relation.respond_to?(:order)
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
relation
|
|
469
|
+
end
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
def build_association_attribute(reflection, association, options)
|
|
473
|
+
case reflection.macro
|
|
474
|
+
when :belongs_to
|
|
475
|
+
(reflection.respond_to?(:options) && reflection.options[:foreign_key]) || :"#{reflection.name}_id"
|
|
476
|
+
when :has_one
|
|
477
|
+
raise ArgumentError, ":has_one associations are not supported by f.association"
|
|
478
|
+
else
|
|
479
|
+
if options[:as] == :select
|
|
480
|
+
html_options = options[:input_html] ||= {}
|
|
481
|
+
html_options[:multiple] = true unless html_options.key?(:multiple)
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
# Force the association to be preloaded for performance.
|
|
485
|
+
if options[:preload] != false && object.respond_to?(association)
|
|
486
|
+
target = object.send(association)
|
|
487
|
+
target.to_a if target.respond_to?(:to_a)
|
|
488
|
+
end
|
|
489
|
+
|
|
490
|
+
:"#{reflection.name.to_s.singularize}_ids"
|
|
491
|
+
end
|
|
492
|
+
end
|
|
493
|
+
|
|
476
494
|
# Find an input based on the attribute name.
|
|
477
|
-
def find_input(attribute_name, options={}, &block)
|
|
495
|
+
def find_input(attribute_name, options = {}, &block)
|
|
478
496
|
column = find_attribute_column(attribute_name)
|
|
479
497
|
input_type = default_input_type(attribute_name, column, options)
|
|
480
498
|
|
|
@@ -488,7 +506,7 @@ module SimpleForm
|
|
|
488
506
|
# Attempt to guess the better input type given the defined options. By
|
|
489
507
|
# default alwayls fallback to the user :as option, or to a :select when a
|
|
490
508
|
# collection is given.
|
|
491
|
-
def default_input_type(attribute_name, column, options)
|
|
509
|
+
def default_input_type(attribute_name, column, options)
|
|
492
510
|
return options[:as].to_sym if options[:as]
|
|
493
511
|
return :select if options[:collection]
|
|
494
512
|
custom_type = find_custom_type(attribute_name.to_s) and return custom_type
|
|
@@ -513,24 +531,24 @@ module SimpleForm
|
|
|
513
531
|
end
|
|
514
532
|
end
|
|
515
533
|
|
|
516
|
-
def find_custom_type(attribute_name)
|
|
534
|
+
def find_custom_type(attribute_name)
|
|
517
535
|
SimpleForm.input_mappings.find { |match, type|
|
|
518
536
|
attribute_name =~ match
|
|
519
537
|
}.try(:last) if SimpleForm.input_mappings
|
|
520
538
|
end
|
|
521
539
|
|
|
522
|
-
def file_method?(attribute_name)
|
|
540
|
+
def file_method?(attribute_name)
|
|
523
541
|
file = @object.send(attribute_name) if @object.respond_to?(attribute_name)
|
|
524
542
|
file && SimpleForm.file_methods.any? { |m| file.respond_to?(m) }
|
|
525
543
|
end
|
|
526
544
|
|
|
527
|
-
def find_attribute_column(attribute_name)
|
|
528
|
-
if @object.respond_to?(:column_for_attribute)
|
|
545
|
+
def find_attribute_column(attribute_name)
|
|
546
|
+
if @object.respond_to?(:column_for_attribute) && @object.has_attribute?(attribute_name)
|
|
529
547
|
@object.column_for_attribute(attribute_name)
|
|
530
548
|
end
|
|
531
549
|
end
|
|
532
550
|
|
|
533
|
-
def find_association_reflection(association)
|
|
551
|
+
def find_association_reflection(association)
|
|
534
552
|
if @object.class.respond_to?(:reflect_on_association)
|
|
535
553
|
@object.class.reflect_on_association(association)
|
|
536
554
|
end
|
|
@@ -543,24 +561,42 @@ module SimpleForm
|
|
|
543
561
|
# b) Or use the found mapping
|
|
544
562
|
# 2) If not, fallbacks to #{input_type}Input
|
|
545
563
|
# 3) If not, fallbacks to SimpleForm::Inputs::#{input_type}Input
|
|
546
|
-
def find_mapping(input_type)
|
|
564
|
+
def find_mapping(input_type)
|
|
547
565
|
discovery_cache[input_type] ||=
|
|
548
566
|
if mapping = self.class.mappings[input_type]
|
|
549
567
|
mapping_override(mapping) || mapping
|
|
550
568
|
else
|
|
551
569
|
camelized = "#{input_type.to_s.camelize}Input"
|
|
552
|
-
|
|
570
|
+
attempt_mapping_with_custom_namespace(camelized) ||
|
|
571
|
+
attempt_mapping(camelized, Object) ||
|
|
572
|
+
attempt_mapping(camelized, self.class) ||
|
|
553
573
|
raise("No input found for #{input_type}")
|
|
554
574
|
end
|
|
555
575
|
end
|
|
556
576
|
|
|
557
|
-
|
|
558
|
-
|
|
577
|
+
# Attempts to find a wrapper mapping. It follows the following rules:
|
|
578
|
+
#
|
|
579
|
+
# 1) It tries to find a wrapper for the current form
|
|
580
|
+
# 2) If not, it tries to find a config
|
|
581
|
+
def find_wrapper_mapping(input_type)
|
|
582
|
+
if options[:wrapper_mappings] && options[:wrapper_mappings][input_type]
|
|
583
|
+
options[:wrapper_mappings][input_type]
|
|
584
|
+
else
|
|
585
|
+
SimpleForm.wrapper_mappings && SimpleForm.wrapper_mappings[input_type]
|
|
586
|
+
end
|
|
587
|
+
end
|
|
588
|
+
|
|
589
|
+
def find_wrapper(input_type, options)
|
|
590
|
+
if name = options[:wrapper] || find_wrapper_mapping(input_type)
|
|
591
|
+
name.respond_to?(:render) ? name : SimpleForm.wrapper(name)
|
|
592
|
+
else
|
|
593
|
+
wrapper
|
|
594
|
+
end
|
|
559
595
|
end
|
|
560
596
|
|
|
561
597
|
# If cache_discovery is enabled, use the class level cache that persists
|
|
562
598
|
# between requests, otherwise use the instance one.
|
|
563
|
-
def discovery_cache
|
|
599
|
+
def discovery_cache
|
|
564
600
|
if SimpleForm.cache_discovery
|
|
565
601
|
self.class.discovery_cache
|
|
566
602
|
else
|
|
@@ -568,14 +604,16 @@ module SimpleForm
|
|
|
568
604
|
end
|
|
569
605
|
end
|
|
570
606
|
|
|
571
|
-
def mapping_override(klass)
|
|
607
|
+
def mapping_override(klass)
|
|
572
608
|
name = klass.name
|
|
573
609
|
if name =~ /^SimpleForm::Inputs/
|
|
574
|
-
|
|
610
|
+
input_name = name.split("::").last
|
|
611
|
+
attempt_mapping_with_custom_namespace(input_name) ||
|
|
612
|
+
attempt_mapping(input_name, Object)
|
|
575
613
|
end
|
|
576
614
|
end
|
|
577
615
|
|
|
578
|
-
def attempt_mapping(mapping, at)
|
|
616
|
+
def attempt_mapping(mapping, at)
|
|
579
617
|
return if SimpleForm.inputs_discovery == false && at == Object
|
|
580
618
|
|
|
581
619
|
begin
|
|
@@ -584,5 +622,15 @@ module SimpleForm
|
|
|
584
622
|
raise if e.message !~ /#{mapping}$/
|
|
585
623
|
end
|
|
586
624
|
end
|
|
625
|
+
|
|
626
|
+
def attempt_mapping_with_custom_namespace(input_name)
|
|
627
|
+
SimpleForm.custom_inputs_namespaces.each do |namespace|
|
|
628
|
+
if (mapping = attempt_mapping(input_name, namespace.constantize))
|
|
629
|
+
return mapping
|
|
630
|
+
end
|
|
631
|
+
end
|
|
632
|
+
|
|
633
|
+
nil
|
|
634
|
+
end
|
|
587
635
|
end
|
|
588
636
|
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
|
|
|
@@ -45,7 +47,7 @@ module SimpleForm
|
|
|
45
47
|
end
|
|
46
48
|
|
|
47
49
|
# Always enabled.
|
|
48
|
-
enable :hint
|
|
50
|
+
enable :hint
|
|
49
51
|
|
|
50
52
|
# Usually disabled, needs to be enabled explicitly passing true as option.
|
|
51
53
|
disable :maxlength, :placeholder, :pattern, :min_max
|
|
@@ -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
|
-
|