simple_form 3.0.4 → 3.1.0.rc1

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 (72) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +32 -43
  3. data/MIT-LICENSE +1 -1
  4. data/README.md +146 -71
  5. data/lib/generators/simple_form/install_generator.rb +2 -2
  6. data/lib/generators/simple_form/templates/README +3 -4
  7. data/lib/generators/simple_form/templates/config/initializers/simple_form.rb +19 -3
  8. data/lib/generators/simple_form/templates/config/initializers/simple_form_bootstrap.rb +83 -22
  9. data/lib/generators/simple_form/templates/config/initializers/simple_form_foundation.rb +1 -1
  10. data/lib/generators/simple_form/templates/config/locales/simple_form.en.yml +7 -2
  11. data/lib/simple_form.rb +38 -6
  12. data/lib/simple_form/action_view_extensions/form_helper.rb +1 -1
  13. data/lib/simple_form/components/errors.rb +27 -5
  14. data/lib/simple_form/components/hints.rb +2 -2
  15. data/lib/simple_form/components/html5.rb +1 -1
  16. data/lib/simple_form/components/label_input.rb +20 -2
  17. data/lib/simple_form/components/labels.rb +9 -5
  18. data/lib/simple_form/components/maxlength.rb +1 -1
  19. data/lib/simple_form/components/min_max.rb +1 -1
  20. data/lib/simple_form/components/pattern.rb +1 -1
  21. data/lib/simple_form/components/placeholders.rb +2 -2
  22. data/lib/simple_form/components/readonly.rb +1 -1
  23. data/lib/simple_form/form_builder.rb +92 -59
  24. data/lib/simple_form/helpers.rb +5 -5
  25. data/lib/simple_form/inputs/base.rb +34 -12
  26. data/lib/simple_form/inputs/block_input.rb +1 -1
  27. data/lib/simple_form/inputs/boolean_input.rb +23 -13
  28. data/lib/simple_form/inputs/collection_input.rb +32 -9
  29. data/lib/simple_form/inputs/collection_radio_buttons_input.rb +6 -11
  30. data/lib/simple_form/inputs/collection_select_input.rb +4 -2
  31. data/lib/simple_form/inputs/date_time_input.rb +12 -2
  32. data/lib/simple_form/inputs/file_input.rb +4 -2
  33. data/lib/simple_form/inputs/grouped_collection_select_input.rb +15 -3
  34. data/lib/simple_form/inputs/hidden_input.rb +4 -2
  35. data/lib/simple_form/inputs/numeric_input.rb +5 -4
  36. data/lib/simple_form/inputs/password_input.rb +4 -2
  37. data/lib/simple_form/inputs/priority_input.rb +4 -2
  38. data/lib/simple_form/inputs/range_input.rb +1 -1
  39. data/lib/simple_form/inputs/string_input.rb +4 -2
  40. data/lib/simple_form/inputs/text_input.rb +4 -2
  41. data/lib/simple_form/railtie.rb +7 -0
  42. data/lib/simple_form/tags.rb +7 -0
  43. data/lib/simple_form/version.rb +1 -1
  44. data/lib/simple_form/wrappers.rb +1 -0
  45. data/lib/simple_form/wrappers/builder.rb +5 -5
  46. data/lib/simple_form/wrappers/leaf.rb +28 -0
  47. data/lib/simple_form/wrappers/many.rb +5 -6
  48. data/lib/simple_form/wrappers/root.rb +1 -1
  49. data/lib/simple_form/wrappers/single.rb +5 -3
  50. data/test/action_view_extensions/builder_test.rb +2 -2
  51. data/test/components/label_test.rb +1 -1
  52. data/test/form_builder/association_test.rb +17 -0
  53. data/test/form_builder/error_notification_test.rb +1 -1
  54. data/test/form_builder/error_test.rb +51 -32
  55. data/test/form_builder/general_test.rb +2 -2
  56. data/test/form_builder/input_field_test.rb +21 -37
  57. data/test/form_builder/label_test.rb +24 -1
  58. data/test/form_builder/wrapper_test.rb +67 -0
  59. data/test/generators/simple_form_generator_test.rb +2 -2
  60. data/test/inputs/boolean_input_test.rb +50 -2
  61. data/test/inputs/collection_check_boxes_input_test.rb +40 -11
  62. data/test/inputs/collection_radio_buttons_input_test.rb +76 -17
  63. data/test/inputs/collection_select_input_test.rb +108 -3
  64. data/test/inputs/datetime_input_test.rb +105 -38
  65. data/test/inputs/discovery_test.rb +12 -1
  66. data/test/inputs/grouped_collection_select_input_test.rb +36 -0
  67. data/test/inputs/string_input_test.rb +20 -0
  68. data/test/simple_form_test.rb +8 -0
  69. data/test/support/discovery_inputs.rb +12 -2
  70. data/test/support/misc_helpers.rb +46 -8
  71. data/test/support/models.rb +49 -24
  72. metadata +7 -7
