simple_form 1.5.2 → 2.0.0.rc

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. data/CHANGELOG.md +224 -0
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +817 -0
  4. data/lib/generators/simple_form/install_generator.rb +15 -1
  5. data/lib/generators/simple_form/templates/README +12 -0
  6. data/lib/generators/simple_form/templates/config/initializers/simple_form.rb.tt +173 -0
  7. data/lib/simple_form.rb +109 -43
  8. data/lib/simple_form/action_view_extensions/builder.rb +158 -53
  9. data/lib/simple_form/action_view_extensions/form_helper.rb +29 -22
  10. data/lib/simple_form/components.rb +11 -1
  11. data/lib/simple_form/components/errors.rb +6 -24
  12. data/lib/simple_form/components/hints.rb +7 -21
  13. data/lib/simple_form/components/html5.rb +26 -0
  14. data/lib/simple_form/components/labels.rb +15 -13
  15. data/lib/simple_form/components/maxlength.rb +41 -0
  16. data/lib/simple_form/components/min_max.rb +49 -0
  17. data/lib/simple_form/components/pattern.rb +34 -0
  18. data/lib/simple_form/components/placeholders.rb +5 -17
  19. data/lib/simple_form/components/readonly.rb +22 -0
  20. data/lib/simple_form/core_ext/hash.rb +16 -0
  21. data/lib/simple_form/error_notification.rb +8 -1
  22. data/lib/simple_form/form_builder.rb +86 -22
  23. data/lib/simple_form/helpers.rb +7 -4
  24. data/lib/simple_form/helpers/autofocus.rb +11 -0
  25. data/lib/simple_form/helpers/disabled.rb +15 -0
  26. data/lib/simple_form/helpers/readonly.rb +15 -0
  27. data/lib/simple_form/helpers/required.rb +7 -10
  28. data/lib/simple_form/helpers/validators.rb +4 -4
  29. data/lib/simple_form/inputs.rb +17 -13
  30. data/lib/simple_form/inputs/base.rb +50 -81
  31. data/lib/simple_form/inputs/boolean_input.rb +43 -4
  32. data/lib/simple_form/inputs/collection_check_boxes_input.rb +21 -0
  33. data/lib/simple_form/inputs/collection_input.rb +27 -13
  34. data/lib/simple_form/inputs/collection_radio_buttons_input.rb +69 -0
  35. data/lib/simple_form/inputs/collection_select_input.rb +14 -0
  36. data/lib/simple_form/inputs/date_time_input.rb +2 -2
  37. data/lib/simple_form/inputs/grouped_collection_select_input.rb +41 -0
  38. data/lib/simple_form/inputs/hidden_input.rb +3 -6
  39. data/lib/simple_form/inputs/numeric_input.rb +3 -51
  40. data/lib/simple_form/inputs/password_input.rb +1 -2
  41. data/lib/simple_form/inputs/priority_input.rb +2 -2
  42. data/lib/simple_form/inputs/range_input.rb +1 -3
  43. data/lib/simple_form/inputs/string_input.rb +6 -8
  44. data/lib/simple_form/inputs/text_input.rb +1 -2
  45. data/lib/simple_form/version.rb +1 -1
  46. data/lib/simple_form/wrappers.rb +8 -0
  47. data/lib/simple_form/wrappers/builder.rb +75 -0
  48. data/lib/simple_form/wrappers/many.rb +68 -0
  49. data/lib/simple_form/wrappers/root.rb +34 -0
  50. data/lib/simple_form/wrappers/single.rb +18 -0
  51. data/test/action_view_extensions/builder_test.rb +195 -100
  52. data/test/action_view_extensions/form_helper_test.rb +24 -2
  53. data/test/components/label_test.rb +20 -5
  54. data/test/form_builder/association_test.rb +167 -0
  55. data/test/form_builder/button_test.rb +28 -0
  56. data/test/{error_notification_test.rb → form_builder/error_notification_test.rb} +2 -1
  57. data/test/form_builder/error_test.rb +101 -0
  58. data/test/form_builder/general_test.rb +348 -0
  59. data/test/form_builder/hint_test.rb +115 -0
  60. data/test/form_builder/input_field_test.rb +51 -0
  61. data/test/form_builder/label_test.rb +51 -0
  62. data/test/form_builder/wrapper_test.rb +140 -0
  63. data/test/generators/simple_form_generator_test.rb +32 -0
  64. data/test/inputs/boolean_input_test.rb +91 -0
  65. data/test/inputs/collection_check_boxes_input_test.rb +224 -0
  66. data/test/inputs/collection_radio_buttons_input_test.rb +326 -0
  67. data/test/inputs/collection_select_input_test.rb +241 -0
  68. data/test/inputs/datetime_input_test.rb +85 -0
  69. data/test/inputs/disabled_test.rb +38 -0
  70. data/test/inputs/discovery_test.rb +61 -0
  71. data/test/inputs/file_input_test.rb +16 -0
  72. data/test/inputs/general_test.rb +69 -0
  73. data/test/inputs/grouped_collection_select_input_test.rb +109 -0
  74. data/test/inputs/hidden_input_test.rb +30 -0
  75. data/test/inputs/numeric_input_test.rb +167 -0
  76. data/test/inputs/priority_input_test.rb +43 -0
  77. data/test/inputs/readonly_test.rb +61 -0
  78. data/test/inputs/required_test.rb +113 -0
  79. data/test/inputs/string_input_test.rb +140 -0
  80. data/test/inputs/text_input_test.rb +24 -0
  81. data/test/{discovery_inputs.rb → support/discovery_inputs.rb} +0 -0
  82. data/test/support/misc_helpers.rb +48 -6
  83. data/test/support/mock_controller.rb +2 -2
  84. data/test/support/models.rb +20 -5
  85. data/test/test_helper.rb +5 -8
  86. metadata +123 -98
  87. data/.gitignore +0 -3
  88. data/.gitmodules +0 -3
  89. data/.travis.yml +0 -15
  90. data/CHANGELOG.rdoc +0 -159
  91. data/Gemfile +0 -9
  92. data/README.rdoc +0 -466
  93. data/Rakefile +0 -27
  94. data/lib/generators/simple_form/templates/config/initializers/simple_form.rb +0 -93
  95. data/lib/simple_form/components/wrapper.rb +0 -38
  96. data/lib/simple_form/helpers/has_errors.rb +0 -15
  97. data/lib/simple_form/helpers/maxlength.rb +0 -24
  98. data/lib/simple_form/helpers/pattern.rb +0 -28
  99. data/simple_form.gemspec +0 -25
  100. data/test/components/error_test.rb +0 -56
  101. data/test/components/hint_test.rb +0 -74
  102. data/test/components/wrapper_test.rb +0 -63
  103. data/test/custom_components.rb +0 -7
  104. data/test/form_builder_test.rb +0 -930
  105. data/test/inputs_test.rb +0 -995
