actionview 6.1.4.1 → 7.0.0.alpha1

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 (72) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +98 -287
  3. data/MIT-LICENSE +1 -1
  4. data/lib/action_view/base.rb +3 -3
  5. data/lib/action_view/buffers.rb +2 -2
  6. data/lib/action_view/cache_expiry.rb +46 -32
  7. data/lib/action_view/dependency_tracker/erb_tracker.rb +154 -0
  8. data/lib/action_view/dependency_tracker/ripper_tracker.rb +59 -0
  9. data/lib/action_view/dependency_tracker.rb +6 -147
  10. data/lib/action_view/digestor.rb +7 -4
  11. data/lib/action_view/flows.rb +4 -4
  12. data/lib/action_view/gem_version.rb +4 -4
  13. data/lib/action_view/helpers/active_model_helper.rb +1 -1
  14. data/lib/action_view/helpers/asset_tag_helper.rb +84 -29
  15. data/lib/action_view/helpers/asset_url_helper.rb +7 -7
  16. data/lib/action_view/helpers/atom_feed_helper.rb +3 -4
  17. data/lib/action_view/helpers/cache_helper.rb +51 -3
  18. data/lib/action_view/helpers/capture_helper.rb +2 -2
  19. data/lib/action_view/helpers/controller_helper.rb +2 -2
  20. data/lib/action_view/helpers/csp_helper.rb +1 -1
  21. data/lib/action_view/helpers/csrf_helper.rb +1 -1
  22. data/lib/action_view/helpers/date_helper.rb +5 -5
  23. data/lib/action_view/helpers/debug_helper.rb +3 -1
  24. data/lib/action_view/helpers/form_helper.rb +72 -12
  25. data/lib/action_view/helpers/form_options_helper.rb +65 -33
  26. data/lib/action_view/helpers/form_tag_helper.rb +73 -30
  27. data/lib/action_view/helpers/javascript_helper.rb +3 -5
  28. data/lib/action_view/helpers/number_helper.rb +3 -4
  29. data/lib/action_view/helpers/output_safety_helper.rb +2 -2
  30. data/lib/action_view/helpers/rendering_helper.rb +1 -1
  31. data/lib/action_view/helpers/sanitize_helper.rb +2 -2
  32. data/lib/action_view/helpers/tag_helper.rb +17 -4
  33. data/lib/action_view/helpers/tags/base.rb +2 -14
  34. data/lib/action_view/helpers/tags/check_box.rb +1 -1
  35. data/lib/action_view/helpers/tags/collection_select.rb +1 -1
  36. data/lib/action_view/helpers/tags/time_field.rb +10 -1
  37. data/lib/action_view/helpers/tags/weekday_select.rb +27 -0
  38. data/lib/action_view/helpers/tags.rb +3 -2
  39. data/lib/action_view/helpers/text_helper.rb +24 -13
  40. data/lib/action_view/helpers/translation_helper.rb +1 -2
  41. data/lib/action_view/helpers/url_helper.rb +110 -81
  42. data/lib/action_view/helpers.rb +25 -25
  43. data/lib/action_view/lookup_context.rb +33 -52
  44. data/lib/action_view/model_naming.rb +1 -1
  45. data/lib/action_view/path_set.rb +16 -22
  46. data/lib/action_view/railtie.rb +15 -2
  47. data/lib/action_view/render_parser.rb +188 -0
  48. data/lib/action_view/renderer/abstract_renderer.rb +2 -2
  49. data/lib/action_view/renderer/partial_renderer.rb +0 -34
  50. data/lib/action_view/renderer/renderer.rb +4 -4
  51. data/lib/action_view/renderer/streaming_template_renderer.rb +3 -3
  52. data/lib/action_view/renderer/template_renderer.rb +6 -2
  53. data/lib/action_view/rendering.rb +2 -2
  54. data/lib/action_view/ripper_ast_parser.rb +198 -0
  55. data/lib/action_view/routing_url_for.rb +1 -1
  56. data/lib/action_view/template/error.rb +108 -13
  57. data/lib/action_view/template/handlers/erb.rb +6 -0
  58. data/lib/action_view/template/handlers.rb +3 -3
  59. data/lib/action_view/template/html.rb +3 -3
  60. data/lib/action_view/template/inline.rb +3 -3
  61. data/lib/action_view/template/raw_file.rb +3 -3
  62. data/lib/action_view/template/resolver.rb +84 -311
  63. data/lib/action_view/template/text.rb +3 -3
  64. data/lib/action_view/template/types.rb +14 -12
  65. data/lib/action_view/template.rb +10 -1
  66. data/lib/action_view/template_details.rb +66 -0
  67. data/lib/action_view/template_path.rb +64 -0
  68. data/lib/action_view/test_case.rb +6 -2
  69. data/lib/action_view/testing/resolvers.rb +11 -12
  70. data/lib/action_view/unbound_template.rb +33 -7
  71. data/lib/action_view.rb +3 -4
  72. metadata +25 -18
