actionview 4.2.11.1 → 6.1.5.1

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 +243 -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 +5 -3
  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 +302 -69
  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 +45 -32
  116. data/lib/action_view/helpers/record_tag_helper.rb +0 -108
  117. data/lib/action_view/tasks/dependencies.rake +0 -23
@@ -1,7 +1,9 @@
1
- require 'action_view/helpers/javascript_helper'
2
- require 'active_support/core_ext/array/access'
3
- require 'active_support/core_ext/hash/keys'
4
- require 'active_support/core_ext/string/output_safety'
1
+ # frozen_string_literal: true
2
+
3
+ require "action_view/helpers/javascript_helper"
4
+ require "active_support/core_ext/array/access"
5
+ require "active_support/core_ext/hash/keys"
6
+ require "active_support/core_ext/string/output_safety"
5
7
 
6
8
  module ActionView
7
9
  # = Action View URL Helpers
@@ -35,21 +37,31 @@ module ActionView
35
37
  when :back
36
38
  _back_url
37
39
  else
38
- raise ArgumentError, "arguments passed to url_for can't be handled. Please require " +
40
+ raise ArgumentError, "arguments passed to url_for can't be handled. Please require " \
39
41
  "routes or provide your own implementation"
40
42
  end
41
43
  end
42
44
 
43
45
  def _back_url # :nodoc:
44
- referrer = controller.respond_to?(:request) && controller.request.env["HTTP_REFERER"]
45
- referrer || 'javascript:history.back()'
46
+ _filtered_referrer || "javascript:history.back()"
47
+ end
48
+ private :_back_url
49
+
50
+ def _filtered_referrer # :nodoc:
51
+ if controller.respond_to?(:request)
52
+ referrer = controller.request.env["HTTP_REFERER"]
53
+ if referrer && URI(referrer).scheme != "javascript"
54
+ referrer
55
+ end
56
+ end
57
+ rescue URI::InvalidURIError
46
58
  end
47
- protected :_back_url
59
+ private :_filtered_referrer
48
60
 
49
- # Creates a link tag of the given +name+ using a URL created by the set of +options+.
61
+ # Creates an anchor element of the given +name+ using a URL created by the set of +options+.
50
62
  # See the valid options in the documentation for +url_for+. It's also possible to
51
- # pass a String instead of an options hash, which generates a link tag that uses the
52
- # value of the String as the href for the link. Using a <tt>:back</tt> Symbol instead
63
+ # pass a \String instead of an options hash, which generates an anchor element that uses the
64
+ # value of the \String as the href for the link. Using a <tt>:back</tt> \Symbol instead
53
65
  # of an options hash will generate a link to the referrer (a JavaScript back link
54
66
  # will be used in place of a referrer if none exists). If +nil+ is passed as the name
55
67
  # the value of the link itself will become the name.
@@ -95,10 +107,9 @@ module ActionView
95
107
  # driver to prompt with the question specified (in this case, the
96
108
  # resulting text would be <tt>question?</tt>. If the user accepts, the
97
109
  # link is processed normally, otherwise no action is taken.
98
- # * <tt>:disable_with</tt> - Value of this parameter will be
99
- # used as the value for a disabled version of the submit
100
- # button when the form is submitted. This feature is provided
101
- # by the unobtrusive JavaScript driver.
110
+ # * <tt>:disable_with</tt> - Value of this parameter will be used as the
111
+ # name for a disabled version of the link. This feature is provided by
112
+ # the unobtrusive JavaScript driver.
102
113
  #
103
114
  # ==== Examples
104
115
  # Because it relies on +url_for+, +link_to+ supports both older-style controller/action/id arguments
@@ -128,6 +139,11 @@ module ActionView
128
139
  # link_to "Profiles", controller: "profiles"
129
140
  # # => <a href="/profiles">Profiles</a>
130
141
  #
