simple_form 2.0.4 → 2.1.0

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 (36) hide show
  1. data/CHANGELOG.md +33 -301
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +30 -12
  4. data/lib/generators/simple_form/install_generator.rb +7 -4
  5. data/lib/generators/simple_form/templates/config/initializers/simple_form_foundation.rb +26 -0
  6. data/lib/simple_form.rb +11 -8
  7. data/lib/simple_form/action_view_extensions/builder.rb +32 -18
  8. data/lib/simple_form/action_view_extensions/builder.rb.orig +247 -0
  9. data/lib/simple_form/components.rb +12 -10
  10. data/lib/simple_form/components/hints.rb +1 -1
  11. data/lib/simple_form/components/label_input.rb +1 -1
  12. data/lib/simple_form/components/maxlength.rb +1 -1
  13. data/lib/simple_form/components/min_max.rb +1 -1
  14. data/lib/simple_form/components/pattern.rb +1 -1
  15. data/lib/simple_form/form_builder.rb +6 -3
  16. data/lib/simple_form/form_builder.rb.orig +486 -0
  17. data/lib/simple_form/helpers/validators.rb +2 -2
  18. data/lib/simple_form/inputs.rb +19 -17
  19. data/lib/simple_form/inputs/base.rb +8 -2
  20. data/lib/simple_form/inputs/date_time_input.rb +0 -4
  21. data/lib/simple_form/version.rb +1 -1
  22. data/lib/simple_form/version.rb.orig +7 -0
  23. data/lib/simple_form/wrappers/root.rb +3 -1
  24. data/test/action_view_extensions/builder_test.rb +113 -67
  25. data/test/action_view_extensions/form_helper_test.rb +5 -0
  26. data/test/components/label_test.rb +27 -17
  27. data/test/form_builder/association_test.rb +10 -7
  28. data/test/form_builder/general_test.rb +48 -25
  29. data/test/form_builder/input_field_test.rb +45 -0
  30. data/test/form_builder/wrapper_test.rb +22 -0
  31. data/test/generators/simple_form_generator_test.rb +8 -0
  32. data/test/inputs/datetime_input_test.rb +2 -2
  33. data/test/inputs/grouped_collection_select_input_test.rb +15 -0
  34. data/test/support/misc_helpers.rb +6 -0
  35. data/test/test_helper.rb +6 -1
  36. metadata +12 -2
@@ -0,0 +1,26 @@
1
+ # Use this setup block to configure all options available in SimpleForm.
2
+ SimpleForm.setup do |config|
3
+ config.wrappers :foundation, :class => :input, :hint_class => :field_with_hint, :error_class => :error do |b|
4
+ b.use :html5
5
+ b.use :placeholder
6
+ b.optional :maxlength
7
+ b.optional :pattern
8
+ b.optional :min_max
9
+ b.optional :readonly
10
+ b.use :label_input
11
+ b.use :error, :wrap_with => { :tag => :small }
12
+
13
+ # Uncomment the following line to enable hints. The line is commented out by default since Foundation
14
+ # does't provide styles for hints. You will need to provide your own CSS styles for hints.
15
+ # b.use :hint, :wrap_with => { :tag => :span, :class => :hint }
16
+ end
17
+
18
+ # CSS class for buttons
19
+ config.button_class = 'button'
20
+
21
+ # CSS class to add for error notification helper.
22
+ config.error_notification_class = 'alert-box alert'
23
+
24
+ # The default wrapper to be used by the FormBuilder.
25
+ config.default_wrapper = :foundation
26
+ end
data/lib/simple_form.rb CHANGED
@@ -6,14 +6,17 @@ require 'active_support/core_ext/hash/except'
6
6
  require 'active_support/core_ext/hash/reverse_merge'
7
7
 
8
8
  module SimpleForm
9
- autoload :Components, 'simple_form/components'
10
- autoload :ErrorNotification, 'simple_form/error_notification'
11
- autoload :FormBuilder, 'simple_form/form_builder'
12
- autoload :Helpers, 'simple_form/helpers'
13
- autoload :I18nCache, 'simple_form/i18n_cache'
14
- autoload :Inputs, 'simple_form/inputs'
15
- autoload :MapType, 'simple_form/map_type'
16
- autoload :Wrappers, 'simple_form/wrappers'
9
+ extend ActiveSupport::Autoload
10
+
11
+ autoload :Helpers
12
+ autoload :Wrappers
13
+
14
+ eager_autoload do
15
+ autoload :Components
16
+ autoload :ErrorNotification
17
+ autoload :FormBuilder
18
+ autoload :Inputs
19
+ end
17
20
 
