diff options
Diffstat (limited to 'actionview/lib/action_view/template.rb')
-rw-r--r-- | actionview/lib/action_view/template.rb | 106 |
1 files changed, 56 insertions, 50 deletions
diff --git a/actionview/lib/action_view/template.rb b/actionview/lib/action_view/template.rb index d8585514d5..070d82cf17 100644 --- a/actionview/lib/action_view/template.rb +++ b/actionview/lib/action_view/template.rb @@ -1,12 +1,16 @@ -require 'active_support/core_ext/object/try' -require 'active_support/core_ext/kernel/singleton_class' -require 'thread' +# frozen_string_literal: true + +require "active_support/core_ext/object/try" +require "active_support/core_ext/kernel/singleton_class" +require "thread" module ActionView # = Action View Template class Template extend ActiveSupport::Autoload + mattr_accessor :finalize_compiled_template_methods, default: true + # === Encodings in ActionView::Template # # ActionView::Template is one of a few sources of potential @@ -65,8 +69,7 @@ module ActionView # If you want to provide an alternate mechanism for # specifying encodings (like ERB does via <%# encoding: ... %>), # you may indicate that you will handle encodings yourself - # by implementing <tt>self.handles_encoding?</tt> - # on your handler. + # by implementing <tt>handles_encoding?</tt> on your handler. # # If you do, Rails will not try to encode the String # into the default_internal, passing you the unaltered @@ -130,7 +133,6 @@ module ActionView @source = source @identifier = identifier @handler = handler - @cache_name = extract_resource_cache_name @compiled = false @original_encoding = nil @locals = details[:locals] || [] @@ -141,8 +143,8 @@ module ActionView @compile_mutex = Mutex.new end - # Returns if the underlying handler supports streaming. If so, - # a streaming buffer *may* be passed when it start rendering. + # Returns whether the underlying handler supports streaming. If so, + # a streaming buffer *may* be passed when it starts rendering. def supports_streaming? handler.respond_to?(:supports_streaming?) && handler.supports_streaming? end @@ -153,8 +155,8 @@ module ActionView # This method is instrumented as "!render_template.action_view". Notice that # we use a bang in this instrumentation because you don't want to # consume this in production. This is only slow if it's being listened to. - def render(view, locals, buffer=nil, &block) - instrument("!render_template") do + def render(view, locals, buffer = nil, &block) + instrument_render_template do compile!(view) view.send(method_name, locals, buffer, &block) end @@ -166,10 +168,6 @@ module ActionView @type ||= Types[@formats.first] if @formats.first end - def eligible_for_collection_caching?(as: nil) - @cache_name == (as || inferred_cache_name).to_s - end - # Receives a view object and return a template similar to self by using @virtual_path. # # This method is useful if you have a template object but it does not contain its source @@ -185,12 +183,12 @@ module ActionView name = pieces.pop partial = !!name.sub!(/^_/, "") lookup.disable_cache do - lookup.find_template(name, [ pieces.join('/') ], partial, @locals) + lookup.find_template(name, [ pieces.join("/") ], partial, @locals) end end def inspect - @inspect ||= defined?(Rails.root) ? identifier.sub("#{Rails.root}/", '') : identifier + @inspect ||= defined?(Rails.root) ? identifier.sub("#{Rails.root}/", "") : identifier end # This method is responsible for properly setting the encoding of the @@ -209,7 +207,7 @@ module ActionView # Look for # encoding: *. If we find one, we'll encode the # String in that encoding, otherwise, we'll use the # default external encoding. - if source.sub!(/\A#{ENCODING_FLAG}/, '') + if source.sub!(/\A#{ENCODING_FLAG}/, "") encoding = magic_encoding = $1 else encoding = Encoding.default_external @@ -237,11 +235,24 @@ module ActionView end end - protected + + # Exceptions are marshalled when using the parallel test runner with DRb, so we need + # to ensure that references to the template object can be marshalled as well. This means forgoing + # the marshalling of the compiler mutex and instantiating that again on unmarshalling. + def marshal_dump # :nodoc: + [ @source, @identifier, @handler, @compiled, @original_encoding, @locals, @virtual_path, @updated_at, @formats, @variants ] + end + + def marshal_load(array) # :nodoc: + @source, @identifier, @handler, @compiled, @original_encoding, @locals, @virtual_path, @updated_at, @formats, @variants = *array + @compile_mutex = Mutex.new + end + + private # Compile a template. This method ensures a template is compiled # just once and removes the source after it is compiled. - def compile!(view) #:nodoc: + def compile!(view) return if @compiled # Templates can be used concurrently in threaded environments @@ -282,14 +293,13 @@ module ActionView # encode the source into <tt>Encoding.default_internal</tt>. # In general, this means that templates will be UTF-8 inside of Rails, # regardless of the original source encoding. - def compile(mod) #:nodoc: + def compile(mod) encode! - method_name = self.method_name code = @handler.call(self) # Make sure that the resulting String to be eval'd is in the # encoding of the code - source = <<-end_src + source = +<<-end_src def #{method_name}(local_assigns, output_buffer) _old_virtual_path, @virtual_path = @virtual_path, #{@virtual_path.inspect};_old_output_buffer = @output_buffer;#{locals_code};#{code} ensure @@ -312,10 +322,12 @@ module ActionView end mod.module_eval(source, identifier, 0) - ObjectSpace.define_finalizer(self, Finalizer[method_name, mod]) + if finalize_compiled_template_methods + ObjectSpace.define_finalizer(self, Finalizer[method_name, mod]) + end end - def handle_render_error(view, e) #:nodoc: + def handle_render_error(view, e) if e.is_a?(Template::Error) e.sub_template_of(self) raise e @@ -325,48 +337,42 @@ module ActionView template = refresh(view) template.encode! end - raise Template::Error.new(template, e) + raise Template::Error.new(template) end end - def locals_code #:nodoc: - # Double assign to suppress the dreaded 'assigned but unused variable' warning - @locals.each_with_object('') { |key, code| code << "#{key} = #{key} = local_assigns[:#{key}];" } + def locals_code + # Only locals with valid variable names get set directly. Others will + # still be available in local_assigns. + locals = @locals - Module::RUBY_RESERVED_KEYWORDS + locals = locals.grep(/\A@?(?![A-Z0-9])(?:[[:alnum:]_]|[^\0-\177])+\z/) + + # Assign for the same variable is to suppress unused variable warning + locals.each_with_object(+"") { |key, code| code << "#{key} = local_assigns[:#{key}]; #{key} = #{key};" } end - def method_name #:nodoc: + def method_name @method_name ||= begin - m = "_#{identifier_method_name}__#{@identifier.hash}_#{__id__}" - m.tr!('-', '_') + m = +"_#{identifier_method_name}__#{@identifier.hash}_#{__id__}" + m.tr!("-", "_") m end end - def identifier_method_name #:nodoc: - inspect.tr('^a-z_', '_') - end - - def instrument(action, &block) - payload = { virtual_path: @virtual_path, identifier: @identifier } - ActiveSupport::Notifications.instrument("#{action}.action_view", payload, &block) + def identifier_method_name + inspect.tr("^a-z_", "_") end - EXPLICIT_COLLECTION = /# Template Collection: (?<resource_name>\w+)/ - - def extract_resource_cache_name - if match = @source.match(EXPLICIT_COLLECTION) || resource_cache_call_match - match[:resource_name] - end + def instrument(action, &block) # :doc: + ActiveSupport::Notifications.instrument("#{action}.action_view", instrument_payload, &block) end - def resource_cache_call_match - if @handler.respond_to?(:resource_cache_call_pattern) - @source.match(@handler.resource_cache_call_pattern) - end + def instrument_render_template(&block) + ActiveSupport::Notifications.instrument("!render_template.action_view", instrument_payload, &block) end - def inferred_cache_name - @inferred_cache_name ||= @virtual_path.split('/').last.sub('_', '') + def instrument_payload + { virtual_path: @virtual_path, identifier: @identifier } end end end |