@@ -1,11 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "action_view/helpers/tag_helper"
4
- require "active_support/core_ext/symbol/starts_ends_with"
5
4
 
6
5
  module ActionView
7
6
  # = Action View Translation Helpers
8
- module Helpers #:nodoc:
7
+ module Helpers # :nodoc:
9
8
  module TranslationHelper
10
9
  extend ActiveSupport::Concern
11
10
 
@@ -1,13 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "action_view/helpers/javascript_helper"
4
3
  require "active_support/core_ext/array/access"
5
4
  require "active_support/core_ext/hash/keys"
6
5
  require "active_support/core_ext/string/output_safety"
6
+ require "action_view/helpers/tag_helper"
7
7
 
8
8
  module ActionView
9
9
  # = Action View URL Helpers
10
- module Helpers #:nodoc:
10
+ module Helpers # :nodoc:
11
11
  # Provides a set of methods for making links and getting URLs that
12
12
  # depend on the routing subsystem (see ActionDispatch::Routing).
13
13
  # This allows you to use the same format for links in views
@@ -29,6 +29,8 @@ module ActionView
29
29
  end
30
30
  end
31
31
 
32
+ mattr_accessor :button_to_generates_button_tag, default: false
33
+
32
34
  # Basic implementation of url_for to allow use helpers without routes existence
33
35
  def url_for(options = nil) # :nodoc:
34
36
  case options
@@ -101,17 +103,8 @@ module ActionView
101
103
  # completion of the Ajax request and performing JavaScript operations once
102
104
  # they're complete
103
105
  #
104
- # ==== Data attributes
105
- #
106
- # * <tt>confirm: 'question?'</tt> - This will allow the unobtrusive JavaScript
107
- # driver to prompt with the question specified (in this case, the
108
- # resulting text would be <tt>question?</tt>. If the user accepts, the
109
- # link is processed normally, otherwise no action is taken.
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.
113
- #
114
106
  # ==== Examples
107
+ #
115
108
  # Because it relies on +url_for+, +link_to+ supports both older-style controller/action/id arguments
116
109
  # and newer RESTful routes. Current Rails style favors RESTful routes whenever possible, so base
117
110
  # your application on resources and use
@@ -184,15 +177,27 @@ module ActionView
184
177
  # link_to("Destroy", "http://www.example.com", method: :delete)
185
178
  # # => <a href='http://www.example.com' rel="nofollow" data-method="delete">Destroy</a>
186
179
  #
187
- # You can also use custom data attributes using the <tt>:data</tt> option:
188
- #
189
- # link_to "Visit Other Site", "http://www.rubyonrails.org/", data: { confirm: "Are you sure?" }
190
- # # => <a href="http://www.rubyonrails.org/" data-confirm="Are you sure?">Visit Other Site</a>
191
- #
192
180
  # Also you can set any link attributes such as <tt>target</tt>, <tt>rel</tt>, <tt>type</tt>:
193
181
  #
194
182
  # link_to "External link", "http://www.rubyonrails.org/", target: "_blank", rel: "nofollow"
195
183
  # # => <a href="http://www.rubyonrails.org/" target="_blank" rel="nofollow">External link</a>
184
+ #
185
+ # ==== Deprecated: Rails UJS attributes
186
+ #
187
+ # Prior to Rails 7, Rails shipped with a JavaScript library called @rails/ujs on by default. Following Rails 7,
188
+ # this library is no longer on by default. This library integrated with the following options:
189
+ #
190
+ # * <tt>confirm: 'question?'</tt> - This will allow the unobtrusive JavaScript
191
+ # driver to prompt with the question specified (in this case, the
192
+ # resulting text would be <tt>question?</tt>. If the user accepts, the
193
+ # link is processed normally, otherwise no action is taken.
194
+ # * <tt>:disable_with</tt> - Value of this parameter will be used as the
195
+ # name for a disabled version of the link. This feature is provided by
196
+ # the unobtrusive JavaScript driver.
197
+ #
198
+ # link_to "Visit Other Site", "http://www.rubyonrails.org/", data: { confirm: "Are you sure?" }
199
+ # # => <a href="http://www.rubyonrails.org/" data-confirm="Are you sure?">Visit Other Site</a>
200
+ #
196
201
  def link_to(name = nil, options = nil, html_options = nil, &block)