18
21
  ## CONFIGURATION OPTIONS
19
22
 
@@ -92,7 +92,7 @@ module SimpleForm
92
92
  rendered_collection = render_collection(
93
93
  collection, value_method, text_method, options, html_options
94
94
  ) do |item, value, text, default_html_options|
95
- builder = instantiate_builder(RadioButtonBuilder, attribute, item, value, text, default_html_options)
95
+ builder = instantiate_collection_builder(RadioButtonBuilder, attribute, item, value, text, default_html_options)
96
96
 
97
97
  if block_given?
98
98
  yield builder
@@ -167,7 +167,7 @@ module SimpleForm
167
167
  collection, value_method, text_method, options, html_options
168
168
  ) do |item, value, text, default_html_options|
169
169
  default_html_options[:multiple] = true
170
- builder = instantiate_builder(CheckBoxBuilder, attribute, item, value, text, default_html_options)
170
+ builder = instantiate_collection_builder(CheckBoxBuilder, attribute, item, value, text, default_html_options)
171
171
 
172
172
  if block_given?
173
173
  yield builder
@@ -194,7 +194,7 @@ module SimpleForm
194
194
  # end
195
195
  def simple_fields_for(*args, &block)
196
196
  options = args.extract_options!
197
- options[:wrapper] ||= self.options[:wrapper]
197
+ options[:wrapper] = self.options[:wrapper] if options[:wrapper].nil?
198
198
  options[:defaults] ||= self.options[:defaults]
199
199
 
200
200
  if self.class < ActionView::Helpers::FormBuilder
@@ -207,7 +207,7 @@ module SimpleForm
207
207
 
208
208
  private
209
209
 
210
- def instantiate_builder(builder_class, attribute, item, value, text, html_options)
210
+ def instantiate_collection_builder(builder_class, attribute, item, value, text, html_options)
211
211
  builder_class.new(self, attribute, item,
212
212
  sanitize_attribute_name(attribute, value), text, value, html_options)
213
213
  end
@@ -277,34 +277,48 @@ end
277
277
  module ActionView::Helpers
278
278
  class FormBuilder
279
279
  include SimpleForm::ActionViewExtensions::Builder
280
+ end
280
281
 
281
- # Override default Rails collection_select helper to handle lambdas/procs in
282
+ module FormOptionsHelper
283
+ # Override Rails options_from_collection_for_select to handle lambdas/procs in
282
284
  # text and value methods, so it works the same way as collection_radio_buttons
283
285
  # and collection_check_boxes in SimpleForm. If none of text/value methods is a
284
286
  # callable object, then it just delegates back to original collection select.
285
- #
286
- alias :original_collection_select :collection_select
287
- def collection_select(attribute, collection, value_method, text_method, options={}, html_options={})
287
+ # FIXME: remove when support only Rails 4.0 forward
288
+ # https://github.com/rails/rails/commit/9035324367526af0300477a58b6d3efc15d1a5a8
289
+ alias :original_options_from_collection_for_select :options_from_collection_for_select
290
+ def options_from_collection_for_select(collection, value_method, text_method, selected = nil)
288
291
  if value_method.respond_to?(:call) || text_method.respond_to?(:call)
289
292
  collection = collection.map do |item|
290
293
  value = value_for_collection(item, value_method)
291
294
  text = value_for_collection(item, text_method)
292
295
 
293
- default_html_options = default_html_options_for_collection(item, value, options, html_options)
294
- disabled = value if default_html_options[:disabled]
295
- selected = value if default_html_options[:selected]
296
-
297
- [value, text, selected, disabled]
296
+ [value, text]
298
297
  end
299
298
 
300
- [:disabled, :selected].each do |option|
301
- option_value = collection.map(&:pop).compact
302
- options[option] = option_value if option_value.present?
303
- end
304
299
  value_method, text_method = :first, :last
