From cc9a0de6602f3c33e5ffa168c75f90451b47a208 Mon Sep 17 00:00:00 2001 From: John Hawthorn Date: Wed, 12 Sep 2018 10:50:38 -0700 Subject: Use wildcard glob for optimized template resolving --- actionview/lib/action_view/template/resolver.rb | 64 ++++++++++++++++++++----- 1 file changed, 51 insertions(+), 13 deletions(-) (limited to 'actionview/lib') diff --git a/actionview/lib/action_view/template/resolver.rb b/actionview/lib/action_view/template/resolver.rb index 5a86f10973..301b28d9ea 100644 --- a/actionview/lib/action_view/template/resolver.rb +++ b/actionview/lib/action_view/template/resolver.rb @@ -221,9 +221,7 @@ module ActionView end def query(path, details, formats, outside_app_allowed) - query = build_query(path, details) - - template_paths = find_template_paths(query) + template_paths = find_template_paths_from_details(path, details) template_paths = reject_files_external_to_app(template_paths) unless outside_app_allowed template_paths.map do |template| @@ -243,6 +241,11 @@ module ActionView files.reject { |filename| !inside_path?(@path, filename) } end + def find_template_paths_from_details(path, details) + query = build_query(path, details) + find_template_paths(query) + end + def find_template_paths(query) Dir[query].uniq.reject do |filename| File.directory?(filename) || @@ -362,19 +365,54 @@ module ActionView # An Optimized resolver for Rails' most common case. class OptimizedFileSystemResolver < FileSystemResolver #:nodoc: - def build_query(path, details) - query = escape_entry(File.join(@path, path)) + private - exts = EXTENSIONS.map do |ext, prefix| - if ext == :variants && details[ext] == :any - "{#{prefix}*,}" - else - "{#{details[ext].compact.uniq.map { |e| "#{prefix}#{e}," }.join}}" + def find_template_paths_from_details(path, details) + # Instead of checking for every possible path, as our other globs would + # do, scan the directory for files with the right prefix. + query = "#{escape_entry(File.join(@path, path))}*" + + regex = build_regex(path, details) + + Dir[query].uniq.reject do |filename| + # This regex match does double duty of finding only files which match + # details (instead of just matching the prefix) and also filtering for + # case-insensitive file systems. + !filename.match(regex) || + File.directory?(filename) + end.sort_by do |filename| + # Because we scanned the directory, instead of checking for files + # one-by-one, they will be returned in an arbitrary order. + # We can use the matches found by the regex and sort by their index in + # details. + match = filename.match(regex) + EXTENSIONS.keys.reverse.map do |ext| + if match[ext].nil? + # No match should be last + details[ext].length + else + found = match[ext].to_sym + details[ext].index(found) + end + end end - end.join + end - query + exts - end + def build_regex(path, details) + query = escape_entry(File.join(@path, path)) + exts = EXTENSIONS.map do |ext, prefix| + match = + if ext == :variants && details[ext] == :any + ".*?" + else + details[ext].compact.uniq.map { |e| Regexp.escape(e) }.join("|") + end + prefix = Regexp.escape(prefix) + "(#{prefix}(?<#{ext}>#{match}))?" + end.join + + %r{\A#{query}#{exts}\z} + end end # The same as FileSystemResolver but does not allow templates to store -- cgit v1.2.3 From a33776b0f516e15b366f398d45e6959e8b8bcb73 Mon Sep 17 00:00:00 2001 From: John Hawthorn Date: Wed, 12 Sep 2018 18:21:18 -0700 Subject: Fix for variants: :any special case --- actionview/lib/action_view/template/resolver.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'actionview/lib') diff --git a/actionview/lib/action_view/template/resolver.rb b/actionview/lib/action_view/template/resolver.rb index 301b28d9ea..db3cff71df 100644 --- a/actionview/lib/action_view/template/resolver.rb +++ b/actionview/lib/action_view/template/resolver.rb @@ -387,7 +387,9 @@ module ActionView # details. match = filename.match(regex) EXTENSIONS.keys.reverse.map do |ext| - if match[ext].nil? + if ext == :variants && details[ext] == :any + match[ext].nil? ? 0 : 1 + elsif match[ext].nil? # No match should be last details[ext].length else -- cgit v1.2.3