actionview 4.2.11.1 → 6.1.5

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionview might be problematic. Click here for more details.

Files changed (117) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +232 -186
  3. data/MIT-LICENSE +1 -2
  4. data/README.rdoc +9 -8
  5. data/lib/action_view/base.rb +115 -39
  6. data/lib/action_view/buffers.rb +18 -1
  7. data/lib/action_view/cache_expiry.rb +52 -0
  8. data/lib/action_view/context.rb +8 -12
  9. data/lib/action_view/dependency_tracker.rb +61 -21
  10. data/lib/action_view/digestor.rb +89 -85
  11. data/lib/action_view/flows.rb +11 -12
  12. data/lib/action_view/gem_version.rb +6 -4
  13. data/lib/action_view/helpers/active_model_helper.rb +16 -11
  14. data/lib/action_view/helpers/asset_tag_helper.rb +282 -83
  15. data/lib/action_view/helpers/asset_url_helper.rb +175 -69
  16. data/lib/action_view/helpers/atom_feed_helper.rb +20 -17
  17. data/lib/action_view/helpers/cache_helper.rb +107 -43
  18. data/lib/action_view/helpers/capture_helper.rb +20 -13
  19. data/lib/action_view/helpers/controller_helper.rb +15 -4
  20. data/lib/action_view/helpers/csp_helper.rb +26 -0
  21. data/lib/action_view/helpers/csrf_helper.rb +8 -6
  22. data/lib/action_view/helpers/date_helper.rb +232 -130
  23. data/lib/action_view/helpers/debug_helper.rb +7 -6
  24. data/lib/action_view/helpers/form_helper.rb +808 -146
  25. data/lib/action_view/helpers/form_options_helper.rb +124 -78
  26. data/lib/action_view/helpers/form_tag_helper.rb +120 -74
  27. data/lib/action_view/helpers/javascript_helper.rb +33 -17
  28. data/lib/action_view/helpers/number_helper.rb +87 -62
  29. data/lib/action_view/helpers/output_safety_helper.rb +36 -4
  30. data/lib/action_view/helpers/rendering_helper.rb +21 -10
  31. data/lib/action_view/helpers/sanitize_helper.rb +30 -31
  32. data/lib/action_view/helpers/tag_helper.rb +269 -68
  33. data/lib/action_view/helpers/tags/base.rb +141 -97
  34. data/lib/action_view/helpers/tags/check_box.rb +20 -19
  35. data/lib/action_view/helpers/tags/checkable.rb +4 -2
  36. data/lib/action_view/helpers/tags/collection_check_boxes.rb +12 -34
  37. data/lib/action_view/helpers/tags/collection_helpers.rb +69 -36
  38. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +6 -12
  39. data/lib/action_view/helpers/tags/collection_select.rb +4 -2
  40. data/lib/action_view/helpers/tags/color_field.rb +4 -3
  41. data/lib/action_view/helpers/tags/date_field.rb +3 -2
  42. data/lib/action_view/helpers/tags/date_select.rb +38 -37
  43. data/lib/action_view/helpers/tags/datetime_field.rb +4 -3
  44. data/lib/action_view/helpers/tags/datetime_local_field.rb +3 -2
  45. data/lib/action_view/helpers/tags/datetime_select.rb +2 -0
  46. data/lib/action_view/helpers/tags/email_field.rb +2 -0
  47. data/lib/action_view/helpers/tags/file_field.rb +2 -0
  48. data/lib/action_view/helpers/tags/grouped_collection_select.rb +4 -2
  49. data/lib/action_view/helpers/tags/hidden_field.rb +6 -0
  50. data/lib/action_view/helpers/tags/label.rb +7 -2
  51. data/lib/action_view/helpers/tags/month_field.rb +3 -2
  52. data/lib/action_view/helpers/tags/number_field.rb +2 -0
  53. data/lib/action_view/helpers/tags/password_field.rb +3 -1
  54. data/lib/action_view/helpers/tags/placeholderable.rb +3 -1
  55. data/lib/action_view/helpers/tags/radio_button.rb +7 -6
  56. data/lib/action_view/helpers/tags/range_field.rb +2 -0
  57. data/lib/action_view/helpers/tags/search_field.rb +14 -9
  58. data/lib/action_view/helpers/tags/select.rb +11 -10
  59. data/lib/action_view/helpers/tags/tel_field.rb +2 -0
  60. data/lib/action_view/helpers/tags/text_area.rb +4 -2
  61. data/lib/action_view/helpers/tags/text_field.rb +8 -8
  62. data/lib/action_view/helpers/tags/time_field.rb +3 -2
  63. data/lib/action_view/helpers/tags/time_select.rb +2 -0
  64. data/lib/action_view/helpers/tags/time_zone_select.rb +3 -1
  65. data/lib/action_view/helpers/tags/translator.rb +15 -16
  66. data/lib/action_view/helpers/tags/url_field.rb +2 -0
  67. data/lib/action_view/helpers/tags/week_field.rb +3 -2
  68. data/lib/action_view/helpers/tags.rb +3 -1
  69. data/lib/action_view/helpers/text_helper.rb +56 -38
  70. data/lib/action_view/helpers/translation_helper.rb +150 -68
  71. data/lib/action_view/helpers/url_helper.rb +284 -117
  72. data/lib/action_view/helpers.rb +5 -3
  73. data/lib/action_view/layouts.rb +68 -63
  74. data/lib/action_view/log_subscriber.rb +77 -10
  75. data/lib/action_view/lookup_context.rb +134 -91
  76. data/lib/action_view/model_naming.rb +3 -1
  77. data/lib/action_view/path_set.rb +26 -24
  78. data/lib/action_view/railtie.rb +62 -13
  79. data/lib/action_view/record_identifier.rb +53 -26
  80. data/lib/action_view/renderer/abstract_renderer.rb +151 -14
  81. data/lib/action_view/renderer/collection_renderer.rb +196 -0
  82. data/lib/action_view/renderer/object_renderer.rb +34 -0
  83. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +102 -0
  84. data/lib/action_view/renderer/partial_renderer.rb +55 -303
  85. data/lib/action_view/renderer/renderer.rb +66 -9
  86. data/lib/action_view/renderer/streaming_template_renderer.rb +58 -54
  87. data/lib/action_view/renderer/template_renderer.rb +82 -73
  88. data/lib/action_view/rendering.rb +71 -45
  89. data/lib/action_view/routing_url_for.rb +34 -23
  90. data/lib/action_view/tasks/cache_digests.rake +25 -0
  91. data/lib/action_view/template/error.rb +44 -29
  92. data/lib/action_view/template/handlers/builder.rb +12 -13
  93. data/lib/action_view/template/handlers/erb/erubi.rb +89 -0
  94. data/lib/action_view/template/handlers/erb.rb +23 -89
  95. data/lib/action_view/template/handlers/html.rb +11 -0
  96. data/lib/action_view/template/handlers/raw.rb +4 -4
  97. data/lib/action_view/template/handlers.rb +12 -8
  98. data/lib/action_view/template/html.rb +10 -11
  99. data/lib/action_view/template/inline.rb +22 -0
  100. data/lib/action_view/template/raw_file.rb +25 -0
  101. data/lib/action_view/template/renderable.rb +24 -0
  102. data/lib/action_view/template/resolver.rb +263 -197
  103. data/lib/action_view/template/sources/file.rb +17 -0
  104. data/lib/action_view/template/sources.rb +13 -0
  105. data/lib/action_view/template/text.rb +8 -10
  106. data/lib/action_view/template/types.rb +18 -18
  107. data/lib/action_view/template.rb +108 -92
  108. data/lib/action_view/test_case.rb +66 -53
  109. data/lib/action_view/testing/resolvers.rb +24 -33
  110. data/lib/action_view/unbound_template.rb +31 -0
  111. data/lib/action_view/version.rb +3 -1
  112. data/lib/action_view/view_paths.rb +73 -58
  113. data/lib/action_view.rb +14 -8
  114. data/lib/assets/compiled/rails-ujs.js +746 -0
  115. metadata +42 -29
  116. data/lib/action_view/helpers/record_tag_helper.rb +0 -108
  117. data/lib/action_view/tasks/dependencies.rake +0 -23
