eager_autoload do
autoload :Error
autoload :RawFile
autoload :Handlers
autoload :HTML
autoload :Inline
autoload :Sources
autoload :Text
autoload :Types
end
extend Template::Handlers
attr_reader :identifier, :handler, :original_encoding, :updated_at
attr_reader :variable, :format, :variant, :locals, :virtual_path
def initialize(source, identifier, handler, format: nil, variant: nil, locals: nil, virtual_path: nil, updated_at: nil)
unless locals
ActiveSupport::Deprecation.warn "ActionView::Template#initialize requires a locals parameter"
locals = []
end
@source = source
@identifier = identifier
@handler = handler
@compiled = false
@locals = locals
@virtual_path = virtual_path
@variable = if @virtual_path
base = @virtual_path[-1] == "/" ? "" : ::File.basename(@virtual_path)
base =~ /\A_?(.*?)(?:\.\w+)*\z/
$1.to_sym
end
if updated_at
ActiveSupport::Deprecation.warn "ActionView::Template#updated_at is deprecated"
@updated_at = updated_at
else
@updated_at = Time.now
end
@format = format
@variant = variant
@compile_mutex = Mutex.new
end
deprecate :original_encoding
deprecate :updated_at
deprecate def virtual_path=(_); end
deprecate def locals=(_); end
deprecate def formats=(_); end
deprecate def formats; Array(format); end
deprecate def variants=(_); end
deprecate def variants; [variant]; end
deprecate def refresh(_); self; end
def supports_streaming?
handler.respond_to?(:supports_streaming?) && handler.supports_streaming?
end
def render(view, locals, buffer = ActionView::OutputBuffer.new, &block)
instrument_render_template do
compile!(view)
view._run(method_name, self, locals, buffer, &block)
end
rescue => e
handle_render_error(view, e)
end
def type
@type ||= Types[format]
end
def short_identifier
@short_identifier ||= defined?(Rails.root) ? identifier.sub("#{Rails.root}/", "") : identifier
end
def inspect
"#<#{self.class.name} #{short_identifier} locals=#{@locals.inspect}>"
end
def source
@source.to_s
end
def encode!
source = self.source
return source unless source.encoding == Encoding::BINARY
if source.sub!(/\A#{ENCODING_FLAG}/, "")
encoding = magic_encoding = $1
else
encoding = Encoding.default_external
end
source.force_encoding(encoding)
if !magic_encoding && @handler.respond_to?(:handles_encoding?) && @handler.handles_encoding?
source
elsif source.valid_encoding?
source.encode!
else
raise WrongEncodingError.new(source, encoding)
end
end
def marshal_dump
[ @source, @identifier, @handler, @compiled, @locals, @virtual_path, @updated_at, @format, @variant ]
end
def marshal_load(array)
@source, @identifier, @handler, @compiled, @locals, @virtual_path, @updated_at, @format, @variant = *array
@compile_mutex = Mutex.new
end
private
def compile!(view)
return if @compiled
@compile_mutex.synchronize do
return if @compiled
mod = view.compiled_method_container
instrument("!compile_template") do
compile(mod)
end
@compiled = true
end
end
class LegacyTemplate < DelegateClass(Template)
attr_reader :source
def initialize(template, source)
super(template)
@source = source
end
end
def compile(mod)
source = encode!
code = @handler.call(self, source)
original_source = source
source = + def #{method_name}(local_assigns, output_buffer) @virtual_path =
source.force_encoding(code.encoding)
source.encode!
unless source.valid_encoding?
raise WrongEncodingError.new(source, Encoding.default_internal)
end
begin
mod.module_eval(source, identifier, 0)
rescue SyntaxError
raise SyntaxErrorInTemplate.new(self, original_source)
end
end
def handle_render_error(view, e)
if e.is_a?(Template::Error)
e.sub_template_of(self)
raise e
else
raise Template::Error.new(self)
end
end
def locals_code
locals = @locals - Module::RUBY_RESERVED_KEYWORDS
locals = locals.grep(/\A@?(?![A-Z0-9])(?:[[:alnum:]_]|[^\00--\1177])+\z/)
locals.each_with_object(+"") { |key, code| code << "#{key} = local_assigns[:#{key}]; #{key} = #{key};" }
end
def method_name
@method_name ||= begin
m = +"_#{identifier_method_name}__#{@identifier.hash}_#{__id__}"
m.tr!("-", "_")
m
end
end
def identifier_method_name
short_identifier.tr("^a-z_", "_")
end
def instrument(action, &block)
ActiveSupport::Notifications.instrument("#{action}.action_view", instrument_payload, &block)
end
def instrument_render_template(&block)
ActiveSupport::Notifications.instrument("!render_template.action_view", instrument_payload, &block)
end
def instrument_payload
{ virtual_path: @virtual_path, identifier: @identifier }
end
end