197
202
  html_options, options, name = options, name, block if block_given?
198
203
  options ||= {}
@@ -212,20 +217,24 @@ module ActionView
212
217
  # using the +link_to+ method with the <tt>:method</tt> modifier as described in
213
218
  # the +link_to+ documentation.
214
219
  #
215
- # By default, the generated form element has a class name of <tt>button_to</tt>
216
- # to allow styling of the form itself and its children. This can be changed
217
- # using the <tt>:form_class</tt> modifier within +html_options+. You can control
218
- # the form submission and input element behavior using +html_options+.
219
- # This method accepts the <tt>:method</tt> modifier described in the +link_to+ documentation.
220
- # If no <tt>:method</tt> modifier is given, it will default to performing a POST operation.
221
- # You can also disable the button by passing <tt>disabled: true</tt> in +html_options+.
222
- # If you are using RESTful routes, you can pass the <tt>:method</tt>
223
- # to change the HTTP verb used to submit the form.
220
+ # You can control the form and button behavior with +html_options+. Most
221
+ # values in +html_options+ are passed through to the button element. For
222
+ # example, passing a +:class+ option within +html_options+ will set the
223
+ # class attribute of the button element.
224
+ #
225
+ # The class attribute of the form element can be set by passing a
226
+ # +:form_class+ option within +html_options+. It defaults to
227
+ # <tt>"button_to"</tt> to allow styling of the form and its children.
228
+ #
229
+ # The form submits a POST request by default. You can specify a different
230
+ # HTTP verb via the +:method+ option within +html_options+.
224
231
  #
225
232
  # ==== Options
226
233
  # The +options+ hash accepts the same options as +url_for+.
227
234
  #
228
- # There are a few special +html_options+:
235
+ # Most values in +html_options+ are passed through to the button element,
236
+ # but there are a few special options:
237
+ #
229
238
  # * <tt>:method</tt> - \Symbol of HTTP verb. Supported verbs are <tt>:post</tt>, <tt>:get</tt>,
230
239
  # <tt>:delete</tt>, <tt>:patch</tt>, and <tt>:put</tt>. By default it will be <tt>:post</tt>.
231
240
  # * <tt>:disabled</tt> - If set to true, it will generate a disabled button.
@@ -250,12 +259,21 @@ module ActionView
250
259
  # ==== Examples
251
260
  # <%= button_to "New", action: "new" %>
252
261
  # # => "<form method="post" action="/controller/new" class="button_to">
253
- # # <input value="New" type="submit" />
262
+ # # <button type="submit">New</button>
263
+ # # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/>
254
264
  # # </form>"
255
265
  #
256
266
  # <%= button_to "New", new_article_path %>
257
267
  # # => "<form method="post" action="/articles/new" class="button_to">
258
- # # <input value="New" type="submit" />
268
+ # # <button type="submit">New</button>
269
+ # # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/>
270
+ # # </form>"
271
+ #
272
+ # <%= button_to "New", new_article_path, params: { time: Time.now } %>
273
+ # # => "<form method="post" action="/articles/new" class="button_to">
274
+ # # <button type="submit">New</button>
275
+ # # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/>
276
+ # # <input type="hidden" name="time" value="2021-04-08 14:06:09 -0500">
259
277
  # # </form>"
260
278
  #
261
279
  # <%= button_to [:make_happy, @user] do %>
@@ -265,35 +283,34 @@ module ActionView
265
283
  # # <button type="submit">
266
284
  # # Make happy <strong><%= @user.name %></strong>
267
285
  # # </button>
286
+ # # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/>
268
287
  # # </form>"
269
288
  #
270
289
  # <%= button_to "New", { action: "new" }, form_class: "new-thing" %>
271
290
  # # => "<form method="post" action="/controller/new" class="new-thing">
272
- # # <input value="New" type="submit" />
291
+ # # <button type="submit">New</button>
292
+ # # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/>
273
293
  # # </form>"
274
294
  #
275
- #
276
295
  # <%= button_to "Create", { action: "create" }, remote: true, form: { "data-type" => "json" } %>
