simple_form 1.3.0 → 1.4.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 (39) hide show
  1. data/.gitignore +2 -0
  2. data/.gitmodules +3 -0
  3. data/.travis.yml +6 -0
  4. data/CHANGELOG.rdoc +109 -0
  5. data/Gemfile +8 -0
  6. data/Gemfile.lock +82 -0
  7. data/MIT-LICENSE +20 -0
  8. data/README.rdoc +73 -12
  9. data/Rakefile +27 -0
  10. data/lib/generators/simple_form/templates/_form.html.erb +1 -1
  11. data/lib/generators/simple_form/templates/_form.html.haml +2 -10
  12. data/lib/generators/simple_form/templates/_form.html.slim +10 -0
  13. data/lib/generators/simple_form/templates/simple_form.rb +20 -4
  14. data/lib/simple_form/action_view_extensions/builder.rb +39 -15
  15. data/lib/simple_form/action_view_extensions/form_helper.rb +5 -5
  16. data/lib/simple_form/components/errors.rb +5 -1
  17. data/lib/simple_form/components/labels.rb +2 -2
  18. data/lib/simple_form/form_builder.rb +131 -38
  19. data/lib/simple_form/inputs/base.rb +52 -6
  20. data/lib/simple_form/inputs/collection_input.rb +5 -1
  21. data/lib/simple_form/inputs/date_time_input.rb +4 -0
  22. data/lib/simple_form/inputs/mapping_input.rb +0 -6
  23. data/lib/simple_form/inputs/numeric_input.rb +15 -7
  24. data/lib/simple_form/inputs/priority_input.rb +5 -1
  25. data/lib/simple_form/inputs/string_input.rb +14 -3
  26. data/lib/simple_form/map_type.rb +6 -3
  27. data/lib/simple_form/version.rb +1 -1
  28. data/lib/simple_form.rb +28 -1
  29. data/simple_form.gemspec +22 -0
  30. data/test/action_view_extensions/builder_test.rb +39 -7
  31. data/test/action_view_extensions/form_helper_test.rb +12 -0
  32. data/test/components/label_test.rb +54 -0
  33. data/test/discovery_inputs.rb +21 -0
  34. data/test/form_builder_test.rb +230 -0
  35. data/test/inputs_test.rb +234 -14
  36. data/test/support/misc_helpers.rb +26 -0
  37. data/test/support/models.rb +41 -3
  38. data/test/test_helper.rb +13 -4
  39. metadata +24 -27
@@ -5,13 +5,17 @@ module SimpleForm
5
5
  extend MapType
6
6
  include SimpleForm::Inputs
7
7
 
8
- map_type :password, :text, :file, :to => SimpleForm::Inputs::MappingInput
9
- map_type :string, :email, :search, :tel, :url, :to => SimpleForm::Inputs::StringInput
10
- map_type :integer, :decimal, :float, :to => SimpleForm::Inputs::NumericInput
11
- map_type :select, :radio, :check_boxes, :to => SimpleForm::Inputs::CollectionInput
12
- map_type :date, :time, :datetime, :to => SimpleForm::Inputs::DateTimeInput
13
- map_type :country, :time_zone, :to => SimpleForm::Inputs::PriorityInput
14
- map_type :boolean, :to => SimpleForm::Inputs::BooleanInput
8
+ map_type :text, :file, :to => SimpleForm::Inputs::MappingInput
9
+ map_type :string, :password, :email, :search, :tel, :url, :to => SimpleForm::Inputs::StringInput
10
+ map_type :integer, :decimal, :float, :to => SimpleForm::Inputs::NumericInput
11
+ map_type :select, :radio, :check_boxes, :to => SimpleForm::Inputs::CollectionInput
12
+ map_type :date, :time, :datetime, :to => SimpleForm::Inputs::DateTimeInput
13
+ map_type :country, :time_zone, :to => SimpleForm::Inputs::PriorityInput
14
+ map_type :boolean, :to => SimpleForm::Inputs::BooleanInput
15
+
16
+ def self.discovery_cache
17
+ @discovery_cache ||= {}
18
+ end
15
19
 
16
20
  # Basic input helper, combines all components in the stack to generate
17
21
  # input html based on options the user define and some guesses through
@@ -85,12 +89,31 @@ module SimpleForm
85
89
  if block_given?
