Pass locals in to the template object on construction

This commit ensures that locals are passed in to the template objects
when they are constructed, then removes the `locals=` mutator on the
template object.  This means we don't need to mutate Template objects
with locals in the `decorate` method.
This commit is contained in:
Aaron Patterson 2019-02-25 15:14:53 -08:00
parent a92e5eb4ae
commit d4015a7f06
No known key found for this signature in database
GPG Key ID: 953170BCB4FFAFC6
7 changed files with 36 additions and 27 deletions

View File

@ -39,7 +39,7 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest
def call(env)
env["action_dispatch.show_detailed_exceptions"] = @detailed
req = ActionDispatch::Request.new(env)
template = ActionView::Template.new(File.read(__FILE__), __FILE__, ActionView::Template::Handlers::Raw.new, format: :html)
template = ActionView::Template.new(File.read(__FILE__), __FILE__, ActionView::Template::Handlers::Raw.new, format: :html, locals: [])
case req.path
when "/pass"

View File

@ -122,23 +122,28 @@ module ActionView
extend Template::Handlers
attr_accessor :locals, :virtual_path
attr_accessor :virtual_path
attr_reader :source, :identifier, :handler, :original_encoding, :updated_at
attr_reader :variable, :format, :variant
attr_reader :variable, :format, :variant, :locals
def initialize(source, identifier, handler, format: nil, variant: nil, **details)
def initialize(source, identifier, handler, format: nil, variant: nil, locals: nil, **details)
unless format
ActiveSupport::Deprecation.warn "ActionView::Template#initialize requires a format parameter"
format = :html
end
unless locals
ActiveSupport::Deprecation.warn "ActionView::Template#initialize requires a locals parameter"
locals = []
end
@source = source
@identifier = identifier
@handler = handler
@compiled = false
@original_encoding = nil
@locals = details[:locals] || []
@locals = locals
@virtual_path = details[:virtual_path]
@variable = if @virtual_path
@ -153,6 +158,7 @@ module ActionView
@compile_mutex = Mutex.new
end
deprecate def locals=(_); end
deprecate def formats=(_); end
deprecate def formats; Array(format); end
deprecate def variants=(_); end

View File

@ -143,14 +143,18 @@ module ActionView
# Normalizes the arguments and passes it on to find_templates.
def find_all(name, prefix = nil, partial = false, details = {}, key = nil, locals = [])
locals = locals.map(&:to_s).sort!.freeze
cached(key, [name, prefix, partial], details, locals) do
find_templates(name, prefix, partial, details)
find_templates(name, prefix, partial, details, locals)
end
end
def find_all_anywhere(name, prefix, partial = false, details = {}, key = nil, locals = [])
locals = locals.map(&:to_s).sort!.freeze
cached(key, [name, prefix, partial], details, locals) do
find_templates(name, prefix, partial, details, true)
find_templates(name, prefix, partial, details, true, locals)
end
end
@ -165,7 +169,7 @@ module ActionView
# This is what child classes implement. No defaults are needed
# because Resolver guarantees that the arguments are present and
# normalized.
def find_templates(name, prefix, partial, details, outside_app_allowed = false)
def find_templates(name, prefix, partial, details, outside_app_allowed = false, locals = [])
raise NotImplementedError, "Subclasses must implement a find_templates(name, prefix, partial, details, outside_app_allowed = false) method"
end
@ -180,7 +184,6 @@ module ActionView
# resolver is fresher before returning it.
def cached(key, path_info, details, locals)
name, prefix, partial = path_info
locals = locals.map(&:to_s).sort!
if key
@cache.cache(key, name, prefix, partial, locals) do
@ -195,7 +198,6 @@ module ActionView
def decorate(templates, path_info, details, locals)
cached = nil
templates.each do |t|
t.locals = locals
t.virtual_path ||= (cached ||= build_path(*path_info))
end
end
@ -213,12 +215,12 @@ module ActionView
private
def find_templates(name, prefix, partial, details, outside_app_allowed = false)
def find_templates(name, prefix, partial, details, outside_app_allowed = false, locals)
path = Path.build(name, prefix, partial)
query(path, details, details[:formats], outside_app_allowed)
query(path, details, details[:formats], outside_app_allowed, locals)
end
def query(path, details, formats, outside_app_allowed)
def query(path, details, formats, outside_app_allowed, locals)
template_paths = find_template_paths_from_details(path, details)
template_paths = reject_files_external_to_app(template_paths) unless outside_app_allowed
@ -229,6 +231,7 @@ module ActionView
virtual_path: path.virtual,
format: format,
variant: variant,
locals: locals,
updated_at: mtime(template)
)
end