@@ -1,55 +1,57 @@
1
- require 'cgi'
2
- require 'erb'
3
- require 'action_view/helpers/form_helper'
4
- require 'active_support/core_ext/string/output_safety'
5
- require 'active_support/core_ext/array/extract_options'
6
- require 'active_support/core_ext/array/wrap'
1
+ # frozen_string_literal: true
2
+
3
+ require "cgi"
4
+ require "erb"
5
+ require "action_view/helpers/form_helper"
6
+ require "active_support/core_ext/string/output_safety"
7
+ require "active_support/core_ext/array/extract_options"
8
+ require "active_support/core_ext/array/wrap"
7
9
 
8
10
  module ActionView
9
11
  # = Action View Form Option Helpers
10
- module Helpers
12
+ module Helpers #:nodoc:
11
13
  # Provides a number of methods for turning different kinds of containers into a set of option tags.
12
14
  #
13
15
  # The <tt>collection_select</tt>, <tt>select</tt> and <tt>time_zone_select</tt> methods take an <tt>options</tt> parameter, a hash:
14
16
  #
15
17
  # * <tt>:include_blank</tt> - set to true or a prompt string if the first option element of the select element is a blank. Useful if there is not a default value required for the select element.
16
18
  #
17
- # select("post", "category", Post::CATEGORIES, {include_blank: true})
19
+ # select("post", "category", Post::CATEGORIES, { include_blank: true })
18
20
  #
