simple_form_awesome 2.2.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.
Files changed (103) hide show
  1. data/CHANGELOG.md +327 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +25 -0
  4. data/lib/generators/simple_form/USAGE +3 -0
  5. data/lib/generators/simple_form/install_generator.rb +48 -0
  6. data/lib/generators/simple_form/templates/AUI_README +19 -0
  7. data/lib/generators/simple_form/templates/README +12 -0
  8. data/lib/generators/simple_form/templates/_form.html.erb +13 -0
  9. data/lib/generators/simple_form/templates/_form.html.haml +10 -0
  10. data/lib/generators/simple_form/templates/_form.html.slim +10 -0
  11. data/lib/generators/simple_form/templates/config/initializers/simple_form.rb +142 -0
  12. data/lib/generators/simple_form/templates/config/initializers/simple_form_aui.rb +21 -0
  13. data/lib/generators/simple_form/templates/config/initializers/simple_form_bootstrap.rb +45 -0
  14. data/lib/generators/simple_form/templates/config/initializers/simple_form_foundation.rb +26 -0
  15. data/lib/generators/simple_form/templates/config/locales/simple_form.en.yml +26 -0
  16. data/lib/simple_form/action_view_extensions/builder.rb +340 -0
  17. data/lib/simple_form/action_view_extensions/form_helper.rb +72 -0
  18. data/lib/simple_form/components/errors.rb +35 -0
  19. data/lib/simple_form/components/hints.rb +18 -0
  20. data/lib/simple_form/components/html5.rb +26 -0
  21. data/lib/simple_form/components/label_input.rb +15 -0
  22. data/lib/simple_form/components/labels.rb +79 -0
  23. data/lib/simple_form/components/maxlength.rb +41 -0
  24. data/lib/simple_form/components/min_max.rb +50 -0
  25. data/lib/simple_form/components/pattern.rb +34 -0
  26. data/lib/simple_form/components/placeholders.rb +16 -0
  27. data/lib/simple_form/components/readonly.rb +22 -0
  28. data/lib/simple_form/components.rb +20 -0
  29. data/lib/simple_form/core_ext/hash.rb +16 -0
  30. data/lib/simple_form/error_notification.rb +48 -0
  31. data/lib/simple_form/form_builder.rb +482 -0
  32. data/lib/simple_form/helpers/autofocus.rb +11 -0
  33. data/lib/simple_form/helpers/disabled.rb +15 -0
  34. data/lib/simple_form/helpers/readonly.rb +15 -0
  35. data/lib/simple_form/helpers/required.rb +35 -0
  36. data/lib/simple_form/helpers/validators.rb +44 -0
  37. data/lib/simple_form/helpers.rb +12 -0
  38. data/lib/simple_form/i18n_cache.rb +22 -0
  39. data/lib/simple_form/inputs/aui_string_input.rb +10 -0
  40. data/lib/simple_form/inputs/base.rb +184 -0
  41. data/lib/simple_form/inputs/block_input.rb +14 -0
  42. data/lib/simple_form/inputs/boolean_input.rb +78 -0
  43. data/lib/simple_form/inputs/collection_check_boxes_input.rb +21 -0
  44. data/lib/simple_form/inputs/collection_input.rb +101 -0
  45. data/lib/simple_form/inputs/collection_radio_buttons_input.rb +63 -0
  46. data/lib/simple_form/inputs/collection_select_input.rb +14 -0
  47. data/lib/simple_form/inputs/date_time_input.rb +28 -0
  48. data/lib/simple_form/inputs/file_input.rb +9 -0
  49. data/lib/simple_form/inputs/grouped_collection_select_input.rb +41 -0
  50. data/lib/simple_form/inputs/hidden_input.rb +17 -0
  51. data/lib/simple_form/inputs/numeric_input.rb +24 -0
  52. data/lib/simple_form/inputs/password_input.rb +12 -0
  53. data/lib/simple_form/inputs/priority_input.rb +24 -0
  54. data/lib/simple_form/inputs/range_input.rb +14 -0
  55. data/lib/simple_form/inputs/string_input.rb +23 -0
  56. data/lib/simple_form/inputs/text_area_input.rb +12 -0
  57. data/lib/simple_form/inputs/text_input.rb +11 -0
  58. data/lib/simple_form/inputs.rb +23 -0
  59. data/lib/simple_form/map_type.rb +16 -0
  60. data/lib/simple_form/version.rb +3 -0
  61. data/lib/simple_form/wrappers/builder.rb +103 -0
  62. data/lib/simple_form/wrappers/many.rb +73 -0
  63. data/lib/simple_form/wrappers/root.rb +36 -0
  64. data/lib/simple_form/wrappers/single.rb +24 -0
  65. data/lib/simple_form/wrappers.rb +8 -0
  66. data/lib/simple_form.rb +221 -0
  67. data/test/action_view_extensions/builder_test.rb +583 -0
  68. data/test/action_view_extensions/form_helper_test.rb +143 -0
  69. data/test/components/label_test.rb +327 -0
  70. data/test/form_builder/association_test.rb +186 -0
  71. data/test/form_builder/button_test.rb +47 -0
  72. data/test/form_builder/error_notification_test.rb +79 -0
  73. data/test/form_builder/error_test.rb +121 -0
  74. data/test/form_builder/general_test.rb +402 -0
  75. data/test/form_builder/hint_test.rb +138 -0
  76. data/test/form_builder/input_field_test.rb +63 -0
  77. data/test/form_builder/label_test.rb +71 -0
  78. data/test/form_builder/wrapper_test.rb +203 -0
  79. data/test/generators/simple_form_generator_test.rb +42 -0
  80. data/test/inputs/boolean_input_test.rb +140 -0
  81. data/test/inputs/collection_check_boxes_input_test.rb +224 -0
  82. data/test/inputs/collection_radio_buttons_input_test.rb +326 -0
  83. data/test/inputs/collection_select_input_test.rb +241 -0
  84. data/test/inputs/datetime_input_test.rb +99 -0
  85. data/test/inputs/disabled_test.rb +78 -0
  86. data/test/inputs/discovery_test.rb +69 -0
  87. data/test/inputs/file_input_test.rb +16 -0
  88. data/test/inputs/general_test.rb +116 -0
  89. data/test/inputs/grouped_collection_select_input_test.rb +118 -0
  90. data/test/inputs/hidden_input_test.rb +30 -0
  91. data/test/inputs/numeric_input_test.rb +173 -0
  92. data/test/inputs/priority_input_test.rb +43 -0
  93. data/test/inputs/readonly_test.rb +101 -0
  94. data/test/inputs/required_test.rb +113 -0
  95. data/test/inputs/string_input_test.rb +146 -0
  96. data/test/inputs/text_input_test.rb +24 -0
  97. data/test/simple_form_test.rb +9 -0
  98. data/test/support/discovery_inputs.rb +27 -0
  99. data/test/support/misc_helpers.rb +138 -0
  100. data/test/support/mock_controller.rb +24 -0
  101. data/test/support/models.rb +216 -0
  102. data/test/test_helper.rb +95 -0
  103. metadata +217 -0
