simple_form 1.5.2 → 2.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.
- data/CHANGELOG.md +234 -0
- data/MIT-LICENSE +1 -1
- data/README.md +816 -0
- data/lib/generators/simple_form/install_generator.rb +15 -1
- data/lib/generators/simple_form/templates/README +12 -0
- data/lib/generators/simple_form/templates/_form.html.erb +2 -2
- data/lib/generators/simple_form/templates/_form.html.haml +2 -2
- data/lib/generators/simple_form/templates/_form.html.slim +4 -4
- data/lib/generators/simple_form/templates/config/initializers/simple_form.rb.tt +176 -0
- data/lib/simple_form/action_view_extensions/builder.rb +206 -59
- data/lib/simple_form/action_view_extensions/form_helper.rb +30 -23
- data/lib/simple_form/components/errors.rb +6 -24
- data/lib/simple_form/components/hints.rb +7 -21
- data/lib/simple_form/components/html5.rb +26 -0
- data/lib/simple_form/components/labels.rb +22 -14
- data/lib/simple_form/components/maxlength.rb +41 -0
- data/lib/simple_form/components/min_max.rb +49 -0
- data/lib/simple_form/components/pattern.rb +34 -0
- data/lib/simple_form/components/placeholders.rb +5 -17
- data/lib/simple_form/components/readonly.rb +22 -0
- data/lib/simple_form/components.rb +11 -1
- data/lib/simple_form/core_ext/hash.rb +16 -0
- data/lib/simple_form/error_notification.rb +9 -3
- data/lib/simple_form/form_builder.rb +105 -28
- data/lib/simple_form/helpers/autofocus.rb +11 -0
- data/lib/simple_form/helpers/disabled.rb +15 -0
- data/lib/simple_form/helpers/readonly.rb +15 -0
- data/lib/simple_form/helpers/required.rb +10 -11
- data/lib/simple_form/helpers/validators.rb +4 -4
- data/lib/simple_form/helpers.rb +7 -4
- data/lib/simple_form/inputs/base.rb +53 -81
- data/lib/simple_form/inputs/boolean_input.rb +46 -4
- data/lib/simple_form/inputs/collection_check_boxes_input.rb +21 -0
- data/lib/simple_form/inputs/collection_input.rb +27 -13
- data/lib/simple_form/inputs/collection_radio_buttons_input.rb +67 -0
- data/lib/simple_form/inputs/collection_select_input.rb +14 -0
- data/lib/simple_form/inputs/date_time_input.rb +10 -6
- data/lib/simple_form/inputs/grouped_collection_select_input.rb +41 -0
- data/lib/simple_form/inputs/hidden_input.rb +3 -6
- data/lib/simple_form/inputs/numeric_input.rb +3 -51
- data/lib/simple_form/inputs/password_input.rb +1 -2
- data/lib/simple_form/inputs/priority_input.rb +2 -2
- data/lib/simple_form/inputs/range_input.rb +1 -3
- data/lib/simple_form/inputs/string_input.rb +6 -8
- data/lib/simple_form/inputs/text_input.rb +1 -2
- data/lib/simple_form/inputs.rb +17 -13
- data/lib/simple_form/version.rb +1 -1
- data/lib/simple_form/wrappers/builder.rb +103 -0
- data/lib/simple_form/wrappers/many.rb +69 -0
- data/lib/simple_form/wrappers/root.rb +34 -0
- data/lib/simple_form/wrappers/single.rb +18 -0
- data/lib/simple_form/wrappers.rb +8 -0
- data/lib/simple_form.rb +118 -48
- data/test/action_view_extensions/builder_test.rb +285 -102
- data/test/action_view_extensions/form_helper_test.rb +32 -10
- data/test/components/label_test.rb +44 -5
- data/test/form_builder/association_test.rb +177 -0
- data/test/form_builder/button_test.rb +47 -0
- data/test/{error_notification_test.rb → form_builder/error_notification_test.rb} +18 -1
- data/test/form_builder/error_test.rb +121 -0
- data/test/form_builder/general_test.rb +356 -0
- data/test/form_builder/hint_test.rb +123 -0
- data/test/form_builder/input_field_test.rb +63 -0
- data/test/form_builder/label_test.rb +65 -0
- data/test/form_builder/wrapper_test.rb +149 -0
- data/test/generators/simple_form_generator_test.rb +32 -0
- data/test/inputs/boolean_input_test.rb +101 -0
- data/test/inputs/collection_check_boxes_input_test.rb +224 -0
- data/test/inputs/collection_radio_buttons_input_test.rb +326 -0
- data/test/inputs/collection_select_input_test.rb +241 -0
- data/test/inputs/datetime_input_test.rb +99 -0
- data/test/inputs/disabled_test.rb +38 -0
- data/test/inputs/discovery_test.rb +61 -0
- data/test/inputs/file_input_test.rb +16 -0
- data/test/inputs/general_test.rb +69 -0
- data/test/inputs/grouped_collection_select_input_test.rb +118 -0
- data/test/inputs/hidden_input_test.rb +30 -0
- data/test/inputs/numeric_input_test.rb +167 -0
- data/test/inputs/priority_input_test.rb +43 -0
- data/test/inputs/readonly_test.rb +61 -0
- data/test/inputs/required_test.rb +113 -0
- data/test/inputs/string_input_test.rb +140 -0
- data/test/inputs/text_input_test.rb +24 -0
- data/test/support/misc_helpers.rb +53 -12
- data/test/support/mock_controller.rb +2 -2
- data/test/support/models.rb +20 -5
- data/test/test_helper.rb +11 -12
- metadata +124 -96
- data/.gitignore +0 -3
- data/.gitmodules +0 -3
- data/.travis.yml +0 -15
- data/CHANGELOG.rdoc +0 -159
- data/Gemfile +0 -9
- data/README.rdoc +0 -466
- data/Rakefile +0 -27
- data/lib/generators/simple_form/templates/config/initializers/simple_form.rb +0 -93
- data/lib/simple_form/components/wrapper.rb +0 -38
- data/lib/simple_form/helpers/has_errors.rb +0 -15
- data/lib/simple_form/helpers/maxlength.rb +0 -24
- data/lib/simple_form/helpers/pattern.rb +0 -28
- data/simple_form.gemspec +0 -25
- data/test/components/error_test.rb +0 -56
- data/test/components/hint_test.rb +0 -74
- data/test/components/wrapper_test.rb +0 -63
- data/test/custom_components.rb +0 -7
- data/test/form_builder_test.rb +0 -930
- data/test/inputs_test.rb +0 -995
- /data/test/{discovery_inputs.rb → support/discovery_inputs.rb} +0 -0
|
@@ -1,6 +1,14 @@
|
|
|
1
|
+
require 'simple_form/core_ext/hash'
|
|
2
|
+
|
|
1
3
|
module SimpleForm
|
|
2
4
|
class FormBuilder < ActionView::Helpers::FormBuilder
|
|
3
|
-
attr_reader :template, :object_name, :object
|
|
5
|
+
attr_reader :template, :object_name, :object, :wrapper
|
|
6
|
+
|
|
7
|
+
# When action is create or update, we still should use new and edit
|
|
8
|
+
ACTIONS = {
|
|
9
|
+
:create => :new,
|
|
10
|
+
:update => :edit
|
|
11
|
+
}
|
|
4
12
|
|
|
5
13
|
extend MapType
|
|
6
14
|
include SimpleForm::Inputs
|
|
@@ -11,7 +19,10 @@ module SimpleForm
|
|
|
11
19
|
map_type :password, :to => SimpleForm::Inputs::PasswordInput
|
|
12
20
|
map_type :integer, :decimal, :float, :to => SimpleForm::Inputs::NumericInput
|
|
13
21
|
map_type :range, :to => SimpleForm::Inputs::RangeInput
|
|
14
|
-
map_type :
|
|
22
|
+
map_type :check_boxes, :to => SimpleForm::Inputs::CollectionCheckBoxesInput
|
|
23
|
+
map_type :radio_buttons, :to => SimpleForm::Inputs::CollectionRadioButtonsInput
|
|
24
|
+
map_type :select, :to => SimpleForm::Inputs::CollectionSelectInput
|
|
25
|
+
map_type :grouped_select, :to => SimpleForm::Inputs::GroupedCollectionSelectInput
|
|
15
26
|
map_type :date, :time, :datetime, :to => SimpleForm::Inputs::DateTimeInput
|
|
16
27
|
map_type :country, :time_zone, :to => SimpleForm::Inputs::PriorityInput
|
|
17
28
|
map_type :boolean, :to => SimpleForm::Inputs::BooleanInput
|
|
@@ -20,6 +31,12 @@ module SimpleForm
|
|
|
20
31
|
@discovery_cache ||= {}
|
|
21
32
|
end
|
|
22
33
|
|
|
34
|
+
def initialize(*) #:nodoc:
|
|
35
|
+
super
|
|
36
|
+
@defaults = options[:defaults]
|
|
37
|
+
@wrapper = SimpleForm.wrapper(options[:wrapper] || SimpleForm.default_wrapper)
|
|
38
|
+
end
|
|
39
|
+
|
|
23
40
|
# Basic input helper, combines all components in the stack to generate
|
|
24
41
|
# input html based on options the user define and some guesses through
|
|
25
42
|
# database column information. By default a call to input will generate
|
|
@@ -71,8 +88,8 @@ module SimpleForm
|
|
|
71
88
|
#
|
|
72
89
|
# == Collection
|
|
73
90
|
#
|
|
74
|
-
# When playing with collections (:
|
|
75
|
-
# options:
|
|
91
|
+
# When playing with collections (:radio_buttons, :check_boxes and :select
|
|
92
|
+
# inputs), you have three extra options:
|
|
76
93
|
#
|
|
77
94
|
# :collection => use to determine the collection to generate the radio or select
|
|
78
95
|
#
|
|
@@ -86,14 +103,16 @@ module SimpleForm
|
|
|
86
103
|
# given SimpleForm.time_zone_priority and SimpleForm.country_priority are used respectivelly.
|
|
87
104
|
#
|
|
88
105
|
def input(attribute_name, options={}, &block)
|
|
89
|
-
|
|
90
|
-
input_type = default_input_type(attribute_name, column, options)
|
|
106
|
+
options = @defaults.deep_dup.deep_merge(options) if @defaults
|
|
91
107
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
108
|
+
chosen =
|
|
109
|
+
if name = options[:wrapper]
|
|
110
|
+
name.respond_to?(:render) ? name : SimpleForm.wrapper(name)
|
|
111
|
+
else
|
|
112
|
+
wrapper
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
chosen.render find_input(attribute_name, options, &block)
|
|
97
116
|
end
|
|
98
117
|
alias :attribute :input
|
|
99
118
|
|
|
@@ -112,12 +131,9 @@ module SimpleForm
|
|
|
112
131
|
# name="user[name]" size="100" type="text" value="Carlos" />
|
|
113
132
|
#
|
|
114
133
|
def input_field(attribute_name, options={})
|
|
115
|
-
|
|
116
|
-
input_type = default_input_type(attribute_name, column, options)
|
|
117
|
-
|
|
134
|
+
options = options.dup
|
|
118
135
|
options[:input_html] = options.except(:as, :collection, :label_method, :value_method)
|
|
119
|
-
|
|
120
|
-
find_mapping(input_type).new(self, attribute_name, column, input_type, options).input
|
|
136
|
+
SimpleForm::Wrappers::Root.new([:input], :wrapper => false).render find_input(attribute_name, options)
|
|
121
137
|
end
|
|
122
138
|
|
|
123
139
|
# Helper for dealing with association selects/radios, generating the
|
|
@@ -147,6 +163,8 @@ module SimpleForm
|
|
|
147
163
|
# From the options above, only :collection can also be supplied.
|
|
148
164
|
#
|
|
149
165
|
def association(association, options={}, &block)
|
|
166
|
+
options = options.dup
|
|
167
|
+
|
|
150
168
|
return simple_fields_for(*[association,
|
|
151
169
|
options.delete(:collection), options].compact, &block) if block_given?
|
|
152
170
|
|
|
@@ -160,7 +178,7 @@ module SimpleForm
|
|
|
160
178
|
|
|
161
179
|
attribute = case reflection.macro
|
|
162
180
|
when :belongs_to
|
|
163
|
-
reflection.options[:foreign_key] || :"#{reflection.name}_id"
|
|
181
|
+
(reflection.respond_to?(:options) && reflection.options[:foreign_key]) || :"#{reflection.name}_id"
|
|
164
182
|
when :has_one
|
|
165
183
|
raise ":has_one associations are not supported by f.association"
|
|
166
184
|
else
|
|
@@ -188,11 +206,15 @@ module SimpleForm
|
|
|
188
206
|
# f.button :submit
|
|
189
207
|
# end
|
|
190
208
|
#
|
|
191
|
-
# It just acts as a proxy to method name given.
|
|
209
|
+
# It just acts as a proxy to method name given. We also alias original Rails
|
|
210
|
+
# button implementation (3.2 forward (to delegate to the original when
|
|
211
|
+
# calling `f.button :button`.
|
|
192
212
|
#
|
|
213
|
+
# TODO: remove if condition when supporting only Rails 3.2 forward.
|
|
214
|
+
alias_method :button_button, :button if method_defined?(:button)
|
|
193
215
|
def button(type, *args, &block)
|
|
194
|
-
options = args.extract_options
|
|
195
|
-
options[:class] =
|
|
216
|
+
options = args.extract_options!.dup
|
|
217
|
+
options[:class] = [SimpleForm.button_class, options[:class]].compact
|
|
196
218
|
args << options
|
|
197
219
|
if respond_to?("#{type}_button")
|
|
198
220
|
send("#{type}_button", *args, &block)
|
|
@@ -210,10 +232,13 @@ module SimpleForm
|
|
|
210
232
|
# f.error :name, :id => "cool_error"
|
|
211
233
|
#
|
|
212
234
|
def error(attribute_name, options={})
|
|
235
|
+
options = options.dup
|
|
236
|
+
|
|
213
237
|
options[:error_html] = options.except(:error_tag, :error_prefix, :error_method)
|
|
214
238
|
column = find_attribute_column(attribute_name)
|
|
215
239
|
input_type = default_input_type(attribute_name, column, options)
|
|
216
|
-
|
|
240
|
+
wrapper.find(:error).
|
|
241
|
+
render(SimpleForm::Inputs::Base.new(self, attribute_name, column, input_type, options))
|
|
217
242
|
end
|
|
218
243
|
|
|
219
244
|
# Return the error but also considering its name. This is used
|
|
@@ -224,6 +249,8 @@ module SimpleForm
|
|
|
224
249
|
# f.full_error :token #=> <span class="error">Token is invalid</span>
|
|
225
250
|
#
|
|
226
251
|
def full_error(attribute_name, options={})
|
|
252
|
+
options = options.dup
|
|
253
|
+
|
|
227
254
|
options[:error_prefix] ||= if object.class.respond_to?(:human_attribute_name)
|
|
228
255
|
object.class.human_attribute_name(attribute_name.to_s)
|
|
229
256
|
else
|
|
@@ -244,7 +271,9 @@ module SimpleForm
|
|
|
244
271
|
# f.hint "Don't forget to accept this"
|
|
245
272
|
#
|
|
246
273
|
def hint(attribute_name, options={})
|
|
247
|
-
options
|
|
274
|
+
options = options.dup
|
|
275
|
+
|
|
276
|
+
options[:hint_html] = options.except(:hint_tag, :hint)
|
|
248
277
|
if attribute_name.is_a?(String)
|
|
249
278
|
options[:hint] = attribute_name
|
|
250
279
|
attribute_name, column, input_type = nil, nil, nil
|
|
@@ -252,7 +281,9 @@ module SimpleForm
|
|
|
252
281
|
column = find_attribute_column(attribute_name)
|
|
253
282
|
input_type = default_input_type(attribute_name, column, options)
|
|
254
283
|
end
|
|
255
|
-
|
|
284
|
+
|
|
285
|
+
wrapper.find(:hint).
|
|
286
|
+
render(SimpleForm::Inputs::Base.new(self, attribute_name, column, input_type, options))
|
|
256
287
|
end
|
|
257
288
|
|
|
258
289
|
# Creates a default label tag for the given attribute. You can give a label
|
|
@@ -270,10 +301,10 @@ module SimpleForm
|
|
|
270
301
|
#
|
|
271
302
|
def label(attribute_name, *args)
|
|
272
303
|
return super if args.first.is_a?(String) || block_given?
|
|
273
|
-
|
|
274
|
-
options
|
|
275
|
-
options[:
|
|
276
|
-
|
|
304
|
+
|
|
305
|
+
options = args.extract_options!.dup
|
|
306
|
+
options[:label_html] = options.except(:label, :required)
|
|
307
|
+
|
|
277
308
|
column = find_attribute_column(attribute_name)
|
|
278
309
|
input_type = default_input_type(attribute_name, column, options)
|
|
279
310
|
SimpleForm::Inputs::Base.new(self, attribute_name, column, input_type, options).label
|
|
@@ -294,7 +325,53 @@ module SimpleForm
|
|
|
294
325
|
SimpleForm::ErrorNotification.new(self, options).render
|
|
295
326
|
end
|
|
296
327
|
|
|
297
|
-
|
|
328
|
+
# Extract the model names from the object_name mess, ignoring numeric and
|
|
329
|
+
# explicit child indexes.
|
|
330
|
+
#
|
|
331
|
+
# Example:
|
|
332
|
+
#
|
|
333
|
+
# route[blocks_attributes][0][blocks_learning_object_attributes][1][foo_attributes]
|
|
334
|
+
# ["route", "blocks", "blocks_learning_object", "foo"]
|
|
335
|
+
#
|
|
336
|
+
def lookup_model_names
|
|
337
|
+
@lookup_model_names ||= begin
|
|
338
|
+
child_index = options[:child_index]
|
|
339
|
+
names = object_name.to_s.scan(/([a-zA-Z_]+)/).flatten
|
|
340
|
+
names.delete(child_index) if child_index
|
|
341
|
+
names.each { |name| name.gsub!('_attributes', '') }
|
|
342
|
+
names.freeze
|
|
343
|
+
end
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
# The action to be used in lookup.
|
|
347
|
+
def lookup_action
|
|
348
|
+
@lookup_action ||= begin
|
|
349
|
+
action = template.controller.action_name
|
|
350
|
+
return unless action
|
|
351
|
+
action = action.to_sym
|
|
352
|
+
ACTIONS[action] || action
|
|
353
|
+
end
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
private
|
|
357
|
+
|
|
358
|
+
# Find an input based on the attribute name.
|
|
359
|
+
def find_input(attribute_name, options={}, &block) #:nodoc:
|
|
360
|
+
column = find_attribute_column(attribute_name)
|
|
361
|
+
input_type = default_input_type(attribute_name, column, options)
|
|
362
|
+
|
|
363
|
+
if input_type == :radio
|
|
364
|
+
SimpleForm.deprecation_warn "Using `:as => :radio` as input type is " \
|
|
365
|
+
"deprecated, please change it to `:as => :radio_buttons`."
|
|
366
|
+
input_type = :radio_buttons
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
if block_given?
|
|
370
|
+
SimpleForm::Inputs::BlockInput.new(self, attribute_name, column, input_type, options, &block)
|
|
371
|
+
else
|
|
372
|
+
find_mapping(input_type).new(self, attribute_name, column, input_type, options)
|
|
373
|
+
end
|
|
374
|
+
end
|
|
298
375
|
|
|
299
376
|
# Attempt to guess the better input type given the defined options. By
|
|
300
377
|
# default alwayls fallback to the user :as option, or to a :select when a
|
|
@@ -3,7 +3,7 @@ module SimpleForm
|
|
|
3
3
|
module Required
|
|
4
4
|
private
|
|
5
5
|
|
|
6
|
-
def
|
|
6
|
+
def required_field?
|
|
7
7
|
@required
|
|
8
8
|
end
|
|
9
9
|
|
|
@@ -11,26 +11,25 @@ module SimpleForm
|
|
|
11
11
|
if !options[:required].nil?
|
|
12
12
|
options[:required]
|
|
13
13
|
elsif has_validators?
|
|
14
|
-
|
|
15
|
-
v.kind == :presence && valid_validator?(v)
|
|
16
|
-
end
|
|
14
|
+
required_by_validators?
|
|
17
15
|
else
|
|
18
|
-
|
|
16
|
+
required_by_default?
|
|
19
17
|
end
|
|
20
18
|
end
|
|
21
19
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
attribute_required? && SimpleForm.html5 && SimpleForm.browser_validations
|
|
20
|
+
def required_by_validators?
|
|
21
|
+
(attribute_validators + reflection_validators).any? { |v| v.kind == :presence && valid_validator?(v) }
|
|
25
22
|
end
|
|
26
23
|
|
|
27
|
-
def
|
|
24
|
+
def required_by_default?
|
|
28
25
|
SimpleForm.required_by_default
|
|
29
26
|
end
|
|
30
27
|
|
|
28
|
+
# Do not use has_required? because we want to add the class
|
|
29
|
+
# regardless of the required option.
|
|
31
30
|
def required_class
|
|
32
|
-
|
|
31
|
+
required_field? ? :required : :optional
|
|
33
32
|
end
|
|
34
33
|
end
|
|
35
34
|
end
|
|
36
|
-
end
|
|
35
|
+
end
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
module SimpleForm
|
|
2
2
|
module Helpers
|
|
3
3
|
module Validators
|
|
4
|
-
private
|
|
5
|
-
|
|
6
4
|
def has_validators?
|
|
7
|
-
attribute_name && object.class.respond_to?(:validators_on)
|
|
5
|
+
@has_validators ||= attribute_name && object.class.respond_to?(:validators_on)
|
|
8
6
|
end
|
|
9
7
|
|
|
8
|
+
private
|
|
9
|
+
|
|
10
10
|
def attribute_validators
|
|
11
11
|
object.class.validators_on(attribute_name)
|
|
12
12
|
end
|
|
@@ -37,7 +37,7 @@ module SimpleForm
|
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
def find_validator(validator)
|
|
40
|
-
attribute_validators.find { |v| validator === v }
|
|
40
|
+
attribute_validators.find { |v| validator === v } if has_validators?
|
|
41
41
|
end
|
|
42
42
|
end
|
|
43
43
|
end
|
data/lib/simple_form/helpers.rb
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
module SimpleForm
|
|
2
|
+
# Helpers are made of several helpers that cannot be turned on automatically.
|
|
3
|
+
# For instance, disabled cannot be turned on automatically, it requires the
|
|
4
|
+
# user to explicitly pass the option :disabled => true so it may work.
|
|
2
5
|
module Helpers
|
|
3
|
-
autoload :
|
|
4
|
-
autoload :
|
|
5
|
-
autoload :
|
|
6
|
-
autoload :Validators, 'simple_form/helpers/validators'
|
|
6
|
+
autoload :Autofocus, 'simple_form/helpers/autofocus'
|
|
7
|
+
autoload :Disabled, 'simple_form/helpers/disabled'
|
|
8
|
+
autoload :Readonly, 'simple_form/helpers/readonly'
|
|
7
9
|
autoload :Required, 'simple_form/helpers/required'
|
|
10
|
+
autoload :Validators, 'simple_form/helpers/validators'
|
|
8
11
|
end
|
|
9
12
|
end
|
|
@@ -3,49 +3,71 @@ module SimpleForm
|
|
|
3
3
|
class Base
|
|
4
4
|
extend I18nCache
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
:update => :edit
|
|
10
|
-
}
|
|
11
|
-
|
|
6
|
+
include SimpleForm::Helpers::Autofocus
|
|
7
|
+
include SimpleForm::Helpers::Disabled
|
|
8
|
+
include SimpleForm::Helpers::Readonly
|
|
12
9
|
include SimpleForm::Helpers::Required
|
|
13
10
|
include SimpleForm::Helpers::Validators
|
|
14
|
-
include SimpleForm::Helpers::Maxlength
|
|
15
|
-
include SimpleForm::Helpers::Pattern
|
|
16
11
|
|
|
17
12
|
include SimpleForm::Components::Errors
|
|
18
13
|
include SimpleForm::Components::Hints
|
|
14
|
+
include SimpleForm::Components::HTML5
|
|
19
15
|
include SimpleForm::Components::LabelInput
|
|
16
|
+
include SimpleForm::Components::Maxlength
|
|
17
|
+
include SimpleForm::Components::MinMax
|
|
18
|
+
include SimpleForm::Components::Pattern
|
|
20
19
|
include SimpleForm::Components::Placeholders
|
|
21
|
-
include SimpleForm::Components::
|
|
20
|
+
include SimpleForm::Components::Readonly
|
|
21
|
+
|
|
22
|
+
attr_reader :attribute_name, :column, :input_type, :reflection,
|
|
23
|
+
:options, :input_html_options, :input_html_classes, :html_classes
|
|
24
|
+
|
|
25
|
+
delegate :template, :object, :object_name, :lookup_model_names, :lookup_action, :to => :@builder
|
|
22
26
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
27
|
+
class_attribute :default_options
|
|
28
|
+
self.default_options = {}
|
|
29
|
+
|
|
30
|
+
def self.enable(*keys)
|
|
31
|
+
options = self.default_options.dup
|
|
32
|
+
keys.each { |key| options.delete(key) }
|
|
33
|
+
self.default_options = options
|
|
26
34
|
end
|
|
27
35
|
|
|
28
|
-
def self.disable(*
|
|
29
|
-
|
|
36
|
+
def self.disable(*keys)
|
|
37
|
+
options = self.default_options.dup
|
|
38
|
+
keys.each { |key| options[key] = false }
|
|
39
|
+
self.default_options = options
|
|
30
40
|
end
|
|
31
41
|
|
|
32
|
-
|
|
33
|
-
|
|
42
|
+
# Always enabled.
|
|
43
|
+
enable :hint
|
|
34
44
|
|
|
35
|
-
|
|
45
|
+
# Usually disabled, needs to be enabled explicitly passing true as option.
|
|
46
|
+
disable :maxlength, :placeholder, :pattern, :min_max
|
|
36
47
|
|
|
37
48
|
def initialize(builder, attribute_name, column, input_type, options = {})
|
|
49
|
+
super
|
|
50
|
+
|
|
38
51
|
@builder = builder
|
|
39
52
|
@attribute_name = attribute_name
|
|
40
53
|
@column = column
|
|
41
54
|
@input_type = input_type
|
|
42
55
|
@reflection = options.delete(:reflection)
|
|
43
|
-
@options = options
|
|
56
|
+
@options = options.reverse_merge!(self.class.default_options)
|
|
44
57
|
@required = calculate_required
|
|
58
|
+
|
|
59
|
+
# Notice that html_options_for receives a reference to input_html_classes.
|
|
60
|
+
# This means that classes added dynamically to input_html_classes will
|
|
61
|
+
# still propagate to input_html_options.
|
|
62
|
+
@html_classes = SimpleForm.additional_classes_for(:input) {
|
|
63
|
+
[input_type, required_class, readonly_class, disabled_class].compact
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@input_html_classes = @html_classes.dup
|
|
45
67
|
@input_html_options = html_options_for(:input, input_html_classes).tap do |o|
|
|
46
|
-
o[:
|
|
47
|
-
o[:disabled] = true if
|
|
48
|
-
o[:autofocus] = true if has_autofocus?
|
|
68
|
+
o[:readonly] = true if has_readonly?
|
|
69
|
+
o[:disabled] = true if has_disabled?
|
|
70
|
+
o[:autofocus] = true if has_autofocus?
|
|
49
71
|
end
|
|
50
72
|
end
|
|
51
73
|
|
|
@@ -57,20 +79,6 @@ module SimpleForm
|
|
|
57
79
|
options
|
|
58
80
|
end
|
|
59
81
|
|
|
60
|
-
def input_html_classes
|
|
61
|
-
[input_type, required_class]
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def render
|
|
65
|
-
content = "".html_safe
|
|
66
|
-
components_list.each do |component|
|
|
67
|
-
next if options[component] == false
|
|
68
|
-
rendered = send(component)
|
|
69
|
-
content.safe_concat rendered.to_s if rendered
|
|
70
|
-
end
|
|
71
|
-
wrap(content)
|
|
72
|
-
end
|
|
73
|
-
|
|
74
82
|
private
|
|
75
83
|
|
|
76
84
|
def add_size!
|
|
@@ -81,17 +89,8 @@ module SimpleForm
|
|
|
81
89
|
column && column.limit
|
|
82
90
|
end
|
|
83
91
|
|
|
84
|
-
def
|
|
85
|
-
|
|
86
|
-
ActiveSupport::Deprecation.warn "The option :components of f.input is deprecated. Please turn off each component individually instead."
|
|
87
|
-
components
|
|
88
|
-
else
|
|
89
|
-
SimpleForm.components
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
def has_autofocus?
|
|
94
|
-
options[:autofocus]
|
|
92
|
+
def nested_boolean_style?
|
|
93
|
+
options.fetch(:boolean_style, SimpleForm.boolean_style) == :nested
|
|
95
94
|
end
|
|
96
95
|
|
|
97
96
|
# Find reflection name when available, otherwise use attribute
|
|
@@ -100,22 +99,19 @@ module SimpleForm
|
|
|
100
99
|
end
|
|
101
100
|
|
|
102
101
|
# Retrieve options for the given namespace from the options hash
|
|
103
|
-
def html_options_for(namespace,
|
|
102
|
+
def html_options_for(namespace, css_classes)
|
|
104
103
|
html_options = options[:"#{namespace}_html"] || {}
|
|
105
|
-
|
|
104
|
+
css_classes << html_options[:class] if html_options.key?(:class)
|
|
105
|
+
html_options[:class] = css_classes
|
|
106
106
|
html_options
|
|
107
107
|
end
|
|
108
108
|
|
|
109
|
-
def disabled?
|
|
110
|
-
options[:disabled] == true
|
|
111
|
-
end
|
|
112
|
-
|
|
113
109
|
# Lookup translations for the given namespace using I18n, based on object name,
|
|
114
110
|
# actual action and attribute name. Lookup priority as follows:
|
|
115
111
|
#
|
|
116
112
|
# simple_form.{namespace}.{model}.{action}.{attribute}
|
|
117
113
|
# simple_form.{namespace}.{model}.{attribute}
|
|
118
|
-
# simple_form.{namespace}.{attribute}
|
|
114
|
+
# simple_form.{namespace}.defaults.{attribute}
|
|
119
115
|
#
|
|
120
116
|
# Namespace is used for :labels and :hints.
|
|
121
117
|
#
|
|
@@ -130,7 +126,7 @@ module SimpleForm
|
|
|
130
126
|
# simple_form.{namespace}.{model}.{nested}.{attribute}
|
|
131
127
|
# simple_form.{namespace}.{nested}.{action}.{attribute}
|
|
132
128
|
# simple_form.{namespace}.{nested}.{attribute}
|
|
133
|
-
# simple_form.{namespace}.{attribute}
|
|
129
|
+
# simple_form.{namespace}.defaults.{attribute}
|
|
134
130
|
#
|
|
135
131
|
# Example:
|
|
136
132
|
#
|
|
@@ -144,9 +140,7 @@ module SimpleForm
|
|
|
144
140
|
#
|
|
145
141
|
# Take a look at our locale example file.
|
|
146
142
|
def translate(namespace, default='')
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
model_names = lookup_model_names
|
|
143
|
+
model_names = lookup_model_names.dup
|
|
150
144
|
lookups = []
|
|
151
145
|
|
|
152
146
|
while !model_names.empty?
|
|
@@ -156,34 +150,12 @@ module SimpleForm
|
|
|
156
150
|
lookups << :"#{joined_model_names}.#{lookup_action}.#{reflection_or_attribute_name}"
|
|
157
151
|
lookups << :"#{joined_model_names}.#{reflection_or_attribute_name}"
|
|
158
152
|
end
|
|
159
|
-
lookups << :"
|
|
153
|
+
lookups << :"defaults.#{lookup_action}.#{reflection_or_attribute_name}"
|
|
154
|
+
lookups << :"defaults.#{attribute_name}"
|
|
160
155
|
lookups << default
|
|
161
156
|
|
|
162
157
|
I18n.t(lookups.shift, :scope => :"simple_form.#{namespace}", :default => lookups).presence
|
|
163
158
|
end
|
|
164
|
-
|
|
165
|
-
# Extract the model names from the object_name mess, ignoring numeric and
|
|
166
|
-
# explicit child indexes.
|
|
167
|
-
#
|
|
168
|
-
# Example:
|
|
169
|
-
#
|
|
170
|
-
# route[blocks_attributes][0][blocks_learning_object_attributes][1][foo_attributes]
|
|
171
|
-
# ["route", "blocks", "blocks_learning_object", "foo"]
|
|
172
|
-
#
|
|
173
|
-
def lookup_model_names
|
|
174
|
-
child_index = @builder.options[:child_index]
|
|
175
|
-
names = object_name.to_s.scan(/([a-zA-Z_]+)/).flatten
|
|
176
|
-
names.delete(child_index) if child_index
|
|
177
|
-
names.each { |name| name.gsub!('_attributes', '') }
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
# The action to be used in lookup.
|
|
181
|
-
def lookup_action
|
|
182
|
-
action = template.controller.action_name
|
|
183
|
-
return unless action
|
|
184
|
-
action = action.to_sym
|
|
185
|
-
ACTIONS[action] || action
|
|
186
|
-
end
|
|
187
159
|
end
|
|
188
160
|
end
|
|
189
161
|
end
|
|
@@ -2,21 +2,63 @@ module SimpleForm
|
|
|
2
2
|
module Inputs
|
|
3
3
|
class BooleanInput < Base
|
|
4
4
|
def input
|
|
5
|
-
|
|
5
|
+
if nested_boolean_style?
|
|
6
|
+
build_hidden_field_for_checkbox +
|
|
7
|
+
template.label_tag(nil, :class => "checkbox") {
|
|
8
|
+
build_check_box_without_hidden_field
|
|
9
|
+
}
|
|
10
|
+
else
|
|
11
|
+
build_check_box
|
|
12
|
+
end
|
|
6
13
|
end
|
|
7
14
|
|
|
8
15
|
def label_input
|
|
9
|
-
|
|
16
|
+
if options[:label] == false
|
|
17
|
+
input
|
|
18
|
+
elsif nested_boolean_style?
|
|
19
|
+
html_options = label_html_options.dup
|
|
20
|
+
html_options[:class].push(:checkbox)
|
|
21
|
+
|
|
22
|
+
build_hidden_field_for_checkbox +
|
|
23
|
+
@builder.label(label_target, html_options) {
|
|
24
|
+
build_check_box_without_hidden_field + label_text
|
|
25
|
+
}
|
|
26
|
+
else
|
|
27
|
+
input + label
|
|
28
|
+
end
|
|
10
29
|
end
|
|
11
30
|
|
|
12
31
|
private
|
|
13
32
|
|
|
33
|
+
# Build a checkbox tag using default unchecked value. This allows us to
|
|
34
|
+
# reuse the method for nested boolean style, but with no unchecked value,
|
|
35
|
+
# which won't generate the hidden checkbox. This is the default functionality
|
|
36
|
+
# in Rails > 3.2.1, and is backported in SimpleForm AV helpers.
|
|
37
|
+
def build_check_box(unchecked_value='0')
|
|
38
|
+
@builder.check_box(attribute_name, input_html_options, '1', unchecked_value)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Build a checkbox without generating the hidden field. See
|
|
42
|
+
# #build_hidden_field_for_checkbox for more info.
|
|
43
|
+
def build_check_box_without_hidden_field
|
|
44
|
+
build_check_box(nil)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Create a hidden field for the current checkbox, so we can simulate Rails
|
|
48
|
+
# functionality with hidden + checkbox, but under a nested context, where
|
|
49
|
+
# we need the hidden field to be *outside* the label (otherwise it
|
|
50
|
+
# generates invalid html - html5 only).
|
|
51
|
+
def build_hidden_field_for_checkbox
|
|
52
|
+
@builder.hidden_field(attribute_name, :value => '0', :id => nil,
|
|
53
|
+
:disabled => input_html_options[:disabled])
|
|
54
|
+
end
|
|
55
|
+
|
|
14
56
|
# Booleans are not required by default because in most of the cases
|
|
15
57
|
# it makes no sense marking them as required. The only exception is
|
|
16
58
|
# Terms of Use usually presented at most sites sign up screen.
|
|
17
|
-
def
|
|
59
|
+
def required_by_default?
|
|
18
60
|
false
|
|
19
61
|
end
|
|
20
62
|
end
|
|
21
63
|
end
|
|
22
|
-
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module SimpleForm
|
|
2
|
+
module Inputs
|
|
3
|
+
class CollectionCheckBoxesInput < CollectionRadioButtonsInput
|
|
4
|
+
protected
|
|
5
|
+
|
|
6
|
+
# Checkbox components do not use the required html tag.
|
|
7
|
+
# More info: https://github.com/plataformatec/simple_form/issues/340#issuecomment-2871956
|
|
8
|
+
def has_required?
|
|
9
|
+
false
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def build_nested_boolean_style_item_tag(collection_builder)
|
|
13
|
+
collection_builder.check_box + collection_builder.text
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def item_wrapper_class
|
|
17
|
+
"checkbox"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|