simple_form 1.2.0 → 1.2.1

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 (38) hide show
  1. data/README.rdoc +30 -30
  2. data/lib/generators/simple_form/USAGE +1 -1
  3. data/lib/generators/simple_form/install_generator.rb +5 -6
  4. data/lib/generators/simple_form/templates/_form.html.haml +18 -0
  5. data/lib/generators/simple_form/templates/en.yml +14 -0
  6. data/lib/generators/simple_form/templates/simple_form.rb +21 -8
  7. data/lib/simple_form.rb +29 -8
  8. data/lib/simple_form/action_view_extensions/form_helper.rb +24 -1
  9. data/lib/simple_form/components.rb +5 -4
  10. data/lib/simple_form/components/errors.rb +13 -5
  11. data/lib/simple_form/components/hints.rb +1 -1
  12. data/lib/simple_form/components/label_input.rb +13 -0
  13. data/lib/simple_form/components/labels.rb +1 -1
  14. data/lib/simple_form/components/wrapper.rb +12 -2
  15. data/lib/simple_form/error_notification.rb +44 -0
  16. data/lib/simple_form/form_builder.rb +17 -1
  17. data/lib/simple_form/inputs.rb +1 -0
  18. data/lib/simple_form/inputs/base.rb +17 -9
  19. data/lib/simple_form/inputs/boolean_input.rb +22 -0
  20. data/lib/simple_form/inputs/collection_input.rb +6 -6
  21. data/lib/simple_form/inputs/date_time_input.rb +4 -4
  22. data/lib/simple_form/inputs/mapping_input.rb +0 -1
  23. data/lib/simple_form/inputs/numeric_input.rb +5 -1
  24. data/lib/simple_form/inputs/string_input.rb +5 -6
  25. data/lib/simple_form/version.rb +1 -1
  26. data/test/action_view_extensions/builder_test.rb +18 -18
  27. data/test/action_view_extensions/form_helper_test.rb +5 -5
  28. data/test/components/error_test.rb +14 -5
  29. data/test/components/hint_test.rb +1 -1
  30. data/test/components/label_test.rb +4 -3
  31. data/test/components/wrapper_test.rb +54 -0
  32. data/test/error_notification_test.rb +61 -0
  33. data/test/form_builder_test.rb +31 -11
  34. data/test/inputs_test.rb +55 -51
  35. data/test/support/mock_controller.rb +4 -4
  36. data/test/support/models.rb +11 -8
  37. data/test/test_helper.rb +8 -9
  38. metadata +30 -4
@@ -14,7 +14,7 @@ module SimpleForm
14
14
  end
15
15
 
16
16
  def hint_html_options
17
- html_options_for(:hint, :hint)
17
+ html_options_for(:hint, [:hint])
18
18
  end
19
19
  end
20
20
  end
@@ -0,0 +1,13 @@
1
+ module SimpleForm
2
+ module Components
3
+ module LabelInput
4
+ def self.included(base)
5
+ base.send :include, SimpleForm::Components::Labels
6
+ end
7
+
8
+ def label_input
9
+ (options[:label] == false ? "" : label) + input
10
+ end
11
+ end
12
+ end
13
+ end
@@ -36,7 +36,7 @@ module SimpleForm
36
36
  end
37
37
 
38
38
  def label_html_options
39
- label_options = html_options_for(:label, input_type, required_class)
39
+ label_options = html_options_for(:label, [input_type, required_class])
40
40
  label_options[:for] = options[:input_html][:id] if options.key?(:input_html)
41
41
  label_options
42
42
  end
@@ -13,9 +13,19 @@ module SimpleForm
13
13
  options[:wrapper_tag] || SimpleForm.wrapper_tag
14
14
  end
15
15
 
16
+ def wrapper_class
17
+ options[:wrapper_class] || SimpleForm.wrapper_class
18
+ end
19
+
20
+ def wrapper_error_class
21
+ options[:wrapper_error_class] || SimpleForm.wrapper_error_class
22
+ end
23
+
16
24
  def wrapper_html_options
17
- html_options_for(:wrapper, "input", input_type, required_class)
25
+ css_classes = input_html_classes.unshift(wrapper_class)
26
+ css_classes << wrapper_error_class if has_errors?
27
+ html_options_for(:wrapper, css_classes)
18
28
  end
19
29
  end
