actionview 4.1.13 → 6.1.3.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionview might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/CHANGELOG.md +181 -359
- data/MIT-LICENSE +1 -1
- data/README.rdoc +12 -6
- data/lib/action_view/base.rb +115 -43
- data/lib/action_view/buffers.rb +22 -4
- data/lib/action_view/cache_expiry.rb +52 -0
- data/lib/action_view/context.rb +8 -12
- data/lib/action_view/dependency_tracker.rb +61 -21
- data/lib/action_view/digestor.rb +89 -84
- data/lib/action_view/flows.rb +12 -13
- data/lib/action_view/gem_version.rb +6 -4
- data/lib/action_view/helpers/active_model_helper.rb +16 -11
- data/lib/action_view/helpers/asset_tag_helper.rb +311 -105
- data/lib/action_view/helpers/asset_url_helper.rb +197 -80
- data/lib/action_view/helpers/atom_feed_helper.rb +20 -17
- data/lib/action_view/helpers/cache_helper.rb +109 -45
- data/lib/action_view/helpers/capture_helper.rb +20 -22
- data/lib/action_view/helpers/controller_helper.rb +15 -4
- data/lib/action_view/helpers/csp_helper.rb +26 -0
- data/lib/action_view/helpers/csrf_helper.rb +8 -6
- data/lib/action_view/helpers/date_helper.rb +245 -140
- data/lib/action_view/helpers/debug_helper.rb +14 -17
- data/lib/action_view/helpers/form_helper.rb +875 -148
- data/lib/action_view/helpers/form_options_helper.rb +128 -82
- data/lib/action_view/helpers/form_tag_helper.rb +253 -91
- data/lib/action_view/helpers/javascript_helper.rb +37 -15
- data/lib/action_view/helpers/number_helper.rb +100 -77
- data/lib/action_view/helpers/output_safety_helper.rb +42 -10
- data/lib/action_view/helpers/rendering_helper.rb +26 -15
- data/lib/action_view/helpers/sanitize_helper.rb +79 -164
- data/lib/action_view/helpers/tag_helper.rb +277 -64
- data/lib/action_view/helpers/tags/base.rb +143 -92
- data/lib/action_view/helpers/tags/check_box.rb +20 -19
- data/lib/action_view/helpers/tags/checkable.rb +4 -2
- data/lib/action_view/helpers/tags/collection_check_boxes.rb +12 -30
- data/lib/action_view/helpers/tags/collection_helpers.rb +69 -36
- data/lib/action_view/helpers/tags/collection_radio_buttons.rb +6 -12
- data/lib/action_view/helpers/tags/collection_select.rb +4 -2
- data/lib/action_view/helpers/tags/color_field.rb +4 -3
- data/lib/action_view/helpers/tags/date_field.rb +3 -2
- data/lib/action_view/helpers/tags/date_select.rb +38 -37
- data/lib/action_view/helpers/tags/datetime_field.rb +14 -5
- data/lib/action_view/helpers/tags/datetime_local_field.rb +3 -2
- data/lib/action_view/helpers/tags/datetime_select.rb +2 -0
- data/lib/action_view/helpers/tags/email_field.rb +2 -0
- data/lib/action_view/helpers/tags/file_field.rb +2 -0
- data/lib/action_view/helpers/tags/grouped_collection_select.rb +4 -2
- data/lib/action_view/helpers/tags/hidden_field.rb +2 -0
- data/lib/action_view/helpers/tags/label.rb +41 -22
- data/lib/action_view/helpers/tags/month_field.rb +3 -2
- data/lib/action_view/helpers/tags/number_field.rb +2 -0
- data/lib/action_view/helpers/tags/password_field.rb +3 -1
- data/lib/action_view/helpers/tags/placeholderable.rb +24 -0
- data/lib/action_view/helpers/tags/radio_button.rb +7 -6
- data/lib/action_view/helpers/tags/range_field.rb +2 -0
- data/lib/action_view/helpers/tags/search_field.rb +3 -0
- data/lib/action_view/helpers/tags/select.rb +11 -10
- data/lib/action_view/helpers/tags/tel_field.rb +2 -0
- data/lib/action_view/helpers/tags/text_area.rb +7 -1
- data/lib/action_view/helpers/tags/text_field.rb +11 -7
- data/lib/action_view/helpers/tags/time_field.rb +3 -2
- data/lib/action_view/helpers/tags/time_select.rb +2 -0
- data/lib/action_view/helpers/tags/time_zone_select.rb +3 -1
- data/lib/action_view/helpers/tags/translator.rb +39 -0
- data/lib/action_view/helpers/tags/url_field.rb +2 -0
- data/lib/action_view/helpers/tags/week_field.rb +3 -2
- data/lib/action_view/helpers/tags.rb +4 -1
- data/lib/action_view/helpers/text_helper.rb +80 -45
- data/lib/action_view/helpers/translation_helper.rb +148 -67
- data/lib/action_view/helpers/url_helper.rb +289 -147
- data/lib/action_view/helpers.rb +5 -3
- data/lib/action_view/layouts.rb +68 -63
- data/lib/action_view/log_subscriber.rb +80 -13
- data/lib/action_view/lookup_context.rb +137 -92
- data/lib/action_view/model_naming.rb +4 -2
- data/lib/action_view/path_set.rb +30 -16
- data/lib/action_view/railtie.rb +62 -13
- data/lib/action_view/record_identifier.rb +53 -26
- data/lib/action_view/renderer/abstract_renderer.rb +152 -13
- data/lib/action_view/renderer/collection_renderer.rb +196 -0
- data/lib/action_view/renderer/object_renderer.rb +34 -0
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +102 -0
- data/lib/action_view/renderer/partial_renderer.rb +61 -261
- data/lib/action_view/renderer/renderer.rb +67 -6
- data/lib/action_view/renderer/streaming_template_renderer.rb +58 -54
- data/lib/action_view/renderer/template_renderer.rb +83 -75
- data/lib/action_view/rendering.rb +73 -46
- data/lib/action_view/routing_url_for.rb +54 -17
- data/lib/action_view/tasks/cache_digests.rake +25 -0
- data/lib/action_view/template/error.rb +44 -29
- data/lib/action_view/template/handlers/builder.rb +12 -13
- data/lib/action_view/template/handlers/erb/erubi.rb +89 -0
- data/lib/action_view/template/handlers/erb.rb +23 -89
- data/lib/action_view/template/handlers/html.rb +11 -0
- data/lib/action_view/template/handlers/raw.rb +4 -4
- data/lib/action_view/template/handlers.rb +22 -9
- data/lib/action_view/template/html.rb +10 -11
- data/lib/action_view/template/inline.rb +22 -0
- data/lib/action_view/template/raw_file.rb +25 -0
- data/lib/action_view/template/renderable.rb +24 -0
- data/lib/action_view/template/resolver.rb +267 -181
- data/lib/action_view/template/sources/file.rb +17 -0
- data/lib/action_view/template/sources.rb +13 -0
- data/lib/action_view/template/text.rb +8 -10
- data/lib/action_view/template/types.rb +18 -18
- data/lib/action_view/template.rb +109 -99
- data/lib/action_view/test_case.rb +73 -53
- data/lib/action_view/testing/resolvers.rb +24 -33
- data/lib/action_view/unbound_template.rb +31 -0
- data/lib/action_view/version.rb +3 -1
- data/lib/action_view/view_paths.rb +74 -44
- data/lib/action_view.rb +14 -9
- data/lib/assets/compiled/rails-ujs.js +746 -0
- metadata +71 -26
- data/lib/action_view/helpers/record_tag_helper.rb +0 -108
- data/lib/action_view/tasks/dependencies.rake +0 -23
- data/lib/action_view/vendor/html-scanner/html/document.rb +0 -68
- data/lib/action_view/vendor/html-scanner/html/node.rb +0 -532
- data/lib/action_view/vendor/html-scanner/html/sanitizer.rb +0 -188
- data/lib/action_view/vendor/html-scanner/html/selector.rb +0 -830
- data/lib/action_view/vendor/html-scanner/html/tokenizer.rb +0 -107
- data/lib/action_view/vendor/html-scanner/html/version.rb +0 -11
- data/lib/action_view/vendor/html-scanner.rb +0 -20
data/lib/action_view/helpers.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/benchmarkable"
|
2
4
|
|
3
5
|
module ActionView #:nodoc:
|
4
6
|
module Helpers #:nodoc:
|
@@ -11,6 +13,7 @@ module ActionView #:nodoc:
|
|
11
13
|
autoload :CacheHelper
|
12
14
|
autoload :CaptureHelper
|
13
15
|
autoload :ControllerHelper
|
16
|
+
autoload :CspHelper
|
14
17
|
autoload :CsrfHelper
|
15
18
|
autoload :DateHelper
|
16
19
|
autoload :DebugHelper
|
@@ -20,7 +23,6 @@ module ActionView #:nodoc:
|
|
20
23
|
autoload :JavaScriptHelper, "action_view/helpers/javascript_helper"
|
21
24
|
autoload :NumberHelper
|
22
25
|
autoload :OutputSafetyHelper
|
23
|
-
autoload :RecordTagHelper
|
24
26
|
autoload :RenderingHelper
|
25
27
|
autoload :SanitizeHelper
|
26
28
|
autoload :TagHelper
|
@@ -44,6 +46,7 @@ module ActionView #:nodoc:
|
|
44
46
|
include CacheHelper
|
45
47
|
include CaptureHelper
|
46
48
|
include ControllerHelper
|
49
|
+
include CspHelper
|
47
50
|
include CsrfHelper
|
48
51
|
include DateHelper
|
49
52
|
include DebugHelper
|
@@ -53,7 +56,6 @@ module ActionView #:nodoc:
|
|
53
56
|
include JavaScriptHelper
|
54
57
|
include NumberHelper
|
55
58
|
include OutputSafetyHelper
|
56
|
-
include RecordTagHelper
|
57
59
|
include RenderingHelper
|
58
60
|
include SanitizeHelper
|
59
61
|
include TagHelper
|
data/lib/action_view/layouts.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "action_view/rendering"
|
2
|
-
require "active_support/core_ext/module/
|
4
|
+
require "active_support/core_ext/module/redefine_method"
|
3
5
|
|
4
6
|
module ActionView
|
5
7
|
# Layouts reverse the common pattern of including shared headers and footers in many templates to isolate changes in
|
@@ -91,16 +93,16 @@ module ActionView
|
|
91
93
|
# layout false
|
92
94
|
#
|
93
95
|
# In these examples, we have three implicit lookup scenarios:
|
94
|
-
# * The BankController uses the "bank" layout.
|
95
|
-
# * The ExchangeController uses the "exchange" layout.
|
96
|
-
# * The CurrencyController inherits the layout from BankController.
|
96
|
+
# * The +BankController+ uses the "bank" layout.
|
97
|
+
# * The +ExchangeController+ uses the "exchange" layout.
|
98
|
+
# * The +CurrencyController+ inherits the layout from BankController.
|
97
99
|
#
|
98
100
|
# However, when a layout is explicitly set, the explicitly set layout wins:
|
99
|
-
# * The InformationController uses the "information" layout, explicitly set.
|
100
|
-
# * The TellerController also uses the "information" layout, because the parent explicitly set it.
|
101
|
-
# * The EmployeeController uses the "employee" layout, because it set the layout to nil
|
102
|
-
# * The VaultController chooses a layout dynamically by calling the <tt>access_level_layout</tt> method.
|
103
|
-
# * The TillController does not use a layout at all.
|
101
|
+
# * The +InformationController+ uses the "information" layout, explicitly set.
|
102
|
+
# * The +TellerController+ also uses the "information" layout, because the parent explicitly set it.
|
103
|
+
# * The +EmployeeController+ uses the "employee" layout, because it set the layout to +nil+, resetting the parent configuration.
|
104
|
+
# * The +VaultController+ chooses a layout dynamically by calling the <tt>access_level_layout</tt> method.
|
105
|
+
# * The +TillController+ does not use a layout at all.
|
104
106
|
#
|
105
107
|
# == Types of layouts
|
106
108
|
#
|
@@ -148,8 +150,8 @@ module ActionView
|
|
148
150
|
# The template will be looked always in <tt>app/views/layouts/</tt> folder. But you can point
|
149
151
|
# <tt>layouts</tt> folder direct also. <tt>layout "layouts/demo"</tt> is the same as <tt>layout "demo"</tt>.
|
150
152
|
#
|
151
|
-
# Setting the layout to nil forces it to be looked up in the filesystem and fallbacks to the parent behavior if none exists.
|
152
|
-
# Setting it to nil is useful to re-enable template lookup overriding a previous configuration set in the parent:
|
153
|
+
# Setting the layout to +nil+ forces it to be looked up in the filesystem and fallbacks to the parent behavior if none exists.
|
154
|
+
# Setting it to +nil+ is useful to re-enable template lookup overriding a previous configuration set in the parent:
|
153
155
|
#
|
154
156
|
# class ApplicationController < ActionController::Base
|
155
157
|
# layout "application"
|
@@ -204,9 +206,9 @@ module ActionView
|
|
204
206
|
include ActionView::Rendering
|
205
207
|
|
206
208
|
included do
|
207
|
-
class_attribute :_layout, :
|
208
|
-
|
209
|
-
|
209
|
+
class_attribute :_layout, instance_accessor: false
|
210
|
+
class_attribute :_layout_conditions, instance_accessor: false, default: {}
|
211
|
+
|
210
212
|
_write_layout_method
|
211
213
|
end
|
212
214
|
|
@@ -222,37 +224,39 @@ module ActionView
|
|
222
224
|
# that if no layout conditions are used, this method is not used
|
223
225
|
module LayoutConditions # :nodoc:
|
224
226
|
private
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
227
|
+
# Determines whether the current action has a layout definition by
|
228
|
+
# checking the action name against the :only and :except conditions
|
229
|
+
# set by the <tt>layout</tt> method.
|
230
|
+
#
|
231
|
+
# ==== Returns
|
232
|
+
# * <tt>Boolean</tt> - True if the action has a layout definition, false otherwise.
|
233
|
+
def _conditional_layout?
|
234
|
+
return unless super
|
235
|
+
|
236
|
+
conditions = _layout_conditions
|
237
|
+
|
238
|
+
if only = conditions[:only]
|
239
|
+
only.include?(action_name)
|
240
|
+
elsif except = conditions[:except]
|
241
|
+
!except.include?(action_name)
|
242
|
+
else
|
243
|
+
true
|
244
|
+
end
|
243
245
|
end
|
244
|
-
end
|
245
246
|
end
|
246
247
|
|
247
248
|
# Specify the layout to use for this class.
|
248
249
|
#
|
249
250
|
# If the specified layout is a:
|
250
251
|
# String:: the String is the template name
|
251
|
-
# Symbol:: call the method specified by the symbol
|
252
|
+
# Symbol:: call the method specified by the symbol
|
253
|
+
# Proc:: call the passed Proc
|
252
254
|
# false:: There is no layout
|
253
255
|
# true:: raise an ArgumentError
|
254
256
|
# nil:: Force default layout behavior with inheritance
|
255
257
|
#
|
258
|
+
# Return value of +Proc+ and +Symbol+ arguments should be +String+, +false+, +true+ or +nil+
|
259
|
+
# with the same meaning as described above.
|
256
260
|
# ==== Parameters
|
257
261
|
# * <tt>layout</tt> - The layout to use.
|
258
262
|
#
|
@@ -262,7 +266,7 @@ module ActionView
|
|
262
266
|
def layout(layout, conditions = {})
|
263
267
|
include LayoutConditions unless conditions.empty?
|
264
268
|
|
265
|
-
conditions.each {|k, v| conditions[k] = Array(v).map
|
269
|
+
conditions.each { |k, v| conditions[k] = Array(v).map(&:to_s) }
|
266
270
|
self._layout_conditions = conditions
|
267
271
|
|
268
272
|
self._layout = layout
|
@@ -274,10 +278,10 @@ module ActionView
|
|
274
278
|
# If a layout is not explicitly mentioned then look for a layout with the controller's name.
|
275
279
|
# if nothing is found then try same procedure to find super class's layout.
|
276
280
|
def _write_layout_method # :nodoc:
|
277
|
-
|
281
|
+
silence_redefinition_of_method(:_layout)
|
278
282
|
|
279
|
-
prefixes
|
280
|
-
default_behavior = "lookup_context.find_all('#{_implied_layout_name}', #{prefixes.inspect}).first || super"
|
283
|
+
prefixes = /\blayouts/.match?(_implied_layout_name) ? [] : ["layouts"]
|
284
|
+
default_behavior = "lookup_context.find_all('#{_implied_layout_name}', #{prefixes.inspect}, false, [], { formats: formats }).first || super"
|
281
285
|
name_clause = if name
|
282
286
|
default_behavior
|
283
287
|
else
|
@@ -286,7 +290,8 @@ module ActionView
|
|
286
290
|
RUBY
|
287
291
|
end
|
288
292
|
|
289
|
-
layout_definition =
|
293
|
+
layout_definition = \
|
294
|
+
case _layout
|
290
295
|
when String
|
291
296
|
_layout.inspect
|
292
297
|
when Symbol
|
@@ -301,7 +306,7 @@ module ActionView
|
|
301
306
|
RUBY
|
302
307
|
when Proc
|
303
308
|
define_method :_layout_from_proc, &_layout
|
304
|
-
|
309
|
+
private :_layout_from_proc
|
305
310
|
<<-RUBY
|
306
311
|
result = _layout_from_proc(#{_layout.arity == 0 ? '' : 'self'})
|
307
312
|
return #{default_behavior} if result.nil?
|
@@ -313,10 +318,11 @@ module ActionView
|
|
313
318
|
raise ArgumentError, "Layouts must be specified as a String, Symbol, Proc, false, or nil"
|
314
319
|
when nil
|
315
320
|
name_clause
|
316
|
-
|
321
|
+
end
|
317
322
|
|
318
|
-
|
319
|
-
|
323
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
324
|
+
# frozen_string_literal: true
|
325
|
+
def _layout(lookup_context, formats)
|
320
326
|
if _conditional_layout?
|
321
327
|
#{layout_definition}
|
322
328
|
else
|
@@ -328,15 +334,14 @@ module ActionView
|
|
328
334
|
end
|
329
335
|
|
330
336
|
private
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
end
|
337
|
+
# If no layout is supplied, look for a template named the return
|
338
|
+
# value of this method.
|
339
|
+
#
|
340
|
+
# ==== Returns
|
341
|
+
# * <tt>String</tt> - A template name
|
342
|
+
def _implied_layout_name
|
343
|
+
controller_path
|
344
|
+
end
|
340
345
|
end
|
341
346
|
|
342
347
|
def _normalize_options(options) # :nodoc:
|
@@ -366,13 +371,12 @@ module ActionView
|
|
366
371
|
end
|
367
372
|
|
368
373
|
private
|
369
|
-
|
370
374
|
def _conditional_layout?
|
371
375
|
true
|
372
376
|
end
|
373
377
|
|
374
378
|
# This will be overwritten by _write_layout_method
|
375
|
-
def _layout; end
|
379
|
+
def _layout(*); end
|
376
380
|
|
377
381
|
# Determine the layout for a given name, taking into account the name type.
|
378
382
|
#
|
@@ -382,8 +386,8 @@ module ActionView
|
|
382
386
|
case name
|
383
387
|
when String then _normalize_layout(name)
|
384
388
|
when Proc then name
|
385
|
-
when true then Proc.new { _default_layout(true) }
|
386
|
-
when :default then Proc.new { _default_layout(false) }
|
389
|
+
when true then Proc.new { |lookup_context, formats| _default_layout(lookup_context, formats, true) }
|
390
|
+
when :default then Proc.new { |lookup_context, formats| _default_layout(lookup_context, formats, false) }
|
387
391
|
when false, nil then nil
|
388
392
|
else
|
389
393
|
raise ArgumentError,
|
@@ -392,21 +396,22 @@ module ActionView
|
|
392
396
|
end
|
393
397
|
|
394
398
|
def _normalize_layout(value)
|
395
|
-
value.is_a?(String) && value
|
399
|
+
value.is_a?(String) && !value.match?(/\blayouts/) ? "layouts/#{value}" : value
|
396
400
|
end
|
397
401
|
|
398
402
|
# Returns the default layout for this controller.
|
399
403
|
# Optionally raises an exception if the layout could not be found.
|
400
404
|
#
|
401
405
|
# ==== Parameters
|
402
|
-
# * <tt>
|
403
|
-
#
|
406
|
+
# * <tt>formats</tt> - The formats accepted to this layout
|
407
|
+
# * <tt>require_layout</tt> - If set to +true+ and layout is not found,
|
408
|
+
# an +ArgumentError+ exception is raised (defaults to +false+)
|
404
409
|
#
|
405
410
|
# ==== Returns
|
406
|
-
# * <tt>template</tt> - The template object for the default layout (or nil)
|
407
|
-
def _default_layout(require_layout = false)
|
411
|
+
# * <tt>template</tt> - The template object for the default layout (or +nil+)
|
412
|
+
def _default_layout(lookup_context, formats, require_layout = false)
|
408
413
|
begin
|
409
|
-
value = _layout if action_has_layout?
|
414
|
+
value = _layout(lookup_context, formats) if action_has_layout?
|
410
415
|
rescue NameError => e
|
411
416
|
raise e, "Could not render layout: #{e.message}"
|
412
417
|
end
|
@@ -420,7 +425,7 @@ module ActionView
|
|
420
425
|
end
|
421
426
|
|
422
427
|
def _include_layout?(options)
|
423
|
-
(options.keys & [:body, :
|
428
|
+
(options.keys & [:body, :plain, :html, :inline, :partial]).empty? || options.key?(:layout)
|
424
429
|
end
|
425
430
|
end
|
426
431
|
end
|
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/log_subscriber"
|
2
4
|
|
3
5
|
module ActionView
|
4
6
|
# = Action View Log Subscriber
|
@@ -13,31 +15,96 @@ module ActionView
|
|
13
15
|
end
|
14
16
|
|
15
17
|
def render_template(event)
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
18
|
+
info do
|
19
|
+
message = +" Rendered #{from_rails_root(event.payload[:identifier])}"
|
20
|
+
message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout]
|
21
|
+
message << " (Duration: #{event.duration.round(1)}ms | Allocations: #{event.allocations})"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def render_partial(event)
|
26
|
+
debug do
|
27
|
+
message = +" Rendered #{from_rails_root(event.payload[:identifier])}"
|
28
|
+
message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout]
|
29
|
+
message << " (Duration: #{event.duration.round(1)}ms | Allocations: #{event.allocations})"
|
30
|
+
message << " #{cache_message(event.payload)}" unless event.payload[:cache_hit].nil?
|
31
|
+
message
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def render_layout(event)
|
36
|
+
info do
|
37
|
+
message = +" Rendered layout #{from_rails_root(event.payload[:identifier])}"
|
38
|
+
message << " (Duration: #{event.duration.round(1)}ms | Allocations: #{event.allocations})"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def render_collection(event)
|
43
|
+
identifier = event.payload[:identifier] || "templates"
|
44
|
+
|
45
|
+
debug do
|
46
|
+
message = +" Rendered collection of #{from_rails_root(identifier)}"
|
47
|
+
message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout]
|
48
|
+
message << " #{render_count(event.payload)} (Duration: #{event.duration.round(1)}ms | Allocations: #{event.allocations})"
|
49
|
+
message
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def start(name, id, payload)
|
54
|
+
log_rendering_start(payload, name)
|
55
|
+
|
56
|
+
super
|
21
57
|
end
|
22
|
-
alias :render_partial :render_template
|
23
|
-
alias :render_collection :render_template
|
24
58
|
|
25
59
|
def logger
|
26
60
|
ActionView::Base.logger
|
27
61
|
end
|
28
62
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
def from_rails_root(string)
|
63
|
+
private
|
64
|
+
EMPTY = ""
|
65
|
+
def from_rails_root(string) # :doc:
|
33
66
|
string = string.sub(rails_root, EMPTY)
|
34
67
|
string.sub!(VIEWS_PATTERN, EMPTY)
|
35
68
|
string
|
36
69
|
end
|
37
70
|
|
38
|
-
def rails_root
|
71
|
+
def rails_root # :doc:
|
39
72
|
@root ||= "#{Rails.root}/"
|
40
73
|
end
|
74
|
+
|
75
|
+
def render_count(payload) # :doc:
|
76
|
+
if payload[:cache_hits]
|
77
|
+
"[#{payload[:cache_hits]} / #{payload[:count]} cache hits]"
|
78
|
+
else
|
79
|
+
"[#{payload[:count]} times]"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def cache_message(payload) # :doc:
|
84
|
+
case payload[:cache_hit]
|
85
|
+
when :hit
|
86
|
+
"[cache hit]"
|
87
|
+
when :miss
|
88
|
+
"[cache miss]"
|
89
|
+
end
|
90
|
+
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
|
41
108
|
end
|
42
109
|
end
|
43
110
|
|
@@ -1,48 +1,44 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "concurrent/map"
|
4
|
+
require "active_support/core_ext/module/attribute_accessors"
|
5
|
+
require "action_view/template/resolver"
|
5
6
|
|
6
7
|
module ActionView
|
7
8
|
# = Action View Lookup Context
|
8
9
|
#
|
9
|
-
# LookupContext is the object responsible
|
10
|
-
# templates, i.e. view paths and details.
|
11
|
-
#
|
12
|
-
#
|
10
|
+
# <tt>LookupContext</tt> is the object responsible for holding all information
|
11
|
+
# required for looking up templates, i.e. view paths and details.
|
12
|
+
# <tt>LookupContext</tt> is also responsible for generating a key, given to
|
13
|
+
# view paths, used in the resolver cache lookup. Since this key is generated
|
14
|
+
# only once during the request, it speeds up all cache accesses.
|
13
15
|
class LookupContext #:nodoc:
|
14
16
|
attr_accessor :prefixes, :rendered_format
|
15
17
|
|
16
|
-
mattr_accessor :fallbacks
|
17
|
-
@@fallbacks = FallbackFileSystemResolver.instances
|
18
|
+
mattr_accessor :fallbacks, default: FallbackFileSystemResolver.instances
|
18
19
|
|
19
|
-
mattr_accessor :registered_details
|
20
|
-
self.registered_details = []
|
20
|
+
mattr_accessor :registered_details, default: []
|
21
21
|
|
22
|
-
def self.register_detail(name,
|
23
|
-
|
24
|
-
|
22
|
+
def self.register_detail(name, &block)
|
23
|
+
registered_details << name
|
24
|
+
Accessors::DEFAULT_PROCS[name] = block
|
25
25
|
|
26
|
-
Accessors.
|
26
|
+
Accessors.define_method(:"default_#{name}", &block)
|
27
27
|
Accessors.module_eval <<-METHOD, __FILE__, __LINE__ + 1
|
28
28
|
def #{name}
|
29
|
-
@details
|
29
|
+
@details[:#{name}] || []
|
30
30
|
end
|
31
31
|
|
32
32
|
def #{name}=(value)
|
33
33
|
value = value.present? ? Array(value) : default_#{name}
|
34
34
|
_set_detail(:#{name}, value) if value != @details[:#{name}]
|
35
35
|
end
|
36
|
-
|
37
|
-
remove_possible_method :initialize_details
|
38
|
-
def initialize_details(details)
|
39
|
-
#{initialize.join("\n")}
|
40
|
-
end
|
41
36
|
METHOD
|
42
37
|
end
|
43
38
|
|
44
39
|
# Holds accessors for the registered details.
|
45
40
|
module Accessors #:nodoc:
|
41
|
+
DEFAULT_PROCS = {}
|
46
42
|
end
|
47
43
|
|
48
44
|
register_detail(:locale) do
|
@@ -54,32 +50,45 @@ module ActionView
|
|
54
50
|
end
|
55
51
|
register_detail(:formats) { ActionView::Base.default_formats || [:html, :text, :js, :css, :xml, :json] }
|
56
52
|
register_detail(:variants) { [] }
|
57
|
-
register_detail(:handlers){ Template::Handlers.extensions }
|
53
|
+
register_detail(:handlers) { Template::Handlers.extensions }
|
58
54
|
|
59
55
|
class DetailsKey #:nodoc:
|
60
56
|
alias :eql? :equal?
|
61
|
-
alias :object_hash :hash
|
62
57
|
|
63
|
-
|
64
|
-
@
|
58
|
+
@details_keys = Concurrent::Map.new
|
59
|
+
@digest_cache = Concurrent::Map.new
|
60
|
+
@view_context_mutex = Mutex.new
|
65
61
|
|
66
|
-
def self.
|
62
|
+
def self.digest_cache(details)
|
63
|
+
@digest_cache[details_cache_key(details)] ||= Concurrent::Map.new
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.details_cache_key(details)
|
67
67
|
if details[:formats]
|
68
68
|
details = details.dup
|
69
|
-
|
70
|
-
details[:formats] = details[:formats].select { |v|
|
71
|
-
syms.include? v
|
72
|
-
}
|
69
|
+
details[:formats] &= Template::Types.symbols
|
73
70
|
end
|
74
|
-
@details_keys[details] ||= new
|
71
|
+
@details_keys[details] ||= Object.new
|
75
72
|
end
|
76
73
|
|
77
74
|
def self.clear
|
75
|
+
ActionView::ViewPaths.all_view_paths.each do |path_set|
|
76
|
+
path_set.each(&:clear_cache)
|
77
|
+
end
|
78
|
+
ActionView::LookupContext.fallbacks.each(&:clear_cache)
|
79
|
+
@view_context_class = nil
|
78
80
|
@details_keys.clear
|
81
|
+
@digest_cache.clear
|
79
82
|
end
|
80
83
|
|
81
|
-
def
|
82
|
-
@
|
84
|
+
def self.digest_caches
|
85
|
+
@digest_cache.values
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.view_context_class(klass)
|
89
|
+
@view_context_mutex.synchronize do
|
90
|
+
@view_context_class ||= klass.with_empty_template_cache
|
91
|
+
end
|
83
92
|
end
|
84
93
|
end
|
85
94
|
|
@@ -90,7 +99,7 @@ module ActionView
|
|
90
99
|
# Calculate the details key. Remove the handlers from calculation to improve performance
|
91
100
|
# since the user cannot modify it explicitly.
|
92
101
|
def details_key #:nodoc:
|
93
|
-
@details_key ||= DetailsKey.
|
102
|
+
@details_key ||= DetailsKey.details_cache_key(@details) if @cache
|
94
103
|
end
|
95
104
|
|
96
105
|
# Temporary skip passing the details_key forward.
|
@@ -101,10 +110,10 @@ module ActionView
|
|
101
110
|
@cache = old_value
|
102
111
|
end
|
103
112
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
@
|
113
|
+
private
|
114
|
+
def _set_detail(key, value) # :doc:
|
115
|
+
@details = @details.dup if @digest_cache || @details_key
|
116
|
+
@digest_cache = nil
|
108
117
|
@details_key = nil
|
109
118
|
@details[key] = value
|
110
119
|
end
|
@@ -114,12 +123,6 @@ module ActionView
|
|
114
123
|
module ViewPaths
|
115
124
|
attr_reader :view_paths, :html_fallback_for_js
|
116
125
|
|
117
|
-
# Whenever setting view paths, makes a copy so we can manipulate then in
|
118
|
-
# instance objects as we wish.
|
119
|
-
def view_paths=(paths)
|
120
|
-
@view_paths = ActionView::PathSet.new(Array(paths))
|
121
|
-
end
|
122
|
-
|
123
126
|
def find(name, prefixes = [], partial = false, keys = [], options = {})
|
124
127
|
@view_paths.find(*args_for_lookup(name, prefixes, partial, keys, options))
|
125
128
|
end
|
@@ -129,39 +132,51 @@ module ActionView
|
|
129
132
|
@view_paths.find_all(*args_for_lookup(name, prefixes, partial, keys, options))
|
130
133
|
end
|
131
134
|
|
132
|
-
def exists?(name, prefixes = [], partial = false, keys = [], options
|
135
|
+
def exists?(name, prefixes = [], partial = false, keys = [], **options)
|
133
136
|
@view_paths.exists?(*args_for_lookup(name, prefixes, partial, keys, options))
|
134
137
|
end
|
135
138
|
alias :template_exists? :exists?
|
136
139
|
|
137
|
-
|
140
|
+
def any?(name, prefixes = [], partial = false)
|
141
|
+
@view_paths.exists?(*args_for_any(name, prefixes, partial))
|
142
|
+
end
|
143
|
+
alias :any_templates? :any?
|
144
|
+
|
145
|
+
# Adds fallbacks to the view paths. Useful in cases when you are rendering
|
146
|
+
# a :file.
|
138
147
|
def with_fallbacks
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
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)
|
144
157
|
end
|
145
|
-
yield
|
146
|
-
ensure
|
147
|
-
added_resolvers.times { view_paths.pop }
|
148
158
|
end
|
149
159
|
|
150
|
-
|
160
|
+
private
|
161
|
+
# Whenever setting view paths, makes a copy so that we can manipulate them in
|
162
|
+
# instance objects as we wish.
|
163
|
+
def build_view_paths(paths)
|
164
|
+
ActionView::PathSet.new(Array(paths))
|
165
|
+
end
|
151
166
|
|
152
|
-
def args_for_lookup(name, prefixes, partial, keys, details_options)
|
167
|
+
def args_for_lookup(name, prefixes, partial, keys, details_options)
|
153
168
|
name, prefixes = normalize_name(name, prefixes)
|
154
169
|
details, details_key = detail_args_for(details_options)
|
155
170
|
[name, prefixes, partial || false, details, details_key, keys]
|
156
171
|
end
|
157
172
|
|
158
173
|
# Compute details hash and key according to user options (e.g. passed from #render).
|
159
|
-
def detail_args_for(options)
|
174
|
+
def detail_args_for(options) # :doc:
|
160
175
|
return @details, details_key if options.empty? # most common path.
|
161
176
|
user_details = @details.merge(options)
|
162
177
|
|
163
178
|
if @cache
|
164
|
-
details_key = DetailsKey.
|
179
|
+
details_key = DetailsKey.details_cache_key(user_details)
|
165
180
|
else
|
166
181
|
details_key = nil
|
167
182
|
end
|
@@ -169,18 +184,44 @@ module ActionView
|
|
169
184
|
[user_details, details_key]
|
170
185
|
end
|
171
186
|
|
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
|
+
def detail_args_for_any
|
194
|
+
@detail_args_for_any ||= begin
|
195
|
+
details = {}
|
196
|
+
|
197
|
+
registered_details.each do |k|
|
198
|
+
if k == :variants
|
199
|
+
details[k] = :any
|
200
|
+
else
|
201
|
+
details[k] = Accessors::DEFAULT_PROCS[k].call
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
if @cache
|
206
|
+
[details, DetailsKey.details_cache_key(details)]
|
207
|
+
else
|
208
|
+
[details, nil]
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
172
213
|
# Support legacy foo.erb names even though we now ignore .erb
|
173
214
|
# as well as incorrectly putting part of the path in the template
|
174
215
|
# name instead of the prefix.
|
175
|
-
def normalize_name(name, prefixes)
|
216
|
+
def normalize_name(name, prefixes)
|
176
217
|
prefixes = prefixes.presence
|
177
|
-
parts = name.to_s.split(
|
218
|
+
parts = name.to_s.split("/")
|
178
219
|
parts.shift if parts.first.empty?
|
179
|
-
name
|
220
|
+
name = parts.pop
|
180
221
|
|
181
222
|
return name, prefixes || [""] if parts.empty?
|
182
223
|
|
183
|
-
parts = parts.join(
|
224
|
+
parts = parts.join("/")
|
184
225
|
prefixes = prefixes ? prefixes.map { |p| "#{p}/#{parts}" } : [parts]
|
185
226
|
|
186
227
|
return name, prefixes
|
@@ -192,21 +233,47 @@ module ActionView
|
|
192
233
|
include ViewPaths
|
193
234
|
|
194
235
|
def initialize(view_paths, details = {}, prefixes = [])
|
195
|
-
@
|
196
|
-
@
|
236
|
+
@details_key = nil
|
237
|
+
@digest_cache = nil
|
197
238
|
@cache = true
|
198
239
|
@prefixes = prefixes
|
199
|
-
@rendered_format = nil
|
200
240
|
|
201
|
-
|
202
|
-
|
241
|
+
@details = initialize_details({}, details)
|
242
|
+
@view_paths = build_view_paths(view_paths)
|
243
|
+
end
|
244
|
+
|
245
|
+
def digest_cache
|
246
|
+
@digest_cache ||= DetailsKey.digest_cache(@details)
|
247
|
+
end
|
248
|
+
|
249
|
+
def with_prepended_formats(formats)
|
250
|
+
details = @details.dup
|
251
|
+
details[:formats] = formats
|
252
|
+
|
253
|
+
self.class.new(@view_paths, details, @prefixes)
|
254
|
+
end
|
255
|
+
|
256
|
+
def initialize_details(target, details)
|
257
|
+
registered_details.each do |k|
|
258
|
+
target[k] = details[k] || Accessors::DEFAULT_PROCS[k].call
|
259
|
+
end
|
260
|
+
target
|
203
261
|
end
|
262
|
+
private :initialize_details
|
204
263
|
|
205
264
|
# Override formats= to expand ["*/*"] values and automatically
|
206
265
|
# add :html as fallback to :js.
|
207
266
|
def formats=(values)
|
208
267
|
if values
|
268
|
+
values = values.dup
|
209
269
|
values.concat(default_formats) if values.delete "*/*"
|
270
|
+
values.uniq!
|
271
|
+
|
272
|
+
invalid_values = (values - Template::Types.symbols)
|
273
|
+
unless invalid_values.empty?
|
274
|
+
raise ArgumentError, "Invalid formats: #{invalid_values.map(&:inspect).join(", ")}"
|
275
|
+
end
|
276
|
+
|
210
277
|
if values == [:js]
|
211
278
|
values << :html
|
212
279
|
@html_fallback_for_js = true
|
@@ -215,19 +282,13 @@ module ActionView
|
|
215
282
|
super(values)
|
216
283
|
end
|
217
284
|
|
218
|
-
# Do not use the default locale on template lookup.
|
219
|
-
def skip_default_locale!
|
220
|
-
@skip_default_locale = true
|
221
|
-
self.locale = nil
|
222
|
-
end
|
223
|
-
|
224
285
|
# Override locale to return a symbol instead of array.
|
225
286
|
def locale
|
226
287
|
@details[:locale].first
|
227
288
|
end
|
228
289
|
|
229
290
|
# Overload locale= to also set the I18n.locale. If the current I18n.config object responds
|
230
|
-
# to original_config, it means that it
|
291
|
+
# to original_config, it means that it has a copy of the original I18n configuration and it's
|
231
292
|
# acting as proxy, which we need to skip.
|
232
293
|
def locale=(value)
|
233
294
|
if value
|
@@ -235,23 +296,7 @@ module ActionView
|
|
235
296
|
config.locale = value
|
236
297
|
end
|
237
298
|
|
238
|
-
super(
|
239
|
-
end
|
240
|
-
|
241
|
-
# A method which only uses the first format in the formats array for layout lookup.
|
242
|
-
def with_layout_format
|
243
|
-
if formats.size == 1
|
244
|
-
yield
|
245
|
-
else
|
246
|
-
old_formats = formats
|
247
|
-
_set_detail(:formats, formats[0,1])
|
248
|
-
|
249
|
-
begin
|
250
|
-
yield
|
251
|
-
ensure
|
252
|
-
_set_detail(:formats, old_formats)
|
253
|
-
end
|
254
|
-
end
|
299
|
+
super(default_locale)
|
255
300
|
end
|
256
301
|
end
|
257
302
|
end
|