@@ -0,0 +1,16 @@
1
+ # TODO: Delete this file when we drop support for Rails 3.0
2
+ # This method is already implemented in active_support 3.1
3
+
4
+ unless Hash.new.respond_to?(:deep_dup)
5
+ class Hash
6
+ # Returns a deep copy of hash.
7
+ def deep_dup
8
+ duplicate = self.dup
9
+ duplicate.each_pair do |k,v|
10
+ tv = duplicate[k]
11
+ duplicate[k] = tv.is_a?(Hash) && v.is_a?(Hash) ? tv.deep_dup : v
12
+ end
13
+ duplicate
14
+ end
15
+ end
16
+ end
@@ -1,7 +1,6 @@
1
1
  module SimpleForm
2
2
  class ErrorNotification
3
3
  delegate :object, :object_name, :template, :to => :@builder
4
- include SimpleForm::Helpers::HasErrors
5
4
 
6
5
  def initialize(builder, options)
7
6
  @builder = builder
@@ -17,6 +16,14 @@ module SimpleForm
17
16
 
18
17
  protected
19
18
 
19
+ def errors
20
+ object.errors
21
+ end
22
+
23
+ def has_errors?
24
+ object && object.respond_to?(:errors) && errors.present?
25
+ end
26
+
20
27
  def error_message