19
21
  # could become:
20
22
  #
21
- # <select name="post[category]">
22
- # <option></option>
23
- # <option>joke</option>
24
- # <option>poem</option>
23
+ # <select name="post[category]" id="post_category">
24
+ # <option value="" label=" "></option>
25
+ # <option value="joke">joke</option>
26
+ # <option value="poem">poem</option>
25
27
  # </select>
26
28
  #
27
29
  # Another common case is a select tag for a <tt>belongs_to</tt>-associated object.
28
30
  #
29
31
  # Example with <tt>@post.person_id => 2</tt>:
30
32
  #
31
- # select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, {include_blank: 'None'})
33
+ # select("post", "person_id", Person.all.collect { |p| [ p.name, p.id ] }, { include_blank: 'None' })
32
34
  #
33
35
  # could become:
34
36
  #
35
- # <select name="post[person_id]">
37
+ # <select name="post[person_id]" id="post_person_id">
36
38
  # <option value="">None</option>
37
39
  # <option value="1">David</option>
38
- # <option value="2" selected="selected">Sam</option>
39
- # <option value="3">Tobias</option>
40
+ # <option value="2" selected="selected">Eileen</option>
41
+ # <option value="3">Rafael</option>
40
42
  # </select>
41
43
  #
42
44
  # * <tt>:prompt</tt> - set to true or a prompt string. When the select element doesn't have a value yet, this prepends an option with a generic prompt -- "Please select" -- or the given prompt string.
43
45
  #
44
- # select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, {prompt: 'Select Person'})
46
+ # select("post", "person_id", Person.all.collect { |p| [ p.name, p.id ] }, { prompt: 'Select Person' })
45
47
  #
46
48
  # could become:
47
49
  #
48
- # <select name="post[person_id]">
50
+ # <select name="post[person_id]" id="post_person_id">
49
51
  # <option value="">Select Person</option>
50
52
  # <option value="1">David</option>
51
- # <option value="2">Sam</option>
52
- # <option value="3">Tobias</option>
53
+ # <option value="2">Eileen</option>
54
+ # <option value="3">Rafael</option>
53
55
  # </select>
54
56
  #
55
57
  # * <tt>:index</tt> - like the other form helpers, +select+ can accept an <tt>:index</tt> option to manually set the ID used in the resulting output. Unlike other helpers, +select+ expects this
@@ -67,23 +69,22 @@ module ActionView
67
69
  #
68
70
  # * <tt>:disabled</tt> - can be a single value or an array of values that will be disabled options in the final output.
69
71
  #
70
- # select("post", "category", Post::CATEGORIES, {disabled: 'restricted'})
72
+ # select("post", "category", Post::CATEGORIES, { disabled: 'restricted' })
71
73
  #
72
74
  # could become:
73
75
  #
74
- # <select name="post[category]">
75
- # <option></option>
76
- # <option>joke</option>
77
- # <option>poem</option>
78
- # <option disabled="disabled">restricted</option>
76
+ # <select name="post[category]" id="post_category">
77
+ # <option value="joke">joke</option>
78
+ # <option value="poem">poem</option>
79
+ # <option disabled="disabled" value="restricted">restricted</option>
79
80
  # </select>