@@ -6,7 +6,7 @@ module SimpleForm
6
6
  @block = block
7
7
  end
8
8
 
9
- def input
9
+ def input(wrapper_options = nil)
10
10
  template.capture(&@block)
11
11
  end
12
12
  end
@@ -1,31 +1,36 @@
1
1
  module SimpleForm
2
2
  module Inputs
3
3
  class BooleanInput < Base
4
- def input
4
+ def input(wrapper_options = nil)
5
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
6
+
5
7
  if nested_boolean_style?
6
8
  build_hidden_field_for_checkbox +
7
- template.label_tag(nil, class: "checkbox") {
8
- build_check_box_without_hidden_field + inline_label
9
+ template.label_tag(nil, class: SimpleForm.boolean_label_class) {
10
+ build_check_box_without_hidden_field(merged_input_options) +
11
+ inline_label
9
12
  }
10
13
  else
11
- build_check_box
14
+ build_check_box(unchecked_value, merged_input_options)
12
15
  end
13
16
  end
14
17
 
15
- def label_input
18
+ def label_input(wrapper_options = nil)
16
19
  if options[:label] == false
17
- input
20
+ input(wrapper_options)
18
21
  elsif nested_boolean_style?
19
22
  html_options = label_html_options.dup
20
23
  html_options[:class] ||= []
21
- html_options[:class].push(:checkbox)
24
+ html_options[:class].push(SimpleForm.boolean_label_class) if SimpleForm.boolean_label_class
25
+
26
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
22
27
 
23
28
  build_hidden_field_for_checkbox +
24
29
  @builder.label(label_target, html_options) {
25
- build_check_box_without_hidden_field + label_text
30
+ build_check_box_without_hidden_field(merged_input_options) + label_text
26
31
  }
27
32
  else
28
- input + label
33
+ input(wrapper_options) + label(wrapper_options)
29
34
  end
30
35
  end
31
36
 
@@ -35,14 +40,14 @@ module SimpleForm
35
40
  # reuse the method for nested boolean style, but with no unchecked value,
36
41
  # which won't generate the hidden checkbox. This is the default functionality
37
42
  # in Rails > 3.2.1, and is backported in SimpleForm AV helpers.
38
- def build_check_box(unchecked_value = unchecked_value())
43
+ def build_check_box(unchecked_value, options)
39
44
  @builder.check_box(attribute_name, input_html_options, checked_value, unchecked_value)
40
45
  end
41
46
 
42
47
  # Build a checkbox without generating the hidden field. See
43
48
  # #build_hidden_field_for_checkbox for more info.
44
- def build_check_box_without_hidden_field
45
- build_check_box(nil)
49
+ def build_check_box_without_hidden_field(options)
50
+ build_check_box(nil, options)
46
51
  end
47
52
 
48
53
  # Create a hidden field for the current checkbox, so we can simulate Rails
@@ -58,7 +63,12 @@ module SimpleForm
58
63
 
59
64
  def inline_label
60
65
  inline_option = options[:inline_label]
61
- inline_option == true ? label_text : inline_option
66
+
67
+ if inline_option
68
+ label = inline_option == true ? " #{label_text}" : " #{html_escape(inline_option)}"
69
+
70
+ label.html_safe
71
+ end
62
72
  end
63
73
 
64
74
  # Booleans are not required by default because in most of the cases
@@ -12,14 +12,20 @@ module SimpleForm
12
12
  end
13
13
  end
14
14
 
15
- def input
15
+ def input(wrapper_options = nil)
16
16
  raise NotImplementedError,
17
17
  "input should be implemented by classes inheriting from CollectionInput"
18
18
  end
19
19
 
20
20
  def input_options
21
21
  options = super
22
+
22
23
  options[:include_blank] = true unless skip_include_blank?
24
+
25
+ [:prompt, :include_blank].each do |key|
26
+ translate_option options, key
27
+ end
28
+
23
29
  options
24
30
  end
25
31
 
@@ -33,7 +39,7 @@ module SimpleForm
33
39
  end
34
40
 
35
41
  def has_required?
36
- super && (input_options[:include_blank] || multiple?)
42
+ super && (input_options[:include_blank] || input_options[:prompt] || multiple?)
37
43
  end
38
44
 
39
45
  # Check if :include_blank must be included by default.
@@ -70,13 +76,17 @@ module SimpleForm
70
76
  elsif collection_includes_basic_objects?(collection_classes)
71
77
  { label: :to_s, value: :to_s }
72
78
  else
73
- sample = collection.first || collection.last
74
-
75
- { label: SimpleForm.collection_label_methods.find { |m| sample.respond_to?(m) },
76
- value: SimpleForm.collection_value_methods.find { |m| sample.respond_to?(m) } }
79
+ detect_method_from_class(collection_classes)
77
80
  end
78
81
  end
79
82
 
83
+ def detect_method_from_class(collection_classes)
84
+ sample = collection.first || collection.last
85
+
86
+ { label: SimpleForm.collection_label_methods.find { |m| sample.respond_to?(m) },
87
+ value: SimpleForm.collection_value_methods.find { |m| sample.respond_to?(m) } }
88
+ end
89
+
80
90
  def detect_collection_classes(some_collection = collection)
81
91
  some_collection.map { |e| e.class }.uniq
82
92
  end
@@ -88,14 +98,27 @@ module SimpleForm
88
98
  end
89
99
 
90
100
  def translate_collection
91
- if translated_collection = translate(:options)
101
+ if translated_collection = translate_from_namespace(:options)
92
102
  @collection = collection.map do |key|
93
- [translated_collection[key] || key, key]
103
+ html_key = "#{key}_html".to_sym
104
+
105
+ if translated_collection[html_key]
106
+ [translated_collection[html_key].html_safe || key, key.to_s]
107
+ else
108
+ [translated_collection[key] || key, key.to_s]
109
+ end
94
110
  end
95
111
  true
96
112
  end
97
113
  end
114
+
115
+ def translate_option(options, key)
116
+ if options[key] == :translate
117
+ namespace = key.to_s.pluralize
118
+
119
+ options[key] = translate_from_namespace(namespace, true)
120
+ end
121
+ end
98
122
  end
99
123
  end
100
124
  end
101
-
@@ -1,19 +1,21 @@
1
1
  module SimpleForm
2
2
  module Inputs
3
3
  class CollectionRadioButtonsInput < CollectionInput
4
- def input
4
+ def input(wrapper_options = nil)
5
5
  label_method, value_method = detect_collection_methods
6
6
 
7
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
8
+
7
9
  @builder.send("collection_#{input_type}",
8
10
  attribute_name, collection, value_method, label_method,
9
- input_options, input_html_options, &collection_block_for_nested_boolean_style
11
+ input_options, merged_input_options,
12
+ &collection_block_for_nested_boolean_style
10
13
  )
11
14
  end
12
15
 
13
16
  def input_options
14
17
  options = super
15
18
  apply_default_collection_options!(options)
16
- apply_nested_boolean_collection_options!(options) if nested_boolean_style?
17
19
  options
18
20
  end
19
21
 
@@ -23,7 +25,7 @@ module SimpleForm
23
25
  options[:item_wrapper_tag] ||= options.fetch(:item_wrapper_tag, SimpleForm.item_wrapper_tag)
24
26
  options[:item_wrapper_class] = [
25
27
  item_wrapper_class, options[:item_wrapper_class], SimpleForm.item_wrapper_class
26
- ].compact.presence
28
+ ].compact.presence if SimpleForm.include_default_input_wrapper_class
27
29
 
