simple_form 1.4.2 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of simple_form might be problematic. Click here for more details.

Files changed (51) hide show
  1. data/.gitignore +1 -0
  2. data/.travis.yml +9 -1
  3. data/CHANGELOG.rdoc +20 -0
  4. data/Gemfile +5 -4
  5. data/README.rdoc +2 -2
  6. data/Rakefile +1 -1
  7. data/lib/generators/simple_form/install_generator.rb +2 -6
  8. data/lib/generators/simple_form/templates/{simple_form.rb → config/initializers/simple_form.rb} +0 -0
  9. data/lib/generators/simple_form/templates/{en.yml → config/locales/simple_form.en.yml} +0 -0
  10. data/lib/simple_form.rb +1 -1
  11. data/lib/simple_form/action_view_extensions/builder.rb +12 -5
  12. data/lib/simple_form/action_view_extensions/form_helper.rb +13 -8
  13. data/lib/simple_form/components.rb +1 -1
  14. data/lib/simple_form/components/errors.rb +10 -2
  15. data/lib/simple_form/components/hints.rb +10 -0
  16. data/lib/simple_form/components/label_input.rb +5 -3
  17. data/lib/simple_form/components/labels.rb +10 -4
  18. data/lib/simple_form/components/placeholders.rb +10 -4
  19. data/lib/simple_form/error_notification.rb +1 -1
  20. data/lib/simple_form/form_builder.rb +10 -7
  21. data/lib/simple_form/helpers.rb +9 -0
  22. data/lib/simple_form/helpers/has_errors.rb +15 -0
  23. data/lib/simple_form/helpers/maxlength.rb +24 -0
  24. data/lib/simple_form/helpers/pattern.rb +28 -0
  25. data/lib/simple_form/helpers/required.rb +36 -0
  26. data/lib/simple_form/helpers/validators.rb +44 -0
  27. data/lib/simple_form/inputs.rb +5 -2
  28. data/lib/simple_form/inputs/base.rb +24 -47
  29. data/lib/simple_form/inputs/boolean_input.rb +1 -1
  30. data/lib/simple_form/inputs/collection_input.rb +1 -1
  31. data/lib/simple_form/inputs/date_time_input.rb +1 -1
  32. data/lib/simple_form/inputs/file_input.rb +9 -0
  33. data/lib/simple_form/inputs/hidden_input.rb +1 -1
  34. data/lib/simple_form/inputs/numeric_input.rb +20 -13
  35. data/lib/simple_form/inputs/password_input.rb +13 -0
  36. data/lib/simple_form/inputs/range_input.rb +16 -0
  37. data/lib/simple_form/inputs/string_input.rb +7 -24
  38. data/lib/simple_form/inputs/text_input.rb +12 -0
  39. data/lib/simple_form/version.rb +1 -1
  40. data/test/action_view_extensions/builder_test.rb +59 -0
  41. data/test/action_view_extensions/form_helper_test.rb +21 -0
  42. data/test/discovery_inputs.rb +2 -2
  43. data/test/form_builder_test.rb +54 -0
  44. data/test/inputs_test.rb +185 -20
  45. data/test/support/models.rb +23 -2
  46. data/test/test_helper.rb +1 -0
  47. metadata +17 -11
  48. data/Gemfile.lock +0 -54
  49. data/init.rb +0 -1
  50. data/lib/simple_form/has_errors.rb +0 -14
  51. data/lib/simple_form/inputs/mapping_input.rb +0 -29
