Delay recalculating sitemap until absolutely necessary, avoiding redundant recalculations.

This commit is contained in:
Ben Hollis 2012-09-15 22:24:39 -07:00
parent a8a555c101
commit 6c241603ac
6 changed files with 70 additions and 36 deletions

View file

@ -242,6 +242,7 @@ module Middleman::Cli
# Double-check for compass sprites # Double-check for compass sprites
@app.files.find_new_files((Pathname(@app.source_dir) + @app.images_dir).relative_path_from(@app.root_path)) @app.files.find_new_files((Pathname(@app.source_dir) + @app.images_dir).relative_path_from(@app.root_path))
@app.sitemap.ensure_resource_list_updated!
# Sort paths to be built by the above order. This is primarily so Compass can # Sort paths to be built by the above order. This is primarily so Compass can
# find files in the build folder when it needs to generate sprites for the # find files in the build folder when it needs to generate sprites for the

View file

@ -11,7 +11,7 @@ module Middleman
/^\.sass-cache\//, /^\.sass-cache\//,
/^\.git\//, /^\.git\//,
/^\.gitignore$/, /^\.gitignore$/,
/^\.DS_Store$/, /\.DS_Store/,
/^build\//, /^build\//,
/^\.rbenv-.*$/, /^\.rbenv-.*$/,
/^Gemfile$/, /^Gemfile$/,
@ -116,17 +116,14 @@ module Middleman
path = Pathname(path) path = Pathname(path)
return unless path.exist? return unless path.exist?
glob = (path + "**/*").to_s glob = (path + "**").to_s
subset = @known_paths.select { |p| p.fnmatch(glob) } subset = @known_paths.select { |p| p.fnmatch(glob) }
::Middleman::Util.all_files_under(path).each do |filepath| ::Middleman::Util.all_files_under(path).each do |filepath|
if only_new next if only_new && subset.include?(filepath)
next if subset.include?(filepath)
else
subset.delete(filepath)
end
self.did_change(filepath) subset.delete(filepath)
did_change(filepath)
end end
subset.each(&method(:did_delete)) unless only_new subset.each(&method(:did_delete)) unless only_new

View file

@ -14,7 +14,6 @@ module Middleman
def initialize(sitemap) def initialize(sitemap)
@sitemap = sitemap @sitemap = sitemap
@app = @sitemap.app @app = @sitemap.app
@file_paths_on_disk = Set.new @file_paths_on_disk = Set.new
scoped_self = self scoped_self = self
@ -22,17 +21,18 @@ module Middleman
# Register file change callback # Register file change callback
@app.files.changed do |file| @app.files.changed do |file|
scoped_self.touch_file(file, !scoped_self.waiting_for_ready) scoped_self.touch_file(file)
end end
# Register file delete callback # Register file delete callback
@app.files.deleted do |file| @app.files.deleted do |file|
scoped_self.remove_file(file, !scoped_self.waiting_for_ready) scoped_self.remove_file(file)
end end
@app.ready do @app.ready do
scoped_self.waiting_for_ready = false scoped_self.waiting_for_ready = false
scoped_self.sitemap.rebuild_resource_list!(:on_disk_ready) # Make sure the sitemap is ready for the first request
sitemap.ensure_resource_list_updated!
end end
end end
@ -55,7 +55,13 @@ module Middleman
# in case one of the other manipulators # in case one of the other manipulators
# (like asset_hash) cares about the contents of this file, # (like asset_hash) cares about the contents of this file,
# whether or not it belongs in the sitemap (like a partial) # whether or not it belongs in the sitemap (like a partial)
@sitemap.rebuild_resource_list!(:touched_file) if rebuild @sitemap.rebuild_resource_list!(:touched_file)
unless waiting_for_ready || @app.build?
# Force sitemap rebuild so the next request is ready to go.
# Skip this during build because the builder will control sitemap refresh.
@sitemap.ensure_resource_list_updated!
end
end end
# Remove a file from the store # Remove a file from the store
@ -63,7 +69,12 @@ module Middleman
# @return [void] # @return [void]
def remove_file(file, rebuild=true) def remove_file(file, rebuild=true)
if @file_paths_on_disk.delete?(file) if @file_paths_on_disk.delete?(file)
@sitemap.rebuild_resource_list!(:removed_file) if rebuild @sitemap.rebuild_resource_list!(:removed_file)
unless waiting_for_ready || @app.build?
# Force sitemap rebuild so the next request is ready to go.
# Skip this during build because the builder will control sitemap refresh.
@sitemap.ensure_resource_list_updated!
end
end end
end end

View file