21
28
  @message || translate_error_notification
22
29
  end
@@ -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 :select, :radio, :check_boxes, :to => SimpleForm::Inputs::CollectionInput
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 (:radio and :select inputs), you have three extra
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
- column = find_attribute_column(attribute_name)
90
- input_type = default_input_type(attribute_name, column, options)
106
+ options = @defaults.deep_dup.deep_merge(options) if @defaults
91
107
 
92
- if block_given?
93
- SimpleForm::Inputs::BlockInput.new(self, attribute_name, column, input_type, options, &block).render
94
- else
95
- find_mapping(input_type).new(self, attribute_name, column, input_type, options).render
96
- end
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,8 @@ module SimpleForm
112
131
  # name="user[name]" size="100" type="text" value="Carlos" />
113
132
  #
114
133
  def input_field(attribute_name, options={})
115
- column = find_attribute_column(attribute_name)
116
- input_type = default_input_type(attribute_name, column, options)
117
-
118
134
  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
135
+ SimpleForm::Wrappers::Root.new([:input], :wrapper => false).render find_input(attribute_name, options)
121
136
  end
122
137
 
123
138
  # Helper for dealing with association selects/radios, generating the
@@ -160,7 +175,7 @@ module SimpleForm
160
175
 
161
176
  attribute = case reflection.macro
162
177
  when :belongs_to
163
- reflection.options[:foreign_key] || :"#{reflection.name}_id"
178
+ (reflection.respond_to?(:options) && reflection.options[:foreign_key]) || :"#{reflection.name}_id"
164
179
  when :has_one
165
180
  raise ":has_one associations are not supported by f.association"
166
181
  else
@@ -192,7 +207,7 @@ module SimpleForm
192
207
  #
193
208
  def button(type, *args, &block)
194
209
  options = args.extract_options!
195
- options[:class] = "button #{options[:class]}".strip
210
+ options[:class] = [SimpleForm.button_class, options[:class]].compact
196
211
  args << options
197
212
  if respond_to?("#{type}_button")
198
213
  send("#{type}_button", *args, &block)
@@ -213,7 +228,8 @@ module SimpleForm
213
228
  options[:error_html] = options.except(:error_tag, :error_prefix, :error_method)
214
229
  column = find_attribute_column(attribute_name)
215
230
  input_type = default_input_type(attribute_name, column, options)
216
- SimpleForm::Inputs::Base.new(self, attribute_name, column, input_type, options).error
231
+ wrapper.find(:error).
232
+ render(SimpleForm::Inputs::Base.new(self, attribute_name, column, input_type, options))
217
233
  end
218
234
 
219
235
  # Return the error but also considering its name. This is used
@@ -244,7 +260,7 @@ module SimpleForm
244
260
  # f.hint "Don't forget to accept this"
245
261
  #
246
262
  def hint(attribute_name, options={})
247
- options[:hint_html] = options.except(:hint_tag)
263
+ options[:hint_html] = options.except(:hint_tag, :hint)
248
264
  if attribute_name.is_a?(String)
249
265
  options[:hint] = attribute_name
250
266
  attribute_name, column, input_type = nil, nil, nil
@@ -252,7 +268,9 @@ module SimpleForm
252
268
  column = find_attribute_column(attribute_name)
253
269
  input_type = default_input_type(attribute_name, column, options)
254
270
  end