86
90
  SimpleForm::Inputs::BlockInput.new(self, attribute_name, column, input_type, options, &block).render
87
91
  else
88
- klass = self.class.mappings[input_type] || self.class.const_get("#{input_type.to_s.camelize}Input")
89
- klass.new(self, attribute_name, column, input_type, options).render
92
+ find_mapping(input_type).new(self, attribute_name, column, input_type, options).render
90
93
  end
91
94
  end
92
95
  alias :attribute :input
93
96
 
97
+ # Creates a input tag for the given attribute. All the given options
98
+ # are sent as :input_html.
99
+ #
100
+ # == Examples
101
+ #
102
+ # simple_form_for @user do |f|
103
+ # f.input_field :name
104
+ # end
105
+ #
106
+ # This is the output html (only the input portion, not the form):
107
+ #
108
+ # <input class="string required" id="user_name" maxlength="100"
109
+ # name="user[name]" size="100" type="text" value="Carlos" />
110
+ #
111
+ def input_field(attribute_name, options={})
112
+ options[:input_html] = options.except(:as)
113
+ options.merge!(:components => [:input], :wrapper => false)
114
+ input(attribute_name, options)
115
+ end
116
+
94
117
  # Helper for dealing with association selects/radios, generating the
95
118
  # collection automatically. It's just a wrapper to input, so all options
96
119
  # supported in input are also supported by association. Some extra options
@@ -133,7 +156,7 @@ module SimpleForm
133
156
  when :belongs_to
134
157
  reflection.options[:foreign_key] || :"#{reflection.name}_id"
135
158
  when :has_one
136
- raise ":has_one association are not supported by f.association"
159
+ raise ":has_one associations are not supported by f.association"
137
160
  else
138
161
  if options[:as] == :select
139
162
  html_options = options[:input_html] ||= {}
@@ -141,6 +164,12 @@ module SimpleForm
141
164
  html_options[:multiple] = true unless html_options.key?(:multiple)
142
165
  end
143
166
 
167
+ # Force the association to be preloaded for performance.
168
+ if options[:preload] != false && object.respond_to?(association)
169
+ target = object.send(association)
170
+ target.to_a if target.respond_to?(:to_a)
171
+ end
172
+
144
173
  :"#{reflection.name.to_s.singularize}_ids"
145
174
  end
146
175
 
@@ -175,12 +204,29 @@ module SimpleForm
175
204
  # f.error :name, :id => "cool_error"
176
205
  #
177
206
  def error(attribute_name, options={})
178
- options[:error_html] = options
207
+ options[:error_html] = options.dup
179
208
  column = find_attribute_column(attribute_name)
180
209
  input_type = default_input_type(attribute_name, column, options)
181
210
  SimpleForm::Inputs::Base.new(self, attribute_name, column, input_type, options).error
182
211
  end
183
212
 
213
+ # Return the error but also considering its name. This is used
214
+ # when errors for a hidden field need to be shown.
215
+ #
216
+ # == Examples
217
+ #
218
+ # f.full_error :token #=> <span class="error">Token is invalid</span>
219
+ #
220
+ def full_error(attribute_name, options={})
221
+ options[:error_prefix] ||= if object.class.respond_to?(:human_attribute_name)
222
+ object.class.human_attribute_name(attribute_name.to_s)
223
+ else
224
+ attribute_name.to_s.humanize
225
+ end
226
+
227
+ error(attribute_name, options)
228
+ end
229
+
184
230
  # Creates a hint tag for the given attribute. Accepts a symbol indicating
185
231
  # an attribute for I18n lookup or a string. All the given options are sent
186
232
  # as :hint_html.
@@ -192,7 +238,7 @@ module SimpleForm
192
238
  # f.hint "Don't forget to accept this"
193
239
  #
194
240
  def hint(attribute_name, options={})
195
- options[:hint_html] = options
241
+ options[:hint_html] = options.dup
196
242
  if attribute_name.is_a?(String)
197
243
  options[:hint] = attribute_name
198
244
  attribute_name, column, input_type = nil, nil, nil
@@ -219,8 +265,8 @@ module SimpleForm
219
265
  def label(attribute_name, *args)
220
266
  return super if args.first.is_a?(String)
221
267
  options = args.extract_options!
268
+ options[:label_html] = options.dup
222
269
  options[:label] = options.delete(:label)
223
- options[:label_html] = options
224
270
  options[:required] = options.delete(:required)
