simple_form 1.2.0 → 1.2.1

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.

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)