255
- SimpleForm::Inputs::Base.new(self, attribute_name, column, input_type, options).hint
271
+
272
+ wrapper.find(:hint).
273
+ render(SimpleForm::Inputs::Base.new(self, attribute_name, column, input_type, options))
256
274
  end
257
275
 
258
276
  # Creates a default label tag for the given attribute. You can give a label
@@ -294,7 +312,53 @@ module SimpleForm
294
312
  SimpleForm::ErrorNotification.new(self, options).render
295
313
  end
296
314
 
297
- private
315
+ # Extract the model names from the object_name mess, ignoring numeric and
316
+ # explicit child indexes.
317
+ #
318
+ # Example:
319
+ #
320
+ # route[blocks_attributes][0][blocks_learning_object_attributes][1][foo_attributes]
321
+ # ["route", "blocks", "blocks_learning_object", "foo"]
322
+ #
323
+ def lookup_model_names
324
+ @lookup_model_names ||= begin
325
+ child_index = options[:child_index]
326
+ names = object_name.to_s.scan(/([a-zA-Z_]+)/).flatten
327
+ names.delete(child_index) if child_index
328
+ names.each { |name| name.gsub!('_attributes', '') }
329
+ names.freeze
330
+ end
331
+ end
332
+
333
+ # The action to be used in lookup.
334
+ def lookup_action
335
+ @lookup_action ||= begin
336
+ action = template.controller.action_name
337
+ return unless action
338
+ action = action.to_sym
339
+ ACTIONS[action] || action
340
+ end
341
+ end
342
+
343
+ private
344
+
345
+ # Find an input based on the attribute name.
346
+ def find_input(attribute_name, options={}, &block) #:nodoc:
347
+ column = find_attribute_column(attribute_name)
348
+ input_type = default_input_type(attribute_name, column, options)
349
+
350
+ if input_type == :radio
351
+ SimpleForm.deprecation_warn "Using `:as => :radio` as input type is " \
352
+ "deprecated, please change it to `:as => :radio_buttons`."
353
+ input_type = :radio_buttons
354
+ end
355
+
356
+ if block_given?
357
+ SimpleForm::Inputs::BlockInput.new(self, attribute_name, column, input_type, options, &block)
358
+ else
359
+ find_mapping(input_type).new(self, attribute_name, column, input_type, options)
360
+ end
361
+ end
298
362
 
299
363
  # Attempt to guess the better input type given the defined options. By
300
364
  # default alwayls fallback to the user :as option, or to a :select when a
@@ -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 :HasErrors, 'simple_form/helpers/has_errors'
4
- autoload :Maxlength, 'simple_form/helpers/maxlength'
5
- autoload :Pattern, 'simple_form/helpers/pattern'
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
@@ -0,0 +1,11 @@
1
+ module SimpleForm
2
+ module Helpers
3
+ module Autofocus
4
+ private
5
+
6
+ def has_autofocus?
7
+ options[:autofocus] == true
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,15 @@
1
+ module SimpleForm
2
+ module Helpers
3
+ module Disabled
4
+ private
5
+
6
+ def has_disabled?
7
+ options[:disabled] == true
8
+ end
9
+
10
+ def disabled_class
11
+ :disabled if has_disabled?
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module SimpleForm
2
+ module Helpers
3
+ module Readonly
4
+ private
5
+
6
+ def readonly_class
7
+ :readonly if has_readonly?
8
+ end
9
+
10
+ def has_readonly?
11
+ options[:readonly] == true
12
+ end
13
+ end
14
+ end
15
+ end
@@ -3,7 +3,7 @@ module SimpleForm
3
3
  module Required
4
4
  private
5
5
 
6
- def attribute_required?
6
+ def required_field?
7
7
  @required
8
8
  end
9
9
 
@@ -15,22 +15,19 @@ module SimpleForm
15
15
  v.kind == :presence && valid_validator?(v)
16
16
  end
17
17
  else
18
- attribute_required_by_default?
18
+ required_by_default?
19
19
  end
20
20
  end
21
21
 