20
30
  end
21
- end
31
+ end
@@ -0,0 +1,44 @@
1
+ module SimpleForm
2
+ class ErrorNotification
3
+ delegate :object, :object_name, :template, :to => :@builder
4
+
5
+ def initialize(builder, options)
6
+ @builder = builder
7
+ @message = options.delete(:message)
8
+ @options = options
9
+ end
10
+
11
+ def render
12
+ if has_errors?
13
+ template.content_tag(error_notification_tag, error_message, html_options)
14
+ end
15
+ end
16
+
17
+ protected
18
+
19
+ def error_message
20
+ @message || translate_error_notification
21
+ end
22
+
23
+ def error_notification_tag
24
+ SimpleForm.error_notification_tag
25
+ end
26
+
27
+ def has_errors?
28
+ object && object.respond_to?(:errors) && object.errors.present?
29
+ end
30
+
31
+ def html_options
32
+ @options[:class] = "error_notification #{@options[:class]}".strip
33
+ @options
34
+ end
35
+
36
+ def translate_error_notification
37
+ lookups = []
38
+ lookups << :"#{object_name}"
39
+ lookups << :default_message
40
+ lookups << "Some errors were found, please take a look:"
41
+ I18n.t(lookups.shift, :scope => :"simple_form.error_notification", :default => lookups)
42
+ end
43
+ end
44
+ end
@@ -6,12 +6,13 @@ module SimpleForm
6
6
  extend MapType
7
7
  include SimpleForm::Inputs
8
8
 
9
- map_type :boolean, :password, :text, :file, :to => SimpleForm::Inputs::MappingInput
9
+ map_type :password, :text, :file, :to => SimpleForm::Inputs::MappingInput
10
10
  map_type :string, :email, :url, :to => SimpleForm::Inputs::StringInput
11
11
  map_type :integer, :decimal, :float, :to => SimpleForm::Inputs::NumericInput
12
12
  map_type :select, :radio, :check_boxes, :to => SimpleForm::Inputs::CollectionInput
13
13
  map_type :date, :time, :datetime, :to => SimpleForm::Inputs::DateTimeInput
14
14
  map_type :country, :time_zone, :to => SimpleForm::Inputs::PriorityInput
15
+ map_type :boolean, :to => SimpleForm::Inputs::BooleanInput
15
16
 
16
17
  # Basic input helper, combines all components in the stack to generate
17
18
  # input html based on options the user define and some guesses through
@@ -215,6 +216,21 @@ module SimpleForm
215
216
  SimpleForm::Inputs::Base.new(self).label
216
217
  end
217
218
 
219
+ # Creates an error notification message that only appears when the form object
220
+ # has some error. You can give a specific message with the :message option,
221
+ # otherwise it will look for a message using I18n. All other options given are
222
+ # passed straight as html options to the html tag.
223
+ #
224
+ # == Examples
225
+ #
226
+ # f.error_notification
227
+ # f.error_notification :message => 'Something went wrong'
228
+ # f.error_notification :id => 'user_error_message', :class => 'form_error'
229
+ #
230
+ def error_notification(options={})
231
+ SimpleForm::ErrorNotification.new(self, options).render
232
+ end
233
+
218
234
  private
219
235
 
220
236
  # Setup default simple form attributes.
@@ -2,6 +2,7 @@ module SimpleForm
2
2
  module Inputs
3
3
  autoload :Base, 'simple_form/inputs/base'
4
4
  autoload :BlockInput, 'simple_form/inputs/block_input'
5
+ autoload :BooleanInput, 'simple_form/inputs/boolean_input'
5
6
  autoload :CollectionInput, 'simple_form/inputs/collection_input'
6
7
  autoload :DateTimeInput, 'simple_form/inputs/date_time_input'
7
8
  autoload :HiddenInput, 'simple_form/inputs/hidden_input'
@@ -11,7 +11,7 @@ module SimpleForm
11
11
 
12
12
  include SimpleForm::Components::Errors
13
13
  include SimpleForm::Components::Hints
14
- include SimpleForm::Components::Labels
14
+ include SimpleForm::Components::LabelInput
15
15
  include SimpleForm::Components::Wrapper
16
16
 
17
17
  delegate :template, :object, :object_name, :attribute_name, :column,
@@ -30,16 +30,20 @@ module SimpleForm
30
30
  end