@@ -0,0 +1,36 @@
1
+ module SimpleForm
2
+ module Helpers
3
+ module Required
4
+ private
5
+
6
+ def attribute_required?
7
+ @required
8
+ end
9
+
10
+ def calculate_required
11
+ if !options[:required].nil?
12
+ options[:required]
13
+ elsif has_validators?
14
+ (attribute_validators + reflection_validators).any? do |v|
15
+ v.kind == :presence && valid_validator?(v)
16
+ end
17
+ else
18
+ attribute_required_by_default?
19
+ end
20
+ end
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?
28
+ SimpleForm.required_by_default
29
+ end
30
+
31
+ def required_class
32
+ attribute_required? ? :required : :optional
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,44 @@
1
+ module SimpleForm
2
+ module Helpers
3
+ module Validators
4
+ private
5
+
6
+ def has_validators?
7
+ attribute_name && object.class.respond_to?(:validators_on)
8
+ end
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(validator)
40
+ attribute_validators.find { |v| validator === v }
41
+ end
42
+ end
43
+ end
44
+ end
@@ -5,10 +5,13 @@ module SimpleForm
5
5
  autoload :BooleanInput, 'simple_form/inputs/boolean_input'
6
6
  autoload :CollectionInput, 'simple_form/inputs/collection_input'
7
7
  autoload :DateTimeInput, 'simple_form/inputs/date_time_input'
8
+ autoload :FileInput, 'simple_form/inputs/file_input'
8
9
  autoload :HiddenInput, 'simple_form/inputs/hidden_input'
9
- autoload :MappingInput, 'simple_form/inputs/mapping_input'
10
10
  autoload :NumericInput, 'simple_form/inputs/numeric_input'
11
+ autoload :PasswordInput, 'simple_form/inputs/password_input'
11
12
  autoload :PriorityInput, 'simple_form/inputs/priority_input'
13
+ autoload :RangeInput, 'simple_form/inputs/range_input'
12
14
  autoload :StringInput, 'simple_form/inputs/string_input'
15
+ autoload :TextInput, 'simple_form/inputs/text_input'
13
16
  end
14
- end
17
+ end
@@ -9,12 +9,26 @@ module SimpleForm
9
9
  :update => :edit
10
10
  }
11
11
 
12
+ include SimpleForm::Helpers::Required
13
+ include SimpleForm::Helpers::Validators
14
+ include SimpleForm::Helpers::Maxlength
15
+ include SimpleForm::Helpers::Pattern
16
+
12
17
  include SimpleForm::Components::Errors
13
18
  include SimpleForm::Components::Hints
14
19
  include SimpleForm::Components::LabelInput
15
20
  include SimpleForm::Components::Placeholders
16
21
  include SimpleForm::Components::Wrapper
17
22
 
23
+ # Enables certain components support to the given input.
24
+ def self.enable(*args)
25
+ args.each { |m| alias_method m, :"enabled_#{m}" }
26
+ end
27
+
28
+ def self.disable(*args)
29
+ args.each { |m| alias_method m, :"disabled_#{m}" }
30
+ end
31
+
18
32
  attr_reader :attribute_name, :column, :input_type, :reflection,
19
33
  :options, :input_html_options
20
34
 
@@ -27,8 +41,9 @@ module SimpleForm
27
41
  @input_type = input_type
28
42
  @reflection = options.delete(:reflection)
29
43
  @options = options
44
+ @required = calculate_required
30
45
  @input_html_options = html_options_for(:input, input_html_classes).tap do |o|
31
- o[:required] = true if has_required? # Don't make this conditional on HTML5 here, because we want the CSS class to be set
46
+ o[:required] = true if has_required?
32
47
  o[:disabled] = true if disabled?
33
48
  o[:autofocus] = true if has_autofocus? && SimpleForm.html5
34
49
  end
@@ -56,57 +71,24 @@ module SimpleForm
56
71
  wrap(content)
57
72
  end
58
73
 
59
- protected
74
+ private
60
75
 
61
- def components_list
62
- options[:components] || SimpleForm.components
76
+ def add_size!
77
+ input_html_options[:size] ||= [limit, SimpleForm.default_input_size].compact.min
63
78
  end
64
79
 
65
- def attribute_required?
66
- if !options[:required].nil?
67
- options[:required]
68
- elsif has_validators?
69
- (attribute_validators + reflection_validators).any? do |v|
70
- v.kind == :presence && !conditional_validators?(v)
71
- end
72
- else
73
- attribute_required_by_default?
74
- end
80
+ def limit
81
+ column && column.limit
75
82
  end
76
83
 