80
81
  #
81
82
  # When used with the <tt>collection_select</tt> helper, <tt>:disabled</tt> can also be a Proc that identifies those options that should be disabled.
82
83
  #
83
- # collection_select(:post, :category_id, Category.all, :id, :name, {disabled: lambda{|category| category.archived? }})
84
+ # collection_select(:post, :category_id, Category.all, :id, :name, { disabled: -> (category) { category.archived? } })
84
85
  #
85
86
  # If the categories "2008 stuff" and "Christmas" return true when the method <tt>archived?</tt> is called, this would return:
86
- # <select name="post[category_id]">
87
+ # <select name="post[category_id]" id="post_category_id">
87
88
  # <option value="1" disabled="disabled">2008 stuff</option>
88
89
  # <option value="2" disabled="disabled">Christmas</option>
89
90
  # <option value="3">Jokes</option>
@@ -105,15 +106,15 @@ module ActionView
105
106
  #
106
107
  # For example:
107
108
  #
108
- # select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, { include_blank: true })
109
+ # select("post", "person_id", Person.all.collect { |p| [ p.name, p.id ] }, { include_blank: true })
109
110
  #
110
111
  # would become:
111
112
  #
112
- # <select name="post[person_id]">
113
- # <option value=""></option>
113
+ # <select name="post[person_id]" id="post_person_id">
114
+ # <option value="" label=" "></option>
114
115
  # <option value="1" selected="selected">David</option>
115
- # <option value="2">Sam</option>
116
- # <option value="3">Tobias</option>
116
+ # <option value="2">Eileen</option>
117
+ # <option value="3">Rafael</option>
117
118
  # </select>
118
119
  #
119
120
  # assuming the associated person has ID 1.
@@ -141,7 +142,7 @@ module ActionView
141
142
  #
142
143
  # The HTML specification says when +multiple+ parameter passed to select and all options got deselected
143
144
  # web browsers do not send any value to server. Unfortunately this introduces a gotcha:
144
- # if an +User+ model has many +roles+ and have +role_ids+ accessor, and in the form that edits roles of the user
145
+ # if a +User+ model has many +roles+ and have +role_ids+ accessor, and in the form that edits roles of the user
145
146
  # the user deselects all roles from +role_ids+ multiple select box, no +role_ids+ parameter is sent. So,
146
147
  # any mass-assignment idiom like
147
148
  #
@@ -192,7 +193,7 @@ module ActionView
192
193
  # collection_select(:post, :author_id, Author.all, :id, :name_with_initial, prompt: true)
193
194
  #
194
195
  # If <tt>@post.author_id</tt> is already <tt>1</tt>, this would return:
195
- # <select name="post[author_id]">
196
+ # <select name="post[author_id]" id="post_author_id">
196
197
  # <option value="">Please select</option>
197
198
  # <option value="1" selected="selected">D. Heinemeier Hansson</option>
198
199
  # <option value="2">D. Thomas</option>
@@ -212,9 +213,13 @@ module ActionView
212
213
  # * +method+ - The attribute of +object+ corresponding to the select tag
213
214
  # * +collection+ - An array of objects representing the <tt><optgroup></tt> tags.
214
215
  # * +group_method+ - The name of a method which, when called on a member of +collection+, returns an
215
- # array of child objects representing the <tt><option></tt> tags.
216
+ # array of child objects representing the <tt><option></tt> tags. It can also be any object that responds
217
+ # to +call+, such as a +proc+, that will be called for each member of the +collection+ to retrieve the
218
+ # value.
216
219
  # * +group_label_method+ - The name of a method which, when called on a member of +collection+, returns a
217
- # string to be used as the +label+ attribute for its <tt><optgroup></tt> tag.
220
+ # string to be used as the +label+ attribute for its <tt><optgroup></tt> tag. It can also be any object
221
+ # that responds to +call+, such as a +proc+, that will be called for each member of the +collection+ to
222
+ # retrieve the label.
218
223
  # * +option_key_method+ - The name of a method which, when called on a child object of a member of
219
224
  # +collection+, returns a value to be used as the +value+ attribute for its <tt><option></tt> tag.
220
225
  # * +option_value_method+ - The name of a method which, when called on a child object of a member of