277
296
  # # => "<form method="post" action="/images/create" class="button_to" data-remote="true" data-type="json">
278
- # # <input value="Create" type="submit" />
297
+ # # <button type="submit">Create</button>
279
298
  # # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/>
280
299
  # # </form>"
281
300
  #
282
- #
283
301
  # <%= button_to "Delete Image", { action: "delete", id: @image.id },
284
302
  # method: :delete, data: { confirm: "Are you sure?" } %>
285
303
  # # => "<form method="post" action="/images/delete/1" class="button_to">
286
304
  # # <input type="hidden" name="_method" value="delete" />
287
- # # <input data-confirm='Are you sure?' value="Delete Image" type="submit" />
305
+ # # <button data-confirm='Are you sure?' type="submit">Delete Image</button>
288
306
  # # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/>
289
307
  # # </form>"
290
308
  #
291
- #
292
309
  # <%= button_to('Destroy', 'http://www.example.com',
293
310
  # method: :delete, remote: true, data: { confirm: 'Are you sure?', disable_with: 'loading...' }) %>
294
311
  # # => "<form class='button_to' method='post' action='http://www.example.com' data-remote='true'>
295
312
  # # <input name='_method' value='delete' type='hidden' />
296
- # # <input value='Destroy' type='submit' data-disable-with='loading...' data-confirm='Are you sure?' />
313
+ # # <button type='submit' data-disable-with='loading...' data-confirm='Are you sure?'>Destroy</button>
297
314
  # # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/>
298
315
  # # </form>"
299
316
  # #
@@ -327,8 +344,8 @@ module ActionView
327
344
  html_options = convert_options_to_data_attributes(options, html_options)
328
345
  html_options["type"] = "submit"
329
346
 
330
- button = if block_given?
331
- content_tag("button", html_options, &block)
347
+ button = if block_given? || button_to_generates_button_tag
348
+ content_tag("button", name || url, html_options, &block)
332
349
  else
333
350
  html_options["value"] = name || url
334
351
  tag("input", html_options)
@@ -466,9 +483,9 @@ module ActionView
466
483
  # mail_to "me@domain.com", "My email"
467
484
  # # => <a href="mailto:me@domain.com">My email</a>
468
485
  #
469
- # mail_to "me@domain.com", "My email", cc: "ccaddress@domain.com",
486
+ # mail_to "me@domain.com", cc: "ccaddress@domain.com",
470
487
  # subject: "This is an example email"
471
- # # => <a href="mailto:me@domain.com?cc=ccaddress@domain.com&subject=This%20is%20an%20example%20email">My email</a>
488
+ # # => <a href="mailto:me@domain.com?cc=ccaddress@domain.com&subject=This%20is%20an%20example%20email">me@domain.com</a>
472
489
  #
473
490
  # You can use a block as well if your link target is hard to fit into the name parameter. ERB example:
474
491
  #
@@ -479,7 +496,7 @@ module ActionView
479
496
  # <strong>Email me:</strong> <span>me@domain.com</span>
480
497
  # </a>
481
498
  def mail_to(email_address, name = nil, html_options = {}, &block)
482
- html_options, name = name, nil if block_given?
499
+ html_options, name = name, nil if name.is_a?(Hash)
483
500
  html_options = (html_options || {}).stringify_keys
484
501
 