142
+ # When name is +nil+ the href is presented instead
143
+ #
144
+ # link_to nil, "http://example.com"
145
+ # # => <a href="http://www.example.com">http://www.example.com</a>
146
+ #
131
147
  # You can use a block as well if your link target is hard to fit into the name parameter. ERB example:
132
148
  #
133
149
  # <%= link_to(@profile) do %>
@@ -161,7 +177,7 @@ module ActionView
161
177
  # # => <a href="/searches?query=ruby+on+rails">Ruby on Rails search</a>
162
178
  #
163
179
  # link_to "Nonsense search", searches_path(foo: "bar", baz: "quux")
164
- # # => <a href="/searches?foo=bar&amp;baz=quux">Nonsense search</a>
180
+ # # => <a href="/searches?foo=bar&baz=quux">Nonsense search</a>
165
181
  #
166
182
  # The only option specific to +link_to+ (<tt>:method</tt>) is used as follows:
167
183
  #
@@ -172,6 +188,11 @@ module ActionView
172
188
  #
173
189
  # link_to "Visit Other Site", "http://www.rubyonrails.org/", data: { confirm: "Are you sure?" }
174
190
  # # => <a href="http://www.rubyonrails.org/" data-confirm="Are you sure?">Visit Other Site</a>
191
+ #
192
+ # Also you can set any link attributes such as <tt>target</tt>, <tt>rel</tt>, <tt>type</tt>:
193
+ #
194
+ # link_to "External link", "http://www.rubyonrails.org/", target: "_blank", rel: "nofollow"
195
+ # # => <a href="http://www.rubyonrails.org/" target="_blank" rel="nofollow">External link</a>
175
196
  def link_to(name = nil, options = nil, html_options = nil, &block)
176
197
  html_options, options, name = options, name, block if block_given?
177
198
  options ||= {}
@@ -179,9 +200,9 @@ module ActionView
179
200
  html_options = convert_options_to_data_attributes(options, html_options)
180
201
 
181
202
  url = url_for(options)
182
- html_options['href'] ||= url
203
+ html_options["href"] ||= url
183
204
 
184
- content_tag(:a, name || url, html_options, &block)
205
+ content_tag("a", name || url, html_options, &block)
185
206
  end
186
207
 
187
208
  # Generates a form containing a single button that submits to the URL created
@@ -205,7 +226,7 @@ module ActionView
205
226
  # The +options+ hash accepts the same options as +url_for+.
206
227
  #
207
228
  # There are a few special +html_options+:
208
- # * <tt>:method</tt> - Symbol of HTTP verb. Supported verbs are <tt>:post</tt>, <tt>:get</tt>,
229
+ # * <tt>:method</tt> - \Symbol of HTTP verb. Supported verbs are <tt>:post</tt>, <tt>:get</tt>,
209
230
  # <tt>:delete</tt>, <tt>:patch</tt>, and <tt>:put</tt>. By default it will be <tt>:post</tt>.
210
231
  # * <tt>:disabled</tt> - If set to true, it will generate a disabled button.
211
232
  # * <tt>:data</tt> - This option can be used to add custom data attributes.
@@ -214,7 +235,7 @@ module ActionView
214
235
  # * <tt>:form</tt> - This hash will be form attributes
215
236
  # * <tt>:form_class</tt> - This controls the class of the form within which the submit button will
216
237
  # be placed
217
- # * <tt>:params</tt> - Hash of parameters to be rendered as hidden fields within the form.
238
+ # * <tt>:params</tt> - \Hash of parameters to be rendered as hidden fields within the form.
218
239
  #
219
240
  # ==== Data attributes
220
241
  #
@@ -232,7 +253,7 @@ module ActionView
232
253
  # # <input value="New" type="submit" />
233
254
  # # </form>"
234
255
  #
235
- # <%= button_to "New", new_articles_path %>
256
+ # <%= button_to "New", new_article_path %>
236
257
  # # => "<form method="post" action="/articles/new" class="button_to">
237
258
  # # <input value="New" type="submit" />
238
259
  # # </form>"
@@ -269,7 +290,7 @@ module ActionView
269
290
  #