@@ -243,7 +248,7 @@ module ActionView
243
248
  #
244
249
  # Possible output:
245
250
  #
246
- # <select name="city[country_id]">
251
+ # <select name="city[country_id]" id="city_country_id">
247
252
  # <optgroup label="Africa">
248
253
  # <option value="1">South Africa</option>
249
254
  # <option value="3">Somalia</option>
@@ -268,25 +273,26 @@ module ActionView
268
273
  # for more information.)
269
274
  #
270
275
  # You can also supply an array of ActiveSupport::TimeZone objects
271
- # as +priority_zones+, so that they will be listed above the rest of the
272
- # (long) list. (You can use ActiveSupport::TimeZone.us_zones as a convenience
273
- # for obtaining a list of the US time zones, or a Regexp to select the zones
274
- # of your choice)
276
+ # as +priority_zones+ so that they will be listed above the rest of the
277
+ # (long) list. You can use ActiveSupport::TimeZone.us_zones for a list
278
+ # of US time zones, ActiveSupport::TimeZone.country_zones(country_code)
279
+ # for another country's time zones, or a Regexp to select the zones of
280
+ # your choice.
275
281
  #
276
282
  # Finally, this method supports a <tt>:default</tt> option, which selects
277
283
  # a default ActiveSupport::TimeZone if the object's time zone is +nil+.
278
284
  #
279
- # time_zone_select( "user", "time_zone", nil, include_blank: true)
285
+ # time_zone_select("user", "time_zone", nil, include_blank: true)
280
286
  #
281
- # time_zone_select( "user", "time_zone", nil, default: "Pacific Time (US & Canada)" )
287
+ # time_zone_select("user", "time_zone", nil, default: "Pacific Time (US & Canada)")
282
288
  #
283
- # time_zone_select( "user", 'time_zone', ActiveSupport::TimeZone.us_zones, default: "Pacific Time (US & Canada)")
289
+ # time_zone_select("user", 'time_zone', ActiveSupport::TimeZone.us_zones, default: "Pacific Time (US & Canada)")
284
290
  #
285
- # time_zone_select( "user", 'time_zone', [ ActiveSupport::TimeZone['Alaska'], ActiveSupport::TimeZone['Hawaii'] ])
291
+ # time_zone_select("user", 'time_zone', [ ActiveSupport::TimeZone['Alaska'], ActiveSupport::TimeZone['Hawaii'] ])
286
292
  #
287
- # time_zone_select( "user", 'time_zone', /Australia/)
293
+ # time_zone_select("user", 'time_zone', /Australia/)
288
294
  #
289
- # time_zone_select( "user", "time_zone", ActiveSupport::TimeZone.all.sort, model: ActiveSupport::TimeZone)
295
+ # time_zone_select("user", "time_zone", ActiveSupport::TimeZone.all.sort, model: ActiveSupport::TimeZone)
290
296
  def time_zone_select(object, method, priority_zones = nil, options = {}, html_options = {})
291
297
  Tags::TimeZoneSelect.new(object, method, self, priority_zones, options, html_options).render
292
298
  end
@@ -302,26 +308,26 @@ module ActionView
302
308
  # # => <option value="DKK">Kroner</option>
303
309
  #
304
310
  # options_for_select([ "VISA", "MasterCard" ], "MasterCard")
305
- # # => <option>VISA</option>
306
- # # => <option selected="selected">MasterCard</option>
311
+ # # => <option value="VISA">VISA</option>
312
+ # # => <option selected="selected" value="MasterCard">MasterCard</option>
307
313
  #
308
314
  # options_for_select({ "Basic" => "$20", "Plus" => "$40" }, "$40")
309
315
  # # => <option value="$20">Basic</option>
310
316
  # # => <option value="$40" selected="selected">Plus</option>
311
317
  #
312
318
  # options_for_select([ "VISA", "MasterCard", "Discover" ], ["VISA", "Discover"])
313
- # # => <option selected="selected">VISA</option>
314
- # # => <option>MasterCard</option>
315
- # # => <option selected="selected">Discover</option>
319
+ # # => <option selected="selected" value="VISA">VISA</option>
320
+ # # => <option value="MasterCard">MasterCard</option>
321
+ # # => <option selected="selected" value="Discover">Discover</option>
316
322
  #