485
502
  extras = %w{ cc bcc body subject reply_to }.map! { |item|
@@ -559,16 +576,14 @@ module ActionView
559
576
  request_uri = url_string.index("?") || check_parameters ? request.fullpath : request.path
560
577
  request_uri = URI::DEFAULT_PARSER.unescape(request_uri).force_encoding(Encoding::BINARY)
561
578
 
562
- if url_string.start_with?("/") && url_string != "/"
563
- url_string.chomp!("/")
564
- request_uri.chomp!("/")
565
- end
566
-
567
579
  if %r{^\w+://}.match?(url_string)
568
- url_string == "#{request.protocol}#{request.host_with_port}#{request_uri}"
569
- else
570
- url_string == request_uri
580
+ request_uri = +"#{request.protocol}#{request.host_with_port}#{request_uri}"
571
581
  end
582
+
583
+ remove_trailing_slash!(url_string)
584
+ remove_trailing_slash!(request_uri)
585
+
586
+ url_string == request_uri
572
587
  end
573
588
 
574
589
  if RUBY_VERSION.start_with?("2.7")
@@ -584,29 +599,37 @@ module ActionView
584
599
  end
585
600
  end
586
601
 
587
- # Creates an SMS anchor link tag to the specified +phone_number+, which is
588
- # also used as the name of the link unless +name+ is specified. Additional
589
- # HTML attributes for the link can be passed in +html_options+.
602
+ # Creates an SMS anchor link tag to the specified +phone_number+. When the
603
+ # link is clicked, the default SMS messaging app is opened ready to send a
604
+ # message to the linked phone number. If the +body+ option is specified,
605
+ # the contents of the message will be preset to +body+.
590
606
  #
591
- # When clicked, an SMS message is prepopulated with the passed phone number
592
- # and optional +body+ value.
607
+ # If +name+ is not specified, +phone_number+ will be used as the name of
608
+ # the link.
593
609
  #
594
- # +sms_to+ has a +body+ option for customizing the SMS message itself by
595
- # passing special keys to +html_options+.
610
+ # A +country_code+ option is supported, which prepends a plus sign and the
611
+ # given country code to the linked phone number. For example,
612
+ # <tt>country_code: "01"</tt> will prepend <tt>+01</tt> to the linked
613
+ # phone number.
614
+ #
615
+ # Additional HTML attributes for the link can be passed via +html_options+.
596
616
  #
597
617
  # ==== Options
618
+ # * <tt>:country_code</tt> - Prepend the country code to the phone number.
598
619
  # * <tt>:body</tt> - Preset the body of the message.
599
620
  #
600
621
  # ==== Examples
601
622
  # sms_to "5155555785"
602
623
  # # => <a href="sms:5155555785;">5155555785</a>
603
624
  #
625
+ # sms_to "5155555785", country_code: "01"
626
+ # # => <a href="sms:+015155555785;">5155555785</a>
627
+ #
604
628
  # sms_to "5155555785", "Text me"
605
629
  # # => <a href="sms:5155555785;">Text me</a>
606
630
  #
607
- # sms_to "5155555785", "Text me",
608
- # body: "Hello Jim I have a question about your product."
609
- # # => <a href="sms:5155555785;?body=Hello%20Jim%20I%20have%20a%20question%20about%20your%20product">Text me</a>
631
+ # sms_to "5155555785", body: "I have a question about your product."
632
+ # # => <a href="sms:5155555785;?body=I%20have%20a%20question%20about%20your%20product">5155555785</a>
610
633
  #
611
634
  # You can use a block as well if your link target is hard to fit into the name parameter. \ERB example:
612
635
  #
@@ -617,46 +640,47 @@ module ActionView
617
640
  # <strong>Text me:</strong>
618
641
  # </a>
619
642
  def sms_to(phone_number, name = nil, html_options = {}, &block)
620
- html_options, name = name, nil if block_given?
643
+ html_options, name = name, nil if name.is_a?(Hash)
621
644
  html_options = (html_options || {}).stringify_keys
622
645
 
623
- extras = %w{ body }.map! { |item|
624
- option = html_options.delete(item).presence || next
625
- "#{item.dasherize}=#{ERB::Util.url_encode(option)}"
626
- }.compact
627
- extras = extras.empty? ? "" : "?&" + extras.join("&")
646
+ country_code = html_options.delete("country_code").presence
647
+ country_code = country_code ? "+#{ERB::Util.url_encode(country_code)}" : ""
648
+
649
+ body = html_options.delete("body").presence
650
+ body = body ? "?&body=#{ERB::Util.url_encode(body)}" : ""
628
651
 
629
652
  encoded_phone_number = ERB::Util.url_encode(phone_number)
630
- html_options["href"] = "sms:#{encoded_phone_number};#{extras}"
653
+ html_options["href"] = "sms:#{country_code}#{encoded_phone_number};#{body}"
631
654
 
632
655
  content_tag("a", name || phone_number, html_options, &block)
633
656
  end
634
657
 
635
- # Creates a TEL anchor link tag to the specified +phone_number+, which is
636
- # also used as the name of the link unless +name+ is specified. Additional
637
- # HTML attributes for the link can be passed in +html_options+.
658
+ # Creates a TEL anchor link tag to the specified +phone_number+. When the
659
+ # link is clicked, the default app to make phone calls is opened and
660
+ # prepopulated with the phone number.
661
+ #
662
+ # If +name+ is not specified, +phone_number+ will be used as the name of
663
+ # the link.
638
664
  #
639
- # When clicked, the default app to make calls is opened, and it
640
- # is prepopulated with the passed phone number and optional
641
- # +country_code+ value.
665
+ # A +country_code+ option is supported, which prepends a plus sign and the
666
+ # given country code to the linked phone number. For example,
667
+ # <tt>country_code: "01"</tt> will prepend <tt>+01</tt> to the linked
668
+ # phone number.
642
669
  #
643
- # +phone_to+ has an optional +country_code+ option which automatically adds the country
644
- # code as well as the + sign in the phone numer that gets prepopulated,
645
- # for example if +country_code: "01"+ +\+01+ will be prepended to the
646
- # phone numer, by passing special keys to +html_options+.
670
+ # Additional HTML attributes for the link can be passed via +html_options+.
647
671
  #
648
672
  # ==== Options
649
- # * <tt>:country_code</tt> - Prepends the country code to the number
673
+ # * <tt>:country_code</tt> - Prepends the country code to the phone number
650
674
  #
651
675
  # ==== Examples
652
676
  # phone_to "1234567890"
653
677
  # # => <a href="tel:1234567890">1234567890</a>
654
678
  #
655
679
  # phone_to "1234567890", "Phone me"
656
- # # => <a href="tel:134567890">Phone me</a>
680
+ # # => <a href="tel:1234567890">Phone me</a>
657
681
  #
658
- # phone_to "1234567890", "Phone me", country_code: "01"
659
- # # => <a href="tel:+015155555785">Phone me</a>
682
+ # phone_to "1234567890", country_code: "01"
683
+ # # => <a href="tel:+011234567890">1234567890</a>
660
684
  #
661
685
  # You can use a block as well if your link target is hard to fit into the name parameter. \ERB example:
662
686
  #
@@ -667,7 +691,7 @@ module ActionView
667
691
  # <strong>Phone me:</strong>
668
692
  # </a>
669
693
  def phone_to(phone_number, name = nil, html_options = {}, &block)
670
- html_options, name = name, nil if block_given?
694
+ html_options, name = name, nil if name.is_a?(Hash)
671
695
  html_options = (html_options || {}).stringify_keys
672
696
 
673
697
  country_code = html_options.delete("country_code").presence
@@ -779,6 +803,11 @@ module ActionView
779
803
 
780
804
  params.sort_by { |pair| pair[:name] }
781
805
  end
806
+
807
+ def remove_trailing_slash!(url_string)
808
+ trailing_index = (url_string.index("?") || 0) - 1
809
+ url_string[trailing_index] = "" if url_string[trailing_index] == "/"
810
+ end
782
811
  end
783
812
  end
784
813
  end
@@ -1,34 +1,34 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/benchmarkable"
4
+ require "action_view/helpers/capture_helper"
5
+ require "action_view/helpers/output_safety_helper"
6
+ require "action_view/helpers/tag_helper"
7
+ require "action_view/helpers/url_helper"
8
+ require "action_view/helpers/sanitize_helper"
9
+ require "action_view/helpers/text_helper"
10
+ require "action_view/helpers/active_model_helper"
11
+ require "action_view/helpers/asset_tag_helper"
12
+ require "action_view/helpers/asset_url_helper"
13
+ require "action_view/helpers/atom_feed_helper"
14
+ require "action_view/helpers/cache_helper"
15
+ require "action_view/helpers/controller_helper"
16
+ require "action_view/helpers/csp_helper"
17
+ require "action_view/helpers/csrf_helper"
18
+ require "action_view/helpers/date_helper"
19
+ require "action_view/helpers/debug_helper"
20
+ require "action_view/helpers/form_tag_helper"
21
+ require "action_view/helpers/form_helper"
22
+ require "action_view/helpers/form_options_helper"
23
+ require "action_view/helpers/javascript_helper"
24
+ require "action_view/helpers/number_helper"
25
+ require "action_view/helpers/rendering_helper"
26
+ require "action_view/helpers/translation_helper"
4
27
 
5
- module ActionView #:nodoc:
6
- module Helpers #:nodoc:
28
+ module ActionView # :nodoc:
29
+ module Helpers # :nodoc:
7
30
  extend ActiveSupport::Autoload
8
31
 
9
- autoload :ActiveModelHelper
10
- autoload :AssetTagHelper
11
- autoload :AssetUrlHelper
12
- autoload :AtomFeedHelper
13
- autoload :CacheHelper
14
- autoload :CaptureHelper
15
- autoload :ControllerHelper
16
- autoload :CspHelper
17
- autoload :CsrfHelper
18
- autoload :DateHelper
19
- autoload :DebugHelper
20
- autoload :FormHelper
21
- autoload :FormOptionsHelper
22
- autoload :FormTagHelper
23
- autoload :JavaScriptHelper, "action_view/helpers/javascript_helper"
24
- autoload :NumberHelper
25
- autoload :OutputSafetyHelper
26
- autoload :RenderingHelper
27
- autoload :SanitizeHelper
28
- autoload :TagHelper
29
- autoload :TextHelper
30
- autoload :TranslationHelper
31
- autoload :UrlHelper
32
32
  autoload :Tags
33
33
 
34
34
  def self.eager_load!
@@ -12,12 +12,11 @@ module ActionView
12
12
  # <tt>LookupContext</tt> is also responsible for generating a key, given to
13
13
  # view paths, used in the resolver cache lookup. Since this key is generated
14
14
  # only once during the request, it speeds up all cache accesses.
15
- class LookupContext #:nodoc:
15
+ class LookupContext # :nodoc:
16
16
  attr_accessor :prefixes, :rendered_format
17
17
 
18
- mattr_accessor :fallbacks, default: FallbackFileSystemResolver.instances
19
-
20
- mattr_accessor :registered_details, default: []
18
+ singleton_class.attr_accessor :registered_details
19
+ self.registered_details = []
21
20
 
22
21
  def self.register_detail(name, &block)
23
22
  registered_details << name
@@ -37,7 +36,7 @@ module ActionView
37
36
  end
38
37
 
39
38
  # Holds accessors for the registered details.
40
- module Accessors #:nodoc:
39
+ module Accessors # :nodoc:
41
40
  DEFAULT_PROCS = {}
42
41
  end
43
42
 
@@ -52,7 +51,7 @@ module ActionView
52
51
  register_detail(:variants) { [] }
53
52
  register_detail(:handlers) { Template::Handlers.extensions }
54
53
 
55
- class DetailsKey #:nodoc:
54
+ class DetailsKey # :nodoc:
56
55
  alias :eql? :equal?
57
56
 
58
57
  @details_keys = Concurrent::Map.new
@@ -68,14 +67,13 @@ module ActionView
68
67
  details = details.dup
69
68
  details[:formats] &= Template::Types.symbols
70
69
  end
71
- @details_keys[details] ||= Object.new
70
+ @details_keys[details] ||= TemplateDetails::Requested.new(**details)
72
71
  end
73
72
 
74
73
  def self.clear
75
74
  ActionView::ViewPaths.all_view_paths.each do |path_set|
76
75
  path_set.each(&:clear_cache)
77
76
  end
78
- ActionView::LookupContext.fallbacks.each(&:clear_cache)
79
77
  @view_context_class = nil
80
78
  @details_keys.clear
81
79
  @digest_cache.clear
@@ -98,7 +96,7 @@ module ActionView
98
96
 
99
97
  # Calculate the details key. Remove the handlers from calculation to improve performance
100
98
  # since the user cannot modify it explicitly.
101
- def details_key #:nodoc:
99
+ def details_key # :nodoc:
102
100
  @details_key ||= DetailsKey.details_cache_key(@details) if @cache
103
101
  end
104
102
 
@@ -124,39 +122,32 @@ module ActionView
124
122
  attr_reader :view_paths, :html_fallback_for_js
125
123
 
126
124
  def find(name, prefixes = [], partial = false, keys = [], options = {})
127
- @view_paths.find(*args_for_lookup(name, prefixes, partial, keys, options))
125
+ name, prefixes = normalize_name(name, prefixes)
126
+ details, details_key = detail_args_for(options)
127
+ @view_paths.find(name, prefixes, partial, details, details_key, keys)
128
128
  end
129
129
  alias :find_template :find
130
130
 
131
131
  def find_all(name, prefixes = [], partial = false, keys = [], options = {})
132
- @view_paths.find_all(*args_for_lookup(name, prefixes, partial, keys, options))
132
+ name, prefixes = normalize_name(name, prefixes)
133
+ details, details_key = detail_args_for(options)
134
+ @view_paths.find_all(name, prefixes, partial, details, details_key, keys)
133
135
  end
134
136
 
135
137
  def exists?(name, prefixes = [], partial = false, keys = [], **options)
136
- @view_paths.exists?(*args_for_lookup(name, prefixes, partial, keys, options))
138
+ name, prefixes = normalize_name(name, prefixes)
139
+ details, details_key = detail_args_for(options)
140
+ @view_paths.exists?(name, prefixes, partial, details, details_key, keys)
137
141
  end
138
142
  alias :template_exists? :exists?
139
143
 
140
144
  def any?(name, prefixes = [], partial = false)
141
- @view_paths.exists?(*args_for_any(name, prefixes, partial))
145
+ name, prefixes = normalize_name(name, prefixes)
146
+ details, details_key = detail_args_for_any
147
+ @view_paths.exists?(name, prefixes, partial, details, details_key, [])
142
148
  end
143
149
  alias :any_templates? :any?
144
150
 
145
- # Adds fallbacks to the view paths. Useful in cases when you are rendering
146
- # a :file.
147
- def with_fallbacks
148
- view_paths = build_view_paths((@view_paths.paths + self.class.fallbacks).uniq)
149
-
150
- if block_given?
151
- raise ArgumentError, <<~eowarn.squish
152
- Calling `with_fallbacks` with a block is not supported. Call methods on
153
- the lookup context returned by `with_fallbacks` instead.
154
- eowarn
155
- else
156
- ActionView::LookupContext.new(view_paths, @details, @prefixes)
157
- end
158
- end
159
-
160
151
  private
161
152
  # Whenever setting view paths, makes a copy so that we can manipulate them in
162
153
  # instance objects as we wish.
@@ -164,12 +155,6 @@ module ActionView
164
155
  ActionView::PathSet.new(Array(paths))
165
156
  end
166
157
 
167
- def args_for_lookup(name, prefixes, partial, keys, details_options)
168
- name, prefixes = normalize_name(name, prefixes)
169
- details, details_key = detail_args_for(details_options)
170
- [name, prefixes, partial || false, details, details_key, keys]
171
- end
172
-
173
158
  # Compute details hash and key according to user options (e.g. passed from #render).
174
159
  def detail_args_for(options) # :doc:
175
160
  return @details, details_key if options.empty? # most common path.
@@ -184,17 +169,11 @@ module ActionView
184
169
  [user_details, details_key]
185
170
  end
186
171
 
187
- def args_for_any(name, prefixes, partial)
188
- name, prefixes = normalize_name(name, prefixes)
189
- details, details_key = detail_args_for_any
190
- [name, prefixes, partial || false, details, details_key]
191
- end
192
-
193
172
  def detail_args_for_any
194
173
  @detail_args_for_any ||= begin
195
174
  details = {}
196
175
 
197
- registered_details.each do |k|
176
+ LookupContext.registered_details.each do |k|
198
177
  if k == :variants
199
178
  details[k] = :any
200
179
  else
@@ -210,19 +189,21 @@ module ActionView
210
189
  end
211
190
  end
212
191
 
213
- # Support legacy foo.erb names even though we now ignore .erb
214
- # as well as incorrectly putting part of the path in the template
215
- # name instead of the prefix.
192
+ # Fix when prefix is specified as part of the template name
216
193
  def normalize_name(name, prefixes)
217
- prefixes = prefixes.presence
218
- parts = name.to_s.split("/")
219
- parts.shift if parts.first.empty?
220
- name = parts.pop
194
+ name = name.to_s
195
+ idx = name.rindex("/")
196
+ return name, prefixes.presence || [""] unless idx
221
197
 
222
- return name, prefixes || [""] if parts.empty?
198
+ path_prefix = name[0, idx]
199
+ path_prefix = path_prefix.from(1) if path_prefix.start_with?("/")
200
+ name = name.from(idx + 1)
223
201
 
224
- parts = parts.join("/")
225
- prefixes = prefixes ? prefixes.map { |p| "#{p}/#{parts}" } : [parts]
202
+ if !prefixes || prefixes.empty?
203
+ prefixes = [path_prefix]
204
+ else
205
+ prefixes = prefixes.map { |p| "#{p}/#{path_prefix}" }
206
+ end
226
207
 
227
208
  return name, prefixes
228
209
  end
@@ -254,7 +235,7 @@ module ActionView
254
235
  end
255
236
 
256
237
  def initialize_details(target, details)
257
- registered_details.each do |k|
238
+ LookupContext.registered_details.each do |k|
258
239
  target[k] = details[k] || Accessors::DEFAULT_PROCS[k].call
259
240
  end
260
241
  target