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/action_view/template/resolver.rb') 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/action_view/template/resolver.rb') 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 From 1b86d90136efb98c7b331a84ca163587307a49af Mon Sep 17 00:00:00 2001 From: "yuuji.yaginuma" Date: Thu, 17 May 2018 17:32:27 +0900 Subject: Enable `Performance/UnfreezeString` cop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In Ruby 2.3 or later, `String#+@` is available and `+@` is faster than `dup`. ```ruby # frozen_string_literal: true require "bundler/inline" gemfile(true) do source "https://rubygems.org" gem "benchmark-ips" end Benchmark.ips do |x| x.report('+@') { +"" } x.report('dup') { "".dup } x.compare! end ``` ``` $ ruby -v benchmark.rb ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux] Warming up -------------------------------------- +@ 282.289k i/100ms dup 187.638k i/100ms Calculating ------------------------------------- +@ 6.775M (± 3.6%) i/s - 33.875M in 5.006253s dup 3.320M (± 2.2%) i/s - 16.700M in 5.032125s Comparison: +@: 6775299.3 i/s dup: 3320400.7 i/s - 2.04x slower ``` --- actionview/lib/action_view/template/resolver.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actionview/lib/action_view/template/resolver.rb') diff --git a/actionview/lib/action_view/template/resolver.rb b/actionview/lib/action_view/template/resolver.rb index db3cff71df..5027303e86 100644 --- a/actionview/lib/action_view/template/resolver.rb +++ b/actionview/lib/action_view/template/resolver.rb @@ -16,7 +16,7 @@ module ActionView alias_method :partial?, :partial def self.build(name, prefix, partial) - virtual = "".dup + virtual = +"" virtual << "#{prefix}/" unless prefix.empty? virtual << (partial ? "_#{name}" : name) new name, prefix, partial, virtual -- cgit v1.2.3 From aa3dcabd874a3e82e455e85a1c94a7abaac2900a Mon Sep 17 00:00:00 2001 From: Yasuo Honda Date: Wed, 28 Feb 2018 04:33:37 +0000 Subject: Add `Style/RedundantFreeze` to remove redudant `.freeze` Since Rails 6.0 will support Ruby 2.4.1 or higher `# frozen_string_literal: true` magic comment is enough to make string object frozen. This magic comment is enabled by `Style/FrozenStringLiteralComment` cop. * Exclude these files not to auto correct false positive `Regexp#freeze` - 'actionpack/lib/action_dispatch/journey/router/utils.rb' - 'activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb' It has been fixed by https://github.com/rubocop-hq/rubocop/pull/6333 Once the newer version of RuboCop released and available at Code Climate these exclude entries should be removed. * Replace `String#freeze` with `String#-@` manually if explicit frozen string objects are required - 'actionpack/test/controller/test_case_test.rb' - 'activemodel/test/cases/type/string_test.rb' - 'activesupport/lib/active_support/core_ext/string/strip.rb' - 'activesupport/test/core_ext/string_ext_test.rb' - 'railties/test/generators/actions_test.rb' --- actionview/lib/action_view/template/resolver.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'actionview/lib/action_view/template/resolver.rb') diff --git a/actionview/lib/action_view/template/resolver.rb b/actionview/lib/action_view/template/resolver.rb index 5027303e86..08dd6fb510 100644 --- a/actionview/lib/action_view/template/resolver.rb +++ b/actionview/lib/action_view/template/resolver.rb @@ -282,7 +282,7 @@ module ActionView end def escape_entry(entry) - entry.gsub(/[*?{}\[\]]/, '\\\\\\&'.freeze) + entry.gsub(/[*?{}\[\]]/, '\\\\\\&') end # Returns the file mtime from the filesystem. @@ -294,7 +294,7 @@ module ActionView # from the path, or the handler, we should return the array of formats given # to the resolver. def extract_handler_and_format_and_variant(path) - pieces = File.basename(path).split(".".freeze) + pieces = File.basename(path).split(".") pieces.shift extension = pieces.pop -- cgit v1.2.3