Optimize globbed file lookups, fixes nasty performance regression
This commit is contained in:
parent
6ef96cc15a
commit
84acb50b02
14 changed files with 52 additions and 60 deletions
|
@ -60,7 +60,8 @@ module Middleman::Cli
|
||||||
|
|
||||||
builder = Middleman::Builder.new(@app,
|
builder = Middleman::Builder.new(@app,
|
||||||
glob: options['glob'],
|
glob: options['glob'],
|
||||||
clean: options['clean'])
|
clean: options['clean'],
|
||||||
|
parallel: options['parallel'])
|
||||||
|
|
||||||
builder.on_build_event(&method(:on_event))
|
builder.on_build_event(&method(:on_event))
|
||||||
|
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
Feature: Instance Vars
|
|
||||||
In order to share data with layouts and partials via instance variables
|
|
||||||
|
|
||||||
Scenario: Setting an instance var in a template should be visible in its layout
|
|
||||||
Given the Server is running at "more-instance-vars-app"
|
|
||||||
When I go to "/instance-var-set.html"
|
|
||||||
Then I should see "Var is 100"
|
|
||||||
|
|
||||||
Scenario: Setting an instance var in a template should be visible in a partial
|
|
||||||
Given the Server is running at "more-instance-vars-app"
|
|
||||||
When I go to "/instance-var-set.html"
|
|
||||||
Then I should see "My var is here!"
|
|
||||||
|
|
||||||
Scenario: Setting an instance var in one file should not be visible in another
|
|
||||||
Given the Server is running at "more-instance-vars-app"
|
|
||||||
When I go to "/instance-var-set.html"
|
|
||||||
When I go to "/no-instance-var.html"
|
|
||||||
Then I should see "No var..."
|
|
|
@ -11,11 +11,6 @@ Feature: Provide Sane Defaults for Partial Behavior
|
||||||
When I go to "/sub/index.html"
|
When I go to "/sub/index.html"
|
||||||
Then I should see "Header"
|
Then I should see "Header"
|
||||||
And I should see "Footer"
|
And I should see "Footer"
|
||||||
|
|
||||||
Scenario: Finds shared partials without _ prefix
|
|
||||||
Given the Server is running at "partials-app"
|
|
||||||
When I go to "/using_snippet.html"
|
|
||||||
Then I should see "Snippet"
|
|
||||||
|
|
||||||
Scenario: Prefers partials of the same engine type
|
Scenario: Prefers partials of the same engine type
|
||||||
Given the Server is running at "partials-app"
|
Given the Server is running at "partials-app"
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
<% if @my_var %>
|
|
||||||
My var is here!
|
|
||||||
<% else %>
|
|
||||||
No var...
|
|
||||||
<% end %>
|
|
|
@ -1,2 +0,0 @@
|
||||||
<% @my_var = 100 %>
|
|
||||||
<%= partial 'vartial' %>
|
|
|
@ -1,3 +0,0 @@
|
||||||
Var is <%= @my_var %>
|
|
||||||
|
|
||||||
<%= yield %>
|
|
|
@ -1 +0,0 @@
|
||||||
<%= partial 'vartial' %>
|
|
|
@ -50,7 +50,9 @@ module Middleman
|
||||||
|
|
||||||
queue_current_paths if @cleaning
|
queue_current_paths if @cleaning
|
||||||
prerender_css
|
prerender_css
|
||||||
|
|
||||||
output_files
|
output_files
|
||||||
|
|
||||||
clean if @cleaning
|
clean if @cleaning
|
||||||
|
|
||||||
::Middleman::Profiling.report('build')
|
::Middleman::Profiling.report('build')
|
||||||
|
|
|
@ -82,8 +82,7 @@ class Middleman::CoreExtensions::Internationalization < ::Middleman::Extension
|
||||||
|
|
||||||
resources.each do |resource|
|
resources.each do |resource|
|
||||||
# If it uses file extension localization
|
# If it uses file extension localization
|
||||||
if parse_locale_extension(resource.path)
|
if result = parse_locale_extension(resource.path)
|
||||||
result = parse_locale_extension(resource.path)
|
|
||||||
ext_lang, path, page_id = result
|
ext_lang, path, page_id = result
|
||||||
new_resources << build_resource(path, resource.path, page_id, ext_lang)
|
new_resources << build_resource(path, resource.path, page_id, ext_lang)
|
||||||
# If it's a "localizable template"
|
# If it's a "localizable template"
|
||||||
|
|
|
@ -156,7 +156,7 @@ module Middleman
|
||||||
# @return [Array<Middleman::SourceFile>]
|
# @return [Array<Middleman::SourceFile>]
|
||||||
Contract None => ArrayOf[SourceFile]
|
Contract None => ArrayOf[SourceFile]
|
||||||
def files
|
def files
|
||||||
watchers.map(&:files).flatten.uniq { |f| f[:relative_path] }
|
watchers.flat_map(&:files).uniq { |f| f[:relative_path] }
|
||||||
end
|
end
|
||||||
|
|
||||||
# Find a file given a type and path.
|
# Find a file given a type and path.
|
||||||
|
@ -168,9 +168,10 @@ module Middleman
|
||||||
Contract Symbol, String, Maybe[Bool] => Maybe[SourceFile]
|
Contract Symbol, String, Maybe[Bool] => Maybe[SourceFile]
|
||||||
def find(type, path, glob=false)
|
def find(type, path, glob=false)
|
||||||
watchers
|
watchers
|
||||||
|
.lazy
|
||||||
.select { |d| d.type == type }
|
.select { |d| d.type == type }
|
||||||
.map { |d| d.find(path, glob) }
|
.map { |d| d.find(path, glob) }
|
||||||
.compact
|
.reject { |d| d.nil? }
|
||||||
.first
|
.first
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -182,6 +183,7 @@ module Middleman
|
||||||
Contract Symbol, String => Bool
|
Contract Symbol, String => Bool
|
||||||
def exists?(type, path)
|
def exists?(type, path)
|
||||||
watchers
|
watchers
|
||||||
|
.lazy
|
||||||
.select { |d| d.type == type }
|
.select { |d| d.type == type }
|
||||||
.any? { |d| d.exists?(path) }
|
.any? { |d| d.exists?(path) }
|
||||||
end
|
end
|
||||||
|
@ -194,6 +196,7 @@ module Middleman
|
||||||
Contract Symbol, String => Maybe[HANDLER]
|
Contract Symbol, String => Maybe[HANDLER]
|
||||||
def watcher_for_path(type, path)
|
def watcher_for_path(type, path)
|
||||||
watchers
|
watchers
|
||||||
|
.lazy
|
||||||
.select { |d| d.type == type }
|
.select { |d| d.type == type }
|
||||||
.find { |d| d.exists?(path) }
|
.find { |d| d.exists?(path) }
|
||||||
end
|
end
|
||||||
|
|
|
@ -43,6 +43,7 @@ module Middleman
|
||||||
@directory = Pathname(directory)
|
@directory = Pathname(directory)
|
||||||
|
|
||||||
@files = {}
|
@files = {}
|
||||||
|
@extensionless_files = {}
|
||||||
|
|
||||||
@validator = options.fetch(:validator, proc { true })
|
@validator = options.fetch(:validator, proc { true })
|
||||||
@ignored = options.fetch(:ignored, proc { false })
|
@ignored = options.fetch(:ignored, proc { false })
|
||||||
|
@ -105,8 +106,7 @@ module Middleman
|
||||||
p = @directory + p if p.relative?
|
p = @directory + p if p.relative?
|
||||||
|
|
||||||
if glob
|
if glob
|
||||||
found = @files.find { |_, v| v[:relative_path].fnmatch(path) }
|
@extensionless_files[p]
|
||||||
found ? found.last : nil
|
|
||||||
else
|
else
|
||||||
@files[p]
|
@files[p]
|
||||||
end
|
end
|
||||||
|
@ -215,7 +215,7 @@ module Middleman
|
||||||
.select(&method(:valid?))
|
.select(&method(:valid?))
|
||||||
|
|
||||||
valid_updates.each do |f|
|
valid_updates.each do |f|
|
||||||
@files[f[:full_path]] = f
|
add_file_to_cache(f)
|
||||||
logger.debug "== Change (#{f[:type]}): #{f[:relative_path]}"
|
logger.debug "== Change (#{f[:type]}): #{f[:relative_path]}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -225,13 +225,23 @@ module Middleman
|
||||||
.select(&method(:valid?))
|
.select(&method(:valid?))
|
||||||
|
|
||||||
valid_removes.each do |f|
|
valid_removes.each do |f|
|
||||||
@files.delete(f[:full_path])
|
remove_file_from_cache(f)
|
||||||
logger.debug "== Deletion (#{f[:type]}): #{f[:relative_path]}"
|
logger.debug "== Deletion (#{f[:type]}): #{f[:relative_path]}"
|
||||||
end
|
end
|
||||||
|
|
||||||
run_callbacks(@on_change_callbacks, valid_updates, valid_removes) unless valid_updates.empty? && valid_removes.empty?
|
run_callbacks(@on_change_callbacks, valid_updates, valid_removes) unless valid_updates.empty? && valid_removes.empty?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def add_file_to_cache(f)
|
||||||
|
@files[f[:full_path]] = f
|
||||||
|
@extensionless_files[f[:full_path].sub_ext('.*')] = f
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove_file_from_cache(f)
|
||||||
|
@files.delete(f[:full_path])
|
||||||
|
@extensionless_files.delete(f[:full_path].sub_ext('.*'))
|
||||||
|
end
|
||||||
|
|
||||||
# Check if this watcher should care about a file.
|
# Check if this watcher should care about a file.
|
||||||
#
|
#
|
||||||
# @param [Middleman::SourceFile] file The file.
|
# @param [Middleman::SourceFile] file The file.
|
||||||
|
|
|
@ -102,6 +102,7 @@ module Middleman
|
||||||
|
|
||||||
partial_file = locate_partial(name)
|
partial_file = locate_partial(name)
|
||||||
|
|
||||||
|
return "" unless partial_file
|
||||||
raise ::Middleman::TemplateRenderer::TemplateNotFound, "Could not locate partial: #{name}" unless partial_file
|
raise ::Middleman::TemplateRenderer::TemplateNotFound, "Could not locate partial: #{name}" unless partial_file
|
||||||
|
|
||||||
source_path = sitemap.file_to_path(partial_file)
|
source_path = sitemap.file_to_path(partial_file)
|
||||||
|
|
|
@ -9,11 +9,24 @@ module Middleman
|
||||||
extend Forwardable
|
extend Forwardable
|
||||||
include Contracts
|
include Contracts
|
||||||
|
|
||||||
def self.cache
|
class Cache
|
||||||
@_cache ||= ::Tilt::Cache.new
|
def initialize
|
||||||
|
@cache = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
def fetch(*key)
|
||||||
|
@cache[key] = yield unless @cache.key?(key)
|
||||||
|
@cache[key]
|
||||||
|
end
|
||||||
|
|
||||||
|
def clear
|
||||||
|
@cache = {}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def_delegator :"self.class", :cache
|
def self.cache
|
||||||
|
@_cache ||= Cache.new
|
||||||
|
end
|
||||||
|
|
||||||
# Custom error class for handling
|
# Custom error class for handling
|
||||||
class TemplateNotFound < RuntimeError; end
|
class TemplateNotFound < RuntimeError; end
|
||||||
|
@ -163,21 +176,9 @@ module Middleman
|
||||||
# @return [String, Boolean] Either the path to the template, or false
|
# @return [String, Boolean] Either the path to the template, or false
|
||||||
Contract IsA['Middleman::Application'], Or[Symbol, String], Maybe[Hash] => Maybe[IsA['Middleman::SourceFile']]
|
Contract IsA['Middleman::Application'], Or[Symbol, String], Maybe[Hash] => Maybe[IsA['Middleman::SourceFile']]
|
||||||
def self.resolve_template(app, request_path, options={})
|
def self.resolve_template(app, request_path, options={})
|
||||||
# Find the path by searching or using the cache
|
# Find the path by searching
|
||||||
relative_path = Util.strip_leading_slash(request_path.to_s)
|
relative_path = Util.strip_leading_slash(request_path.to_s)
|
||||||
|
|
||||||
# Cache lookups in build mode only
|
|
||||||
if app.build?
|
|
||||||
cache.fetch(:resolve_template, relative_path, options) do
|
|
||||||
uncached_resolve_template(app, relative_path, options)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
uncached_resolve_template(app, relative_path, options)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Contract IsA['Middleman::Application'], String, Hash => Maybe[IsA['Middleman::SourceFile']]
|
|
||||||
def self.uncached_resolve_template(app, relative_path, options)
|
|
||||||
# By default, any engine will do
|
# By default, any engine will do
|
||||||
preferred_engines = []
|
preferred_engines = []
|
||||||
|
|
||||||
|
@ -200,7 +201,16 @@ module Middleman
|
||||||
path_with_ext = relative_path.dup
|
path_with_ext = relative_path.dup
|
||||||
path_with_ext << ('.' + preferred_engine) unless preferred_engine.nil?
|
path_with_ext << ('.' + preferred_engine) unless preferred_engine.nil?
|
||||||
|
|
||||||
file = app.files.find(:source, path_with_ext, preferred_engine == '*')
|
globbing = preferred_engine == '*'
|
||||||
|
|
||||||
|
# Cache lookups in build mode only
|
||||||
|
file = if app.build?
|
||||||
|
cache.fetch(path_with_ext, preferred_engine) do
|
||||||
|
app.files.find(:source, path_with_ext, globbing)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
app.files.find(:source, path_with_ext, globbing)
|
||||||
|
end
|
||||||
|
|
||||||
found_template = file if file && (preferred_engine.nil? || ::Tilt[file[:full_path]])
|
found_template = file if file && (preferred_engine.nil? || ::Tilt[file[:full_path]])
|
||||||
break if found_template
|
break if found_template
|
||||||
|
|
Loading…
Add table
Reference in a new issue