317
323
  # You can optionally provide HTML attributes as the last element of the array.
318
324
  #
319
- # options_for_select([ "Denmark", ["USA", {class: 'bold'}], "Sweden" ], ["USA", "Sweden"])
325
+ # options_for_select([ "Denmark", ["USA", { class: 'bold' }], "Sweden" ], ["USA", "Sweden"])
320
326
  # # => <option value="Denmark">Denmark</option>
321
327
  # # => <option value="USA" class="bold" selected="selected">USA</option>
322
328
  # # => <option value="Sweden" selected="selected">Sweden</option>
323
329
  #
324
- # options_for_select([["Dollar", "$", {class: "bold"}], ["Kroner", "DKK", {onclick: "alert('HI');"}]])
330
+ # options_for_select([["Dollar", "$", { class: "bold" }], ["Kroner", "DKK", { onclick: "alert('HI');" }]])
325
331
  # # => <option value="$" class="bold">Dollar</option>
326
332
  # # => <option value="DKK" onclick="alert('HI');">Kroner</option>
327
333
  #
@@ -351,18 +357,18 @@ module ActionView
351
357
  return container if String === container
352
358
 
353
359
  selected, disabled = extract_selected_and_disabled(selected).map do |r|
354
- Array(r).map { |item| item.to_s }
360
+ Array(r).map(&:to_s)
355
361
  end
356
362
 
357
363
  container.map do |element|
358
364
  html_attributes = option_html_attributes(element)
359
- text, value = option_text_and_value(element).map { |item| item.to_s }
365
+ text, value = option_text_and_value(element).map(&:to_s)
360
366
 
361
367
  html_attributes[:selected] ||= option_value_selected?(value, selected)
362
368
  html_attributes[:disabled] ||= disabled && option_value_selected?(value, disabled)
363
369
  html_attributes[:value] = value
364
370
 
365
- content_tag_string(:option, text, html_attributes)
371
+ tag_builder.content_tag_string(:option, text, html_attributes)
366
372
  end.join("\n").html_safe
367
373
  end
368
374
 
@@ -454,9 +460,9 @@ module ActionView
454
460
  def option_groups_from_collection_for_select(collection, group_method, group_label_method, option_key_method, option_value_method, selected_key = nil)
455
461
  collection.map do |group|
456
462
  option_tags = options_from_collection_for_select(
457
- group.send(group_method), option_key_method, option_value_method, selected_key)
463
+ value_for_collection(group, group_method), option_key_method, option_value_method, selected_key)
458
464
 
459
- content_tag(:optgroup, option_tags, label: group.send(group_label_method))
465
+ content_tag("optgroup", option_tags, label: value_for_collection(group, group_label_method))
460
466
  end.join.html_safe
461
467
  end
462
468
 
@@ -528,7 +534,7 @@ module ActionView
528
534
  body = "".html_safe
529
535
 
530
536
  if prompt
531
- body.safe_concat content_tag(:option, prompt_text(prompt), value: "")
537
+ body.safe_concat content_tag("option", prompt_text(prompt), value: "")
532
538
  end
533
539
 
534
540
  grouped_options.each do |container|
@@ -541,14 +547,14 @@ module ActionView
541
547
  end
542
548
 
543
549
  html_attributes = { label: label }.merge!(html_attributes)
544
- body.safe_concat content_tag(:optgroup, options_for_select(container, selected_key), html_attributes)
550
+ body.safe_concat content_tag("optgroup", options_for_select(container, selected_key), html_attributes)
545
551
  end
546
552
 
547
553
  body
548
554
  end
549
555
 
550
556
  # Returns a string of option tags for pretty much any time zone in the
551
- # world. Supply a ActiveSupport::TimeZone name as +selected+ to have it
557
+ # world. Supply an ActiveSupport::TimeZone name as +selected+ to have it
552
558
  # marked as the selected option tag. You can also supply an array of
553
559
  # ActiveSupport::TimeZone objects as +priority_zones+, so that they will
554
560
  # be listed above the rest of the (long) list. (You can use
@@ -556,12 +562,13 @@ module ActionView
556
562
  # of the US time zones, or a Regexp to select the zones of your choice)
557
563
  #
558
564
  # The +selected+ parameter must be either +nil+, or a string that names