225
271
  column = find_attribute_column(attribute_name)
226
272
  input_type = default_input_type(attribute_name, column, options)
@@ -250,47 +296,94 @@ module SimpleForm
250
296
  def default_input_type(attribute_name, column, options) #:nodoc:
251
297
  return options[:as].to_sym if options[:as]
252
298
  return :select if options[:collection]
299
+ custom_type = find_custom_type(attribute_name.to_s) and return custom_type
253
300
 
254
301
  input_type = column.try(:type)
255
-
256
302
  case input_type
257
- when :timestamp
258
- :datetime
259
- when :string, nil
260
- match = case attribute_name.to_s
261
- when /password/ then :password
262
- when /time_zone/ then :time_zone
263
- when /country/ then :country
264
- when /email/ then :email
265
- when /phone/ then :tel
266
- when /url/ then :url
267
- else
268
- SimpleForm.input_mappings.find { |match, type|
269
- attribute_name.to_s =~ match
270
- }.try(:last) if SimpleForm.input_mappings
271
- end
272
-
273
- match || input_type || file_method?(attribute_name) || :string
303
+ when :timestamp
304
+ :datetime
305
+ when :string, nil
306
+ case attribute_name.to_s
307
+ when /password/ then :password
308
+ when /time_zone/ then :time_zone
309
+ when /country/ then :country
310
+ when /email/ then :email
311
+ when /phone/ then :tel
312
+ when /url/ then :url
274
313
  else
275
- input_type
314
+ file_method?(attribute_name) ? :file : (input_type || :string)
315
+ end
316
+ else
317
+ input_type
276
318
  end
277
319
  end
278
320
 
279
- # Checks if attribute is a file_method.
321
+ def find_custom_type(attribute_name) #:nodoc:
322
+ SimpleForm.input_mappings.find { |match, type|
323
+ attribute_name =~ match
324
+ }.try(:last) if SimpleForm.input_mappings
325
+ end
326
+
280
327
  def file_method?(attribute_name) #:nodoc:
281
328
  file = @object.send(attribute_name) if @object.respond_to?(attribute_name)
282
- :file if file && SimpleForm.file_methods.any? { |m| file.respond_to?(m) }
329
+ file && SimpleForm.file_methods.any? { |m| file.respond_to?(m) }
283
330
  end
284
331
 
285
- # Finds the database column for the given attribute
286
332
  def find_attribute_column(attribute_name) #:nodoc:
287
- @object.column_for_attribute(attribute_name) if @object.respond_to?(:column_for_attribute)
333
+ if @object.respond_to?(:column_for_attribute)
334
+ @object.column_for_attribute(attribute_name)
335
+ end
288
336
  end
289
337
 
290
- # Find reflection related to association
291
338
  def find_association_reflection(association) #:nodoc:
292
- @object.class.reflect_on_association(association) if @object.class.respond_to?(:reflect_on_association)
339
+ if @object.class.respond_to?(:reflect_on_association)
340
+ @object.class.reflect_on_association(association)
341
+ end
293
342
  end
294
343
 
344
+ # Attempts to find a mapping. It follows the following rules:
345
+ #
346
+ # 1) It tries to find a registered mapping, if succeeds:
347
+ # a) Try to find an alternative with the same name in the Object scope
348
+ # b) Or use the found mapping
349
+ # 2) If not, fallbacks to #{input_type}Input
350
+ # 3) If not, fallbacks to SimpleForm::Inputs::#{input_type}Input
351
+ def find_mapping(input_type) #:nodoc:
352
+ discovery_cache[input_type] ||=
353
+ if mapping = self.class.mappings[input_type]
354
+ mapping_override(mapping) || mapping
355
+ else
356
+ camelized = "#{input_type.to_s.camelize}Input"
357
+ attempt_mapping(camelized, Object) || attempt_mapping(camelized, self.class) ||
358
+ raise("No input found for #{input_type}")
359
+ end
360
+ end
361
+
362
+ # If cache_discovery is enabled, use the class level cache that persists
363
+ # between requests, otherwise use the instance one.
364
+ def discovery_cache #:nodoc:
365
+ if SimpleForm.cache_discovery
366
+ self.class.discovery_cache
367
+ else
368
+ @discovery_cache ||= {}
369
+ end
370
+ end
371
+
372
+ def mapping_override(klass) #:nodoc:
373
+ name = klass.name
374
+ if name =~ /^SimpleForm::Inputs/
375
+ attempt_mapping name.split("::").last, Object
376
+ end
377
+ end
378
+
379
+ def attempt_mapping(mapping, at) #:nodoc:
380
+ return if SimpleForm.inputs_discovery == false && at == Object
381
+
382
+ begin
383
+ at.const_get(mapping)
384
+ rescue NameError => e
385
+ e.message =~ /#{mapping}$/ ? nil : raise
386
+ end
387
+ end
295
388
  end