270
291
  #
271
292
  # <%= button_to('Destroy', 'http://www.example.com',
272
- # method: "delete", remote: true, data: { confirm: 'Are you sure?', disable_with: 'loading...' }) %>
293
+ # method: :delete, remote: true, data: { confirm: 'Are you sure?', disable_with: 'loading...' }) %>
273
294
  # # => "<form class='button_to' method='post' action='http://www.example.com' data-remote='true'>
274
295
  # # <input name='_method' value='delete' type='hidden' />
275
296
  # # <input value='Destroy' type='submit' data-disable-with='loading...' data-confirm='Are you sure?' />
@@ -280,42 +301,47 @@ module ActionView
280
301
  html_options, options = options, name if block_given?
281
302
  options ||= {}
282
303
  html_options ||= {}
283
-
284
304
  html_options = html_options.stringify_keys
285
- convert_boolean_attributes!(html_options, %w(disabled))
286
305
 
287
306
  url = options.is_a?(String) ? options : url_for(options)
288
- remote = html_options.delete('remote')
289
- params = html_options.delete('params')
290
-
291
- method = html_options.delete('method').to_s
292
- method_tag = BUTTON_TAG_METHOD_VERBS.include?(method) ? method_tag(method) : ''.html_safe
293
-
294
- form_method = method == 'get' ? 'get' : 'post'
295
- form_options = html_options.delete('form') || {}
296
- form_options[:class] ||= html_options.delete('form_class') || 'button_to'
297
- form_options.merge!(method: form_method, action: url)
298
- form_options.merge!("data-remote" => "true") if remote
299
-
300
- request_token_tag = form_method == 'post' ? token_tag : ''
307
+ remote = html_options.delete("remote")
308
+ params = html_options.delete("params")
309
+
310
+ method = html_options.delete("method").to_s
311
+ method_tag = BUTTON_TAG_METHOD_VERBS.include?(method) ? method_tag(method) : "".html_safe
312
+
313
+ form_method = method == "get" ? "get" : "post"
314
+ form_options = html_options.delete("form") || {}
315
+ form_options[:class] ||= html_options.delete("form_class") || "button_to"
316
+ form_options[:method] = form_method
317
+ form_options[:action] = url
318
+ form_options[:'data-remote'] = true if remote
319
+
320
+ request_token_tag = if form_method == "post"
321
+ request_method = method.empty? ? "post" : method
322
+ token_tag(nil, form_options: { action: url, method: request_method })
323
+ else
324
+ ""
325
+ end
301
326
 
302
327
  html_options = convert_options_to_data_attributes(options, html_options)
303
- html_options['type'] = 'submit'
328
+ html_options["type"] = "submit"
304
329
 
305
330
  button = if block_given?
306
- content_tag('button', html_options, &block)
331
+ content_tag("button", html_options, &block)
307
332
  else
308
- html_options['value'] = name || url
309
- tag('input', html_options)
333
+ html_options["value"] = name || url
334
+ tag("input", html_options)
310
335
  end
311
336
 
312
337
  inner_tags = method_tag.safe_concat(button).safe_concat(request_token_tag)
313
338
  if params
314
- params.each do |param_name, value|
315
- inner_tags.safe_concat tag(:input, type: "hidden", name: param_name, value: value.to_param)
339
+ to_form_params(params).each do |param|
340
+ inner_tags.safe_concat tag(:input, type: "hidden", name: param[:name], value: param[:value],
341
+ autocomplete: "off")
316
342
  end
317
343
  end
318
- content_tag('form', inner_tags, form_options)
344
+ content_tag("form", inner_tags, form_options)
319
345
  end
320
346
 
321
347
  # Creates a link tag of the given +name+ using a URL created by the set of
@@ -387,8 +413,7 @@ module ActionView
387
413
  # Creates a link tag of the given +name+ using a URL created by the set of
388
414
  # +options+ if +condition+ is true, otherwise only the name is
