actionview 4.2.11.1 → 6.0.4

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