296
389
  end
@@ -28,8 +28,9 @@ module SimpleForm
28
28
  @reflection = options.delete(:reflection)
29
29
  @options = options
30
30
  @input_html_options = html_options_for(:input, input_html_classes).tap do |o|
31
- o[:required] = true if attribute_required?
32
- o[:disabled] = true if disabled?
31
+ o[:required] = true if has_required? # Don't make this conditional on HTML5 here, because we want the CSS class to be set
32
+ o[:disabled] = true if disabled?
33
+ o[:autofocus] = true if has_autofocus? && SimpleForm.html5
33
34
  end
34
35
  end
35
36
 
@@ -71,8 +72,17 @@ module SimpleForm
71
72
  end
72
73
  end
73
74
 
75
+ # Whether this input is valid for HTML 5 required attribute.
76
+ def has_required?
77
+ attribute_required? && SimpleForm.html5
78
+ end
79
+
80
+ def has_autofocus?
81
+ options[:autofocus]
82
+ end
83
+
74
84
  def has_validators?
75
- object.class.respond_to?(:validators_on)
85
+ attribute_name && object.class.respond_to?(:validators_on)
76
86
  end
77
87
 
78
88
  def attribute_validators
@@ -120,6 +130,15 @@ module SimpleForm
120
130
  # Action is the action being rendered, usually :new or :edit.
121
131
  # And attribute is the attribute itself, :name for example.
122
132
  #
133
+ # The lookup for nested attributes is also done in a nested format using
134
+ # both model and nested object names, such as follow:
135
+ #
136
+ # simple_form.{namespace}.{model}.{nested}.{action}.{attribute}
137
+ # simple_form.{namespace}.{model}.{nested}.{attribute}
138
+ # simple_form.{namespace}.{nested}.{action}.{attribute}
139
+ # simple_form.{namespace}.{nested}.{attribute}
140
+ # simple_form.{namespace}.{attribute}
141
+ #
123
142
  # Example:
124
143
  #
125
144
  # simple_form:
@@ -133,14 +152,36 @@ module SimpleForm
133
152
  # Take a look at our locale example file.
134
153
  def translate(namespace, default='')
135
154
  return nil unless SimpleForm.translate
136
- lookups = []
137
- lookups << :"#{object_name}.#{lookup_action}.#{reflection_or_attribute_name}"
138
- lookups << :"#{object_name}.#{reflection_or_attribute_name}"
155
+
156
+ model_names = lookup_model_names
157
+ lookups = []
158
+
159
+ while !model_names.empty?
160
+ joined_model_names = model_names.join(".")
161
+ model_names.shift
162
+
163
+ lookups << :"#{joined_model_names}.#{lookup_action}.#{reflection_or_attribute_name}"
164
+ lookups << :"#{joined_model_names}.#{reflection_or_attribute_name}"
165
+ end
139
166
  lookups << :"#{reflection_or_attribute_name}"
140
167
  lookups << default
168
+
141
169
  I18n.t(lookups.shift, :scope => :"simple_form.#{namespace}", :default => lookups).presence
142
170
  end
143
171
 
172
+ # Extract the model names from the object_name mess.
173
+ #
174
+ # Example:
175
+ #
176
+ # route[blocks_attributes][0][blocks_learning_object_attributes][1][foo_attributes]
177
+ # ["route", "blocks", "blocks_learning_object", "foo"]
178
+ #
179
+ def lookup_model_names
180
+ object_name.to_s.scan(/([a-zA-Z_]+)/).flatten.map do |x|
181
+ x.gsub('_attributes', '')
182
+ end
183
+ end
184
+
144
185
  # The action to be used in lookup.
145
186
  def lookup_action
146
187
  action = template.controller.action_name
@@ -148,6 +189,11 @@ module SimpleForm
148
189
  action = action.to_sym