389
415
  # returned. To specialize the default behavior, you can pass a block that
390
- # accepts the name or the full argument list for +link_to_unless+ (see the examples
391
- # in +link_to_unless+).
416
+ # accepts the name or the full argument list for +link_to_if+.
392
417
  #
393
418
  # ==== Examples
394
419
  # <%= link_to_if(@current_user.nil?, "Login", { controller: "sessions", action: "new" }) %>
@@ -428,6 +453,7 @@ module ActionView
428
453
  # * <tt>:body</tt> - Preset the body of the email.
429
454
  # * <tt>:cc</tt> - Carbon Copy additional recipients on the email.
430
455
  # * <tt>:bcc</tt> - Blind Carbon Copy additional recipients on the email.
456
+ # * <tt>:reply_to</tt> - Preset the Reply-To field of the email.
431
457
  #
432
458
  # ==== Obfuscation
433
459
  # Prior to Rails 4.0, +mail_to+ provided options for encoding the address
@@ -457,73 +483,64 @@ module ActionView
457
483
  html_options, name = name, nil if block_given?
458
484
  html_options = (html_options || {}).stringify_keys
459
485
 
460
- extras = %w{ cc bcc body subject }.map! { |item|
461
- option = html_options.delete(item) || next
462
- "#{item}=#{Rack::Utils.escape_path(option)}"
486
+ extras = %w{ cc bcc body subject reply_to }.map! { |item|
487
+ option = html_options.delete(item).presence || next
488
+ "#{item.dasherize}=#{ERB::Util.url_encode(option)}"
463
489
  }.compact
464
- extras = extras.empty? ? '' : '?' + extras.join('&')
490
+ extras = extras.empty? ? "" : "?" + extras.join("&")
465
491
 
466
- encoded_email_address = ERB::Util.url_encode(email_address ? email_address.to_str : '').gsub("%40", "@")
492
+ encoded_email_address = ERB::Util.url_encode(email_address).gsub("%40", "@")
467
493
  html_options["href"] = "mailto:#{encoded_email_address}#{extras}"
468
494
 
469
- content_tag(:a, name || email_address, html_options, &block)
495
+ content_tag("a", name || email_address, html_options, &block)
470
496
  end
471
497
 
472
498
  # True if the current request URI was generated by the given +options+.
473
499
  #
474
500
  # ==== Examples
475
- # Let's say we're in the <tt>http://www.example.com/shop/checkout?order=desc</tt> action.
501
+ # Let's say we're in the <tt>http://www.example.com/shop/checkout?order=desc&page=1</tt> action.
476
502
  #
477
503
  # current_page?(action: 'process')
478
504
  # # => false
479
505
  #
480
- # current_page?(controller: 'shop', action: 'checkout')
481
- # # => true
482
- #
483
- # current_page?(controller: 'shop', action: 'checkout', order: 'asc')
484
- # # => false
485
- #
486
506
  # current_page?(action: 'checkout')
487
507
  # # => true
488
508
  #
489
509
  # current_page?(controller: 'library', action: 'checkout')
490
510
  # # => false
491
511
  #
492
- # current_page?('http://www.example.com/shop/checkout')
493
- # # => true
494
- #
495
- # current_page?('/shop/checkout')
512
+ # current_page?(controller: 'shop', action: 'checkout')
496
513
  # # => true
497
514
  #
498
- # Let's say we're in the <tt>http://www.example.com/shop/checkout?order=desc&page=1</tt> action.
499
- #
500
- # current_page?(action: 'process')
515
+ # current_page?(controller: 'shop', action: 'checkout', order: 'asc')
501
516
  # # => false
502
517
  #
503
- # current_page?(controller: 'shop', action: 'checkout')
504
- # # => true
505
- #
506
518
  # current_page?(controller: 'shop', action: 'checkout', order: 'desc', page: '1')
507
519
  # # => true
508
520
  #
509
521
  # current_page?(controller: 'shop', action: 'checkout', order: 'desc', page: '2')
510
522
  # # => false
511
523
  #
512
- # current_page?(controller: 'shop', action: 'checkout', order: 'desc')
524
+ # current_page?('http://www.example.com/shop/checkout')
525
+ # # => true
526
+ #
527
+ # current_page?('http://www.example.com/shop/checkout', check_parameters: true)
513
528
  # # => false
514
529
  #
515
- # current_page?(action: 'checkout')
530
+ # current_page?('/shop/checkout')
516
531
  # # => true
517
532
  #
518
- # current_page?(controller: 'library', action: 'checkout')
519
- # # => false
533
+ # current_page?('http://www.example.com/shop/checkout?order=desc&page=1')
534
+ # # => true
520
535
  #
521
536
  # Let's say we're in the <tt>http://www.example.com/products</tt> action with method POST in case of invalid product.
522
537
  #
523
538
  # current_page?(controller: 'product', action: 'index')
524
539
  # # => false
525
540
  #
526
- def current_page?(options)
541
+ # We can also pass in the symbol arguments instead of strings.
542
+ #
543
+ def current_page?(options = nil, check_parameters: false, **options_as_kwargs)
527
544
  unless request
528
545
  raise "You cannot use helpers that need to determine the current " \
529
546
  "page unless your view context provides a Request object " \
@@ -532,89 +549,239 @@ module ActionView
532
549
 
533
550
  return false unless request.get? || request.head?
534
551
 
535
- url_string = URI.parser.unescape(url_for(options)).force_encoding(Encoding::BINARY)
552
+ options ||= options_as_kwargs
553
+ check_parameters ||= options.is_a?(Hash) && options.delete(:check_parameters)
554
+ url_string = URI::DEFAULT_PARSER.unescape(url_for(options)).force_encoding(Encoding::BINARY)
536
555
 
537
556
  # We ignore any extra parameters in the request_uri if the
538
- # submitted url doesn't have any either. This lets the function
557
+ # submitted URL doesn't have any either. This lets the function
539
558
  # work with things like ?order=asc
540
- request_uri = url_string.index("?") ? request.fullpath : request.path
541
- request_uri = URI.parser.unescape(request_uri).force_encoding(Encoding::BINARY)
559
+ # the behaviour can be disabled with check_parameters: true
560
+ request_uri = url_string.index("?") || check_parameters ? request.fullpath : request.path
561
+ request_uri = URI::DEFAULT_PARSER.unescape(request_uri).force_encoding(Encoding::BINARY)
542
562
 
543
- if url_string =~ /^\w+:\/\//
544
- url_string == "#{request.protocol}#{request.host_with_port}#{request_uri}"
545
- else
546
- url_string == request_uri
563
+ if %r{^\w+://}.match?(url_string)
564
+ request_uri = +"#{request.protocol}#{request.host_with_port}#{request_uri}"
565
+ end
566
+
567
+ remove_trailing_slash!(url_string)
568
+ remove_trailing_slash!(request_uri)
569
+
570
+ url_string == request_uri
571
+ end
572
+
573
+ if RUBY_VERSION.start_with?("2.7")
574
+ using Module.new {
575
+ refine UrlHelper do
576
+ alias :_current_page? :current_page?
577
+ end
578
+ }
579
+
580
+ def current_page?(*args) # :nodoc:
581
+ options = args.pop
582
+ options.is_a?(Hash) ? _current_page?(*args, **options) : _current_page?(*args, options)
547
583
  end
548
584
  end
549
585
 
586
+ # Creates an SMS anchor link tag to the specified +phone_number+, which is
587
+ # also used as the name of the link unless +name+ is specified. Additional
588
+ # HTML attributes for the link can be passed in +html_options+.
589
+ #
590
+ # When clicked, an SMS message is prepopulated with the passed phone number
591
+ # and optional +body+ value.
592
+ #
593
+ # +sms_to+ has a +body+ option for customizing the SMS message itself by
594
+ # passing special keys to +html_options+.
595
+ #
596
+ # ==== Options
597
+ # * <tt>:body</tt> - Preset the body of the message.
598
+ #
599
+ # ==== Examples
600
+ # sms_to "5155555785"
601
+ # # => <a href="sms:5155555785;">5155555785</a>
602
+ #
603
+ # sms_to "5155555785", "Text me"
604
+ # # => <a href="sms:5155555785;">Text me</a>
605
+ #
606
+ # sms_to "5155555785", "Text me",
607
+ # body: "Hello Jim I have a question about your product."
608
+ # # => <a href="sms:5155555785;?body=Hello%20Jim%20I%20have%20a%20question%20about%20your%20product">Text me</a>
609
+ #
610
+ # You can use a block as well if your link target is hard to fit into the name parameter. \ERB example:
611
+ #
612
+ # <%= sms_to "5155555785" do %>
613
+ # <strong>Text me:</strong>
614
+ # <% end %>
615
+ # # => <a href="sms:5155555785;">
616
+ # <strong>Text me:</strong>
617
+ # </a>
618
+ def sms_to(phone_number, name = nil, html_options = {}, &block)
619
+ html_options, name = name, nil if block_given?
620
+ html_options = (html_options || {}).stringify_keys
621
+
622
+ extras = %w{ body }.map! { |item|
623
+ option = html_options.delete(item).presence || next
624
+ "#{item.dasherize}=#{ERB::Util.url_encode(option)}"
625
+ }.compact
626
+ extras = extras.empty? ? "" : "?&" + extras.join("&")
627
+
628
+ encoded_phone_number = ERB::Util.url_encode(phone_number)
629
+ html_options["href"] = "sms:#{encoded_phone_number};#{extras}"
630
+
631
+ content_tag("a", name || phone_number, html_options, &block)
632
+ end
633
+
634
+ # Creates a TEL anchor link tag to the specified +phone_number+, which is
635
+ # also used as the name of the link unless +name+ is specified. Additional
636
+ # HTML attributes for the link can be passed in +html_options+.
637
+ #
638
+ # When clicked, the default app to make calls is opened, and it
639
+ # is prepopulated with the passed phone number and optional
640
+ # +country_code+ value.
641
+ #
642
+ # +phone_to+ has an optional +country_code+ option which automatically adds the country
643
+ # code as well as the + sign in the phone numer that gets prepopulated,
644
+ # for example if +country_code: "01"+ +\+01+ will be prepended to the
645
+ # phone numer, by passing special keys to +html_options+.
646
+ #
647
+ # ==== Options
648
+ # * <tt>:country_code</tt> - Prepends the country code to the number
649
+ #
650
+ # ==== Examples
651
+ # phone_to "1234567890"
652
+ # # => <a href="tel:1234567890">1234567890</a>
653
+ #
654
+ # phone_to "1234567890", "Phone me"
655
+ # # => <a href="tel:134567890">Phone me</a>
656
+ #
657
+ # phone_to "1234567890", "Phone me", country_code: "01"
658
+ # # => <a href="tel:+015155555785">Phone me</a>
659
+ #
660
+ # You can use a block as well if your link target is hard to fit into the name parameter. \ERB example:
661
+ #
662
+ # <%= phone_to "1234567890" do %>
663
+ # <strong>Phone me:</strong>
664
+ # <% end %>
665
+ # # => <a href="tel:1234567890">
666
+ # <strong>Phone me:</strong>
667
+ # </a>
668
+ def phone_to(phone_number, name = nil, html_options = {}, &block)
669
+ html_options, name = name, nil if block_given?
670
+ html_options = (html_options || {}).stringify_keys
671
+
672
+ country_code = html_options.delete("country_code").presence
673
+ country_code = country_code.nil? ? "" : "+#{ERB::Util.url_encode(country_code)}"
674
+
675
+ encoded_phone_number = ERB::Util.url_encode(phone_number)
676
+ html_options["href"] = "tel:#{country_code}#{encoded_phone_number}"
677
+
678
+ content_tag("a", name || phone_number, html_options, &block)
679
+ end
680
+
550
681
  private
551
682
  def convert_options_to_data_attributes(options, html_options)
552
683
  if html_options
553
684
  html_options = html_options.stringify_keys
554
- html_options['data-remote'] = 'true' if link_to_remote_options?(options) || link_to_remote_options?(html_options)
685
+ html_options["data-remote"] = "true" if link_to_remote_options?(options) || link_to_remote_options?(html_options)
555
686
 
556
- method = html_options.delete('method')
687
+ method = html_options.delete("method")
557
688
 
558
689
  add_method_to_attributes!(html_options, method) if method
559
690
 
560
691
  html_options
561
692
  else
562
- link_to_remote_options?(options) ? {'data-remote' => 'true'} : {}
693
+ link_to_remote_options?(options) ? { "data-remote" => "true" } : {}
563
694
  end
564
695
  end
565
696
 
566
697
  def link_to_remote_options?(options)
567
698
  if options.is_a?(Hash)
568
- options.delete('remote') || options.delete(:remote)
699
+ options.delete("remote") || options.delete(:remote)
569
700
  end
570
701
  end
571
702
 
572
703
  def add_method_to_attributes!(html_options, method)
573
- if method && method.to_s.downcase != "get" && html_options["rel"] !~ /nofollow/
574
- html_options["rel"] = "#{html_options["rel"]} nofollow".lstrip
704
+ if method_not_get_method?(method) && !html_options["rel"]&.match?(/nofollow/)
705
+ if html_options["rel"].blank?
706
+ html_options["rel"] = "nofollow"
707
+ else
708
+ html_options["rel"] = "#{html_options["rel"]} nofollow"
709
+ end
575
710
  end
576
711
  html_options["data-method"] = method
577
712
  end
578
713
 
579
- # Processes the +html_options+ hash, converting the boolean
580
- # attributes from true/false form into the form required by
581
- # HTML/XHTML. (An attribute is considered to be boolean if
582
- # its name is listed in the given +bool_attrs+ array.)
583
- #
584
- # More specifically, for each boolean attribute in +html_options+
585
- # given as:
714
+ STRINGIFIED_COMMON_METHODS = {
715
+ get: "get",
716
+ delete: "delete",
717
+ patch: "patch",
718
+ post: "post",
719
+ put: "put",
720
+ }.freeze
721
+
722
+ def method_not_get_method?(method)
723
+ return false unless method
724
+ (STRINGIFIED_COMMON_METHODS[method] || method.to_s.downcase) != "get"
725
+ end
726
+
727
+ def token_tag(token = nil, form_options: {})
728
+ if token != false && defined?(protect_against_forgery?) && protect_against_forgery?
729
+ token ||= form_authenticity_token(form_options: form_options)
730
+ tag(:input, type: "hidden", name: request_forgery_protection_token.to_s, value: token, autocomplete: "off")
731
+ else
732
+ ""
733
+ end
734
+ end
735
+
736
+ def method_tag(method)
737
+ tag("input", type: "hidden", name: "_method", value: method.to_s, autocomplete: "off")
738
+ end
739
+
740
+ # Returns an array of hashes each containing :name and :value keys
741
+ # suitable for use as the names and values of form input fields:
586
742
  #
587
- # "attr" => bool_value
743
+ # to_form_params(name: 'David', nationality: 'Danish')
744
+ # # => [{name: 'name', value: 'David'}, {name: 'nationality', value: 'Danish'}]
588
745
  #
589
- # if the associated +bool_value+ evaluates to true, it is
590
- # replaced with the attribute's name; otherwise the attribute is
591
- # removed from the +html_options+ hash. (See the XHTML 1.0 spec,
592
- # section 4.5 "Attribute Minimization" for more:
593
- # http://www.w3.org/TR/xhtml1/#h-4.5)
746
+ # to_form_params(country: { name: 'Denmark' })
747
+ # # => [{name: 'country[name]', value: 'Denmark'}]
594
748
  #
595
- # Returns the updated +html_options+ hash, which is also modified
596
- # in place.
749
+ # to_form_params(countries: ['Denmark', 'Sweden']})
750
+ # # => [{name: 'countries[]', value: 'Denmark'}, {name: 'countries[]', value: 'Sweden'}]
597
751
  #
598
- # Example:
752
+ # An optional namespace can be passed to enclose key names:
599
753
  #
600
- # convert_boolean_attributes!( html_options,
601
- # %w( checked disabled readonly ) )
602
- def convert_boolean_attributes!(html_options, bool_attrs)
603
- bool_attrs.each { |x| html_options[x] = x if html_options.delete(x) }
604
- html_options
605
- end
754
+ # to_form_params({ name: 'Denmark' }, 'country')
755
+ # # => [{name: 'country[name]', value: 'Denmark'}]
756
+ def to_form_params(attribute, namespace = nil)
757
+ attribute = if attribute.respond_to?(:permitted?)
758
+ attribute.to_h
759
+ else
760
+ attribute
761
+ end
606
762
 
607
- def token_tag(token=nil)
608
- if token != false && protect_against_forgery?
609
- token ||= form_authenticity_token
610
- tag(:input, type: "hidden", name: request_forgery_protection_token.to_s, value: token)
763
+ params = []
764
+ case attribute
765
+ when Hash
766
+ attribute.each do |key, value|
767
+ prefix = namespace ? "#{namespace}[#{key}]" : key
768
+ params.push(*to_form_params(value, prefix))
769
+ end
770
+ when Array
771
+ array_prefix = "#{namespace}[]"
772
+ attribute.each do |value|
773
+ params.push(*to_form_params(value, array_prefix))
774
+ end
611
775
  else
612
- ''
776
+ params << { name: namespace.to_s, value: attribute.to_param }
613
777
  end
778
+
779
+ params.sort_by { |pair| pair[:name] }
614
780
  end
615
781
 
616
- def method_tag(method)
617
- tag('input', type: 'hidden', name: '_method', value: method.to_s)
782
+ def remove_trailing_slash!(url_string)
783
+ trailing_index = (url_string.index("?") || 0) - 1
784
+ url_string[trailing_index] = "" if url_string[trailing_index] == "/"
618
785
  end
619
786
  end
620
787
  end
@@ -1,4 +1,6 @@
1
- require 'active_support/benchmarkable'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/benchmarkable"
2
4
 
3
5
  module ActionView #:nodoc:
4
6
  module Helpers #:nodoc:
@@ -11,6 +13,7 @@ module ActionView #:nodoc:
11
13
  autoload :CacheHelper
12
14
  autoload :CaptureHelper
13
15
  autoload :ControllerHelper
16
+ autoload :CspHelper
14
17
  autoload :CsrfHelper
15
18
  autoload :DateHelper
16
19
  autoload :DebugHelper
@@ -20,7 +23,6 @@ module ActionView #:nodoc:
20
23
  autoload :JavaScriptHelper, "action_view/helpers/javascript_helper"
21
24
  autoload :NumberHelper
22
25
  autoload :OutputSafetyHelper
23
- autoload :RecordTagHelper
24
26
  autoload :RenderingHelper
25
27
  autoload :SanitizeHelper
26
28
  autoload :TagHelper
@@ -44,6 +46,7 @@ module ActionView #:nodoc:
44
46
  include CacheHelper
45
47
  include CaptureHelper
46
48
  include ControllerHelper
49
+ include CspHelper
47
50
  include CsrfHelper
48
51
  include DateHelper
49
52
  include DebugHelper
@@ -53,7 +56,6 @@ module ActionView #:nodoc:
53
56
  include JavaScriptHelper
54
57
  include NumberHelper
55
58
  include OutputSafetyHelper
56
- include RecordTagHelper
57
59
  include RenderingHelper
58
60
  include SanitizeHelper
59
61
  include TagHelper