300
+ selected = extract_selected_and_disabled_and_call_procs selected, collection
301
+ end
302
+
303
+ original_options_from_collection_for_select collection, value_method, text_method, selected
304
+ end
305
+
306
+ private
307
+
308
+ def extract_selected_and_disabled_and_call_procs(selected, collection)
309
+ selected, disabled = extract_selected_and_disabled selected
310
+ selected_disabled = { :selected => selected, :disabled => disabled }
311
+
312
+ selected_disabled.each do |key, check|
313
+ if check.is_a? Proc
314
+ values = collection.map { |option| option.first if check.call(option.first) }
315
+ selected_disabled[key] = values
316
+ end
305
317
  end
318
+ end
306
319
 
307
- original_collection_select(attribute, collection, value_method, text_method, options, html_options)
320
+ def value_for_collection(item, value) #:nodoc:
321
+ value.respond_to?(:call) ? value.call(item) : item.send(value)
308
322
  end
309
323
  end
310
324
 
@@ -0,0 +1,247 @@
1
+ module SimpleForm
2
+ module ActionViewExtensions
3
+ # A collection of methods required by simple_form but added to rails default form.
4
+ # This means that you can use such methods outside simple_form context.
5
+ module Builder
6
+
7
+ # Wrapper for using SimpleForm inside a default rails form.
8
+ # Example:
9
+ #
10
+ # form_for @user do |f|
11
+ # f.simple_fields_for :posts do |posts_form|
12
+ # # Here you have all simple_form methods available
13
+ # posts_form.input :title
14
+ # end
15
+ # end
16
+ def simple_fields_for(*args, &block)
17
+ options = args.extract_options!
18
+ options[:wrapper] = self.options[:wrapper] if options[:wrapper].nil?
19
+ options[:defaults] ||= self.options[:defaults]
20
+
21
+ if self.class < ActionView::Helpers::FormBuilder
22
+ options[:builder] ||= self.class
23
+ else
24
+ options[:builder] ||= SimpleForm::FormBuilder
25
+ end
26
+ fields_for(*(args << options), &block)
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ module SimpleForm
33
+ module Tags
34
+ module CollectionExtensions
35
+ private
36
+
37
+ def render_collection
38
+ item_wrapper_tag = @options.fetch(:item_wrapper_tag, :span)
39
+ item_wrapper_class = @options[:item_wrapper_class]
40
+
41
+ @collection.map do |item|
42
+ value = value_for_collection(item, @value_method)
43
+ text = value_for_collection(item, @text_method)
44
+ default_html_options = default_html_options_for_collection(item, value)
45
+
46
+ rendered_item = yield item, value, text, default_html_options
47
+
48
+ item_wrapper_tag ? @template_object.content_tag(item_wrapper_tag, rendered_item, :class => item_wrapper_class) : rendered_item
49
+ end.join.html_safe
50
+ end
51
+
52
+ def wrap_rendered_collection(collection)
53
+ wrapper_tag = @options[:collection_wrapper_tag]
54
+
55
+ if wrapper_tag
56
+ wrapper_class = @options[:collection_wrapper_class]
57
+ @template_object.content_tag(wrapper_tag, collection, :class => wrapper_class)
58
+ else
59
+ collection
60
+ end
61
+ end
62
+ end
63
+
64
+ class CollectionRadioButtons < ActionView::Helpers::Tags::CollectionRadioButtons
65
+ include CollectionExtensions
66
+
67
+ def render
68
+ wrap_rendered_collection(super)
69
+ end
70
+
71
+ private
72
+
73
+ def render_component(builder)
74
+ builder.radio_button + builder.label(:class => "collection_radio_buttons")
75
+ end
76
+ end
77
+
78
+ class CollectionCheckBoxes < ActionView::Helpers::Tags::CollectionCheckBoxes
79
+ include CollectionExtensions
80
+
81
+ def render
82
+ wrap_rendered_collection(super)
83
+ end
84
+
85
+ private
86
+
87
+ def render_component(builder)
88
+ builder.check_box + builder.label(:class => "collection_check_boxes")
89
+ end
90
+ end
91
+ end
92
+ end
93
+
94
+ module ActionView::Helpers
95
+ class FormBuilder
96
+ include SimpleForm::ActionViewExtensions::Builder
97
+ end
98
+
99
+ <<<<<<< HEAD
100
+ # Create a collection of radio inputs for the attribute. Basically this
101
+ # helper will create a radio input associated with a label for each
102
+ # text/value option in the collection, using value_method and text_method
103
+ # to convert these text/value. You can give a symbol or a proc to both
104
+ # value_method and text_method, that will be evaluated for each item in
105
+ # the collection.
106
+ #
107
+ # == Examples
108
+ #
109
+ # form_for @user do |f|
110
+ # f.collection_radio_buttons :options, [[true, 'Yes'] ,[false, 'No']], :first, :last
111
+ # end
112
+ #
113
+ # <input id="user_options_true" name="user[options]" type="radio" value="true" />
114
+ # <label class="collection_radio_buttons" for="user_options_true">Yes</label>
115
+ # <input id="user_options_false" name="user[options]" type="radio" value="false" />
116
+ # <label class="collection_radio_buttons" for="user_options_false">No</label>
117
+ #
118
+ # It is also possible to give a block that should generate the radio +
119
+ # label. To wrap the radio with the label, for instance:
120
+ #
121
+ # form_for @user do |f|
122
+ # f.collection_radio_buttons(
123
+ # :options, [[true, 'Yes'] ,[false, 'No']], :first, :last
124
+ # ) do |b|
125
+ # b.label { b.radio_button + b.text }
126
+ # end
127
+ # end
128
+ #
129
+ # == Options
130
+ #
131
+ # Collection radio accepts some extra options:
132
+ #
133
+ # * checked => the value that should be checked initially.
134
+ #
135
+ # * disabled => the value or values that should be disabled. Accepts a single
136
+ # item or an array of items.
137
+ #
138
+ # * collection_wrapper_tag => the tag to wrap the entire collection.
139
+ #
140
+ # * collection_wrapper_class => the CSS class to use for collection_wrapper_tag
141
+ #
142
+ # * item_wrapper_tag => the tag to wrap each item in the collection.
143
+ #
144
+ # * item_wrapper_class => the CSS class to use for item_wrapper_tag
145
+ #
146
+ # * a block => to generate the label + radio or any other component.
147
+ def collection_radio_buttons(method, collection, value_method, text_method, options = {}, html_options = {}, &block)
148
+ SimpleForm::Tags::CollectionRadioButtons.new(@object_name, method, @template, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options)).render(&block)
149
+ =======
150
+ module FormOptionsHelper
151
+ # Override Rails options_from_collection_for_select to handle lambdas/procs in
152
+ # text and value methods, so it works the same way as collection_radio_buttons
153
+ # and collection_check_boxes in SimpleForm. If none of text/value methods is a
154
+ # callable object, then it just delegates back to original collection select.
155
+ # FIXME: remove when support only Rails 4.0 forward
156
+ # https://github.com/rails/rails/commit/9035324367526af0300477a58b6d3efc15d1a5a8
157
+ alias :original_options_from_collection_for_select :options_from_collection_for_select
158
+ def options_from_collection_for_select(collection, value_method, text_method, selected = nil)
159
+ if value_method.respond_to?(:call) || text_method.respond_to?(:call)
160
+ collection = collection.map do |item|
161
+ value = value_for_collection(item, value_method)
162
+ text = value_for_collection(item, text_method)
163
+
164
+ [value, text]
165
+ end
166
+
167
+ value_method, text_method = :first, :last
168
+ selected = extract_selected_and_disabled_and_call_procs selected, collection
169
+ end
170
+
171
+ original_options_from_collection_for_select collection, value_method, text_method, selected
172
+ end
173
+
174
+ private
175
+
176
+ def extract_selected_and_disabled_and_call_procs(selected, collection)
177
+ selected, disabled = extract_selected_and_disabled selected
178
+ selected_disabled = { :selected => selected, :disabled => disabled }
179
+
180
+ selected_disabled.each do |key, check|
181
+ if check.is_a? Proc
182
+ values = collection.map { |option| option.first if check.call(option.first) }
183
+ selected_disabled[key] = values
184
+ end
185
+ end
186
+ end
187
+
188
+ def value_for_collection(item, value) #:nodoc:
189
+ value.respond_to?(:call) ? value.call(item) : item.send(value)
190
+ >>>>>>> master
191
+ end
192
+
193
+ # Creates a collection of check boxes for each item in the collection,
194
+ # associated with a clickable label. Use value_method and text_method to
195
+ # convert items in the collection for use as text/value in check boxes.
196
+ # You can give a symbol or a proc to both value_method and text_method,
197
+ # that will be evaluated for each item in the collection.
198
+ #
199
+ # == Examples
200
+ #
201
+ # form_for @user do |f|
202
+ # f.collection_check_boxes :options, [[true, 'Yes'] ,[false, 'No']], :first, :last
203
+ # end
204
+ #
205
+ # <input name="user[options][]" type="hidden" value="" />
206
+ # <input id="user_options_true" name="user[options][]" type="checkbox" value="true" />
207
+ # <label class="collection_check_boxes" for="user_options_true">Yes</label>
208
+ # <input name="user[options][]" type="hidden" value="" />
209
+ # <input id="user_options_false" name="user[options][]" type="checkbox" value="false" />
210
+ # <label class="collection_check_boxes" for="user_options_false">No</label>
211
+ #
212
+ # It is also possible to give a block that should generate the check box +
213
+ # label. To wrap the check box with the label, for instance:
214
+ #
215
+ # form_for @user do |f|
216
+ # f.collection_check_boxes(
217
+ # :options, [[true, 'Yes'] ,[false, 'No']], :first, :last
218
+ # ) do |b|
219
+ # b.label { b.check_box + b.text }
220
+ # end
221
+ # end
222
+ #
223
+ # == Options
224
+ #
225
+ # Collection check box accepts some extra options:
226
+ #
227
+ # * checked => the value or values that should be checked initially. Accepts
228
+ # a single item or an array of items. It overrides existing associations.
229
+ #
230
+ # * disabled => the value or values that should be disabled. Accepts a single
231
+ # item or an array of items.
232
+ #
233
+ # * collection_wrapper_tag => the tag to wrap the entire collection.
234
+ #
235
+ # * collection_wrapper_class => the CSS class to use for collection_wrapper_tag. This option
236
+ # is ignored if the :collection_wrapper_tag option is blank.
237
+ #
238
+ # * item_wrapper_tag => the tag to wrap each item in the collection.
239
+ #
240
+ # * item_wrapper_class => the CSS class to use for item_wrapper_tag
241
+ #
242
+ # * a block => to generate the label + check box or any other component.
243
+ def collection_check_boxes(method, collection, value_method, text_method, options = {}, html_options = {}, &block)
244
+ SimpleForm::Tags::CollectionCheckBoxes.new(@object_name, method, @template, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options)).render(&block)
245
+ end
246
+ end
247
+ end
@@ -6,15 +6,17 @@ module SimpleForm
6
6
  # to the input in order to be enabled. On the other hand, things like