149
190
  ACTIONS[action] || action
150
191
  end
192
+
193
+ def input_method
194
+ self.class.mappings[input_type] or
195
+ raise("Could not find method for #{input_type.inspect}")
196
+ end
151
197
  end
152
198
  end
153
199
  end
@@ -14,7 +14,6 @@ module SimpleForm
14
14
 
15
15
  def input
16
16
  label_method, value_method = detect_collection_methods
17
-
18
17
  @builder.send(:"collection_#{input_type}", attribute_name, collection,
19
18
  value_method, label_method, input_options, input_html_options)
20
19
  end
@@ -31,6 +30,11 @@ module SimpleForm
31
30
  @collection ||= (options.delete(:collection) || self.class.boolean_collection).to_a
32
31
  end
33
32
 
33
+ # Select components does not allow the required html tag.
34
+ def has_required?
35
+ super && input_type != :select
36
+ end
37
+
34
38
  # Check if :include_blank must be included by default.
35
39
  def skip_include_blank?
36
40
  (options.keys & [:prompt, :include_blank, :default, :selected]).any? ||
@@ -7,6 +7,10 @@ module SimpleForm
7
7
 
8
8
  private
9
9
 
10
+ def has_required?
11
+ false
12
+ end
13
+
10
14
  def label_target
11
15
  case input_type
12
16
  when :date, :datetime
@@ -4,7 +4,6 @@ module SimpleForm
4
4
  class MappingInput < Base
5
5
  extend MapType
6
6
 
7
- map_type :password, :to => :password_field
8
7
  map_type :text, :to => :text_area
9
8
  map_type :file, :to => :file_field
10
9
 
@@ -14,11 +13,6 @@ module SimpleForm
14
13
 
15
14
  private
16
15
 
17
- def input_method
18
- self.class.mappings[input_type] or
19
- raise("Could not find method for #{input_type.inspect}")
20
- end
21
-
22
16
  def has_placeholder?
23
17
  (text? || password?) && placeholder_present?
24
18
  end
@@ -2,10 +2,10 @@ module SimpleForm
2
2
  module Inputs
3
3
  class NumericInput < Base
4
4
  def input
5
- input_html_options[:type] ||= "number"
5
+ input_html_options[:type] ||= "number" if SimpleForm.html5
6
6
  input_html_options[:size] ||= SimpleForm.default_input_size
7
- input_html_options[:step] ||= 1 if integer?
8
- infer_attributes_from_validations!
7
+ input_html_options[:step] ||= integer? ? 1 : "any" if SimpleForm.html5
8
+ infer_attributes_from_validations! if SimpleForm.html5
9
9
  @builder.text_field(attribute_name, input_html_options)
10
10
  end
11
11
 
@@ -35,23 +35,31 @@ module SimpleForm
35
35
 
36
36
  def minimum_value(validator_options)
37
37
  if integer? && validator_options.key?(:greater_than)
38
- validator_options[:greater_than] + 1
38
+ evaluate_validator_option(validator_options[:greater_than]) + 1
39
39
  else
40
- validator_options[:greater_than_or_equal_to]
40
+ evaluate_validator_option(validator_options[:greater_than_or_equal_to])
41
41
  end
42
42
  end
43
43
 
44
44
  def maximum_value(validator_options)
45
45
  if integer? && validator_options.key?(:less_than)
46
- validator_options[:less_than] - 1
46
+ evaluate_validator_option(validator_options[:less_than]) - 1
47
47
  else
48
- validator_options[:less_than_or_equal_to]
48
+ evaluate_validator_option(validator_options[:less_than_or_equal_to])
49
49
  end
50
50
  end
51
51
 
52
52
  def find_numericality_validator
53
53
  attribute_validators.find { |v| ActiveModel::Validations::NumericalityValidator === v }
54
54
  end
55
+
56
+ private
57
+
58
+ def evaluate_validator_option(option)
59
+ return option if option.is_a?(Numeric)
60
+ return object.send(option) if option.is_a?(Symbol)
61
+ return option.call(object) if option.respond_to?(:call)
62
+ end
55
63
  end
56
64
  end
57
65
  end
@@ -12,9 +12,13 @@ module SimpleForm
12
12
 
13
13
  protected
14
14
 
15
+ def has_required?
16
+ false
17
+ end
18
+
15
19
  def skip_include_blank?