22
- # Whether this input is valid for HTML 5 required attribute.
23
- def has_required?
24
- attribute_required? && SimpleForm.html5 && SimpleForm.browser_validations
25
- end
26
-
27
- def attribute_required_by_default?
22
+ def required_by_default?
28
23
  SimpleForm.required_by_default
29
24
  end
30
25
 
26
+ # Do not use has_required? because we want to add the class
27
+ # regardless of the required option.
31
28
  def required_class
32
- attribute_required? ? :required : :optional
29
+ required_field? ? :required : :optional
33
30
  end
34
31
  end
35
32
  end
36
- end
33
+ 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
@@ -1,17 +1,21 @@
1
1
  module SimpleForm
2
2
  module Inputs
3
- autoload :Base, 'simple_form/inputs/base'
4
- autoload :BlockInput, 'simple_form/inputs/block_input'
5
- autoload :BooleanInput, 'simple_form/inputs/boolean_input'
6
- autoload :CollectionInput, 'simple_form/inputs/collection_input'
7
- autoload :DateTimeInput, 'simple_form/inputs/date_time_input'
8
- autoload :FileInput, 'simple_form/inputs/file_input'
9
- autoload :HiddenInput, 'simple_form/inputs/hidden_input'
10
- autoload :NumericInput, 'simple_form/inputs/numeric_input'
11
- autoload :PasswordInput, 'simple_form/inputs/password_input'
12
- autoload :PriorityInput, 'simple_form/inputs/priority_input'
13
- autoload :RangeInput, 'simple_form/inputs/range_input'
14
- autoload :StringInput, 'simple_form/inputs/string_input'
15
- autoload :TextInput, 'simple_form/inputs/text_input'
3
+ autoload :Base, 'simple_form/inputs/base'
4
+ autoload :BlockInput, 'simple_form/inputs/block_input'
5
+ autoload :BooleanInput, 'simple_form/inputs/boolean_input'
6
+ autoload :CollectionCheckBoxesInput, 'simple_form/inputs/collection_check_boxes_input'
7
+ autoload :CollectionInput, 'simple_form/inputs/collection_input'
8
+ autoload :CollectionRadioButtonsInput, 'simple_form/inputs/collection_radio_buttons_input'
9
+ autoload :CollectionSelectInput, 'simple_form/inputs/collection_select_input'
10
+ autoload :DateTimeInput, 'simple_form/inputs/date_time_input'
11
+ autoload :FileInput, 'simple_form/inputs/file_input'
12
+ autoload :GroupedCollectionSelectInput, 'simple_form/inputs/grouped_collection_select_input'
13
+ autoload :HiddenInput, 'simple_form/inputs/hidden_input'
14
+ autoload :NumericInput, 'simple_form/inputs/numeric_input'
15
+ autoload :PasswordInput, 'simple_form/inputs/password_input'
16
+ autoload :PriorityInput, 'simple_form/inputs/priority_input'
17
+ autoload :RangeInput, 'simple_form/inputs/range_input'
18
+ autoload :StringInput, 'simple_form/inputs/string_input'
19
+ autoload :TextInput, 'simple_form/inputs/text_input'
16
20
  end
17
21
  end
@@ -3,49 +3,68 @@ module SimpleForm
3
3
  class Base
4
4
  extend I18nCache
5
5
 
