actionview 7.1.5.2 → 8.1.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 +96 -436
- data/README.rdoc +1 -1
- data/lib/action_view/base.rb +29 -11
- data/lib/action_view/buffers.rb +1 -1
- data/lib/action_view/cache_expiry.rb +9 -3
- data/lib/action_view/dependency_tracker/erb_tracker.rb +37 -28
- data/lib/action_view/dependency_tracker/ruby_tracker.rb +43 -0
- data/lib/action_view/dependency_tracker/wildcard_resolver.rb +32 -0
- data/lib/action_view/dependency_tracker.rb +8 -2
- data/lib/action_view/digestor.rb +6 -2
- data/lib/action_view/gem_version.rb +3 -3
- data/lib/action_view/helpers/asset_tag_helper.rb +39 -8
- data/lib/action_view/helpers/atom_feed_helper.rb +1 -3
- data/lib/action_view/helpers/cache_helper.rb +10 -2
- data/lib/action_view/helpers/capture_helper.rb +2 -2
- data/lib/action_view/helpers/controller_helper.rb +6 -2
- data/lib/action_view/helpers/csrf_helper.rb +1 -1
- data/lib/action_view/helpers/date_helper.rb +28 -4
- data/lib/action_view/helpers/form_helper.rb +277 -272
- data/lib/action_view/helpers/form_options_helper.rb +39 -35
- data/lib/action_view/helpers/form_tag_helper.rb +115 -72
- data/lib/action_view/helpers/javascript_helper.rb +5 -1
- data/lib/action_view/helpers/number_helper.rb +14 -0
- data/lib/action_view/helpers/output_safety_helper.rb +5 -6
- data/lib/action_view/helpers/rendering_helper.rb +160 -50
- data/lib/action_view/helpers/sanitize_helper.rb +6 -0
- data/lib/action_view/helpers/tag_helper.rb +225 -48
- data/lib/action_view/helpers/tags/base.rb +11 -9
- data/lib/action_view/helpers/tags/check_box.rb +9 -3
- data/lib/action_view/helpers/tags/collection_check_boxes.rb +4 -3
- data/lib/action_view/helpers/tags/collection_helpers.rb +2 -1
- data/lib/action_view/helpers/tags/datetime_field.rb +1 -1
- data/lib/action_view/helpers/tags/file_field.rb +7 -2
- data/lib/action_view/helpers/tags/hidden_field.rb +1 -1
- data/lib/action_view/helpers/tags/label.rb +3 -10
- data/lib/action_view/helpers/tags/radio_button.rb +1 -1
- data/lib/action_view/helpers/tags/select.rb +6 -1
- data/lib/action_view/helpers/tags/select_renderer.rb +6 -4
- data/lib/action_view/helpers/tags/text_area.rb +1 -1
- data/lib/action_view/helpers/tags/text_field.rb +1 -1
- data/lib/action_view/helpers/text_helper.rb +11 -4
- data/lib/action_view/helpers/translation_helper.rb +6 -1
- data/lib/action_view/helpers/url_helper.rb +42 -90
- data/lib/action_view/layouts.rb +9 -11
- data/lib/action_view/locale/en.yml +3 -0
- data/lib/action_view/log_subscriber.rb +9 -8
- data/lib/action_view/railtie.rb +12 -2
- data/lib/action_view/record_identifier.rb +22 -1
- data/lib/action_view/render_parser/prism_render_parser.rb +139 -0
- data/lib/action_view/{ripper_ast_parser.rb → render_parser/ripper_render_parser.rb} +162 -10
- data/lib/action_view/render_parser.rb +21 -169
- data/lib/action_view/renderer/abstract_renderer.rb +1 -1
- data/lib/action_view/renderer/partial_renderer.rb +18 -2
- data/lib/action_view/renderer/renderer.rb +32 -38
- data/lib/action_view/renderer/streaming_template_renderer.rb +8 -2
- data/lib/action_view/renderer/template_renderer.rb +3 -3
- data/lib/action_view/rendering.rb +6 -7
- data/lib/action_view/structured_event_subscriber.rb +97 -0
- data/lib/action_view/template/error.rb +18 -3
- data/lib/action_view/template/handlers/erb/erubi.rb +1 -1
- data/lib/action_view/template/handlers/erb.rb +77 -44
- data/lib/action_view/template/raw_file.rb +4 -0
- data/lib/action_view/template/renderable.rb +7 -1
- data/lib/action_view/template/resolver.rb +0 -3
- data/lib/action_view/template.rb +39 -12
- data/lib/action_view/test_case.rb +57 -62
- data/lib/action_view.rb +4 -0
- metadata +17 -14
- data/lib/action_view/dependency_tracker/ripper_tracker.rb +0 -59
|
@@ -166,7 +166,7 @@ module ActionView
|
|
|
166
166
|
# highlight('You searched for: rails', 'rails', highlighter: '<a href="search?q=\1">\1</a>')
|
|
167
167
|
# # => "You searched for: <a href=\"search?q=rails\">rails</a>"
|
|
168
168
|
#
|
|
169
|
-
# highlight('You searched for: rails', 'rails') { |match| link_to(search_path(q: match
|
|
169
|
+
# highlight('You searched for: rails', 'rails') { |match| link_to(search_path(q: match)) }
|
|
170
170
|
# # => "You searched for: <a href=\"search?q=rails\">rails</a>"
|
|
171
171
|
#
|
|
172
172
|
# highlight('<a href="javascript:alert(\'no!\')">ruby</a> on rails', 'rails', sanitize: false)
|
|
@@ -260,7 +260,14 @@ module ActionView
|
|
|
260
260
|
prefix, first_part = cut_excerpt_part(:first, first_part, separator, options)
|
|
261
261
|
postfix, second_part = cut_excerpt_part(:second, second_part, separator, options)
|
|
262
262
|
|
|
263
|
-
affix = [
|
|
263
|
+
affix = [
|
|
264
|
+
first_part,
|
|
265
|
+
!first_part.empty? ? separator : "",
|
|
266
|
+
phrase,
|
|
267
|
+
!second_part.empty? ? separator : "",
|
|
268
|
+
second_part
|
|
269
|
+
].join.strip
|
|
270
|
+
|
|
264
271
|
[prefix, affix, postfix].join
|
|
265
272
|
end
|
|
266
273
|
|
|
@@ -271,7 +278,7 @@ module ActionView
|
|
|
271
278
|
#
|
|
272
279
|
# The word will be pluralized using rules defined for the locale
|
|
273
280
|
# (you must define your own inflection rules for languages other than English).
|
|
274
|
-
# See ActiveSupport::Inflector.pluralize
|
|
281
|
+
# See ActiveSupport::Inflector.pluralize.
|
|
275
282
|
#
|
|
276
283
|
# pluralize(1, 'person')
|
|
277
284
|
# # => "1 person"
|
|
@@ -346,7 +353,7 @@ module ActionView
|
|
|
346
353
|
# ==== Options
|
|
347
354
|
# * <tt>:sanitize</tt> - If +false+, does not sanitize +text+.
|
|
348
355
|
# * <tt>:sanitize_options</tt> - Any extra options you want appended to the sanitize.
|
|
349
|
-
# * <tt>:wrapper_tag</tt> - String representing the wrapper tag, defaults to <tt>"p"</tt
|
|
356
|
+
# * <tt>:wrapper_tag</tt> - String representing the wrapper tag, defaults to <tt>"p"</tt>.
|
|
350
357
|
#
|
|
351
358
|
# ==== Examples
|
|
352
359
|
# my_text = "Here is some basic text...\n...with a line break."
|
|
@@ -140,7 +140,12 @@ module ActionView
|
|
|
140
140
|
end
|
|
141
141
|
|
|
142
142
|
def missing_translation(key, options)
|
|
143
|
-
|
|
143
|
+
locale = options[:locale] || I18n.locale
|
|
144
|
+
|
|
145
|
+
i18n_exception = I18n::MissingTranslation.new(locale, key, options)
|
|
146
|
+
I18n.exception_handler.call(i18n_exception, locale, key, options)
|
|
147
|
+
|
|
148
|
+
keys = I18n.normalize_keys(locale, key, options[:scope])
|
|
144
149
|
|
|
145
150
|
title = +"translation missing: #{keys.join(".")}"
|
|
146
151
|
|
|
@@ -195,42 +195,6 @@ module ActionView
|
|
|
195
195
|
# link_to "Visit Other Site", "https://rubyonrails.org/", data: { turbo_confirm: "Are you sure?" }
|
|
196
196
|
# # => <a href="https://rubyonrails.org/" data-turbo-confirm="Are you sure?">Visit Other Site</a>
|
|
197
197
|
#
|
|
198
|
-
# ==== Deprecated: \Rails UJS Attributes
|
|
199
|
-
#
|
|
200
|
-
# Prior to \Rails 7, \Rails shipped with a JavaScript library called <tt>@rails/ujs</tt> on by default. Following \Rails 7,
|
|
201
|
-
# this library is no longer on by default. This library integrated with the following options:
|
|
202
|
-
#
|
|
203
|
-
# * <tt>method: symbol of HTTP verb</tt> - This modifier will dynamically
|
|
204
|
-
# create an HTML form and immediately submit the form for processing using
|
|
205
|
-
# the HTTP verb specified. Useful for having links perform a POST operation
|
|
206
|
-
# in dangerous actions like deleting a record (which search bots can follow
|
|
207
|
-
# while spidering your site). Supported verbs are <tt>:post</tt>, <tt>:delete</tt>, <tt>:patch</tt>, and <tt>:put</tt>.
|
|
208
|
-
# Note that if the user has JavaScript disabled, the request will fall back
|
|
209
|
-
# to using GET. If <tt>href: '#'</tt> is used and the user has JavaScript
|
|
210
|
-
# disabled clicking the link will have no effect. If you are relying on the
|
|
211
|
-
# POST behavior, you should check for it in your controller's action by using
|
|
212
|
-
# the request object's methods for <tt>post?</tt>, <tt>delete?</tt>, <tt>patch?</tt>, or <tt>put?</tt>.
|
|
213
|
-
# * <tt>remote: true</tt> - This will allow <tt>@rails/ujs</tt>
|
|
214
|
-
# to make an Ajax request to the URL in question instead of following
|
|
215
|
-
# the link.
|
|
216
|
-
#
|
|
217
|
-
# <tt>@rails/ujs</tt> also integrated with the following +:data+ options:
|
|
218
|
-
#
|
|
219
|
-
# * <tt>confirm: "question?"</tt> - This will allow <tt>@rails/ujs</tt>
|
|
220
|
-
# to prompt with the question specified (in this case, the
|
|
221
|
-
# resulting text would be <tt>question?</tt>). If the user accepts, the
|
|
222
|
-
# link is processed normally, otherwise no action is taken.
|
|
223
|
-
# * <tt>:disable_with</tt> - Value of this parameter will be used as the
|
|
224
|
-
# name for a disabled version of the link.
|
|
225
|
-
#
|
|
226
|
-
# ===== \Rails UJS Examples
|
|
227
|
-
#
|
|
228
|
-
# link_to "Remove Profile", profile_path(@profile), method: :delete
|
|
229
|
-
# # => <a href="/profiles/1" rel="nofollow" data-method="delete">Remove Profile</a>
|
|
230
|
-
#
|
|
231
|
-
# link_to "Visit Other Site", "http://www.rubyonrails.org/", data: { confirm: "Are you sure?" }
|
|
232
|
-
# # => <a href="http://www.rubyonrails.org/" data-confirm="Are you sure?">Visit Other Site</a>
|
|
233
|
-
#
|
|
234
198
|
def link_to(name = nil, options = nil, html_options = nil, &block)
|
|
235
199
|
html_options, options, name = options, name, block if block_given?
|
|
236
200
|
options ||= {}
|
|
@@ -256,8 +220,9 @@ module ActionView
|
|
|
256
220
|
# +:form_class+ option within +html_options+. It defaults to
|
|
257
221
|
# <tt>"button_to"</tt> to allow styling of the form and its children.
|
|
258
222
|
#
|
|
259
|
-
# The form submits a POST request by default
|
|
260
|
-
#
|
|
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+.
|
|
261
226
|
#
|
|
262
227
|
# If the HTML button generated from +button_to+ does not work with your layout, you can
|
|
263
228
|
# consider using the +link_to+ method with the +data-turbo-method+
|
|
@@ -328,32 +293,6 @@ module ActionView
|
|
|
328
293
|
# # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6" autocomplete="off"/>
|
|
329
294
|
# # </form>"
|
|
330
295
|
#
|
|
331
|
-
# ==== Deprecated: \Rails UJS Attributes
|
|
332
|
-
#
|
|
333
|
-
# Prior to \Rails 7, \Rails shipped with a JavaScript library called <tt>@rails/ujs</tt> on by default. Following \Rails 7,
|
|
334
|
-
# this library is no longer on by default. This library integrated with the following options:
|
|
335
|
-
#
|
|
336
|
-
# * <tt>:remote</tt> - If set to true, will allow <tt>@rails/ujs</tt> to control the
|
|
337
|
-
# submit behavior. By default this behavior is an Ajax submit.
|
|
338
|
-
#
|
|
339
|
-
# <tt>@rails/ujs</tt> also integrated with the following +:data+ options:
|
|
340
|
-
#
|
|
341
|
-
# * <tt>confirm: "question?"</tt> - This will allow <tt>@rails/ujs</tt>
|
|
342
|
-
# to prompt with the question specified (in this case, the
|
|
343
|
-
# resulting text would be <tt>question?</tt>). If the user accepts, the
|
|
344
|
-
# button is processed normally, otherwise no action is taken.
|
|
345
|
-
# * <tt>:disable_with</tt> - Value of this parameter will be
|
|
346
|
-
# used as the value for a disabled version of the submit
|
|
347
|
-
# button when the form is submitted.
|
|
348
|
-
#
|
|
349
|
-
# ===== \Rails UJS Examples
|
|
350
|
-
#
|
|
351
|
-
# <%= button_to "Create", { action: "create" }, remote: true, form: { "data-type" => "json" } %>
|
|
352
|
-
# # => "<form method="post" action="/images/create" class="button_to" data-remote="true" data-type="json">
|
|
353
|
-
# # <button type="submit">Create</button>
|
|
354
|
-
# # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6" autocomplete="off"/>
|
|
355
|
-
# # </form>"
|
|
356
|
-
#
|
|
357
296
|
def button_to(name = nil, options = nil, html_options = nil, &block)
|
|
358
297
|
html_options, options = options, name if block_given?
|
|
359
298
|
html_options ||= {}
|
|
@@ -402,8 +341,9 @@ module ActionView
|
|
|
402
341
|
inner_tags = method_tag.safe_concat(button).safe_concat(request_token_tag)
|
|
403
342
|
if params
|
|
404
343
|
to_form_params(params).each do |param|
|
|
405
|
-
|
|
406
|
-
|
|
344
|
+
options = { type: "hidden", name: param[:name], value: param[:value] }
|
|
345
|
+
options[:autocomplete] = "off" unless ActionView::Base.remove_hidden_field_autocomplete
|
|
346
|
+
inner_tags.safe_concat tag(:input, **options)
|
|
407
347
|
end
|
|
408
348
|
end
|
|
409
349
|
html = content_tag("form", inner_tags, form_options)
|
|
@@ -561,8 +501,6 @@ module ActionView
|
|
|
561
501
|
content_tag("a", name || email_address, html_options, &block)
|
|
562
502
|
end
|
|
563
503
|
|
|
564
|
-
RFC2396_PARSER = defined?(URI::RFC2396_PARSER) ? URI::RFC2396_PARSER : URI::RFC2396_Parser.new
|
|
565
|
-
|
|
566
504
|
# True if the current request URI was generated by the given +options+.
|
|
567
505
|
#
|
|
568
506
|
# ==== Examples
|
|
@@ -601,32 +539,55 @@ module ActionView
|
|
|
601
539
|
# current_page?('http://www.example.com/shop/checkout?order=desc&page=1')
|
|
602
540
|
# # => true
|
|
603
541
|
#
|
|
604
|
-
#
|
|
542
|
+
# Different actions may share the same URL path but have a different HTTP method. Let's say we
|
|
543
|
+
# sent a POST to <tt>http://www.example.com/products</tt> and rendered a validation error.
|
|
605
544
|
#
|
|
606
545
|
# current_page?(controller: 'product', action: 'index')
|
|
607
546
|
# # => false
|
|
608
547
|
#
|
|
548
|
+
# current_page?(controller: 'product', action: 'create')
|
|
549
|
+
# # => false
|
|
550
|
+
#
|
|
551
|
+
# current_page?(controller: 'product', action: 'create', method: :post)
|
|
552
|
+
# # => true
|
|
553
|
+
#
|
|
554
|
+
# current_page?(controller: 'product', action: 'index', method: [:get, :post])
|
|
555
|
+
# # => true
|
|
556
|
+
#
|
|
609
557
|
# We can also pass in the symbol arguments instead of strings.
|
|
610
558
|
#
|
|
611
|
-
def current_page?(options = nil, check_parameters: false, **options_as_kwargs)
|
|
559
|
+
def current_page?(options = nil, check_parameters: false, method: :get, **options_as_kwargs)
|
|
612
560
|
unless request
|
|
613
561
|
raise "You cannot use helpers that need to determine the current " \
|
|
614
562
|
"page unless your view context provides a Request object " \
|
|
615
563
|
"in a #request method"
|
|
616
564
|
end
|
|
617
565
|
|
|
618
|
-
|
|
566
|
+
if options.is_a?(Hash)
|
|
567
|
+
check_parameters = options.delete(:check_parameters) { check_parameters }
|
|
568
|
+
method = options.delete(:method) { method }
|
|
569
|
+
else
|
|
570
|
+
options ||= options_as_kwargs
|
|
571
|
+
end
|
|
572
|
+
|
|
573
|
+
method_matches = case method
|
|
574
|
+
when :get
|
|
575
|
+
request.get? || request.head?
|
|
576
|
+
when Array
|
|
577
|
+
method.include?(request.method_symbol) || (method.include?(:get) && request.head?)
|
|
578
|
+
else
|
|
579
|
+
method == request.method_symbol
|
|
580
|
+
end
|
|
581
|
+
return false unless method_matches
|
|
619
582
|
|
|
620
|
-
|
|
621
|
-
check_parameters ||= options.is_a?(Hash) && options.delete(:check_parameters)
|
|
622
|
-
url_string = RFC2396_PARSER.unescape(url_for(options)).force_encoding(Encoding::BINARY)
|
|
583
|
+
url_string = URI::RFC2396_PARSER.unescape(url_for(options)).force_encoding(Encoding::BINARY)
|
|
623
584
|
|
|
624
585
|
# We ignore any extra parameters in the request_uri if the
|
|
625
586
|
# submitted URL doesn't have any either. This lets the function
|
|
626
587
|
# work with things like ?order=asc
|
|
627
588
|
# the behavior can be disabled with check_parameters: true
|
|
628
589
|
request_uri = url_string.index("?") || check_parameters ? request.fullpath : request.path
|
|
629
|
-
request_uri = RFC2396_PARSER.unescape(request_uri).force_encoding(Encoding::BINARY)
|
|
590
|
+
request_uri = URI::RFC2396_PARSER.unescape(request_uri).force_encoding(Encoding::BINARY)
|
|
630
591
|
|
|
631
592
|
if %r{^\w+://}.match?(url_string)
|
|
632
593
|
request_uri = +"#{request.protocol}#{request.host_with_port}#{request_uri}"
|
|
@@ -638,19 +599,6 @@ module ActionView
|
|
|
638
599
|
url_string == request_uri
|
|
639
600
|
end
|
|
640
601
|
|
|
641
|
-
if RUBY_VERSION.start_with?("2.7")
|
|
642
|
-
using Module.new {
|
|
643
|
-
refine UrlHelper do
|
|
644
|
-
alias :_current_page? :current_page?
|
|
645
|
-
end
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
def current_page?(*args) # :nodoc:
|
|
649
|
-
options = args.pop
|
|
650
|
-
options.is_a?(Hash) ? _current_page?(*args, **options) : _current_page?(*args, options)
|
|
651
|
-
end
|
|
652
|
-
end
|
|
653
|
-
|
|
654
602
|
# Creates an SMS anchor link tag to the specified +phone_number+. When the
|
|
655
603
|
# link is clicked, the default SMS messaging app is opened ready to send a
|
|
656
604
|
# message to the linked phone number. If the +body+ option is specified,
|
|
@@ -827,14 +775,18 @@ module ActionView
|
|
|
827
775
|
else
|
|
828
776
|
token
|
|
829
777
|
end
|
|
830
|
-
|
|
778
|
+
options = { type: "hidden", name: request_forgery_protection_token.to_s, value: token }
|
|
779
|
+
options[:autocomplete] = "off" unless ActionView::Base.remove_hidden_field_autocomplete
|
|
780
|
+
tag(:input, **options)
|
|
831
781
|
else
|
|
832
782
|
""
|
|
833
783
|
end
|
|
834
784
|
end
|
|
835
785
|
|
|
836
786
|
def method_tag(method)
|
|
837
|
-
|
|
787
|
+
options = { type: "hidden", name: "_method", value: method.to_s }
|
|
788
|
+
options[:autocomplete] = "off" unless ActionView::Base.remove_hidden_field_autocomplete
|
|
789
|
+
tag("input", **options)
|
|
838
790
|
end
|
|
839
791
|
|
|
840
792
|
# Returns an array of hashes each containing :name and :value keys
|
data/lib/action_view/layouts.rb
CHANGED
|
@@ -209,11 +209,9 @@ module ActionView
|
|
|
209
209
|
|
|
210
210
|
included do
|
|
211
211
|
class_attribute :_layout, instance_accessor: false
|
|
212
|
-
class_attribute :_layout_conditions, instance_accessor: false, default: {}
|
|
212
|
+
class_attribute :_layout_conditions, instance_accessor: false, instance_reader: true, default: {}
|
|
213
213
|
|
|
214
214
|
_write_layout_method
|
|
215
|
-
|
|
216
|
-
delegate :_layout_conditions, to: :class
|
|
217
215
|
end
|
|
218
216
|
|
|
219
217
|
module ClassMethods
|
|
@@ -286,7 +284,7 @@ module ActionView
|
|
|
286
284
|
silence_redefinition_of_method(:_layout)
|
|
287
285
|
|
|
288
286
|
prefixes = /\blayouts/.match?(_implied_layout_name) ? [] : ["layouts"]
|
|
289
|
-
default_behavior = "lookup_context.find_all('#{_implied_layout_name}', #{prefixes.inspect}, false,
|
|
287
|
+
default_behavior = "lookup_context.find_all('#{_implied_layout_name}', #{prefixes.inspect}, false, keys, { formats: formats }).first || super"
|
|
290
288
|
name_clause = if name
|
|
291
289
|
default_behavior
|
|
292
290
|
else
|
|
@@ -327,7 +325,7 @@ module ActionView
|
|
|
327
325
|
|
|
328
326
|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
329
327
|
# frozen_string_literal: true
|
|
330
|
-
def _layout(lookup_context, formats)
|
|
328
|
+
def _layout(lookup_context, formats, keys)
|
|
331
329
|
if _conditional_layout?
|
|
332
330
|
#{layout_definition}
|
|
333
331
|
else
|
|
@@ -349,7 +347,7 @@ module ActionView
|
|
|
349
347
|
end
|
|
350
348
|
end
|
|
351
349
|
|
|
352
|
-
def
|
|
350
|
+
def _process_render_template_options(options) # :nodoc:
|
|
353
351
|
super
|
|
354
352
|
|
|
355
353
|
if _include_layout?(options)
|
|
@@ -391,8 +389,8 @@ module ActionView
|
|
|
391
389
|
case name
|
|
392
390
|
when String then _normalize_layout(name)
|
|
393
391
|
when Proc then name
|
|
394
|
-
when true then Proc.new { |lookup_context, formats| _default_layout(lookup_context, formats, true) }
|
|
395
|
-
when :default then Proc.new { |lookup_context, formats| _default_layout(lookup_context, formats, false) }
|
|
392
|
+
when true then Proc.new { |lookup_context, formats, keys| _default_layout(lookup_context, formats, keys, true) }
|
|
393
|
+
when :default then Proc.new { |lookup_context, formats, keys| _default_layout(lookup_context, formats, keys, false) }
|
|
396
394
|
when false, nil then nil
|
|
397
395
|
else
|
|
398
396
|
raise ArgumentError,
|
|
@@ -414,9 +412,9 @@ module ActionView
|
|
|
414
412
|
#
|
|
415
413
|
# ==== Returns
|
|
416
414
|
# * <tt>template</tt> - The template object for the default layout (or +nil+)
|
|
417
|
-
def _default_layout(lookup_context, formats, require_layout = false)
|
|
415
|
+
def _default_layout(lookup_context, formats, keys, require_layout = false)
|
|
418
416
|
begin
|
|
419
|
-
value = _layout(lookup_context, formats) if action_has_layout?
|
|
417
|
+
value = _layout(lookup_context, formats, keys) if action_has_layout?
|
|
420
418
|
rescue NameError => e
|
|
421
419
|
raise e, "Could not render layout: #{e.message}"
|
|
422
420
|
end
|
|
@@ -430,7 +428,7 @@ module ActionView
|
|
|
430
428
|
end
|
|
431
429
|
|
|
432
430
|
def _include_layout?(options)
|
|
433
|
-
|
|
431
|
+
!options.keys.intersect?([:body, :plain, :html, :inline, :partial]) || options.key?(:layout)
|
|
434
432
|
end
|
|
435
433
|
end
|
|
436
434
|
end
|
|
@@ -3,10 +3,7 @@
|
|
|
3
3
|
require "active_support/log_subscriber"
|
|
4
4
|
|
|
5
5
|
module ActionView
|
|
6
|
-
|
|
7
|
-
#
|
|
8
|
-
# Provides functionality so that \Rails can output logs from Action View.
|
|
9
|
-
class LogSubscriber < ActiveSupport::LogSubscriber
|
|
6
|
+
class LogSubscriber < ActiveSupport::LogSubscriber # :nodoc:
|
|
10
7
|
VIEWS_PATTERN = /^app\/views\//
|
|
11
8
|
|
|
12
9
|
def initialize
|
|
@@ -18,7 +15,7 @@ module ActionView
|
|
|
18
15
|
info do
|
|
19
16
|
message = +" Rendered #{from_rails_root(event.payload[:identifier])}"
|
|
20
17
|
message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout]
|
|
21
|
-
message << " (Duration: #{event.duration.round(1)}ms |
|
|
18
|
+
message << " (Duration: #{event.duration.round(1)}ms | GC: #{event.gc_time.round(1)}ms)"
|
|
22
19
|
end
|
|
23
20
|
end
|
|
24
21
|
subscribe_log_level :render_template, :debug
|
|
@@ -27,7 +24,7 @@ module ActionView
|
|
|
27
24
|
debug do
|
|
28
25
|
message = +" Rendered #{from_rails_root(event.payload[:identifier])}"
|
|
29
26
|
message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout]
|
|
30
|
-
message << " (Duration: #{event.duration.round(1)}ms |
|
|
27
|
+
message << " (Duration: #{event.duration.round(1)}ms | GC: #{event.gc_time.round(1)}ms)"
|
|
31
28
|
message << " #{cache_message(event.payload)}" unless event.payload[:cache_hit].nil?
|
|
32
29
|
message
|
|
33
30
|
end
|
|
@@ -37,7 +34,7 @@ module ActionView
|
|
|
37
34
|
def render_layout(event)
|
|
38
35
|
info do
|
|
39
36
|
message = +" Rendered layout #{from_rails_root(event.payload[:identifier])}"
|
|
40
|
-
message << " (Duration: #{event.duration.round(1)}ms |
|
|
37
|
+
message << " (Duration: #{event.duration.round(1)}ms | GC: #{event.gc_time.round(1)}ms)"
|
|
41
38
|
end
|
|
42
39
|
end
|
|
43
40
|
subscribe_log_level :render_layout, :info
|
|
@@ -48,7 +45,7 @@ module ActionView
|
|
|
48
45
|
debug do
|
|
49
46
|
message = +" Rendered collection of #{from_rails_root(identifier)}"
|
|
50
47
|
message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout]
|
|
51
|
-
message << " #{render_count(event.payload)} (Duration: #{event.duration.round(1)}ms |
|
|
48
|
+
message << " #{render_count(event.payload)} (Duration: #{event.duration.round(1)}ms | GC: #{event.gc_time.round(1)}ms)"
|
|
52
49
|
message
|
|
53
50
|
end
|
|
54
51
|
end
|
|
@@ -96,6 +93,10 @@ module ActionView
|
|
|
96
93
|
|
|
97
94
|
def finish(name, id, payload)
|
|
98
95
|
end
|
|
96
|
+
|
|
97
|
+
def silenced?(_)
|
|
98
|
+
logger.nil? || !logger.debug?
|
|
99
|
+
end
|
|
99
100
|
end
|
|
100
101
|
|
|
101
102
|
def self.attach_to(*)
|
data/lib/action_view/railtie.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "action_view"
|
|
4
3
|
require "rails"
|
|
4
|
+
require "action_view"
|
|
5
5
|
|
|
6
6
|
module ActionView
|
|
7
7
|
# = Action View Railtie
|
|
@@ -72,8 +72,19 @@ module ActionView
|
|
|
72
72
|
end
|
|
73
73
|
|
|
74
74
|
config.after_initialize do |app|
|
|
75
|
+
ActionView::Helpers::AssetTagHelper.auto_include_nonce_for_scripts = app.config.content_security_policy_nonce_auto && app.config.content_security_policy_nonce_directives.intersect?(["script-src", "script-src-elem", "script-src-attr"]) && app.config.content_security_policy_nonce_generator.present?
|
|
76
|
+
ActionView::Helpers::AssetTagHelper.auto_include_nonce_for_styles = app.config.content_security_policy_nonce_auto && app.config.content_security_policy_nonce_directives.intersect?(["style-src", "style-src-elem", "style-src-attr"]) && app.config.content_security_policy_nonce_generator.present?
|
|
77
|
+
ActionView::Helpers::JavaScriptHelper.auto_include_nonce = app.config.content_security_policy_nonce_auto && app.config.content_security_policy_nonce_directives.intersect?(["script-src", "script-src-elem", "script-src-attr"]) && app.config.content_security_policy_nonce_generator.present?
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
config.after_initialize do |app|
|
|
81
|
+
config.after_initialize do
|
|
82
|
+
ActionView.render_tracker = config.action_view.render_tracker
|
|
83
|
+
end
|
|
84
|
+
|
|
75
85
|
ActiveSupport.on_load(:action_view) do
|
|
76
86
|
app.config.action_view.each do |k, v|
|
|
87
|
+
next if k == :render_tracker
|
|
77
88
|
send "#{k}=", v
|
|
78
89
|
end
|
|
79
90
|
end
|
|
@@ -116,7 +127,6 @@ module ActionView
|
|
|
116
127
|
view_reloader = ActionView::CacheExpiry::ViewReloader.new(watcher: app.config.file_watcher)
|
|
117
128
|
|
|
118
129
|
app.reloaders << view_reloader
|
|
119
|
-
view_reloader.execute
|
|
120
130
|
app.reloader.to_run do
|
|
121
131
|
require_unload_lock!
|
|
122
132
|
view_reloader.execute
|
|
@@ -11,7 +11,7 @@ module ActionView
|
|
|
11
11
|
#
|
|
12
12
|
# Consider for example the following code that form of post:
|
|
13
13
|
#
|
|
14
|
-
# <%=
|
|
14
|
+
# <%= form_with(model: post) do |f| %>
|
|
15
15
|
# <%= f.text_field :body %>
|
|
16
16
|
# <% end %>
|
|
17
17
|
#
|
|
@@ -101,6 +101,27 @@ module ActionView
|
|
|
101
101
|
end
|
|
102
102
|
end
|
|
103
103
|
|
|
104
|
+
# The DOM target convention is to concatenate any number of parameters into a string.
|
|
105
|
+
# Records are passed through dom_id, while string and symbols are retained.
|
|
106
|
+
#
|
|
107
|
+
# dom_target(Post.find(45)) # => "post_45"
|
|
108
|
+
# dom_target(Post.find(45), :edit) # => "post_45_edit"
|
|
109
|
+
# dom_target(Post.find(45), :edit, :special) # => "post_45_edit_special"
|
|
110
|
+
# dom_target(Post.find(45), Comment.find(1)) # => "post_45_comment_1"
|
|
111
|
+
def dom_target(*objects)
|
|
112
|
+
objects.map! do |object|
|
|
113
|
+
case object
|
|
114
|
+
when Symbol, String
|
|
115
|
+
object
|
|
116
|
+
when Class
|
|
117
|
+
dom_class(object)
|
|
118
|
+
else
|
|
119
|
+
dom_id(object)
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
objects.join(JOIN)
|
|
123
|
+
end
|
|
124
|
+
|
|
104
125
|
private
|
|
105
126
|
# Returns a string representation of the key attribute(s) that is suitable for use in an HTML DOM id.
|
|
106
127
|
# This can be overwritten to customize the default generated string representation if desired.
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActionView
|
|
4
|
+
module RenderParser
|
|
5
|
+
class PrismRenderParser < Base # :nodoc:
|
|
6
|
+
def render_calls
|
|
7
|
+
queue = [Prism.parse(@code).value]
|
|
8
|
+
templates = []
|
|
9
|
+
|
|
10
|
+
while (node = queue.shift)
|
|
11
|
+
queue.concat(node.compact_child_nodes)
|
|
12
|
+
next unless node.is_a?(Prism::CallNode)
|
|
13
|
+
|
|
14
|
+
options = render_call_options(node)
|
|
15
|
+
next unless options
|
|
16
|
+
|
|
17
|
+
render_type = (options.keys & RENDER_TYPE_KEYS)[0]
|
|
18
|
+
template, object_template = render_call_template(options[render_type])
|
|
19
|
+
next unless template
|
|
20
|
+
|
|
21
|
+
if options.key?(:object) || options.key?(:collection) || object_template
|
|
22
|
+
next if options.key?(:object) && options.key?(:collection)
|
|
23
|
+
next unless options.key?(:partial)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
if options[:spacer_template].is_a?(Prism::StringNode)
|
|
27
|
+
templates << partial_to_virtual_path(:partial, options[:spacer_template].unescaped)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
templates << partial_to_virtual_path(render_type, template)
|
|
31
|
+
|
|
32
|
+
if render_type != :layout && options[:layout].is_a?(Prism::StringNode)
|
|
33
|
+
templates << partial_to_virtual_path(:layout, options[:layout].unescaped)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
templates
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
# Accept a call node and return a hash of options for the render call.
|
|
42
|
+
# If it doesn't match the expected format, return nil.
|
|
43
|
+
def render_call_options(node)
|
|
44
|
+
# We are only looking for calls to render or render_to_string.
|
|
45
|
+
name = node.name.to_sym
|
|
46
|
+
return if name != :render && name != :render_to_string
|
|
47
|
+
|
|
48
|
+
# We are only looking for calls with arguments.
|
|
49
|
+
arguments = node.arguments
|
|
50
|
+
return unless arguments
|
|
51
|
+
|
|
52
|
+
arguments = arguments.arguments
|
|
53
|
+
length = arguments.length
|
|
54
|
+
|
|
55
|
+
# Get rid of any parentheses to get directly to the contents.
|
|
56
|
+
arguments.map! do |argument|
|
|
57
|
+
current = argument
|
|
58
|
+
|
|
59
|
+
while current.is_a?(Prism::ParenthesesNode) &&
|
|
60
|
+
current.body.is_a?(Prism::StatementsNode) &&
|
|
61
|
+
current.body.body.length == 1
|
|
62
|
+
current = current.body.body.first
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
current
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# We are only looking for arguments that are either a string with an
|
|
69
|
+
# array of locals or a keyword hash with symbol keys.
|
|
70
|
+
options =
|
|
71
|
+
if (length == 1 || length == 2) && !arguments[0].is_a?(Prism::KeywordHashNode)
|
|
72
|
+
{ partial: arguments[0], locals: arguments[1] }
|
|
73
|
+
elsif length == 1 &&
|
|
74
|
+
arguments[0].is_a?(Prism::KeywordHashNode) &&
|
|
75
|
+
arguments[0].elements.all? do |element|
|
|
76
|
+
element.is_a?(Prism::AssocNode) && element.key.is_a?(Prism::SymbolNode)
|
|
77
|
+
end
|
|
78
|
+
arguments[0].elements.to_h do |element|
|
|
79
|
+
[element.key.unescaped.to_sym, element.value]
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
return unless options
|
|
84
|
+
|
|
85
|
+
# Here we validate that the options have the keys we expect.
|
|
86
|
+
keys = options.keys
|
|
87
|
+
return if !keys.intersect?(RENDER_TYPE_KEYS)
|
|
88
|
+
return if (keys - ALL_KNOWN_KEYS).any?
|
|
89
|
+
|
|
90
|
+
# Finally, we can return a valid set of options.
|
|
91
|
+
options
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Accept the node that is being passed in the position of the template
|
|
95
|
+
# and return the template name and whether or not it is an object
|
|
96
|
+
# template.
|
|
97
|
+
def render_call_template(node)
|
|
98
|
+
object_template = false
|
|
99
|
+
template =
|
|
100
|
+
case node.type
|
|
101
|
+
when :string_node
|
|
102
|
+
path = node.unescaped
|
|
103
|
+
path.include?("/") ? path : "#{directory}/#{path}"
|
|
104
|
+
when :interpolated_string_node
|
|
105
|
+
node.parts.map do |node|
|
|
106
|
+
case node.type
|
|
107
|
+
when :string_node
|
|
108
|
+
node.unescaped
|
|
109
|
+
when :embedded_statements_node
|
|
110
|
+
"*"
|
|
111
|
+
else
|
|
112
|
+
return
|
|
113
|
+
end
|
|
114
|
+
end.join("")
|
|
115
|
+
else
|
|
116
|
+
dependency =
|
|
117
|
+
case node.type
|
|
118
|
+
when :class_variable_read_node
|
|
119
|
+
node.slice[2..]
|
|
120
|
+
when :instance_variable_read_node
|
|
121
|
+
node.slice[1..]
|
|
122
|
+
when :global_variable_read_node
|
|
123
|
+
node.slice[1..]
|
|
124
|
+
when :local_variable_read_node
|
|
125
|
+
node.slice
|
|
126
|
+
when :call_node
|
|
127
|
+
node.name.to_s
|
|
128
|
+
else
|
|
129
|
+
return
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
"#{dependency.pluralize}/#{dependency.singularize}"
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
[template, object_template]
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|