actionview 7.0.8.7 → 7.2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +59 -454
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/app/assets/javascripts/rails-ujs.esm.js +686 -0
- data/app/assets/javascripts/rails-ujs.js +630 -0
- data/lib/action_view/base.rb +52 -14
- data/lib/action_view/buffers.rb +106 -8
- data/lib/action_view/cache_expiry.rb +44 -41
- data/lib/action_view/context.rb +1 -1
- data/lib/action_view/dependency_tracker/{ripper_tracker.rb → ruby_tracker.rb} +4 -3
- data/lib/action_view/dependency_tracker.rb +1 -1
- data/lib/action_view/deprecator.rb +7 -0
- data/lib/action_view/digestor.rb +1 -1
- data/lib/action_view/gem_version.rb +4 -4
- data/lib/action_view/helpers/active_model_helper.rb +1 -1
- data/lib/action_view/helpers/asset_tag_helper.rb +151 -55
- data/lib/action_view/helpers/asset_url_helper.rb +6 -5
- data/lib/action_view/helpers/atom_feed_helper.rb +5 -5
- data/lib/action_view/helpers/cache_helper.rb +7 -13
- data/lib/action_view/helpers/capture_helper.rb +30 -10
- data/lib/action_view/helpers/content_exfiltration_prevention_helper.rb +70 -0
- data/lib/action_view/helpers/controller_helper.rb +6 -0
- data/lib/action_view/helpers/csp_helper.rb +2 -2
- data/lib/action_view/helpers/csrf_helper.rb +3 -3
- data/lib/action_view/helpers/date_helper.rb +17 -19
- data/lib/action_view/helpers/debug_helper.rb +3 -3
- data/lib/action_view/helpers/form_helper.rb +248 -214
- data/lib/action_view/helpers/form_options_helper.rb +2 -1
- data/lib/action_view/helpers/form_tag_helper.rb +125 -58
- data/lib/action_view/helpers/javascript_helper.rb +1 -0
- data/lib/action_view/helpers/number_helper.rb +37 -330
- data/lib/action_view/helpers/output_safety_helper.rb +6 -6
- data/lib/action_view/helpers/rendering_helper.rb +1 -1
- data/lib/action_view/helpers/sanitize_helper.rb +51 -21
- data/lib/action_view/helpers/tag_helper.rb +210 -42
- data/lib/action_view/helpers/tags/base.rb +11 -52
- data/lib/action_view/helpers/tags/collection_check_boxes.rb +1 -0
- data/lib/action_view/helpers/tags/collection_radio_buttons.rb +1 -0
- data/lib/action_view/helpers/tags/collection_select.rb +3 -0
- data/lib/action_view/helpers/tags/date_field.rb +1 -1
- data/lib/action_view/helpers/tags/date_select.rb +2 -0
- data/lib/action_view/helpers/tags/datetime_field.rb +14 -6
- data/lib/action_view/helpers/tags/datetime_local_field.rb +11 -2
- data/lib/action_view/helpers/tags/grouped_collection_select.rb +3 -0
- data/lib/action_view/helpers/tags/month_field.rb +1 -1
- data/lib/action_view/helpers/tags/select.rb +3 -0
- data/lib/action_view/helpers/tags/select_renderer.rb +56 -0
- data/lib/action_view/helpers/tags/time_field.rb +1 -1
- data/lib/action_view/helpers/tags/time_zone_select.rb +3 -0
- data/lib/action_view/helpers/tags/week_field.rb +1 -1
- data/lib/action_view/helpers/tags/weekday_select.rb +3 -0
- data/lib/action_view/helpers/tags.rb +2 -0
- data/lib/action_view/helpers/text_helper.rb +157 -85
- data/lib/action_view/helpers/translation_helper.rb +3 -3
- data/lib/action_view/helpers/url_helper.rb +35 -80
- data/lib/action_view/helpers.rb +2 -0
- data/lib/action_view/layouts.rb +8 -8
- data/lib/action_view/log_subscriber.rb +57 -36
- data/lib/action_view/lookup_context.rb +29 -13
- data/lib/action_view/path_registry.rb +57 -0
- data/lib/action_view/path_set.rb +13 -14
- data/lib/action_view/railtie.rb +25 -3
- data/lib/action_view/record_identifier.rb +15 -8
- data/lib/action_view/render_parser/prism_render_parser.rb +127 -0
- data/lib/action_view/{ripper_ast_parser.rb → render_parser/ripper_render_parser.rb} +156 -13
- data/lib/action_view/render_parser.rb +21 -169
- data/lib/action_view/renderer/abstract_renderer.rb +2 -2
- data/lib/action_view/renderer/collection_renderer.rb +10 -2
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +2 -1
- data/lib/action_view/renderer/partial_renderer.rb +2 -1
- data/lib/action_view/renderer/renderer.rb +34 -38
- data/lib/action_view/renderer/streaming_template_renderer.rb +3 -2
- data/lib/action_view/renderer/template_renderer.rb +3 -2
- data/lib/action_view/rendering.rb +26 -8
- data/lib/action_view/template/error.rb +14 -1
- data/lib/action_view/template/handlers/builder.rb +4 -4
- data/lib/action_view/template/handlers/erb/erubi.rb +23 -27
- data/lib/action_view/template/handlers/erb.rb +73 -1
- data/lib/action_view/template/handlers.rb +1 -1
- data/lib/action_view/template/html.rb +1 -1
- data/lib/action_view/template/raw_file.rb +1 -1
- data/lib/action_view/template/renderable.rb +8 -2
- data/lib/action_view/template/resolver.rb +9 -3
- data/lib/action_view/template/text.rb +1 -1
- data/lib/action_view/template/types.rb +25 -34
- data/lib/action_view/template.rb +278 -55
- data/lib/action_view/template_path.rb +2 -0
- data/lib/action_view/test_case.rb +181 -28
- data/lib/action_view/unbound_template.rb +17 -7
- data/lib/action_view/version.rb +1 -1
- data/lib/action_view/view_paths.rb +15 -24
- data/lib/action_view.rb +4 -1
- metadata +26 -26
- data/lib/assets/compiled/rails-ujs.js +0 -777
@@ -3,11 +3,13 @@
|
|
3
3
|
require "active_support/core_ext/array/access"
|
4
4
|
require "active_support/core_ext/hash/keys"
|
5
5
|
require "active_support/core_ext/string/output_safety"
|
6
|
+
require "action_view/helpers/content_exfiltration_prevention_helper"
|
6
7
|
require "action_view/helpers/tag_helper"
|
7
8
|
|
8
9
|
module ActionView
|
9
|
-
# = Action View URL Helpers
|
10
10
|
module Helpers # :nodoc:
|
11
|
+
# = Action View URL \Helpers
|
12
|
+
#
|
11
13
|
# Provides a set of methods for making links and getting URLs that
|
12
14
|
# depend on the routing subsystem (see ActionDispatch::Routing).
|
13
15
|
# This allows you to use the same format for links in views
|
@@ -22,6 +24,7 @@ module ActionView
|
|
22
24
|
extend ActiveSupport::Concern
|
23
25
|
|
24
26
|
include TagHelper
|
27
|
+
include ContentExfiltrationPreventionHelper
|
25
28
|
|
26
29
|
module ClassMethods
|
27
30
|
def _url_for_modules
|
@@ -93,7 +96,7 @@ module ActionView
|
|
93
96
|
# ==== Examples
|
94
97
|
#
|
95
98
|
# Because it relies on +url_for+, +link_to+ supports both older-style controller/action/id arguments
|
96
|
-
# and newer RESTful routes. Current Rails style favors RESTful routes whenever possible, so base
|
99
|
+
# and newer RESTful routes. Current \Rails style favors RESTful routes whenever possible, so base
|
97
100
|
# your application on resources and use
|
98
101
|
#
|
99
102
|
# link_to "Profile", profile_path(@profile)
|
@@ -170,41 +173,27 @@ module ActionView
|
|
170
173
|
# link_to "External link", "http://www.rubyonrails.org/", target: "_blank", rel: "nofollow"
|
171
174
|
# # => <a href="http://www.rubyonrails.org/" target="_blank" rel="nofollow">External link</a>
|
172
175
|
#
|
173
|
-
# ====
|
176
|
+
# ==== Turbo
|
174
177
|
#
|
175
|
-
#
|
176
|
-
# this library is no longer on by default. This library integrated with the following options:
|
178
|
+
# Rails 7 ships with Turbo enabled by default. Turbo provides the following +:data+ options:
|
177
179
|
#
|
178
|
-
# * <tt>
|
179
|
-
#
|
180
|
-
#
|
181
|
-
# in dangerous actions like deleting a record (which search bots can follow
|
182
|
-
# while spidering your site). Supported verbs are <tt>:post</tt>, <tt>:delete</tt>, <tt>:patch</tt>, and <tt>:put</tt>.
|
183
|
-
# Note that if the user has JavaScript disabled, the request will fall back
|
184
|
-
# to using GET. If <tt>href: '#'</tt> is used and the user has JavaScript
|
185
|
-
# disabled clicking the link will have no effect. If you are relying on the
|
186
|
-
# POST behavior, you should check for it in your controller's action by using
|
187
|
-
# the request object's methods for <tt>post?</tt>, <tt>delete?</tt>, <tt>patch?</tt>, or <tt>put?</tt>.
|
188
|
-
# * <tt>remote: true</tt> - This will allow <tt>@rails/ujs</tt>
|
189
|
-
# to make an Ajax request to the URL in question instead of following
|
190
|
-
# the link.
|
180
|
+
# * <tt>turbo_method: symbol of HTTP verb</tt> - Performs a Turbo link visit
|
181
|
+
# with the given HTTP verb. Forms are recommended when performing non-+GET+ requests.
|
182
|
+
# Only use <tt>data-turbo-method</tt> where a form is not possible.
|
191
183
|
#
|
192
|
-
# <tt
|
184
|
+
# * <tt>turbo_confirm: "question?"</tt> - Adds a confirmation dialog to the link with the
|
185
|
+
# given value.
|
193
186
|
#
|
194
|
-
#
|
195
|
-
#
|
196
|
-
# resulting text would be <tt>question?</tt>). If the user accepts, the
|
197
|
-
# link is processed normally, otherwise no action is taken.
|
198
|
-
# * <tt>:disable_with</tt> - Value of this parameter will be used as the
|
199
|
-
# name for a disabled version of the link.
|
187
|
+
# {Consult the Turbo Handbook for more information on the options
|
188
|
+
# above.}[https://turbo.hotwired.dev/handbook/drive#performing-visits-with-a-different-method]
|
200
189
|
#
|
201
|
-
# =====
|
190
|
+
# ===== \Examples
|
202
191
|
#
|
203
|
-
# link_to "
|
204
|
-
# # => <a href="/profiles/1"
|
192
|
+
# link_to "Delete profile", @profile, data: { turbo_method: :delete }
|
193
|
+
# # => <a href="/profiles/1" data-turbo-method="delete">Delete profile</a>
|
205
194
|
#
|
206
|
-
# link_to "Visit Other Site", "
|
207
|
-
# # => <a href="
|
195
|
+
# link_to "Visit Other Site", "https://rubyonrails.org/", data: { turbo_confirm: "Are you sure?" }
|
196
|
+
# # => <a href="https://rubyonrails.org/" data-turbo-confirm="Are you sure?">Visit Other Site</a>
|
208
197
|
#
|
209
198
|
def link_to(name = nil, options = nil, html_options = nil, &block)
|
210
199
|
html_options, options, name = options, name, block if block_given?
|
@@ -221,9 +210,6 @@ module ActionView
|
|
221
210
|
# Generates a form containing a single button that submits to the URL created
|
222
211
|
# by the set of +options+. This is the safest method to ensure links that
|
223
212
|
# cause changes to your data are not triggered by search bots or accelerators.
|
224
|
-
# If the HTML button does not work with your layout, you can also consider
|
225
|
-
# using the +link_to+ method with the <tt>:method</tt> modifier as described in
|
226
|
-
# the +link_to+ documentation.
|
227
213
|
#
|
228
214
|
# You can control the form and button behavior with +html_options+. Most
|
229
215
|
# values in +html_options+ are passed through to the button element. For
|
@@ -234,8 +220,13 @@ module ActionView
|
|
234
220
|
# +:form_class+ option within +html_options+. It defaults to
|
235
221
|
# <tt>"button_to"</tt> to allow styling of the form and its children.
|
236
222
|
#
|
237
|
-
# The form submits a POST request by default
|
238
|
-
#
|
223
|
+
# The form submits a POST request by default if the object is not persisted;
|
224
|
+
# conversely, if the object is persisted, it will submit a PATCH request.
|
225
|
+
# To specify a different HTTP verb use the +:method+ option within +html_options+.
|
226
|
+
#
|
227
|
+
# If the HTML button generated from +button_to+ does not work with your layout, you can
|
228
|
+
# consider using the +link_to+ method with the +data-turbo-method+
|
229
|
+
# attribute as described in the +link_to+ documentation.
|
239
230
|
#
|
240
231
|
# ==== Options
|
241
232
|
# The +options+ hash accepts the same options as +url_for+. To generate a
|
@@ -302,32 +293,6 @@ module ActionView
|
|
302
293
|
# # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6" autocomplete="off"/>
|
303
294
|
# # </form>"
|
304
295
|
#
|
305
|
-
# ==== Deprecated: Rails UJS Attributes
|
306
|
-
#
|
307
|
-
# Prior to Rails 7, Rails shipped with a JavaScript library called <tt>@rails/ujs</tt> on by default. Following Rails 7,
|
308
|
-
# this library is no longer on by default. This library integrated with the following options:
|
309
|
-
#
|
310
|
-
# * <tt>:remote</tt> - If set to true, will allow <tt>@rails/ujs</tt> to control the
|
311
|
-
# submit behavior. By default this behavior is an Ajax submit.
|
312
|
-
#
|
313
|
-
# <tt>@rails/ujs</tt> also integrated with the following +:data+ options:
|
314
|
-
#
|
315
|
-
# * <tt>confirm: "question?"</tt> - This will allow <tt>@rails/ujs</tt>
|
316
|
-
# to prompt with the question specified (in this case, the
|
317
|
-
# resulting text would be <tt>question?</tt>). If the user accepts, the
|
318
|
-
# button is processed normally, otherwise no action is taken.
|
319
|
-
# * <tt>:disable_with</tt> - Value of this parameter will be
|
320
|
-
# used as the value for a disabled version of the submit
|
321
|
-
# button when the form is submitted.
|
322
|
-
#
|
323
|
-
# ===== Rails UJS Examples
|
324
|
-
#
|
325
|
-
# <%= button_to "Create", { action: "create" }, remote: true, form: { "data-type" => "json" } %>
|
326
|
-
# # => "<form method="post" action="/images/create" class="button_to" data-remote="true" data-type="json">
|
327
|
-
# # <button type="submit">Create</button>
|
328
|
-
# # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6" autocomplete="off"/>
|
329
|
-
# # </form>"
|
330
|
-
#
|
331
296
|
def button_to(name = nil, options = nil, html_options = nil, &block)
|
332
297
|
html_options, options = options, name if block_given?
|
333
298
|
html_options ||= {}
|
@@ -380,7 +345,8 @@ module ActionView
|
|
380
345
|
autocomplete: "off")
|
381
346
|
end
|
382
347
|
end
|
383
|
-
content_tag("form", inner_tags, form_options)
|
348
|
+
html = content_tag("form", inner_tags, form_options)
|
349
|
+
prevent_content_exfiltration(html)
|
384
350
|
end
|
385
351
|
|
386
352
|
# Creates a link tag of the given +name+ using a URL created by the set of
|
@@ -495,7 +461,7 @@ module ActionView
|
|
495
461
|
# * <tt>:reply_to</tt> - Preset the +Reply-To+ field of the email.
|
496
462
|
#
|
497
463
|
# ==== Obfuscation
|
498
|
-
# Prior to Rails 4.0, +mail_to+ provided options for encoding the address
|
464
|
+
# Prior to \Rails 4.0, +mail_to+ provided options for encoding the address
|
499
465
|
# in order to hinder email harvesters. To take advantage of these options,
|
500
466
|
# install the +actionview-encoded_mail_to+ gem.
|
501
467
|
#
|
@@ -534,6 +500,8 @@ module ActionView
|
|
534
500
|
content_tag("a", name || email_address, html_options, &block)
|
535
501
|
end
|
536
502
|
|
503
|
+
RFC2396_PARSER = defined?(URI::RFC2396_PARSER) ? URI::RFC2396_PARSER : URI::RFC2396_Parser.new
|
504
|
+
|
537
505
|
# True if the current request URI was generated by the given +options+.
|
538
506
|
#
|
539
507
|
# ==== Examples
|
@@ -590,14 +558,14 @@ module ActionView
|
|
590
558
|
|
591
559
|
options ||= options_as_kwargs
|
592
560
|
check_parameters ||= options.is_a?(Hash) && options.delete(:check_parameters)
|
593
|
-
url_string =
|
561
|
+
url_string = RFC2396_PARSER.unescape(url_for(options)).force_encoding(Encoding::BINARY)
|
594
562
|
|
595
563
|
# We ignore any extra parameters in the request_uri if the
|
596
564
|
# submitted URL doesn't have any either. This lets the function
|
597
565
|
# work with things like ?order=asc
|
598
|
-
# the
|
566
|
+
# the behavior can be disabled with check_parameters: true
|
599
567
|
request_uri = url_string.index("?") || check_parameters ? request.fullpath : request.path
|
600
|
-
request_uri =
|
568
|
+
request_uri = RFC2396_PARSER.unescape(request_uri).force_encoding(Encoding::BINARY)
|
601
569
|
|
602
570
|
if %r{^\w+://}.match?(url_string)
|
603
571
|
request_uri = +"#{request.protocol}#{request.host_with_port}#{request_uri}"
|
@@ -609,19 +577,6 @@ module ActionView
|
|
609
577
|
url_string == request_uri
|
610
578
|
end
|
611
579
|
|
612
|
-
if RUBY_VERSION.start_with?("2.7")
|
613
|
-
using Module.new {
|
614
|
-
refine UrlHelper do
|
615
|
-
alias :_current_page? :current_page?
|
616
|
-
end
|
617
|
-
}
|
618
|
-
|
619
|
-
def current_page?(*args) # :nodoc:
|
620
|
-
options = args.pop
|
621
|
-
options.is_a?(Hash) ? _current_page?(*args, **options) : _current_page?(*args, options)
|
622
|
-
end
|
623
|
-
end
|
624
|
-
|
625
580
|
# Creates an SMS anchor link tag to the specified +phone_number+. When the
|
626
581
|
# link is clicked, the default SMS messaging app is opened ready to send a
|
627
582
|
# message to the linked phone number. If the +body+ option is specified,
|
@@ -757,7 +712,7 @@ module ActionView
|
|
757
712
|
end
|
758
713
|
|
759
714
|
def add_method_to_attributes!(html_options, method)
|
760
|
-
if method_not_get_method?(method) && !html_options["rel"]
|
715
|
+
if method_not_get_method?(method) && !html_options["rel"].to_s.include?("nofollow")
|
761
716
|
if html_options["rel"].blank?
|
762
717
|
html_options["rel"] = "nofollow"
|
763
718
|
else
|
data/lib/action_view/helpers.rb
CHANGED
@@ -12,6 +12,7 @@ require "action_view/helpers/asset_tag_helper"
|
|
12
12
|
require "action_view/helpers/asset_url_helper"
|
13
13
|
require "action_view/helpers/atom_feed_helper"
|
14
14
|
require "action_view/helpers/cache_helper"
|
15
|
+
require "action_view/helpers/content_exfiltration_prevention_helper"
|
15
16
|
require "action_view/helpers/controller_helper"
|
16
17
|
require "action_view/helpers/csp_helper"
|
17
18
|
require "action_view/helpers/csrf_helper"
|
@@ -45,6 +46,7 @@ module ActionView # :nodoc:
|
|
45
46
|
include AtomFeedHelper
|
46
47
|
include CacheHelper
|
47
48
|
include CaptureHelper
|
49
|
+
include ContentExfiltrationPreventionHelper
|
48
50
|
include ControllerHelper
|
49
51
|
include CspHelper
|
50
52
|
include CsrfHelper
|
data/lib/action_view/layouts.rb
CHANGED
@@ -4,12 +4,14 @@ require "action_view/rendering"
|
|
4
4
|
require "active_support/core_ext/module/redefine_method"
|
5
5
|
|
6
6
|
module ActionView
|
7
|
+
# = Action View \Layouts
|
8
|
+
#
|
7
9
|
# Layouts reverse the common pattern of including shared headers and footers in many templates to isolate changes in
|
8
10
|
# repeated setups. The inclusion pattern has pages that look like this:
|
9
11
|
#
|
10
|
-
# <%= render "
|
12
|
+
# <%= render "application/header" %>
|
11
13
|
# Hello World
|
12
|
-
# <%= render "
|
14
|
+
# <%= render "application/footer" %>
|
13
15
|
#
|
14
16
|
# This approach is a decent way of keeping common structures isolated from the changing content, but it's verbose
|
15
17
|
# and if you ever want to change the structure of these two includes, you'll have to change all the templates.
|
@@ -150,7 +152,7 @@ module ActionView
|
|
150
152
|
# The template will be looked always in <tt>app/views/layouts/</tt> folder. But you can point
|
151
153
|
# <tt>layouts</tt> folder direct also. <tt>layout "layouts/demo"</tt> is the same as <tt>layout "demo"</tt>.
|
152
154
|
#
|
153
|
-
# Setting the layout to +nil+ forces it to be looked up in the filesystem and
|
155
|
+
# Setting the layout to +nil+ forces it to be looked up in the filesystem and falls back to the parent behavior if none exists.
|
154
156
|
# Setting it to +nil+ is useful to re-enable template lookup overriding a previous configuration set in the parent:
|
155
157
|
#
|
156
158
|
# class ApplicationController < ActionController::Base
|
@@ -162,7 +164,7 @@ module ActionView
|
|
162
164
|
# end
|
163
165
|
#
|
164
166
|
# class CommentsController < ApplicationController
|
165
|
-
# # Will search for "comments" layout and
|
167
|
+
# # Will search for "comments" layout and fall back to "application" layout
|
166
168
|
# layout nil
|
167
169
|
# end
|
168
170
|
#
|
@@ -207,13 +209,11 @@ module ActionView
|
|
207
209
|
|
208
210
|
included do
|
209
211
|
class_attribute :_layout, instance_accessor: false
|
210
|
-
class_attribute :_layout_conditions, instance_accessor: false, default: {}
|
212
|
+
class_attribute :_layout_conditions, instance_accessor: false, instance_reader: true, default: {}
|
211
213
|
|
212
214
|
_write_layout_method
|
213
215
|
end
|
214
216
|
|
215
|
-
delegate :_layout_conditions, to: :class
|
216
|
-
|
217
217
|
module ClassMethods
|
218
218
|
def inherited(klass) # :nodoc:
|
219
219
|
super
|
@@ -428,7 +428,7 @@ module ActionView
|
|
428
428
|
end
|
429
429
|
|
430
430
|
def _include_layout?(options)
|
431
|
-
|
431
|
+
!options.keys.intersect?([:body, :plain, :html, :inline, :partial]) || options.key?(:layout)
|
432
432
|
end
|
433
433
|
end
|
434
434
|
end
|
@@ -5,7 +5,7 @@ require "active_support/log_subscriber"
|
|
5
5
|
module ActionView
|
6
6
|
# = Action View Log Subscriber
|
7
7
|
#
|
8
|
-
# Provides functionality so that Rails can output logs from Action View.
|
8
|
+
# Provides functionality so that \Rails can output logs from Action View.
|
9
9
|
class LogSubscriber < ActiveSupport::LogSubscriber
|
10
10
|
VIEWS_PATTERN = /^app\/views\//
|
11
11
|
|
@@ -18,26 +18,29 @@ module ActionView
|
|
18
18
|
info do
|
19
19
|
message = +" Rendered #{from_rails_root(event.payload[:identifier])}"
|
20
20
|
message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout]
|
21
|
-
message << " (Duration: #{event.duration.round(1)}ms |
|
21
|
+
message << " (Duration: #{event.duration.round(1)}ms | GC: #{event.gc_time.round(1)}ms)"
|
22
22
|
end
|
23
23
|
end
|
24
|
+
subscribe_log_level :render_template, :debug
|
24
25
|
|
25
26
|
def render_partial(event)
|
26
27
|
debug do
|
27
28
|
message = +" Rendered #{from_rails_root(event.payload[:identifier])}"
|
28
29
|
message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout]
|
29
|
-
message << " (Duration: #{event.duration.round(1)}ms |
|
30
|
+
message << " (Duration: #{event.duration.round(1)}ms | GC: #{event.gc_time.round(1)}ms)"
|
30
31
|
message << " #{cache_message(event.payload)}" unless event.payload[:cache_hit].nil?
|
31
32
|
message
|
32
33
|
end
|
33
34
|
end
|
35
|
+
subscribe_log_level :render_partial, :debug
|
34
36
|
|
35
37
|
def render_layout(event)
|
36
38
|
info do
|
37
39
|
message = +" Rendered layout #{from_rails_root(event.payload[:identifier])}"
|
38
|
-
message << " (Duration: #{event.duration.round(1)}ms |
|
40
|
+
message << " (Duration: #{event.duration.round(1)}ms | GC: #{event.gc_time.round(1)}ms)"
|
39
41
|
end
|
40
42
|
end
|
43
|
+
subscribe_log_level :render_layout, :info
|
41
44
|
|
42
45
|
def render_collection(event)
|
43
46
|
identifier = event.payload[:identifier] || "templates"
|
@@ -45,33 +48,68 @@ module ActionView
|
|
45
48
|
debug do
|
46
49
|
message = +" Rendered collection of #{from_rails_root(identifier)}"
|
47
50
|
message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout]
|
48
|
-
message << " #{render_count(event.payload)} (Duration: #{event.duration.round(1)}ms |
|
51
|
+
message << " #{render_count(event.payload)} (Duration: #{event.duration.round(1)}ms | GC: #{event.gc_time.round(1)}ms)"
|
49
52
|
message
|
50
53
|
end
|
51
54
|
end
|
55
|
+
subscribe_log_level :render_collection, :debug
|
52
56
|
|
53
|
-
|
54
|
-
|
57
|
+
module Utils # :nodoc:
|
58
|
+
def logger
|
59
|
+
ActionView::Base.logger
|
60
|
+
end
|
55
61
|
|
56
|
-
|
57
|
-
|
62
|
+
private
|
63
|
+
def from_rails_root(string)
|
64
|
+
string = string.sub(rails_root, "")
|
65
|
+
string.sub!(VIEWS_PATTERN, "")
|
66
|
+
string
|
67
|
+
end
|
58
68
|
|
59
|
-
|
60
|
-
|
69
|
+
def rails_root # :doc:
|
70
|
+
@root ||= "#{Rails.root}/"
|
71
|
+
end
|
61
72
|
end
|
62
73
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
74
|
+
include Utils
|
75
|
+
|
76
|
+
class Start # :nodoc:
|
77
|
+
include Utils
|
78
|
+
|
79
|
+
def start(name, id, payload)
|
80
|
+
return unless logger
|
81
|
+
logger.debug do
|
82
|
+
qualifier =
|
83
|
+
if name == "render_template.action_view"
|
84
|
+
""
|
85
|
+
elsif name == "render_layout.action_view"
|
86
|
+
"layout "
|
87
|
+
end
|
88
|
+
|
89
|
+
return unless qualifier
|
90
|
+
|
91
|
+
message = +" Rendering #{qualifier}#{from_rails_root(payload[:identifier])}"
|
92
|
+
message << " within #{from_rails_root(payload[:layout])}" if payload[:layout]
|
93
|
+
message
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def finish(name, id, payload)
|
98
|
+
end
|
99
|
+
|
100
|
+
def silenced?(_)
|
101
|
+
logger.nil? || !logger.debug?
|
102
|
+
end
|
69
103
|
end
|
70
104
|
|
71
|
-
def
|
72
|
-
|
105
|
+
def self.attach_to(*)
|
106
|
+
ActiveSupport::Notifications.subscribe("render_template.action_view", ActionView::LogSubscriber::Start.new)
|
107
|
+
ActiveSupport::Notifications.subscribe("render_layout.action_view", ActionView::LogSubscriber::Start.new)
|
108
|
+
|
109
|
+
super
|
73
110
|
end
|
74
111
|
|
112
|
+
private
|
75
113
|
def render_count(payload) # :doc:
|
76
114
|
if payload[:cache_hits]
|
77
115
|
"[#{payload[:cache_hits]} / #{payload[:count]} cache hits]"
|
@@ -88,23 +126,6 @@ module ActionView
|
|
88
126
|
"[cache miss]"
|
89
127
|
end
|
90
128
|
end
|
91
|
-
|
92
|
-
def log_rendering_start(payload, name)
|
93
|
-
debug do
|
94
|
-
qualifier =
|
95
|
-
if name == "render_template.action_view"
|
96
|
-
""
|
97
|
-
elsif name == "render_layout.action_view"
|
98
|
-
"layout "
|
99
|
-
end
|
100
|
-
|
101
|
-
return unless qualifier
|
102
|
-
|
103
|
-
message = +" Rendering #{qualifier}#{from_rails_root(payload[:identifier])}"
|
104
|
-
message << " within #{from_rails_root(payload[:layout])}" if payload[:layout]
|
105
|
-
message
|
106
|
-
end
|
107
|
-
end
|
108
129
|
end
|
109
130
|
end
|
110
131
|
|
@@ -13,7 +13,7 @@ module ActionView
|
|
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
15
|
class LookupContext # :nodoc:
|
16
|
-
attr_accessor :prefixes
|
16
|
+
attr_accessor :prefixes
|
17
17
|
|
18
18
|
singleton_class.attr_accessor :registered_details
|
19
19
|
self.registered_details = []
|
@@ -63,16 +63,20 @@ module ActionView
|
|
63
63
|
end
|
64
64
|
|
65
65
|
def self.details_cache_key(details)
|
66
|
-
|
67
|
-
|
68
|
-
|
66
|
+
@details_keys.fetch(details) do
|
67
|
+
if formats = details[:formats]
|
68
|
+
unless Template::Types.valid_symbols?(formats)
|
69
|
+
details = details.dup
|
70
|
+
details[:formats] &= Template::Types.symbols
|
71
|
+
end
|
72
|
+
end
|
73
|
+
@details_keys[details] ||= TemplateDetails::Requested.new(**details)
|
69
74
|
end
|
70
|
-
@details_keys[details] ||= TemplateDetails::Requested.new(**details)
|
71
75
|
end
|
72
76
|
|
73
77
|
def self.clear
|
74
|
-
ActionView::
|
75
|
-
|
78
|
+
ActionView::PathRegistry.all_resolvers.each do |resolver|
|
79
|
+
resolver.clear_cache
|
76
80
|
end
|
77
81
|
@view_context_class = nil
|
78
82
|
@details_keys.clear
|
@@ -83,9 +87,9 @@ module ActionView
|
|
83
87
|
@digest_cache.values
|
84
88
|
end
|
85
89
|
|
86
|
-
def self.view_context_class
|
90
|
+
def self.view_context_class
|
87
91
|
@view_context_mutex.synchronize do
|
88
|
-
@view_context_class ||=
|
92
|
+
@view_context_class ||= ActionView::Base.with_empty_template_cache
|
89
93
|
end
|
90
94
|
end
|
91
95
|
end
|
@@ -148,11 +152,23 @@ module ActionView
|
|
148
152
|
end
|
149
153
|
alias :any_templates? :any?
|
150
154
|
|
155
|
+
def append_view_paths(paths)
|
156
|
+
@view_paths = build_view_paths(@view_paths.to_a + paths)
|
157
|
+
end
|
158
|
+
|
159
|
+
def prepend_view_paths(paths)
|
160
|
+
@view_paths = build_view_paths(paths + @view_paths.to_a)
|
161
|
+
end
|
162
|
+
|
151
163
|
private
|
152
164
|
# Whenever setting view paths, makes a copy so that we can manipulate them in
|
153
165
|
# instance objects as we wish.
|
154
166
|
def build_view_paths(paths)
|
155
|
-
ActionView::PathSet
|
167
|
+
if ActionView::PathSet === paths
|
168
|
+
paths
|
169
|
+
else
|
170
|
+
ActionView::PathSet.new(Array(paths))
|
171
|
+
end
|
156
172
|
end
|
157
173
|
|
158
174
|
# Compute details hash and key according to user options (e.g. passed from #render).
|
@@ -250,12 +266,12 @@ module ActionView
|
|
250
266
|
values.concat(default_formats) if values.delete "*/*"
|
251
267
|
values.uniq!
|
252
268
|
|
253
|
-
|
254
|
-
|
269
|
+
unless Template::Types.valid_symbols?(values)
|
270
|
+
invalid_values = values - Template::Types.symbols
|
255
271
|
raise ArgumentError, "Invalid formats: #{invalid_values.map(&:inspect).join(", ")}"
|
256
272
|
end
|
257
273
|
|
258
|
-
if values == [:js
|
274
|
+
if (values.length == 1) && (values[0] == :js)
|
259
275
|
values << :html
|
260
276
|
@html_fallback_for_js = true
|
261
277
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionView # :nodoc:
|
4
|
+
module PathRegistry # :nodoc:
|
5
|
+
@view_paths_by_class = {}
|
6
|
+
@file_system_resolvers = {}
|
7
|
+
@file_system_resolver_mutex = Mutex.new
|
8
|
+
@file_system_resolver_hooks = []
|
9
|
+
|
10
|
+
class << self
|
11
|
+
attr_reader :file_system_resolver_hooks
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.get_view_paths(klass)
|
15
|
+
@view_paths_by_class[klass] || get_view_paths(klass.superclass)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.set_view_paths(klass, paths)
|
19
|
+
@view_paths_by_class[klass] = paths
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.cast_file_system_resolvers(paths)
|
23
|
+
paths = Array(paths)
|
24
|
+
|
25
|
+
@file_system_resolver_mutex.synchronize do
|
26
|
+
built_resolver = false
|
27
|
+
paths = paths.map do |path|
|
28
|
+
case path
|
29
|
+
when String, Pathname
|
30
|
+
path = File.expand_path(path)
|
31
|
+
@file_system_resolvers[path] ||=
|
32
|
+
begin
|
33
|
+
built_resolver = true
|
34
|
+
FileSystemResolver.new(path)
|
35
|
+
end
|
36
|
+
else
|
37
|
+
path
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
file_system_resolver_hooks.each(&:call) if built_resolver
|
42
|
+
end
|
43
|
+
|
44
|
+
paths
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.all_resolvers
|
48
|
+
resolvers = [all_file_system_resolvers]
|
49
|
+
resolvers.concat @view_paths_by_class.values.map(&:to_a)
|
50
|
+
resolvers.flatten.uniq
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.all_file_system_resolvers
|
54
|
+
@file_system_resolvers.values
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/action_view/path_set.rb
CHANGED
@@ -13,14 +13,14 @@ module ActionView # :nodoc:
|
|
13
13
|
|
14
14
|
attr_reader :paths
|
15
15
|
|
16
|
-
delegate :[], :include?, :
|
16
|
+
delegate :[], :include?, :size, :each, to: :paths
|
17
17
|
|
18
18
|
def initialize(paths = [])
|
19
|
-
@paths = typecast
|
19
|
+
@paths = typecast(paths).freeze
|
20
20
|
end
|
21
21
|
|
22
22
|
def initialize_copy(other)
|
23
|
-
@paths = other.paths.dup
|
23
|
+
@paths = other.paths.dup.freeze
|
24
24
|
self
|
25
25
|
end
|
26
26
|
|
@@ -32,18 +32,11 @@ module ActionView # :nodoc:
|
|
32
32
|
PathSet.new paths.compact
|
33
33
|
end
|
34
34
|
|
35
|
-
def +(
|
35
|
+
def +(other)
|
36
|
+
array = Array === other ? other : other.paths
|
36
37
|
PathSet.new(paths + array)
|
37
38
|
end
|
38
39
|
|
39
|
-
%w(<< concat push insert unshift).each do |method|
|
40
|
-
class_eval <<-METHOD, __FILE__, __LINE__ + 1
|
41
|
-
def #{method}(*args)
|
42
|
-
paths.#{method}(*typecast(args))
|
43
|
-
end
|
44
|
-
METHOD
|
45
|
-
end
|
46
|
-
|
47
40
|
def find(path, prefixes, partial, details, details_key, locals)
|
48
41
|
find_all(path, prefixes, partial, details, details_key, locals).first ||
|
49
42
|
raise(MissingTemplate.new(self, path, prefixes, partial, details, details_key, locals))
|
@@ -75,9 +68,15 @@ module ActionView # :nodoc:
|
|
75
68
|
paths.map do |path|
|
76
69
|
case path
|
77
70
|
when Pathname, String
|
78
|
-
|
79
|
-
|
71
|
+
# This path should only be reached by "direct" users of
|
72
|
+
# ActionView::Base (not using the ViewPaths or Renderer modules).
|
73
|
+
# We can't cache/de-dup the file system resolver in this case as we
|
74
|
+
# don't know which compiled_method_container we'll be rendering to.
|
75
|
+
FileSystemResolver.new(path)
|
76
|
+
when Resolver
|
80
77
|
path
|
78
|
+
else
|
79
|
+
raise TypeError, "#{path.inspect} is not a valid path: must be a String, Pathname, or Resolver"
|
81
80
|
end
|
82
81
|
end
|
83
82
|
end
|