16
20
  super || input_priority.present?
17
21
  end
18
22
  end
19
23
  end
20
- end
24
+ end
@@ -1,12 +1,19 @@
1
1
  module SimpleForm
2
2
  module Inputs
3
3
  class StringInput < Base
4
+ extend MapType
5
+
6
+ map_type :string, :email, :search, :tel, :url, :to => :text_field
7
+ map_type :password, :to => :password_field
8
+
4
9
  def input
5
10
  input_html_options[:size] ||= [limit, SimpleForm.default_input_size].compact.min
6
- input_html_options[:maxlength] ||= limit if limit
7
- input_html_options[:type] ||= input_type unless string?
11
+ input_html_options[:maxlength] ||= limit if limit && SimpleForm.html5
12
+ if password? || SimpleForm.html5
13
+ input_html_options[:type] ||= input_type unless string?
14
+ end
8
15
 
9
- @builder.text_field(attribute_name, input_html_options)
16
+ @builder.send(input_method, attribute_name, input_html_options)
10
17
  end
11
18
 
12
19
  def input_html_classes
@@ -26,6 +33,10 @@ module SimpleForm
26
33
  def string?
27
34
  input_type == :string
28
35
  end
36
+
37
+ def password?
38
+ input_type == :password
39
+ end
29
40
  end
30
41
  end
31
42
  end
@@ -1,13 +1,16 @@
1
+ require 'active_support/core_ext/class/attribute'
2
+
1
3
  module SimpleForm
2
4
  module MapType
3
- def mappings
4
- @mappings ||= {}
5
+ def self.extended(base)
6
+ base.class_attribute :mappings
7
+ base.mappings = {}
5
8
  end
6
9
 
7
10
  def map_type(*types)
8
11
  map_to = types.extract_options![:to]
9
12
  raise ArgumentError, "You need to give :to as option to map_type" unless map_to
10
- types.each { |t| mappings[t] = map_to }
13
+ self.mappings = mappings.merge types.each_with_object({}) { |t, m| m[t] = map_to }
11
14
  end
12
15
  end
13
16
  end
@@ -1,3 +1,3 @@
1
1
  module SimpleForm
2
- VERSION = "1.3.0".freeze
2
+ VERSION = "1.4.0".freeze
3
3
  end
data/lib/simple_form.rb CHANGED
@@ -61,7 +61,7 @@ module SimpleForm
61
61
 
62
62
  # You can wrap each item in a collection of radio/check boxes with a tag, defaulting to none.
63
63
  mattr_accessor :item_wrapper_tag
64
- @@item_wrapper_tag = nil
64
+ @@item_wrapper_tag = :span
65
65
 
66
66
  # You can wrap all inputs in a pre-defined tag. Default is a div.
67
67
  mattr_accessor :wrapper_tag
@@ -79,10 +79,29 @@ module SimpleForm
79
79
  mattr_accessor :label_text
80
80
  @@label_text = lambda { |label, required| "#{required} #{label}" }
81
81
 
82
+ # You can define the class to use on all labels. Default is nil.
83
+ mattr_accessor :label_class
84
+ @@label_class = nil
85
+
86
+ # You can define the class to use on all forms. Default is simple_form.
87
+ mattr_accessor :form_class
88
+ @@form_class = :simple_form
89
+
82
90
  # Whether attributes are required by default (or not).
83
91
  mattr_accessor :required_by_default
84
92
  @@required_by_default = true
85
93
 
94
+ # Tell browsers whether to use default HTML5 validations (novalidate option).
95
+ mattr_accessor :browser_validations
96
+ @@browser_validations = true
97
+
98
+ # Determines whether HTML5 types (:email, :url, :search, :tel) and attributes
99
+ # (e.g. required) are used or not. True by default.
100
+ # Having this on in non-HTML5 compliant sites can cause odd behavior in
101
+ # HTML5-aware browsers such as Chrome.
102
+ mattr_accessor :html5
103
+ @@html5 = true
104
+
86
105
  # Collection of methods to detect if a file type was given.
87
106
  mattr_accessor :file_methods
88
107
  @@file_methods = [ :mounted_as, :file?, :public_filename ]
@@ -110,6 +129,14 @@ module SimpleForm
110
129
  mattr_accessor :translate
111
130
  @@translate = true
112
131
 