559
- # a ActiveSupport::TimeZone.
565
+ # an ActiveSupport::TimeZone.
560
566
  #
561
567
  # By default, +model+ is the ActiveSupport::TimeZone constant (which can
562
- # be obtained in Active Record as a value object). The only requirement
563
- # is that the +model+ parameter be an object that responds to +all+, and
564
- # returns an array of objects that represent time zones.
568
+ # be obtained in Active Record as a value object). The +model+ parameter
569
+ # must respond to +all+ and return an array of objects that represent time
570
+ # zones; each object must respond to +name+. If a Regexp is given it will
571
+ # attempt to match the zones using <code>match?</code> method.
565
572
  #
566
573
  # NOTE: Only the option tags are returned, you have to wrap this call in
567
574
  # a regular HTML select tag.
@@ -573,11 +580,11 @@ module ActionView
573
580
 
574
581
  if priority_zones
575
582
  if priority_zones.is_a?(Regexp)
576
- priority_zones = zones.select { |z| z =~ priority_zones }
583
+ priority_zones = zones.select { |z| z.match?(priority_zones) }
577
584
  end
578
585
 
579
586
  zone_options.safe_concat options_for_select(convert_zones[priority_zones], selected)
580
- zone_options.safe_concat content_tag(:option, '-------------', value: '', disabled: true)
587
+ zone_options.safe_concat content_tag("option", "-------------", value: "", disabled: true)
581
588
  zone_options.safe_concat "\n"
582
589
 
583
590
  zones = zones - priority_zones
@@ -644,6 +651,24 @@ module ActionView
644
651
  # collection_radio_buttons(:post, :author_id, Author.all, :id, :name_with_initial) do |b|
645
652
  # b.label(:"data-value" => b.value) { b.radio_button + b.text }
646
653
  # end
654
+ #
655
+ # ==== Gotcha
656
+ #
657
+ # The HTML specification says when nothing is selected on a collection of radio buttons
658
+ # web browsers do not send any value to server.
659
+ # Unfortunately this introduces a gotcha:
660
+ # if a +User+ model has a +category_id+ field and in the form no category is selected, no +category_id+ parameter is sent. So,
661
+ # any strong parameters idiom like:
662
+ #
663
+ # params.require(:user).permit(...)
664
+ #
665
+ # will raise an error since no <tt>{user: ...}</tt> will be present.
666
+ #
667
+ # To prevent this the helper generates an auxiliary hidden field before
668
+ # every collection of radio buttons. The hidden field has the same name as collection radio button and blank value.
669
+ #
670
+ # In case if you don't want the helper to generate this hidden field you can specify
671
+ # <tt>include_hidden: false</tt> option.
647
672
  def collection_radio_buttons(object, method, collection, value_method, text_method, options = {}, html_options = {}, &block)
648
673
  Tags::CollectionRadioButtons.new(object, method, self, collection, value_method, text_method, options, html_options).render(&block)
649
674
  end
@@ -707,6 +732,27 @@ module ActionView
707
732
  # collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial) do |b|
708
733
  # b.label(:"data-value" => b.value) { b.check_box + b.text }
709
734
  # end
735
+ #
736
+ # ==== Gotcha
737
+ #
738
+ # When no selection is made for a collection of checkboxes most
739
+ # web browsers will not send any value.
740
+ #
741
+ # For example, if we have a +User+ model with +category_ids+ field and we
742
+ # have the following code in our update action:
743
+ #
744
+ # @user.update(params[:user])
745
+ #
746
+ # If no +category_ids+ are selected then we can safely assume this field
747
+ # will not be updated.
748
+ #
749
+ # This is possible thanks to a hidden field generated by the helper method
750
+ # for every collection of checkboxes.
751
+ # This hidden field is given the same field name as the checkboxes with a
752
+ # blank value.
753
+ #
754
+ # In the rare case you don't want this hidden field, you can pass the
755
+ # <tt>include_hidden: false</tt> option to the helper method.
710
756
  def collection_check_boxes(object, method, collection, value_method, text_method, options = {}, html_options = {}, &block)
711
757
  Tags::CollectionCheckBoxes.new(object, method, self, collection, value_method, text_method, options, html_options).render(&block)
712
758
  end