6
- # When action is create or update, we still should use new and edit
7
- ACTIONS = {
8
- :create => :new,
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::Wrapper
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
- # Enables certain components support to the given input.
24
- def self.enable(*args)
25
- args.each { |m| alias_method m, :"enabled_#{m}" }
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(*args)
29
- args.each { |m| alias_method m, :"disabled_#{m}" }
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
- attr_reader :attribute_name, :column, :input_type, :reflection,
33
- :options, :input_html_options
42
+ # Always enabled.
43
+ enable :hint
34
44
 
35
- delegate :template, :object, :object_name, :to => :@builder
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 = [input_type, required_class, readonly_class, disabled_class].compact
63
+ @input_html_classes = @html_classes.dup
45
64
  @input_html_options = html_options_for(:input, input_html_classes).tap do |o|
46
- o[:required] = true if has_required?
47
- o[:disabled] = true if disabled?
48
- o[:autofocus] = true if has_autofocus? && SimpleForm.html5
65
+ o[:readonly] = true if has_readonly?
66
+ o[:disabled] = true if has_disabled?
67
+ o[:autofocus] = true if has_autofocus?
49
68
  end
50
69
  end
51
70
 
@@ -57,20 +76,6 @@ module SimpleForm
57
76
  options
58
77
  end
59
78
 
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
79
  private
75
80
 
76
81
  def add_size!
@@ -81,17 +86,8 @@ module SimpleForm
81
86
  column && column.limit
82
87
  end
83
88
 
84
- def components_list
85
- if components = options[:components]
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]
89
+ def nested_boolean_style?
90
+ options.fetch(:boolean_style, SimpleForm.boolean_style) == :nested
95
91
  end
96
92
 
97
93
  # Find reflection name when available, otherwise use attribute
@@ -100,22 +96,19 @@ module SimpleForm
100
96
  end
101
97
 
102
98
  # Retrieve options for the given namespace from the options hash
103
- def html_options_for(namespace, extra)
99
+ def html_options_for(namespace, css_classes)
104
100
  html_options = options[:"#{namespace}_html"] || {}
105
- html_options[:class] = (extra << html_options[:class]).join(' ').strip if extra.present?
101
+ css_classes << html_options[:class] if html_options.key?(:class)
102
+ html_options[:class] = css_classes
106
103
  html_options
107
104
  end
108
105
 
109
- def disabled?
110
- options[:disabled] == true
111
- end
112
-
113
106
  # Lookup translations for the given namespace using I18n, based on object name,
114
107
  # actual action and attribute name. Lookup priority as follows:
115
108
  #
116
109
  # simple_form.{namespace}.{model}.{action}.{attribute}
117
110
  # simple_form.{namespace}.{model}.{attribute}
118
- # simple_form.{namespace}.{attribute}
111
+ # simple_form.{namespace}.defaults.{attribute}
119
112
  #
120
113
  # Namespace is used for :labels and :hints.
121
114
  #
@@ -130,7 +123,7 @@ module SimpleForm
130
123
  # simple_form.{namespace}.{model}.{nested}.{attribute}
131
124
  # simple_form.{namespace}.{nested}.{action}.{attribute}
132
125
  # simple_form.{namespace}.{nested}.{attribute}
133
- # simple_form.{namespace}.{attribute}
126
+ # simple_form.{namespace}.defaults.{attribute}
134
127
  #
135
128
  # Example:
136
129
  #
@@ -144,9 +137,7 @@ module SimpleForm
144
137
  #
145
138
  # Take a look at our locale example file.
146
139
  def translate(namespace, default='')
147
- return nil unless SimpleForm.translate
148
-
149
- model_names = lookup_model_names
140
+ model_names = lookup_model_names.dup
150
141
  lookups = []
151
142
 
152
143
  while !model_names.empty?
@@ -156,34 +147,12 @@ module SimpleForm
156
147
  lookups << :"#{joined_model_names}.#{lookup_action}.#{reflection_or_attribute_name}"
157
148
  lookups << :"#{joined_model_names}.#{reflection_or_attribute_name}"
158
149
  end
159
- lookups << :"#{reflection_or_attribute_name}"
150
+ lookups << :"defaults.#{lookup_action}.#{reflection_or_attribute_name}"
151
+ lookups << :"defaults.#{attribute_name}"
160
152
  lookups << default
161
153
 
162
154
  I18n.t(lookups.shift, :scope => :"simple_form.#{namespace}", :default => lookups).presence
163
155
  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
156
  end
188
157
  end
189
158
  end