77
- # Whether this input is valid for HTML 5 required attribute.
78
- def has_required?
79
- attribute_required? && SimpleForm.html5 && SimpleForm.browser_validations
84
+ def components_list
85
+ options[:components] || SimpleForm.components
80
86
  end
81
87
 
82
88
  def has_autofocus?
83
89
  options[:autofocus]
84
90
  end
85
91
 
86
- def has_validators?
87
- attribute_name && object.class.respond_to?(:validators_on)
88
- end
89
-
90
- def attribute_validators
91
- object.class.validators_on(attribute_name)
92
- end
93
-
94
- def reflection_validators
95
- reflection ? object.class.validators_on(reflection.name) : []
96
- end
97
-
98
- def conditional_validators?(validator)
99
- validator.options.include?(:if) || validator.options.include?(:unless)
100
- end
101
-
102
- def attribute_required_by_default?
103
- SimpleForm.required_by_default
104
- end
105
-
106
- def required_class
107
- attribute_required? ? :required : :optional
108
- end
109
-
110
92
  # Find reflection name when available, otherwise use attribute
111
93
  def reflection_or_attribute_name
112
94
  reflection ? reflection.name : attribute_name
@@ -120,7 +102,7 @@ module SimpleForm
120
102
  end
121
103
 
122
104
  def disabled?
123
- options[:disabled]
105
+ options[:disabled] == true
124
106
  end
125
107
 
126
108
  # Lookup translations for the given namespace using I18n, based on object name,
@@ -197,11 +179,6 @@ module SimpleForm
197
179
  action = action.to_sym
198
180
  ACTIONS[action] || action
199
181
  end
200
-
201
- def input_method
202
- self.class.mappings[input_type] or
203
- raise("Could not find method for #{input_type.inspect}")
204
- end
205
182
  end
206
183
  end
207
184
  end
@@ -9,7 +9,7 @@ module SimpleForm
9
9
  input + (options[:label] == false ? "" : label)
10
10
  end
11
11
 
12
- protected
12
+ private
13
13
 
14
14
  # Booleans are not required by default because in most of the cases
15
15
  # it makes no sense marking them as required. The only exception is
@@ -24,7 +24,7 @@ module SimpleForm
24
24
  options
25
25
  end
26
26
 
27
- protected
27
+ private
28
28
 
29
29
  def collection
30
30
  @collection ||= (options.delete(:collection) || self.class.boolean_collection).to_a
@@ -5,7 +5,7 @@ module SimpleForm
5
5
  @builder.send(:"#{input_type}_select", attribute_name, input_options, input_html_options)
6
6
  end
7
7
 
8
- private
8
+ private
9
9
 
10
10
  def has_required?
11
11
  false
@@ -0,0 +1,9 @@
1
+ module SimpleForm
2
+ module Inputs
3
+ class FileInput < Base
4
+ def input
5
+ @builder.file_field(attribute_name, input_html_options)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -6,7 +6,7 @@ module SimpleForm
6
6
  end
7
7
  alias :input :render
8
8
 
9
- protected
9
+ private
10
10
 
11
11
  def attribute_required?
12
12
  false
@@ -1,11 +1,15 @@
1
1
  module SimpleForm
2
2
  module Inputs
3
3
  class NumericInput < Base
4
+ enable :placeholder
5
+
4
6
  def input
5
- input_html_options[:type] ||= "number" if SimpleForm.html5
6
- input_html_options[:size] ||= SimpleForm.default_input_size
7
- input_html_options[:step] ||= integer? ? 1 : "any" if SimpleForm.html5
8
- infer_attributes_from_validations! if SimpleForm.html5
7
+ add_size!
8
+ if SimpleForm.html5
9
+ input_html_options[:type] ||= "number"
10
+ input_html_options[:step] ||= integer? ? 1 : "any"
11
+ infer_attributes_from_validations!
12
+ end
9
13
  @builder.text_field(attribute_name, input_html_options)
10
14
  end
11
15
 
@@ -13,10 +17,11 @@ module SimpleForm
13
17
  super.unshift("numeric")
14
18
  end