28
30
  options[:collection_wrapper_tag] ||= options.fetch(:collection_wrapper_tag, SimpleForm.collection_wrapper_tag)
29
31
  options[:collection_wrapper_class] = [
@@ -31,13 +33,6 @@ module SimpleForm
31
33
  ].compact.presence
32
34
  end
33
35
 
34
- # Force item wrapper to be a label when using nested boolean, to support
35
- # configuring classes through :item_wrapper_class, and to maintain
36
- # compatibility with :inline style and default :item_wrapper_tag.
37
- def apply_nested_boolean_collection_options!(options)
38
- options[:item_wrapper_tag] = :label
39
- end
40
-
41
36
  def collection_block_for_nested_boolean_style
42
37
  return unless nested_boolean_style?
43
38
 
@@ -1,12 +1,14 @@
1
1
  module SimpleForm
2
2
  module Inputs
3
3
  class CollectionSelectInput < CollectionInput
4
- def input
4
+ def input(wrapper_options = nil)
5
5
  label_method, value_method = detect_collection_methods
6
6
 
7
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
8
+
7
9
  @builder.collection_select(
8
10
  attribute_name, collection, value_method, label_method,
9
- input_options, input_html_options
11
+ input_options, merged_input_options
10
12
  )
11
13
  end
12
14
  end