@@ -748,7 +794,7 @@ module ActionView
748
794
  def extract_values_from_collection(collection, value_method, selected)
749
795
  if selected.is_a?(Proc)
750
796
  collection.map do |element|
751
- element.send(value_method) if selected.call(element)
797
+ element.public_send(value_method) if selected.call(element)
752
798
  end.compact
753
799
  else
754
800
  selected
@@ -756,11 +802,11 @@ module ActionView
756
802
  end
757
803
 
758
804
  def value_for_collection(item, value)
759
- value.respond_to?(:call) ? value.call(item) : item.send(value)
805
+ value.respond_to?(:call) ? value.call(item) : item.public_send(value)
760
806
  end
761
807
 
762
808
  def prompt_text(prompt)
763
- prompt.kind_of?(String) ? prompt : I18n.translate('helpers.select.prompt', default: 'Please select')
809
+ prompt.kind_of?(String) ? prompt : I18n.translate("helpers.select.prompt", default: "Please select")
764
810
  end
765
811
  end
766
812
 
@@ -774,7 +820,7 @@ module ActionView
774
820
  #
775
821
  # Please refer to the documentation of the base helper for details.
776
822
  def select(method, choices = nil, options = {}, html_options = {}, &block)
777
- @template.select(@object_name, method, choices, objectify_options(options), @default_options.merge(html_options), &block)
823
+ @template.select(@object_name, method, choices, objectify_options(options), @default_html_options.merge(html_options), &block)
778
824
  end
779
825
 
780
826
  # Wraps ActionView::Helpers::FormOptionsHelper#collection_select for form builders:
@@ -786,7 +832,7 @@ module ActionView
786
832
  #
787
833
  # Please refer to the documentation of the base helper for details.
788
834
  def collection_select(method, collection, value_method, text_method, options = {}, html_options = {})
789
- @template.collection_select(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options))
835
+ @template.collection_select(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_html_options.merge(html_options))
790
836
  end
791
837
 
792
838
  # Wraps ActionView::Helpers::FormOptionsHelper#grouped_collection_select for form builders:
@@ -798,7 +844,7 @@ module ActionView
798
844
  #
799
845
  # Please refer to the documentation of the base helper for details.
800
846
  def grouped_collection_select(method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {})
801
- @template.grouped_collection_select(@object_name, method, collection, group_method, group_label_method, option_key_method, option_value_method, objectify_options(options), @default_options.merge(html_options))
847
+ @template.grouped_collection_select(@object_name, method, collection, group_method, group_label_method, option_key_method, option_value_method, objectify_options(options), @default_html_options.merge(html_options))
802
848
  end
803
849
 
804
850
  # Wraps ActionView::Helpers::FormOptionsHelper#time_zone_select for form builders:
@@ -810,7 +856,7 @@ module ActionView
810
856
  #
811
857
  # Please refer to the documentation of the base helper for details.
812
858
  def time_zone_select(method, priority_zones = nil, options = {}, html_options = {})
813
- @template.time_zone_select(@object_name, method, priority_zones, objectify_options(options), @default_options.merge(html_options))
859
+ @template.time_zone_select(@object_name, method, priority_zones, objectify_options(options), @default_html_options.merge(html_options))
814
860
  end
815
861
 
816
862
  # Wraps ActionView::Helpers::FormOptionsHelper#collection_check_boxes for form builders:
@@ -822,7 +868,7 @@ module ActionView
822
868
  #
823
869
  # Please refer to the documentation of the base helper for details.
824
870
  def collection_check_boxes(method, collection, value_method, text_method, options = {}, html_options = {}, &block)
825
- @template.collection_check_boxes(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options), &block)
871
+ @template.collection_check_boxes(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_html_options.merge(html_options), &block)
826
872
  end
827
873
 
828
874
  # Wraps ActionView::Helpers::FormOptionsHelper#collection_radio_buttons for form builders:
@@ -834,7 +880,7 @@ module ActionView
834
880
  #
835
881
  # Please refer to the documentation of the base helper for details.
836
882
  def collection_radio_buttons(method, collection, value_method, text_method, options = {}, html_options = {}, &block)
837
- @template.collection_radio_buttons(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options), &block)
883
+ @template.collection_radio_buttons(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_html_options.merge(html_options), &block)
838
884
  end
839
885
  end
840
886
  end