31
31
 
32
32
  def input_html_options
33
- html_options_for(:input, input_type, required_class)
33
+ html_options_for(:input, input_html_classes)
34
+ end
35
+
36
+ def input_html_classes
37
+ [input_type, required_class]
34
38
  end
35
39
 
36
40
  def render
37
- content = components_list.map do |component|
41
+ content = "".html_safe
42
+ components_list.each do |component|
38
43
  next if options[component] == false
39
- send(component)
44
+ content.safe_concat send(component).to_s
40
45
  end
41
- content.compact!
42
- wrap(content.join.html_safe)
46
+ wrap(content)
43
47
  end
44
48
 
45
49
  protected
@@ -51,13 +55,17 @@ module SimpleForm
51
55
  def attribute_required?
52
56
  if options.key?(:required)
53
57
  options[:required]
54
- elsif defined?(object.class.validators_on)
58
+ elsif object.class.respond_to?(:validators_on)
55
59
  object.class.validators_on(attribute_name).any? { |v| v.kind == :presence }
56
60
  else
57
- true
61
+ attribute_required_by_default?
58
62
  end
59
63
  end
60
64
 
65
+ def attribute_required_by_default?
66
+ SimpleForm.required_by_default
67
+ end
68
+
61
69
  def required_class
62
70
  attribute_required? ? :required : :optional
63
71
  end
@@ -68,7 +76,7 @@ module SimpleForm
68
76
  end
69
77
 
70
78
  # Retrieve options for the given namespace from the options hash
71
- def html_options_for(namespace, *extra)
79
+ def html_options_for(namespace, extra)
72
80
  html_options = options[:"#{namespace}_html"] || {}
73
81
  html_options[:class] = (extra << html_options[:class]).join(' ').strip if extra.present?
74
82
  html_options
@@ -0,0 +1,22 @@
1
+ module SimpleForm
2
+ module Inputs
3
+ class BooleanInput < Base
4
+ def input
5
+ @builder.check_box(attribute_name, input_html_options)
6
+ end
7
+
8
+ def label_input
9
+ input + (options[:label] == false ? "" : label)
10
+ end
11
+
12
+ protected
13
+
14
+ # Booleans are not required by default because in most of the cases
15
+ # it makes no sense marking them as required. The only exception is
16
+ # Terms of Use usually presented at most sites sign up screen.
17
+ def attribute_required_by_default?
18
+ false
19
+ end
20
+ end
21
+ end
22
+ end
@@ -42,12 +42,12 @@ module SimpleForm
42
42
  sample = collection.first || collection.last
43
43
 
44
44
  case sample
45
- when Array
46
- label, value = :first, :last
47
- when Integer
48
- label, value = :to_s, :to_i
49
- when String, NilClass
50
- label, value = :to_s, :to_s
45
+ when Array
46
+ label, value = :first, :last
47
+ when Integer
48
+ label, value = :to_s, :to_i
49
+ when String, NilClass
50
+ label, value = :to_s, :to_s
51
51
  end
52
52
 
53
53
  options[:label_method] ||= label || SimpleForm.collection_label_methods.find { |m| sample.respond_to?(m) }
@@ -7,10 +7,10 @@ module SimpleForm
7
7
 
8
8
  def label_target
9
9
  case input_type
10
- when :date, :datetime
11
- "#{attribute_name}_1i"
12
- when :time
13
- "#{attribute_name}_4i"
10
+ when :date, :datetime
11
+ "#{attribute_name}_1i"
12
+ when :time
13
+ "#{attribute_name}_4i"
14
14
  end
15
15
  end
16
16
  end
@@ -4,7 +4,6 @@ module SimpleForm
4
4
  class MappingInput < Base
5
5
  extend MapType
6
6
 
7
- map_type :boolean, :to => :check_box
8
7
  map_type :password, :to => :password_field
9
8
  map_type :text, :to => :text_area
10
9
  map_type :file, :to => :file_field
@@ -7,10 +7,14 @@ module SimpleForm
7
7
 
8
8
  def input_html_options
9
9
  input_options = super
10
- input_options[:class] = "numeric #{input_options[:class]}"
10
+ input_options[:type] ||= "number"
11
11
  input_options[:size] ||= SimpleForm.default_input_size
12
12
  input_options
13
13
  end