@@ -1,8 +1,14 @@
1
1
  module SimpleForm
2
2
  module Inputs
3
3
  class DateTimeInput < Base
4
- def input
5
- @builder.send(:"#{input_type}_select", attribute_name, input_options, input_html_options)
4
+ def input(wrapper_options = nil)
5
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
6
+
7
+ if use_html5_inputs?
8
+ @builder.send(:"#{input_type}_field", attribute_name, merged_input_options)
9
+ else
10
+ @builder.send(:"#{input_type}_select", attribute_name, input_options, merged_input_options)
11
+ end
6
12
  end
7
13
 
8
14
  private
@@ -19,6 +25,10 @@ module SimpleForm
19
25
  position = ActionView::Helpers::DateTimeSelector::POSITION[position]
20
26
  "#{attribute_name}_#{position}i"
21
27
  end
28
+
29
+ def use_html5_inputs?
30
+ input_options[:html5]
31
+ end
22
32
  end
23
33
  end
24
34
  end
@@ -1,8 +1,10 @@
1
1
  module SimpleForm
2
2
  module Inputs
3
3
  class FileInput < Base
4
- def input
5
- @builder.file_field(attribute_name, input_html_options)
4
+ def input(wrapper_options = nil)
5
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
6
+
7
+ @builder.file_field(attribute_name, merged_input_options)
6
8
  end
7
9
  end
8
10
  end
@@ -1,11 +1,14 @@
1
1
  module SimpleForm
2
2
  module Inputs
3
3
  class GroupedCollectionSelectInput < CollectionInput
4
- def input
4
+ def input(wrapper_options = nil)
5
5
  label_method, value_method = detect_collection_methods
6
+
7
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
8
+
6
9
  @builder.grouped_collection_select(attribute_name, grouped_collection,
7
10
  group_method, group_label_method, value_method, label_method,
8
- input_options, input_html_options)
11
+ input_options, merged_input_options)
9
12
  end
10
13
 
11
14
  private
@@ -19,7 +22,7 @@ module SimpleForm
19
22
 
20
23
  # Sample collection
21
24
  def collection
22
- @collection ||= grouped_collection.first.try(:send, group_method) || []
25
+ @collection ||= grouped_collection.map { |collection| collection.try(:send, group_method) }.detect(&:present?) || []
23
26
  end
24
27
 
25
28
  def group_method
@@ -36,6 +39,15 @@ module SimpleForm
36
39
 
37
40
  label
38
41
  end
42
+
43
+ def detect_method_from_class(collection_classes)
44
+ return {} if collection_classes.empty?
45
+
46
+ sample = collection_classes.first
47
+
48
+ { label: SimpleForm.collection_label_methods.find { |m| sample.instance_methods.include?(m) },
49
+ value: SimpleForm.collection_value_methods.find { |m| sample.instance_methods.include?(m) } }
50
+ end
39
51
  end
40
52
  end
41
53
  end
@@ -3,8 +3,10 @@ module SimpleForm
3
3
  class HiddenInput < Base
4
4
  disable :label, :errors, :hint, :required
5
5
 
6
- def input
7
- @builder.hidden_field(attribute_name, input_html_options)
6
+ def input(wrapper_options = nil)
7
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
8
+
9
+ @builder.hidden_field(attribute_name, merged_input_options)
8
10
  end