@@ -0,0 +1,482 @@
1
+ require 'simple_form/core_ext/hash'
2
+
3
+ module SimpleForm
4
+ class FormBuilder < ActionView::Helpers::FormBuilder
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
+ }
12
+
13
+ extend MapType
14
+ include SimpleForm::Inputs
15
+
16
+ map_type :text, :to => SimpleForm::Inputs::TextInput
17
+ map_type :file, :to => SimpleForm::Inputs::FileInput
18
+ map_type :string, :email, :search, :tel, :url, :to => SimpleForm::Inputs::StringInput
19
+ map_type :password, :to => SimpleForm::Inputs::PasswordInput
20
+ map_type :integer, :decimal, :float, :to => SimpleForm::Inputs::NumericInput
21
+ map_type :range, :to => SimpleForm::Inputs::RangeInput
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
26
+ map_type :date, :time, :datetime, :to => SimpleForm::Inputs::DateTimeInput
27
+ map_type :country, :time_zone, :to => SimpleForm::Inputs::PriorityInput
28
+ map_type :boolean, :to => SimpleForm::Inputs::BooleanInput
29
+ map_type :text_area, to: SimpleForm::Inputs::TextAreaInput
30
+ map_type :aui_string, to: SimpleForm::Inputs::AuiStringInput
31
+
32
+
33
+ def self.discovery_cache
34
+ @discovery_cache ||= {}
35
+ end
36
+
37
+ def initialize(*) #:nodoc:
38
+ super
39
+ @defaults = options[:defaults]
40
+ @wrapper = SimpleForm.wrapper(options[:wrapper] || SimpleForm.default_wrapper)
41
+ end
42
+
43
+ # Basic input helper, combines all components in the stack to generate
44
+ # input html based on options the user define and some guesses through
45
+ # database column information. By default a call to input will generate
46
+ # label + input + hint (when defined) + errors (when exists), and all can
47
+ # be configured inside a wrapper html.
48
+ #
49
+ # == Examples
50
+ #
51
+ # # Imagine @user has error "can't be blank" on name
52
+ # simple_form_for @user do |f|
53
+ # f.input :name, :hint => 'My hint'
54
+ # end
55
+ #
56
+ # This is the output html (only the input portion, not the form):
57
+ #
58
+ # <label class="string required" for="user_name">
59
+ # <abbr title="required">*</abbr> Super User Name!
60
+ # </label>
61
+ # <input class="string required" id="user_name" maxlength="100"
62
+ # name="user[name]" size="100" type="text" value="Carlos" />
63
+ # <span class="hint">My hint</span>
64
+ # <span class="error">can't be blank</span>
65
+ #
66
+ # Each database type will render a default input, based on some mappings and
67
+ # heuristic to determine which is the best option.
68
+ #
69
+ # You have some options for the input to enable/disable some functions:
70
+ #
71
+ # :as => allows you to define the input type you want, for instance you
72
+ # can use it to generate a text field for a date column.
73
+ #
74
+ # :required => defines whether this attribute is required or not. True
75
+ # by default.
76
+ #
77
+ # The fact SimpleForm is built in components allow the interface to be unified.
78
+ # So, for instance, if you need to disable :hint for a given input, you can pass
79
+ # :hint => false. The same works for :error, :label and :wrapper.
80
+ #
81
+ # Besides the html for any component can be changed. So, if you want to change
82
+ # the label html you just need to give a hash to :label_html. To configure the
83
+ # input html, supply :input_html instead and so on.
84
+ #
85
+ # == Options
86
+ #
87
+ # Some inputs, as datetime, time and select allow you to give extra options, like
88
+ # prompt and/or include blank. Such options are given in plainly:
89
+ #
90
+ # f.input :created_at, :include_blank => true
91
+ #
92
+ # == Collection
93
+ #
94
+ # When playing with collections (:radio_buttons, :check_boxes and :select
95
+ # inputs), you have three extra options:
96
+ #
97
+ # :collection => use to determine the collection to generate the radio or select
98
+ #
99
+ # :label_method => the method to apply on the array collection to get the label
100
+ #
101
+ # :value_method => the method to apply on the array collection to get the value
102
+ #
103
+ # == Priority
104
+ #
105
+ # Some inputs, as :time_zone and :country accepts a :priority option. If none is
106
+ # given SimpleForm.time_zone_priority and SimpleForm.country_priority are used respectively.
107
+ #
108
+ def input(attribute_name, options={}, &block)
109
+ options = @defaults.deep_dup.deep_merge(options) if @defaults
110
+ input = find_input(attribute_name, options, &block)
111
+
112
+ chosen =
113
+ if name = options[:wrapper] || find_wrapper_mapping(input.input_type)
114
+ name.respond_to?(:render) ? name : SimpleForm.wrapper(name)
115
+ else
116
+ wrapper
117
+ end
118
+
119
+ chosen.render input
120
+ end
121
+ alias :attribute :input
122
+
123
+ # Creates a input tag for the given attribute. All the given options
124
+ # are sent as :input_html.
125
+ #
126
+ # == Examples
127
+ #
128
+ # simple_form_for @user do |f|
129
+ # f.input_field :name
130
+ # end
131
+ #
132
+ # This is the output html (only the input portion, not the form):
133
+ #
134
+ # <input class="string required" id="user_name" maxlength="100"
135
+ # name="user[name]" size="100" type="text" value="Carlos" />
136
+ #
137
+ def input_field(attribute_name, options={})
138
+ options = options.dup
139
+ options[:input_html] = options.except(:as, :collection, :label_method, :value_method)
140
+ SimpleForm::Wrappers::Root.new([:input], :wrapper => false).render find_input(attribute_name, options)
141
+ end
142
+
143
+ # Helper for dealing with association selects/radios, generating the
144
+ # collection automatically. It's just a wrapper to input, so all options
145
+ # supported in input are also supported by association. Some extra options
146
+ # can also be given:
147
+ #
148
+ # == Examples
149
+ #
150
+ # simple_form_for @user do |f|
151
+ # f.association :company # Company.all
152
+ # end
153
+ #
154
+ # f.association :company, :collection => Company.all(:order => 'name')
155
+ # # Same as using :order option, but overriding collection
156
+ #
157
+ # == Block
158
+ #
159
+ # When a block is given, association simple behaves as a proxy to
160
+ # simple_fields_for:
161
+ #
162
+ # f.association :company do |c|
163
+ # c.input :name
164
+ # c.input :type
165
+ # end
166
+ #
167
+ # From the options above, only :collection can also be supplied.
168
+ #
169
+ def association(association, options={}, &block)
170
+ options = options.dup
171
+
172
+ return simple_fields_for(*[association,
173
+ options.delete(:collection), options].compact, &block) if block_given?
174
+
175
+ raise ArgumentError, "Association cannot be used in forms not associated with an object" unless @object
176
+
177
+ reflection = find_association_reflection(association)
178
+ raise "Association #{association.inspect} not found" unless reflection
179
+
180
+ options[:as] ||= :select
181
+ options[:collection] ||= options.fetch(:collection) {
182
+ reflection.klass.all(reflection.options.slice(:conditions, :order))
183
+ }
184
+
185
+ attribute = case reflection.macro
186
+ when :belongs_to
187
+ (reflection.respond_to?(:options) && reflection.options[:foreign_key]) || :"#{reflection.name}_id"
188
+ when :has_one
189
+ raise ArgumentError, ":has_one associations are not supported by f.association"
190
+ else
191
+ if options[:as] == :select
192
+ html_options = options[:input_html] ||= {}
193
+ html_options[:size] ||= 5
194
+ html_options[:multiple] = true unless html_options.key?(:multiple)
195
+ end
196
+
197
+ # Force the association to be preloaded for performance.
198
+ if options[:preload] != false && object.respond_to?(association)
199
+ target = object.send(association)
200
+ target.to_a if target.respond_to?(:to_a)
201
+ end
202
+
203
+ :"#{reflection.name.to_s.singularize}_ids"
204
+ end
205
+
206
+ input(attribute, options.merge(:reflection => reflection))
207
+ end
208
+
209
+ # Creates a button:
210
+ #
211
+ # form_for @user do |f|
212
+ # f.button :submit
213
+ # end
214
+ #
215
+ # It just acts as a proxy to method name given. We also alias original Rails
216
+ # button implementation (3.2 forward (to delegate to the original when
217
+ # calling `f.button :button`.
218
+ #
219
+ # TODO: remove if condition when supporting only Rails 3.2 forward.
220
+ alias_method :button_button, :button if method_defined?(:button)
221
+ def button(type, *args, &block)
222
+ options = args.extract_options!.dup
223
+ options[:class] = [SimpleForm.button_class, options[:class]].compact
224
+ args << options
225
+ if respond_to?("#{type}_button")
226
+ send("#{type}_button", *args, &block)
227
+ else
228
+ send(type, *args, &block)
229
+ end
230
+ end
231
+
232
+ # Creates an error tag based on the given attribute, only when the attribute
233
+ # contains errors. All the given options are sent as :error_html.
234
+ #
235
+ # == Examples
236
+ #
237
+ # f.error :name
238
+ # f.error :name, :id => "cool_error"
239
+ #
240
+ def error(attribute_name, options={})
241
+ options = options.dup
242
+
243
+ options[:error_html] = options.except(:error_tag, :error_prefix, :error_method)
244
+ column = find_attribute_column(attribute_name)
245
+ input_type = default_input_type(attribute_name, column, options)
246
+ wrapper.find(:error).
247
+ render(SimpleForm::Inputs::Base.new(self, attribute_name, column, input_type, options))
248
+ end
249
+
250
+ # Return the error but also considering its name. This is used
251
+ # when errors for a hidden field need to be shown.
252
+ #
253
+ # == Examples
254
+ #
255
+ # f.full_error :token #=> <span class="error">Token is invalid</span>
256
+ #
257
+ def full_error(attribute_name, options={})
258
+ options = options.dup
259
+
260
+ options[:error_prefix] ||= if object.class.respond_to?(:human_attribute_name)
261
+ object.class.human_attribute_name(attribute_name.to_s)
262
+ else
263
+ attribute_name.to_s.humanize
264
+ end
265
+
266
+ error(attribute_name, options)
267
+ end
268
+
269
+ # Creates a hint tag for the given attribute. Accepts a symbol indicating
270
+ # an attribute for I18n lookup or a string. All the given options are sent
271
+ # as :hint_html.
272
+ #
273
+ # == Examples
274
+ #
275
+ # f.hint :name # Do I18n lookup
276
+ # f.hint :name, :id => "cool_hint"
277
+ # f.hint "Don't forget to accept this"
278
+ #
279
+ def hint(attribute_name, options={})
280
+ options = options.dup
281
+
282
+ options[:hint_html] = options.except(:hint_tag, :hint)
283
+ if attribute_name.is_a?(String)
284
+ options[:hint] = attribute_name
285
+ attribute_name, column, input_type = nil, nil, nil
286
+ else
287
+ column = find_attribute_column(attribute_name)
288
+ input_type = default_input_type(attribute_name, column, options)
289
+ end
290
+
291
+ wrapper.find(:hint).
292
+ render(SimpleForm::Inputs::Base.new(self, attribute_name, column, input_type, options))
293
+ end
294
+
295
+ # Creates a default label tag for the given attribute. You can give a label
296
+ # through the :label option or using i18n. All the given options are sent
297
+ # as :label_html.
298
+ #
299
+ # == Examples
300
+ #
301
+ # f.label :name # Do I18n lookup
302
+ # f.label :name, "Name" # Same behavior as Rails, do not add required tag
303
+ # f.label :name, :label => "Name" # Same as above, but adds required tag
304
+ #
305
+ # f.label :name, :required => false
306
+ # f.label :name, :id => "cool_label"
307
+ #
308
+ def label(attribute_name, *args)
309
+ return super if args.first.is_a?(String) || block_given?
310
+
311
+ options = args.extract_options!.dup
312
+ options[:label_html] = options.except(:label, :required, :as)
313
+
314
+ column = find_attribute_column(attribute_name)
315
+ input_type = default_input_type(attribute_name, column, options)
316
+ SimpleForm::Inputs::Base.new(self, attribute_name, column, input_type, options).label
317
+ end
318
+
319
+ # Creates an error notification message that only appears when the form object
320
+ # has some error. You can give a specific message with the :message option,
321
+ # otherwise it will look for a message using I18n. All other options given are
322
+ # passed straight as html options to the html tag.
323
+ #
324
+ # == Examples
325
+ #
326
+ # f.error_notification
327
+ # f.error_notification :message => 'Something went wrong'
328
+ # f.error_notification :id => 'user_error_message', :class => 'form_error'
329
+ #
330
+ def error_notification(options={})
331
+ SimpleForm::ErrorNotification.new(self, options).render
332
+ end
333
+
334
+ # Extract the model names from the object_name mess, ignoring numeric and
335
+ # explicit child indexes.
336
+ #
337
+ # Example:
338
+ #
339
+ # route[blocks_attributes][0][blocks_learning_object_attributes][1][foo_attributes]
340
+ # ["route", "blocks", "blocks_learning_object", "foo"]
341
+ #
342
+ def lookup_model_names
343
+ @lookup_model_names ||= begin
344
+ child_index = options[:child_index]
345
+ names = object_name.to_s.scan(/([a-zA-Z_]+)/).flatten
346
+ names.delete(child_index) if child_index
347
+ names.each { |name| name.gsub!('_attributes', '') }
348
+ names.freeze
349
+ end
350
+ end
351
+
352
+ # The action to be used in lookup.
353
+ def lookup_action
354
+ @lookup_action ||= begin
355
+ action = template.controller.action_name
356
+ return unless action
357
+ action = action.to_sym
358
+ ACTIONS[action] || action
359
+ end
360
+ end
361
+
362
+ private
363
+
364
+ # Find an input based on the attribute name.
365
+ def find_input(attribute_name, options={}, &block) #:nodoc:
366
+ column = find_attribute_column(attribute_name)
367
+ input_type = default_input_type(attribute_name, column, options)
368
+
369
+ if input_type == :radio
370
+ SimpleForm.deprecation_warn "Using `:as => :radio` as input type is " \
371
+ "deprecated, please change it to `:as => :radio_buttons`."
372
+ input_type = :radio_buttons
373
+ end
374
+
375
+ if block_given?
376
+ SimpleForm::Inputs::BlockInput.new(self, attribute_name, column, input_type, options, &block)
377
+ else
378
+ find_mapping(input_type).new(self, attribute_name, column, input_type, options)
379
+ end
380
+ end
381
+
382
+ # Attempt to guess the better input type given the defined options. By
383
+ # default alwayls fallback to the user :as option, or to a :select when a
384
+ # collection is given.
385
+ def default_input_type(attribute_name, column, options) #:nodoc:
386
+ return options[:as].to_sym if options[:as]
387
+ return :select if options[:collection]
388
+ custom_type = find_custom_type(attribute_name.to_s) and return custom_type
389
+
390
+ input_type = column.try(:type)
391
+ case input_type
392
+ when :timestamp
393
+ :datetime
394
+ when :string, nil
395
+ case attribute_name.to_s
396
+ when /password/ then :password
397
+ when /time_zone/ then :time_zone
398
+ when /country/ then :country
399
+ when /email/ then :email
400
+ when /phone/ then :tel
401
+ when /url/ then :url
402
+ else
403
+ file_method?(attribute_name) ? :file : (input_type || :string)
404
+ end
405
+ else
406
+ input_type
407
+ end
408
+ end
409
+
410
+ def find_custom_type(attribute_name) #:nodoc:
411
+ SimpleForm.input_mappings.find { |match, type|
412
+ attribute_name =~ match
413
+ }.try(:last) if SimpleForm.input_mappings
414
+ end
415
+
416
+ def file_method?(attribute_name) #:nodoc:
417
+ file = @object.send(attribute_name) if @object.respond_to?(attribute_name)
418
+ file && SimpleForm.file_methods.any? { |m| file.respond_to?(m) }
419
+ end
420
+
421
+ def find_attribute_column(attribute_name) #:nodoc:
422
+ if @object.respond_to?(:column_for_attribute)
423
+ @object.column_for_attribute(attribute_name)
424
+ end
425
+ end
426
+
427
+ def find_association_reflection(association) #:nodoc:
428
+ if @object.class.respond_to?(:reflect_on_association)
429
+ @object.class.reflect_on_association(association)
430
+ end
431
+ end
432
+
433
+ # Attempts to find a mapping. It follows the following rules:
434
+ #
435
+ # 1) It tries to find a registered mapping, if succeeds:
436
+ # a) Try to find an alternative with the same name in the Object scope
437
+ # b) Or use the found mapping
438
+ # 2) If not, fallbacks to #{input_type}Input
439
+ # 3) If not, fallbacks to SimpleForm::Inputs::#{input_type}Input
440
+ def find_mapping(input_type) #:nodoc:
441
+ discovery_cache[input_type] ||=
442
+ if mapping = self.class.mappings[input_type]
443
+ mapping_override(mapping) || mapping
444
+ else
445
+ camelized = "#{input_type.to_s.camelize}Input"
446
+ attempt_mapping(camelized, Object) || attempt_mapping(camelized, self.class) ||
447
+ raise("No input found for #{input_type}")
448
+ end
449
+ end
450
+
451
+ def find_wrapper_mapping(input_type) #:nodoc:
452
+ SimpleForm.wrapper_mappings && SimpleForm.wrapper_mappings[input_type]
453
+ end
454
+
455
+ # If cache_discovery is enabled, use the class level cache that persists
456
+ # between requests, otherwise use the instance one.
457
+ def discovery_cache #:nodoc:
458
+ if SimpleForm.cache_discovery
459
+ self.class.discovery_cache
460
+ else
461
+ @discovery_cache ||= {}
462
+ end
463
+ end
464
+
465
+ def mapping_override(klass) #:nodoc:
466
+ name = klass.name
467
+ if name =~ /^SimpleForm::Inputs/
468
+ attempt_mapping name.split("::").last, Object
469
+ end
470
+ end
471
+
472
+ def attempt_mapping(mapping, at) #:nodoc:
473
+ return if SimpleForm.inputs_discovery == false && at == Object
474
+
475
+ begin
476
+ at.const_get(mapping)
477
+ rescue NameError => e
478
+ raise if e.message !~ /#{mapping}$/
479
+ end
480
+ end
481
+ end
482
+ 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
@@ -0,0 +1,35 @@
1
+ module SimpleForm
2
+ module Helpers
3
+ module Required
4
+ private
5
+
6
+ def required_field?
7
+ @required
8
+ end
9
+
10
+ def calculate_required
11
+ if !options[:required].nil?
12
+ options[:required]
13
+ elsif has_validators?
14
+ required_by_validators?
15
+ else
16
+ required_by_default?
17
+ end
18
+ end
19
+
20
+ def required_by_validators?
21
+ (attribute_validators + reflection_validators).any? { |v| v.kind == :presence && valid_validator?(v) }
22
+ end
23
+
24
+ def required_by_default?
25
+ SimpleForm.required_by_default
26
+ end
27
+
28
+ # Do not use has_required? because we want to add the class
29
+ # regardless of the required option.
30
+ def required_class
31
+ required_field? ? :required : :optional
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,44 @@
1
+ module SimpleForm
2
+ module Helpers
3
+ module Validators
4
+ def has_validators?
5
+ @has_validators ||= attribute_name && object.class.respond_to?(:validators_on)
6
+ end
7
+
8
+ private
9
+
10
+ def attribute_validators
11
+ object.class.validators_on(attribute_name)
12
+ end
13
+
14
+ def reflection_validators
15
+ reflection ? object.class.validators_on(reflection.name) : []
16
+ end
17
+
18
+ def valid_validator?(validator)
19
+ !conditional_validators?(validator) && action_validator_match?(validator)
20
+ end
21
+
22
+ def conditional_validators?(validator)
23
+ validator.options.include?(:if) || validator.options.include?(:unless)
24
+ end
25
+
26
+ def action_validator_match?(validator)
27
+ return true if !validator.options.include?(:on)
28
+
29
+ case validator.options[:on]
30
+ when :save
31
+ true
32
+ when :create
33
+ !object.persisted?
34
+ when :update
35
+ object.persisted?
36
+ end
37
+ end
38
+
39
+ def find_validator(kind)
40
+ attribute_validators.find { |v| v.kind == kind } if has_validators?
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,12 @@
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.
5
+ module Helpers
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
+ end
12
+ end
@@ -0,0 +1,22 @@
1
+ module SimpleForm
2
+ # A lot of configuration values are retrived from I18n,
3
+ # like boolean collection, required string. This module provides
4
+ # caching facility to speed up form construction.
5
+ module I18nCache
6
+ def i18n_cache(key)
7
+ get_i18n_cache(key)[I18n.locale] ||= yield.freeze
8
+ end
9
+
10
+ def get_i18n_cache(key)
11
+ if class_variable_defined?(:"@@#{key}")
12
+ class_variable_get(:"@@#{key}")
13
+ else
14
+ reset_i18n_cache(key)
15
+ end
16
+ end
17
+
18
+ def reset_i18n_cache(key)
19
+ class_variable_set(:"@@#{key}", {})
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,10 @@
1
+ module SimpleForm
2
+ module Inputs
3
+ class AuiStringInput < StringInput
4
+ def input
5
+ input_html_classes.append("text")
6
+ super
7
+ end
8
+ end
9
+ end
10
+ end