14
+
15
+ def input_html_classes
16
+ super.unshift("numeric")
17
+ end
14
18
  end
15
19
  end
16
20
  end
@@ -9,15 +9,14 @@ module SimpleForm
9
9
  input_options = super
10
10
  input_options[:size] ||= [limit, SimpleForm.default_input_size].compact.min
11
11
  input_options[:maxlength] ||= limit if limit
12
-
13
- unless input_type == :string
14
- input_options[:type] ||= input_type
15
- input_options[:class] = "string #{input_options[:class]}"
16
- end
17
-
12
+ input_options[:type] ||= input_type unless input_type == :string
18
13
  input_options
19
14
  end
20
15
 
16
+ def input_html_classes
17
+ input_type == :string ? super : super.unshift("string")
18
+ end
19
+
21
20
  protected
22
21
 
23
22
  def limit
@@ -1,3 +1,3 @@
1
1
  module SimpleForm
2
- VERSION = "1.2.0".freeze
2
+ VERSION = "1.2.1".freeze
3
3
  end
@@ -3,7 +3,7 @@ require 'test_helper'
3
3
  class BuilderTest < ActionView::TestCase
4
4
  # COLLECTION RADIO
5
5
  test 'collection radio accepts a collection and generate inputs from value method' do
6
- concat(form_for @user do |f|
6
+ concat(form_for(@user) do |f|
7
7
  concat f.collection_radio :active, [true, false], :to_s, :to_s
8
8
  end)
9
9
 
@@ -12,7 +12,7 @@ class BuilderTest < ActionView::TestCase
12
12
  end
13
13
 
14
14
  test 'collection radio accepts a collection and generate inputs from label method' do
15
- concat(form_for @user do |f|
15
+ concat(form_for(@user) do |f|
16
16
  concat f.collection_radio :active, [true, false], :to_s, :to_s
17
17
  end)
18
18
 
@@ -21,7 +21,7 @@ class BuilderTest < ActionView::TestCase
21
21
  end
22
22
 
23
23
  test 'collection radio accepts checked item' do
24
- concat(form_for @user do |f|
24
+ concat(form_for(@user) do |f|
25
25
  concat f.collection_radio :active, [[1, true], [0, false]], :last, :first, :checked => true
26
26
  end)
27
27
 
@@ -31,7 +31,7 @@ class BuilderTest < ActionView::TestCase
31
31
 
32
32
  test 'collection radio accepts multiple disabled items' do
33
33
  collection = [[1, true], [0, false], [2, 'other']]
34
- concat(form_for @user do |f|
34
+ concat(form_for(@user) do |f|
35
35
  concat f.collection_radio :active, collection, :last, :first, :disabled => [true, false]
36
36
  end)
37
37
 
@@ -42,7 +42,7 @@ class BuilderTest < ActionView::TestCase
42
42
 
43
43
  test 'collection radio accepts single disable item' do
44
44
  collection = [[1, true], [0, false]]
45
- concat(form_for @user do |f|
45
+ concat(form_for(@user) do |f|
46
46
  concat f.collection_radio :active, collection, :last, :first, :disabled => true
47
47
  end)
48
48
 
@@ -51,7 +51,7 @@ class BuilderTest < ActionView::TestCase
51
51
  end
52
52
 
53
53
  test 'collection radio accepts html options as input' do
54
- concat(form_for @user do |f|
54
+ concat(form_for(@user) do |f|
55
55
  concat f.collection_radio :active, [[1, true], [0, false]], :last, :first, {}, :class => 'radio'
56
56
  end)
57
57
 
@@ -62,7 +62,7 @@ class BuilderTest < ActionView::TestCase
62
62
  # COLLECTION CHECK BOX
63
63
  test 'collection check box accepts a collection and generate a serie of checkboxes for value method' do
64
64
  collection = [Tag.new(1, 'Tag 1'), Tag.new(2, 'Tag 2')]
65
- concat(form_for @user do |f|
65
+ concat(form_for(@user) do |f|
66
66
  concat f.collection_check_boxes :tag_ids, collection, :id, :name
67
67
  end)
68
68
 
@@ -73,7 +73,7 @@ class BuilderTest < ActionView::TestCase
73
73
 
74
74
  test 'collection check box accepts a collection and generate a serie of checkboxes with labels for label method' do
75
75
  collection = [Tag.new(1, 'Tag 1'), Tag.new(2, 'Tag 2')]
76
- concat(form_for @user do |f|
76
+ concat(form_for(@user) do |f|
77
77
  concat f.collection_check_boxes :tag_ids, collection, :id, :name
78
78
  end)
79
79
 
@@ -83,7 +83,7 @@ class BuilderTest < ActionView::TestCase
83
83
 
84
84
  test 'collection check box accepts selected values as :checked option' do
85
85
  collection = (1..3).map{|i| [i, "Tag #{i}"] }
86
- concat(form_for @user do |f|
86
+ concat(form_for(@user) do |f|
87
87
  concat f.collection_check_boxes :tag_ids, collection, :first, :last, :checked => [1, 3]
88
88
  end)
89
89
 
@@ -94,7 +94,7 @@ class BuilderTest < ActionView::TestCase
94
94
 
95
95
  test 'collection check box accepts a single checked value' do
96
96
  collection = (1..3).map{|i| [i, "Tag #{i}"] }
97
- concat(form_for @user do |f|
97
+ concat(form_for(@user) do |f|
98
98
  concat f.collection_check_boxes :tag_ids, collection, :first, :last, :checked => 3
99
99
  end)
100
100
 
@@ -105,7 +105,7 @@ class BuilderTest < ActionView::TestCase
105
105
 
106
106
  test 'collection check box accepts multiple disabled items' do
107
107
  collection = (1..3).map{|i| [i, "Tag #{i}"] }
108
- concat(form_for @user do |f|
108
+ concat(form_for(@user) do |f|
109
109
  concat f.collection_check_boxes :tag_ids, collection, :first, :last, :disabled => [1, 3]
110
110
  end)
111
111
 
@@ -116,7 +116,7 @@ class BuilderTest < ActionView::TestCase
116
116
 
117
117
  test 'collection check box accepts single disable item' do
118
118
  collection = (1..3).map{|i| [i, "Tag #{i}"] }
119
- concat(form_for @user do |f|
119
+ concat(form_for(@user) do |f|
120
120
  concat f.collection_check_boxes :tag_ids, collection, :first, :last, :disabled => 1
121
121
  end)
122
122
 
@@ -127,7 +127,7 @@ class BuilderTest < ActionView::TestCase
127
127
 
128
128
  test 'collection check box accepts a proc to disabled items' do
129
129
  collection = (1..3).map{|i| [i, "Tag #{i}"] }
130
- concat(form_for @user do |f|
130
+ concat(form_for(@user) do |f|
131
131
  concat f.collection_check_boxes :tag_ids, collection, :first, :last, :disabled => proc { |i| i.first == 1 }
132
132
  end)
133
133
 
@@ -138,7 +138,7 @@ class BuilderTest < ActionView::TestCase
138
138
 
139
139
  test 'collection check box accepts html options' do
140
140
  collection = [[1, 'Tag 1'], [2, 'Tag 2']]
141
- concat(form_for @user do |f|
141
+ concat(form_for(@user) do |f|
142
142
  concat f.collection_check_boxes :tag_ids, collection, :first, :last, {}, :class => 'check'
143
143
  end)
144
144
 
@@ -148,8 +148,8 @@ class BuilderTest < ActionView::TestCase
148
148
 
149
149
  test 'collection check box with fields for' do
150
150
  collection = [Tag.new(1, 'Tag 1'), Tag.new(2, 'Tag 2')]
151
- concat(form_for @user do |f|
152
- concat(f.fields_for :post do |p|
151
+ concat(form_for(@user) do |f|
152
+ concat(f.fields_for(:post) do |p|
153
153
  concat p.collection_check_boxes :tag_ids, collection, :id, :name
154
154
  end)
155
155
  end)
@@ -163,8 +163,8 @@ class BuilderTest < ActionView::TestCase
163
163
 
164
164
  # SIMPLE FIELDS
165
165
  test 'simple fields for is available and yields an instance of FormBuilder' do
166
- concat(form_for @user do |f|
167
- concat(f.simple_fields_for :posts do |posts_form|
166
+ concat(form_for(@user) do |f|
167
+ concat(f.simple_fields_for(:posts) do |posts_form|
168
168
  assert posts_form.instance_of?(SimpleForm::FormBuilder)
169
169
  end)
170
170
  end)