132
+ # Automatically discover new inputs in Rails' autoload path.
133
+ mattr_accessor :inputs_discovery
134
+ @@inputs_discovery = true
135
+
136
+ # Cache simple form inputs discovery
137
+ mattr_accessor :cache_discovery
138
+ @@cache_discovery = !Rails.env.development?
139
+
113
140
  # Default way to setup SimpleForm. Run rails generate simple_form:install
114
141
  # to create a fresh initializer with all configuration values.
115
142
  def self.setup
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "simple_form/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "simple_form"
7
+ s.version = SimpleForm::VERSION.dup
8
+ s.platform = Gem::Platform::RUBY
9
+ s.summary = "Forms made easy!"
10
+ s.email = "contact@plataformatec.com.br"
11
+ s.homepage = "http://github.com/plataformatec/simple_form"
12
+ s.description = "Forms made easy!"
13
+ s.authors = ['José Valim', 'Carlos Antônio']
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.test_files -= Dir["test/support/country_select/**/*"]
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.rubyforge_project = "simple_form"
22
+ end
@@ -1,9 +1,11 @@
1
1
  require 'test_helper'
2
2
 
3
3
  class BuilderTest < ActionView::TestCase
4
-
5
- def with_concat_form_for(object, &block)
6
- concat form_for(object, &block)
4
+ def with_custom_form_for(object, *args, &block)
5
+ with_concat_custom_form_for(object) do |f|
6
+ assert f.instance_of?(CustomFormBuilder)
7
+ yield f
8
+ end
7
9
  end
8
10
 
9
11
  def with_collection_radio(object, attribute, collection, value_method, text_method, options={}, html_options={})
@@ -116,10 +118,17 @@ class BuilderTest < ActionView::TestCase
116
118
  assert_select 'form li input[type=radio][value=false]#user_active_false'
117
119
  end
118
120
 
119
- test 'collection radio does not wrap items by default' do
121
+ test 'collection radio wrap items in a span tag by default' do
120
122
  with_collection_radio @user, :active, [true, false], :to_s, :to_s
121
123
 
122
- assert_no_select 'form li'
124
+ assert_select 'form span input[type=radio][value=true]#user_active_true + label'
125
+ assert_select 'form span input[type=radio][value=false]#user_active_false + label'
126
+ end
127
+
128
+ test 'collection radio does not wrap input inside the label' do
129
+ with_collection_radio @user, :active, [true, false], :to_s, :to_s
130
+
131
+ assert_no_select 'form label input'
123
132
  end
124
133
 
125
134
  # COLLECTION CHECK BOX
@@ -259,10 +268,17 @@ class BuilderTest < ActionView::TestCase
259
268
  assert_select 'form li input[type=checkbox][value=false]#user_active_false'
260
269
  end
261
270
 
262
- test 'collection check box does not wrap items by default' do
271
+ test 'collection check box wrap items in a span tag by default' do
272
+ with_collection_check_boxes @user, :active, [true, false], :to_s, :to_s
273
+
274
+ assert_select 'form span input[type=checkbox][value=true]#user_active_true + label'
275
+ assert_select 'form span input[type=checkbox][value=false]#user_active_false + label'
276
+ end
277
+
278
+ test 'collection check box does not wrap input inside the label' do
263
279
  with_collection_check_boxes @user, :active, [true, false], :to_s, :to_s
264
280
 
265
- assert_no_select 'form li'
281
+ assert_no_select 'form label input'
266
282
  end
267
283
 
268
284
  # SIMPLE FIELDS
@@ -273,4 +289,20 @@ class BuilderTest < ActionView::TestCase
273
289
  end
274
290
  end
275
291
  end
292
+
293
+ test 'fields for yields an instance of CustomBuilder if main builder is a CustomBuilder' do
294
+ with_custom_form_for(:user) do |f|
295
+ f.simple_fields_for(:company) do |company|
296
+ assert company.instance_of?(CustomFormBuilder)
297
+ end
298
+ end
299
+ end
300
+
301
+ test 'fields for yields an instance of FormBuilder if it was set in options' do
302
+ with_custom_form_for(:user) do |f|
303
+ f.simple_fields_for(:company, :builder => SimpleForm::FormBuilder) do |company|
304
+ assert company.instance_of?(SimpleForm::FormBuilder)
305
+ end
306
+ end
307
+ end
276
308
  end