15
19
 
16
- protected
20
+ private
17
21
 
18
- def has_placeholder?
19
- placeholder_present?
22
+ # Rails adds the size attr by default, if the :size key does not exist.
23
+ def add_size!
24
+ input_html_options[:size] ||= nil
20
25
  end
21
26
 
22
27
  def infer_attributes_from_validations!
@@ -50,15 +55,17 @@ module SimpleForm
50
55
  end
51
56
 
52
57
  def find_numericality_validator
53
- attribute_validators.find { |v| ActiveModel::Validations::NumericalityValidator === v }
58
+ find_validator(ActiveModel::Validations::NumericalityValidator)
54
59
  end
55
60
 
56
- private
57
-
58
61
  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
+ if option.is_a?(Numeric)
63
+ option
64
+ elsif option.is_a?(Symbol)
65
+ object.send(option)
66
+ elsif option.respond_to?(:call)
67
+ option.call(object)
68
+ end
62
69
  end
63
70
  end
64
71
  end
@@ -0,0 +1,13 @@
1
+ module SimpleForm
2
+ module Inputs
3
+ class PasswordInput < Base
4
+ enable :placeholder
5
+
6
+ def input
7
+ add_size!
8
+ add_maxlength!
9
+ @builder.password_field(attribute_name, input_html_options)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ module SimpleForm
2
+ module Inputs
3
+ class RangeInput < NumericInput
4
+ disable :placeholder
5
+
6
+ def input
7
+ if SimpleForm.html5
8
+ input_html_options[:type] ||= "range"
9
+ input_html_options[:step] ||= 1
10
+ end
11
+
12
+ super
13
+ end
14
+ end
15
+ end
16
+ end
@@ -1,42 +1,25 @@
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
4
+ enable :placeholder
8
5
 
9
6
  def input
10
- input_html_options[:size] ||= [limit, SimpleForm.default_input_size].compact.min
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
15
-
16
- @builder.send(input_method, attribute_name, input_html_options)
7
+ input_html_options[:type] ||= input_type if SimpleForm.html5 && !string?
8
+ add_maxlength!
9
+ add_pattern!
10
+ add_size!
11
+ @builder.text_field(attribute_name, input_html_options)
17
12
  end
18
13
 
19
14
  def input_html_classes
20
15
  string? ? super : super.unshift("string")
21
16
  end
22
17
 
23
- protected
24
-
25
- def limit
26
- column && column.limit
27
- end
28
-
29
- def has_placeholder?
30
- placeholder_present?
31
- end
18
+ private
32
19
 
33
20
  def string?
34
21
  input_type == :string
35
22
  end
36
-
37
- def password?
38
- input_type == :password
39
- end
40
23
  end
41
24
  end
42
25
  end
@@ -0,0 +1,12 @@
1
+ module SimpleForm
2
+ module Inputs
3
+ class TextInput < Base
4
+ enable :placeholder
5
+
6
+ def input
7
+ add_maxlength!
8
+ @builder.text_area(attribute_name, input_html_options)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -1,3 +1,3 @@
1
1
  module SimpleForm
2
- VERSION = "1.4.2".freeze
2
+ VERSION = "1.5.0".freeze
3
3
  end
@@ -96,6 +96,24 @@ class BuilderTest < ActionView::TestCase
96
96
  assert_select 'form ul input[type=radio][value=false]#user_active_false'
97
97
  end
98
98
 
99
+ test 'collection radio does not wrap the collection in the explicitly false collection wrapper tag' do
100
+ swap SimpleForm, :collection_wrapper_tag => :ul do
101
+ with_collection_radio @user, :active, [true, false], :to_s, :to_s, :collection_wrapper_tag => false
102
+
103
+ assert_no_select 'form ul'
104
+ assert_no_select 'form ul'
105
+ end
106
+ end
107
+
108
+ test 'collection radio does not wrap the collection in the explicitly nil collection wrapper tag' do
109
+ swap SimpleForm, :collection_wrapper_tag => :ul do
110
+ with_collection_radio @user, :active, [true, false], :to_s, :to_s, :collection_wrapper_tag => nil
111
+
112
+ assert_no_select 'form ul'
113
+ assert_no_select 'form ul'
114
+ end
115
+ end
116
+
99
117
  test 'collection radio does not wrap the collection by default' do
