mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Introduce Resolver::PathParser
The template resolver is supposed to determine the handler, format, and variant from the path in extract_handler_and_format_and_variant. Previously this behaviour was close but didn't exactly match the behaviour of finding templates, and in some cases (particularly with handlers or formats missing) would return incorrect results. This commit introduces Resolver::PathParser, a class which should be able to accurately from any path inside a view directory be able to tell us exactly the prefix, partial, variant, locale, format, variant, and handler of that template. This works by building a building a regexp from the known handlers and file types. This requires that any resolvers have their cache cleared when new handlers or types are registered (this was already somewhat the requirement, since resolver lookups are cached, but this makes it necessary in more situations).
This commit is contained in:
parent
8762d93afc
commit
e53c45b80c
3 changed files with 48 additions and 15 deletions
|
@ -35,6 +35,44 @@ module ActionView
|
||||||
alias :to_s :to_str
|
alias :to_s :to_str
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class PathParser
|
||||||
|
def initialize
|
||||||
|
@regex = build_path_regex
|
||||||
|
end
|
||||||
|
|
||||||
|
def build_path_regex
|
||||||
|
handlers = Template::Handlers.extensions.map { |x| Regexp.escape(x) }.join("|")
|
||||||
|
formats = Template::Types.symbols.map { |x| Regexp.escape(x) }.join("|")
|
||||||
|
locales = "[a-z]{2}(?:-[A-Z]{2})?"
|
||||||
|
variants = "[^.]*"
|
||||||
|
|
||||||
|
%r{
|
||||||
|
\A
|
||||||
|
(?:(?<prefix>.*)/)?
|
||||||
|
(?<partial>_)?
|
||||||
|
(?<action>.*?)
|
||||||
|
(?:\.(?<locale>#{locales}))??
|
||||||
|
(?:\.(?<format>#{formats}))??
|
||||||
|
(?:\+(?<variant>#{variants}))??
|
||||||
|
(?:\.(?<handler>#{handlers}))?
|
||||||
|
\z
|
||||||
|
}x
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse(path)
|
||||||
|
match = @regex.match(path)
|
||||||
|
{
|
||||||
|
prefix: match[:prefix] || "",
|
||||||
|
action: match[:action],
|
||||||
|
partial: !!match[:partial],
|
||||||
|
locale: match[:locale]&.to_sym,
|
||||||
|
handler: match[:handler]&.to_sym,
|
||||||
|
format: match[:format]&.to_sym,
|
||||||
|
variant: match[:variant]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Threadsafe template cache
|
# Threadsafe template cache
|
||||||
class Cache #:nodoc:
|
class Cache #:nodoc:
|
||||||
class SmallCache < Concurrent::Map
|
class SmallCache < Concurrent::Map
|
||||||
|
@ -172,11 +210,13 @@ module ActionView
|
||||||
@pattern = DEFAULT_PATTERN
|
@pattern = DEFAULT_PATTERN
|
||||||
end
|
end
|
||||||
@unbound_templates = Concurrent::Map.new
|
@unbound_templates = Concurrent::Map.new
|
||||||
|
@path_parser = PathParser.new
|
||||||
super()
|
super()
|
||||||
end
|
end
|
||||||
|
|
||||||
def clear_cache
|
def clear_cache
|
||||||
@unbound_templates.clear
|
@unbound_templates.clear
|
||||||
|
@path_parser = PathParser.new
|
||||||
super()
|
super()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -278,18 +318,11 @@ module ActionView
|
||||||
# from the path, or the handler, we should return the array of formats given
|
# from the path, or the handler, we should return the array of formats given
|
||||||
# to the resolver.
|
# to the resolver.
|
||||||
def extract_handler_and_format_and_variant(path)
|
def extract_handler_and_format_and_variant(path)
|
||||||
pieces = File.basename(path).split(".")
|
details = @path_parser.parse(path)
|
||||||
pieces.shift
|
|
||||||
|
|
||||||
extension = pieces.pop
|
handler = Template.handler_for_extension(details[:handler])
|
||||||
|
format = details[:format] || handler.try(:default_format)
|
||||||
handler = Template.handler_for_extension(extension)
|
variant = details[:variant]
|
||||||
format, variant = pieces.last.split(EXTENSIONS[:variants], 2) if pieces.last
|
|
||||||
format = if format
|
|
||||||
Template::Types[format]&.ref
|
|
||||||
elsif handler.respond_to?(:default_format) # default_format can return nil
|
|
||||||
handler.default_format
|
|
||||||
end
|
|
||||||
|
|
||||||
# Template::Types[format] and handler.default_format can return nil
|
# Template::Types[format] and handler.default_format can return nil
|
||||||
[handler, format, variant]
|
[handler, format, variant]
|
||||||
|
|
|
@ -18,9 +18,13 @@ end
|
||||||
module TemplateHandlerHelper
|
module TemplateHandlerHelper
|
||||||
def with_template_handler(*extensions, handler)
|
def with_template_handler(*extensions, handler)
|
||||||
ActionView::Template.register_template_handler(*extensions, handler)
|
ActionView::Template.register_template_handler(*extensions, handler)
|
||||||
|
ActionController::Base.view_paths.paths.each(&:clear_cache)
|
||||||
|
ActionView::LookupContext::DetailsKey.clear
|
||||||
yield
|
yield
|
||||||
ensure
|
ensure
|
||||||
ActionView::Template.unregister_template_handler(*extensions)
|
ActionView::Template.unregister_template_handler(*extensions)
|
||||||
|
ActionController::Base.view_paths.paths.each(&:clear_cache)
|
||||||
|
ActionView::LookupContext::DetailsKey.clear
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -204,8 +204,6 @@ module ResolverSharedTests
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_templates_no_format_with_variant
|
def test_templates_no_format_with_variant
|
||||||
return skip
|
|
||||||
|
|
||||||
with_file "test/hello_world+mobile.erb", "Hello HTML!"
|
with_file "test/hello_world+mobile.erb", "Hello HTML!"
|
||||||
|
|
||||||
templates = resolver.find_all("hello_world", "test", false, locale: [], formats: [:html, :json], variants: :any, handlers: [:erb, :builder])
|
templates = resolver.find_all("hello_world", "test", false, locale: [], formats: [:html, :json], variants: :any, handlers: [:erb, :builder])
|
||||||
|
@ -218,8 +216,6 @@ module ResolverSharedTests
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_templates_no_format_or_handler_with_variant
|
def test_templates_no_format_or_handler_with_variant
|
||||||
return skip
|
|
||||||
|
|
||||||
with_file "test/hello_world+mobile", "Hello HTML!"
|
with_file "test/hello_world+mobile", "Hello HTML!"
|
||||||
|
|
||||||
templates = resolver.find_all("hello_world", "test", false, locale: [], formats: [:html, :json], variants: :any, handlers: [:erb, :builder])
|
templates = resolver.find_all("hello_world", "test", false, locale: [], formats: [:html, :json], variants: :any, handlers: [:erb, :builder])
|
||||||
|
|
Loading…
Reference in a new issue