7
7
  # hints can generate output automatically by doing I18n lookups.
8
8
  module Components
9
- autoload :Errors, 'simple_form/components/errors'
10
- autoload :Hints, 'simple_form/components/hints'
11
- autoload :HTML5, 'simple_form/components/html5'
12
- autoload :LabelInput, 'simple_form/components/label_input'
13
- autoload :Labels, 'simple_form/components/labels'
14
- autoload :MinMax, 'simple_form/components/min_max'
15
- autoload :Maxlength, 'simple_form/components/maxlength'
16
- autoload :Pattern, 'simple_form/components/pattern'
17
- autoload :Placeholders, 'simple_form/components/placeholders'
18
- autoload :Readonly, 'simple_form/components/readonly'
9
+ extend ActiveSupport::Autoload
10
+
11
+ autoload :Errors
12
+ autoload :Hints
13
+ autoload :HTML5
14
+ autoload :LabelInput
15
+ autoload :Labels
16
+ autoload :MinMax
17
+ autoload :Maxlength
18
+ autoload :Pattern
19
+ autoload :Placeholders
20
+ autoload :Readonly
19
21
  end
20
22
  end
@@ -11,7 +11,7 @@ module SimpleForm
11
11
  end
12
12
 
13
13
  def has_hint?
14
- hint.present?
14
+ options[:hint] != false && hint.present?
15
15
  end
16
16
  end
17
17
  end
@@ -8,7 +8,7 @@ module SimpleForm
8
8
  end
9
9
 
10
10
  def label_input
11
- (options[:label] == false ? "" : label) + input
11
+ options[:label] == false ? input : (label + input)
12
12
  end
13
13
  end
14
14
  end
@@ -23,7 +23,7 @@ module SimpleForm
23
23
  end
24
24
 
25
25
  def find_length_validator
26
- find_validator(ActiveModel::Validations::LengthValidator)
26
+ find_validator(:length)
27
27
  end
28
28
 
29
29
  def has_tokenizer?(length_validator)