100
118
  with_collection_radio @user, :active, [true, false], :to_s, :to_s
101
119
 
@@ -118,6 +136,20 @@ class BuilderTest < ActionView::TestCase
118
136
  assert_select 'form li input[type=radio][value=false]#user_active_false'
119
137
  end
120
138
 
139
+ test 'collection radio does not wrap each label/radio in the explicitly false item wrapper tag' do
140
+ with_collection_radio @user, :active, [true, false], :to_s, :to_s, :item_wrapper_tag => false
141
+
142
+ assert_no_select 'form span input[type=radio][value=true]#user_active_true'
143
+ assert_no_select 'form span input[type=radio][value=false]#user_active_false'
144
+ end
145
+
146
+ test 'collection radio does not wrap each label/radio in the explicitly nil item wrapper tag' do
147
+ with_collection_radio @user, :active, [true, false], :to_s, :to_s, :item_wrapper_tag => nil
148
+
149
+ assert_no_select 'form span input[type=radio][value=true]#user_active_true'
150
+ assert_no_select 'form span input[type=radio][value=false]#user_active_false'
151
+ end
152
+
121
153
  test 'collection radio wrap items in a span tag by default' do
122
154
  with_collection_radio @user, :active, [true, false], :to_s, :to_s
123
155
 
@@ -246,6 +278,13 @@ class BuilderTest < ActionView::TestCase
246
278
  assert_select 'form ul input[type=checkbox][value=false]#user_active_false'
247
279
  end
248
280
 
281
+ test 'collection check box does not wrap the collection in the explicitly false collection wrapper tag' do
282
+ with_collection_check_boxes @user, :active, [true, false], :to_s, :to_s, :collection_wrapper_tag => false, :item_wrapper_tag => false
283
+
284
+ assert_select 'form > input[type=checkbox][value=true]#user_active_true'
285
+ assert_select 'form > input[type=checkbox][value=false]#user_active_false'
286
+ end
287
+
249
288
  test 'collection check box does not wrap the collection by default' do
250
289
  with_collection_check_boxes @user, :active, [true, false], :to_s, :to_s
251
290
 
@@ -268,6 +307,13 @@ class BuilderTest < ActionView::TestCase
268
307
  assert_select 'form li input[type=checkbox][value=false]#user_active_false'
269
308
  end
270
309
 
310
+ test 'collection check box does not wrapp each label/radio in the explicitly false item wrapper tag' do
311
+ with_collection_check_boxes @user, :active, [true, false], :to_s, :to_s, :item_wrapper_tag => false
312
+
313
+ assert_select 'form > input[type=checkbox][value=true]#user_active_true'
314
+ assert_select 'form > input[type=checkbox][value=false]#user_active_false'
315
+ end
316
+
271
317
  test 'collection check box wrap items in a span tag by default' do
272
318
  with_collection_check_boxes @user, :active, [true, false], :to_s, :to_s
273
319
 
@@ -290,6 +336,19 @@ class BuilderTest < ActionView::TestCase
290
336
  end
291
337
  end
292
338
 
339
+ test 'fields for with a hash like model yeilds an instance of FormBuilder' do
340
+ @hash_backed_author = HashBackedAuthor.new
341
+
342
+ with_concat_form_for(:user) do |f|
343
+ f.simple_fields_for(:author, @hash_backed_author) do |author|
344
+ assert author.instance_of?(SimpleForm::FormBuilder)
345
+ author.input :name
346
+ end
347
+ end
348
+
349
+ assert_select "input[name='user[author][name]'][value='hash backed author']"
350
+ end
351
+
293
352
  test 'fields for yields an instance of CustomBuilder if main builder is a CustomBuilder' do
294
353
  with_custom_form_for(:user) do |f|
295
354
  f.simple_fields_for(:company) do |company|