9
11
 
10
12
  private
@@ -3,16 +3,17 @@ module SimpleForm
3
3
  class NumericInput < Base
4
4
  enable :placeholder, :min_max
5
5
 
6
- def input
6
+ def input(wrapper_options = nil)
7
7
  input_html_classes.unshift("numeric")
8
8
  if html5?
9
9
  input_html_options[:type] ||= "number"
10
10
  input_html_options[:step] ||= integer? ? 1 : "any"
11
11
  end
12
- @builder.text_field(attribute_name, input_html_options)
13
- end
14
12
 
15
- private
13
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
14
+
15
+ @builder.text_field(attribute_name, merged_input_options)
16
+ end
16
17
  end
17
18
  end
18
19
  end
@@ -3,8 +3,10 @@ module SimpleForm
3
3
  class PasswordInput < Base
4
4
  enable :placeholder, :maxlength
5
5
 
6
- def input
7
- @builder.password_field(attribute_name, input_html_options)
6
+ def input(wrapper_options = nil)
7
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
8
+
9
+ @builder.password_field(attribute_name, merged_input_options)
8
10
  end
9
11
  end
10
12
  end
@@ -1,9 +1,11 @@
1
1
  module SimpleForm
2
2
  module Inputs
3
3
  class PriorityInput < CollectionSelectInput
4
- def input
4
+ def input(wrapper_options = nil)
5
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
6
+
5
7
  @builder.send(:"#{input_type}_select", attribute_name, input_priority,
6
- input_options, input_html_options)
8
+ input_options, merged_input_options)
7
9
  end
8
10
 
9
11
  def input_priority
@@ -1,7 +1,7 @@
1
1
  module SimpleForm
2
2
  module Inputs
3
3
  class RangeInput < NumericInput
4
- def input
4
+ def input(wrapper_options = nil)
5
5
  if html5?
6
6
  input_html_options[:type] ||= "range"
7
7
  input_html_options[:step] ||= 1
@@ -3,13 +3,15 @@ module SimpleForm
3
3
  class StringInput < Base
4
4
  enable :placeholder, :maxlength, :pattern
5
5
 
6
- def input
6
+ def input(wrapper_options = nil)
7
7
  unless string?
8
8
  input_html_classes.unshift("string")
9
9
  input_html_options[:type] ||= input_type if html5?
10
10
  end
11
11
 
12
- @builder.text_field(attribute_name, input_html_options)
12
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
13
+
14
+ @builder.text_field(attribute_name, merged_input_options)
13
15
  end
14
16
 
15
17
  private
@@ -3,8 +3,10 @@ module SimpleForm
3
3
  class TextInput < Base
4
4
  enable :placeholder, :maxlength
5
5
 
6
- def input
7
- @builder.text_area(attribute_name, input_html_options)
6
+ def input(wrapper_options = nil)
7
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
8
+
9
+ @builder.text_area(attribute_name, merged_input_options)
8
10
  end
9
11
  end
10
12
  end
@@ -3,5 +3,12 @@ require 'rails/railtie'
3
3
  module SimpleForm
4
4
  class Railtie < Rails::Railtie
5
5
  config.eager_load_namespaces << SimpleForm
6
+
7
+ config.after_initialize do
8
+ unless SimpleForm.configured?
9
+ warn '[Simple Form] Simple Form is not configured in the application and will use the default values.' +
10
+ ' Use `rails generate simple_form:install` to generate the Simple Form configuration.'
11
+ end
12
+ end
6
13
  end
7
14
  end
@@ -15,6 +15,13 @@ module SimpleForm
15
15
 
16
16
  rendered_item = yield item, value, text, default_html_options.merge(additional_html_options)
17
17
 
18
+ if @options.fetch(:boolean_style, SimpleForm.boolean_style) == :nested
19
+ label_options = {}
20
+ add_default_name_and_id_for_value(value, label_options)
21
+ label_options['for'] = label_options.delete('id')
22
+ rendered_item = content_tag(:label, rendered_item, label_options)
23
+ end
24
+
18
25
  item_wrapper_tag ? @template_object.content_tag(item_wrapper_tag, rendered_item, class: item_wrapper_class) : rendered_item
19
26
  end.join.html_safe
20
27
  end