View File

@ -23,7 +23,7 @@ module ActionView #:nodoc:
private
def query(path, exts, _, _)
def query(path, exts, _, _, locals)
query = +""
EXTENSIONS.each_key do |ext|
query << "(" << exts[ext].map { |e| e && Regexp.escape(".#{e}") }.join("|") << "|)"
@ -39,6 +39,7 @@ module ActionView #:nodoc:
virtual_path: path.virtual,
format: format,
variant: variant,
locals: locals,
updated_at: updated_at
)
end
@ -48,9 +49,9 @@ module ActionView #:nodoc:
end
class NullResolver < PathResolver
def query(path, exts, _, _)
def query(path, exts, _, _, locals)
handler, format, variant = extract_handler_and_format_and_variant(path, :html)
[ActionView::Template.new("Template generated by Null Resolver", path.virtual, handler, virtual_path: path.virtual, format: format, variant: variant)]
[ActionView::Template.new("Template generated by Null Resolver", path.virtual, handler, virtual_path: path.virtual, format: format, variant: variant, locals: locals)]
end
end
end

View File

@ -60,7 +60,7 @@ module RenderERBUtils
string.strip,
"test template",
ActionView::Template.handler_for_extension(:erb),
format: :html)
format: :html, locals: [])
view = ActionView::Base.with_empty_template_cache
template.render(view.empty, {}).strip

View File

@ -143,8 +143,9 @@ class ViewLoadPathsTest < ActionController::TestCase
"Decorated body",
template.identifier,
template.handler,
virtual_path: template.virtual_path,
format: template.format
virtual_path: template.virtual_path,
format: template.format,
locals: template.locals
)
end
end

View File

@ -39,7 +39,8 @@ class TestERBTemplate < ActiveSupport::TestCase
"partial",
ERBHandler,
virtual_path: "partial",
format: :html
format: :html,
locals: []
)
end
@ -57,7 +58,7 @@ class TestERBTemplate < ActiveSupport::TestCase
end
def new_template(body = "<%= hello %>", details = {})
details = { format: :html }.merge details
details = { format: :html, locals: [] }.merge details
ActionView::Template.new(body.dup, "hello template", details.fetch(:handler) { ERBHandler }, { virtual_path: "hello" }.merge!(details))
end
@ -103,8 +104,7 @@ class TestERBTemplate < ActiveSupport::TestCase
end
def test_locals
@template = new_template("<%= my_local %>")
@template.locals = [:my_local]
@template = new_template("<%= my_local %>", locals: [:my_local])
assert_equal "I am a local", render(my_local: "I am a local")
end
@ -122,16 +122,14 @@ class TestERBTemplate < ActiveSupport::TestCase
end
def test_refresh_with_templates
@template = new_template("Hello", virtual_path: "test/foo/bar")
@template.locals = [:key]
@template = new_template("Hello", virtual_path: "test/foo/bar", locals: [:key])
assert_called_with(@context.lookup_context, :find_template, ["bar", %w(test/foo), false, [:key]], returns: "template") do
assert_equal "template", @template.refresh(@context)
end
end
def test_refresh_with_partials
@template = new_template("Hello", virtual_path: "test/_foo")
@template.locals = [:key]
@template = new_template("Hello", virtual_path: "test/_foo", locals: [:key])
assert_called_with(@context.lookup_context, :find_template, ["foo", %w(test), true, [:key]], returns: "partial") do
assert_equal "partial", @template.refresh(@context)
end