@ -23,41 +23,32 @@ module Middleman
@app = app @app = app
@resources = [] @resources = []
@_cached_metadata = {} @_cached_metadata = {}
@_lookup_cache = { :path => {}, :destination_path => {} }
@resource_list_manipulators = [] @resource_list_manipulators = []
@needs_sitemap_rebuild = true
reset_lookup_cache!
# Register classes which can manipulate the main site map list # Register classes which can manipulate the main site map list
register_resource_list_manipulator(:on_disk, Middleman::Sitemap::Extensions::OnDisk.new(self), false) register_resource_list_manipulator(:on_disk, Middleman::Sitemap::Extensions::OnDisk.new(self))
# Proxies # Proxies
register_resource_list_manipulator(:proxies, @app.proxy_manager, false) register_resource_list_manipulator(:proxies, @app.proxy_manager)
end end
# Register a klass which can manipulate the main site map list # Register a klass which can manipulate the main site map list. Best to register
# these in a before_configuration or after_configuration hook.
#
# @param [Symbol] name Name of the manipulator for debugging # @param [Symbol] name Name of the manipulator for debugging
# @param [Class, Module] inst Abstract namespace which can update the resource list # @param [Class, Module] inst Abstract namespace which can update the resource list
# @param [Boolean] immediately_rebuild Whether the resource list should be immediately recalculated
# @return [void] # @return [void]
def register_resource_list_manipulator(name, inst, immediately_rebuild=true) def register_resource_list_manipulator(name, inst, unused=true)
@resource_list_manipulators << [name, inst] @resource_list_manipulators << [name, inst]
rebuild_resource_list!(:registered_new) if immediately_rebuild rebuild_resource_list!(:registered_new)
end end
# Rebuild the list of resources from scratch, using registed manipulators # Rebuild the list of resources from scratch, using registed manipulators
# @return [void] # @return [void]
def rebuild_resource_list!(reason=nil) def rebuild_resource_list!(reason=nil)
@resources = @resource_list_manipulators.inject([]) do |result, (_, inst)| @needs_sitemap_rebuild = true
newres = inst.manipulate_resource_list(result)
# Reset lookup cache
@_lookup_cache = { :path => {}, :destination_path => {} }
newres.each do |resource|
@_lookup_cache[:path][resource.path] = resource
@_lookup_cache[:destination_path][resource.destination_path] = resource
end
newres
end
end end
# Find a resource given its original path # Find a resource given its original path
@ -65,7 +56,8 @@ module Middleman
# @return [Middleman::Sitemap::Resource] # @return [Middleman::Sitemap::Resource]
def find_resource_by_path(request_path) def find_resource_by_path(request_path)
request_path = ::Middleman::Util.normalize_path(request_path) request_path = ::Middleman::Util.normalize_path(request_path)
@_lookup_cache[:path][request_path] ensure_resource_list_updated!
@_lookup_by_path[request_path]
end end
# Find a resource given its destination path # Find a resource given its destination path
@ -73,13 +65,15 @@ module Middleman
# @return [Middleman::Sitemap::Resource] # @return [Middleman::Sitemap::Resource]
def find_resource_by_destination_path(request_path) def find_resource_by_destination_path(request_path)
request_path = ::Middleman::Util.normalize_path(request_path) request_path = ::Middleman::Util.normalize_path(request_path)
@_lookup_cache[:destination_path][request_path] ensure_resource_list_updated!
@_lookup_by_destination_path[request_path]
end end
# Get the array of all resources # Get the array of all resources
# @param [Boolean] include_ignored Whether to include ignored resources # @param [Boolean] include_ignored Whether to include ignored resources
# @return [Array<Middleman::Sitemap::Resource>] # @return [Array<Middleman::Sitemap::Resource>]
def resources(include_ignored=false) def resources(include_ignored=false)
ensure_resource_list_updated!
if include_ignored if include_ignored
@resources @resources
else else
@ -214,6 +208,36 @@ module Middleman
path path
end end
# Actually update the resource list, assuming anything has called
# rebuild_resource_list! since the last time it was run. This is
# very expensive!
def ensure_resource_list_updated!
return unless @needs_sitemap_rebuild
@needs_sitemap_rebuild = false
@app.logger.debug "== Rebuilding resource list"
@resources = @resource_list_manipulators.inject([]) do |result, (_, inst)|
newres = inst.manipulate_resource_list(result)
# Reset lookup cache
reset_lookup_cache!
newres.each do |resource|
@_lookup_by_path[resource.path] = resource
@_lookup_by_destination_path[resource.destination_path] = resource
end
newres
end
end
private
def reset_lookup_cache!
@_lookup_by_path = {}
@_lookup_by_destination_path = {}
end
end end
end end
end end

View file

@ -14,6 +14,7 @@ module Middleman
:asset_hash, :asset_hash,
AssetHashManager.new(self, exts, ignore) AssetHashManager.new(self, exts, ignore)
) )
use Middleware, :exts => exts, :middleman_app => self, :ignore => ignore use Middleware, :exts => exts, :middleman_app => self, :ignore => ignore
end end
end end

View file

@ -10,7 +10,7 @@ module Middleman
# Once registered # Once registered
def registered(app) def registered(app)
app.ready do app.after_configuration do
sitemap.register_resource_list_manipulator( sitemap.register_resource_list_manipulator(
:directory_indexes, :directory_indexes,
DirectoryIndexManager.new(self) DirectoryIndexManager.new(self)