simple_form 3.0.4 → 3.1.0.rc1

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 (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