2012-04-04 19:26:07 +02:00
# Used for merging results of metadata callbacks
2013-12-28 01:26:31 +01:00
require 'active_support/core_ext/hash/deep_merge'
2012-10-21 06:19:13 +02:00
require 'monitor'
2015-05-04 02:11:49 +02:00
require 'hamster'
2012-04-04 19:26:07 +02:00
2014-07-09 02:02:02 +02:00
# Ignores
Middleman :: Extensions . register :sitemap_ignore , auto_activate : :before_configuration do
require 'middleman-core/sitemap/extensions/ignores'
Middleman :: Sitemap :: Extensions :: Ignores
end
# Files on Disk
Middleman :: Extensions . register :sitemap_ondisk , auto_activate : :before_configuration do
require 'middleman-core/sitemap/extensions/on_disk'
Middleman :: Sitemap :: Extensions :: OnDisk
end
# Endpoints
Middleman :: Extensions . register :sitemap_endpoint , auto_activate : :before_configuration do
require 'middleman-core/sitemap/extensions/request_endpoints'
Middleman :: Sitemap :: Extensions :: RequestEndpoints
end
# Proxies
Middleman :: Extensions . register :sitemap_proxies , auto_activate : :before_configuration do
require 'middleman-core/sitemap/extensions/proxies'
Middleman :: Sitemap :: Extensions :: Proxies
end
# Redirects
Middleman :: Extensions . register :sitemap_redirects , auto_activate : :before_configuration do
require 'middleman-core/sitemap/extensions/redirects'
Middleman :: Sitemap :: Extensions :: Redirects
end
2014-01-01 03:21:30 +01:00
2014-07-03 04:04:34 +02:00
require 'middleman-core/contracts'
2012-05-07 23:41:39 +02:00
module Middleman
# Sitemap namespace
module Sitemap
2015-05-04 02:11:49 +02:00
ManipulatorDescriptor = Struct . new :name , :manipulator , :priority , :custom_name
2012-05-07 23:41:39 +02:00
# The Store class
#
# The Store manages a collection of Resource objects, which represent
# individual items in the sitemap. Resources are indexed by "source path",
# which is the path relative to the source directory, minus any template
# extensions. All "path" parameters used in this class are source paths.
class Store
2014-07-03 04:04:34 +02:00
include Contracts
2015-05-04 02:11:49 +02:00
Contract IsA [ 'Middleman::Application' ]
2014-05-28 08:05:37 +02:00
attr_reader :app
2015-05-04 02:11:49 +02:00
Contract Num
2014-06-29 00:07:43 +02:00
attr_reader :update_count
2012-05-07 23:41:39 +02:00
# Initialize with parent app
# @param [Middleman::Application] app
2015-05-04 02:11:49 +02:00
Contract IsA [ 'Middleman::Application' ] = > Any
2012-05-07 23:41:39 +02:00
def initialize ( app )
2013-12-31 23:41:17 +01:00
@app = app
2012-05-07 23:41:39 +02:00
@resources = [ ]
2014-06-29 00:07:43 +02:00
@update_count = 0
2015-05-04 02:11:49 +02:00
@resource_list_manipulators = :: Hamster . vector
2012-09-16 07:24:39 +02:00
@needs_sitemap_rebuild = true
2014-04-29 19:50:21 +02:00
2012-10-21 06:19:13 +02:00
@lock = Monitor . new
2012-09-16 07:24:39 +02:00
reset_lookup_cache!
2012-08-14 00:39:06 +02:00
2014-07-05 20:17:41 +02:00
@app . config_context . class . send :def_delegator , :app , :sitemap
2012-05-07 23:41:39 +02:00
end
2012-02-09 08:00:29 +01:00
2014-05-11 08:47:04 +02:00
# Register an object which can transform the sitemap resource list. Best to register
# these in a `before_configuration` or `after_configuration` hook.
2012-09-16 07:24:39 +02:00
#
2012-05-07 23:41:39 +02:00
# @param [Symbol] name Name of the manipulator for debugging
2014-05-11 08:47:04 +02:00
# @param [#manipulate_resource_list] manipulator Resource list manipulator
2014-05-12 09:00:53 +02:00
# @param [Numeric] priority Sets the order of this resource list manipulator relative to the rest. By default this is 50, and manipulators run in the order they are registered, but if a priority is provided then this will run ahead of or behind other manipulators.
2015-05-02 22:22:36 +02:00
# @param [Symbol] custom_name The method name to execute.
2012-05-07 23:41:39 +02:00
# @return [void]
2015-05-04 02:11:49 +02:00
Contract Symbol , RespondTo [ :manipulate_resource_list ] , Maybe [ Num ] , Maybe [ Symbol ] = > Any
2015-05-02 22:22:36 +02:00
def register_resource_list_manipulator ( name , manipulator , priority = 50 , custom_name = nil )
2014-05-12 09:00:53 +02:00
# The third argument used to be a boolean - handle those who still pass one
priority = 50 unless priority . is_a? Numeric
2015-05-04 02:11:49 +02:00
@resource_list_manipulators = @resource_list_manipulators . push (
ManipulatorDescriptor . new ( name , manipulator , priority , custom_name )
)
2014-05-12 09:00:53 +02:00
# The index trick is used so that the sort is stable - manipulators with the same priority
# will always be ordered in the same order as they were registered.
n = 0
@resource_list_manipulators = @resource_list_manipulators . sort_by do | m |
n += 1
2015-05-04 02:11:49 +02:00
[ m [ :priority ] , n ]
2014-05-12 09:00:53 +02:00
end
2015-05-04 02:11:49 +02:00
2012-09-16 07:24:39 +02:00
rebuild_resource_list! ( :registered_new )
2012-04-04 19:26:07 +02:00
end
2012-08-14 00:39:06 +02:00
2012-05-07 23:41:39 +02:00
# Rebuild the list of resources from scratch, using registed manipulators
# @return [void]
2015-05-04 02:11:49 +02:00
Contract Maybe [ Symbol ] = > Any
def rebuild_resource_list! ( _name = nil )
2012-10-21 06:19:13 +02:00
@lock . synchronize do
@needs_sitemap_rebuild = true
end
2011-11-21 02:05:29 +01:00
end
2012-08-14 00:39:06 +02:00
2012-05-07 23:41:39 +02:00
# Find a resource given its original path
# @param [String] request_path The original path of a resource.
# @return [Middleman::Sitemap::Resource]
2014-07-03 04:04:34 +02:00
Contract String = > Maybe [ IsA [ 'Middleman::Sitemap::Resource' ] ]
2012-05-07 23:41:39 +02:00
def find_resource_by_path ( request_path )
2012-10-21 06:19:13 +02:00
@lock . synchronize do
request_path = :: Middleman :: Util . normalize_path ( request_path )
ensure_resource_list_updated!
@_lookup_by_path [ request_path ]
end
2012-05-07 23:41:39 +02:00
end
2012-08-14 00:39:06 +02:00
2012-05-07 23:41:39 +02:00
# Find a resource given its destination path
# @param [String] request_path The destination (output) path of a resource.
# @return [Middleman::Sitemap::Resource]
2014-07-03 04:04:34 +02:00
Contract String = > Maybe [ IsA [ 'Middleman::Sitemap::Resource' ] ]
2012-05-07 23:41:39 +02:00
def find_resource_by_destination_path ( request_path )
2012-10-21 06:19:13 +02:00
@lock . synchronize do
request_path = :: Middleman :: Util . normalize_path ( request_path )
ensure_resource_list_updated!
@_lookup_by_destination_path [ request_path ]
end
2012-05-07 23:41:39 +02:00
end
2012-08-14 00:39:06 +02:00
2012-05-07 23:41:39 +02:00
# Get the array of all resources
# @param [Boolean] include_ignored Whether to include ignored resources
# @return [Array<Middleman::Sitemap::Resource>]
2014-07-03 04:04:34 +02:00
Contract Bool = > ResourceList
2012-05-07 23:41:39 +02:00
def resources ( include_ignored = false )
2012-10-21 06:19:13 +02:00
@lock . synchronize do
ensure_resource_list_updated!
if include_ignored
@resources
else
2013-05-23 09:11:09 +02:00
@resources_not_ignored || = @resources . reject ( & :ignored? )
2012-10-21 06:19:13 +02:00
end
2012-05-07 23:41:39 +02:00
end
2012-02-12 20:45:42 +01:00
end
2012-08-14 00:39:06 +02:00
2013-05-23 09:11:09 +02:00
# Invalidate our cached view of resource that are not ingnored. If your extension
# adds ways to ignore files, you should call this to make sure #resources works right.
def invalidate_resources_not_ignored_cache!
@resources_not_ignored = nil
end
2012-05-07 23:41:39 +02:00
# Get the URL path for an on-disk file
# @param [String] file
# @return [String]
2014-12-26 23:11:58 +01:00
Contract Or [ Pathname , IsA [ 'Middleman::SourceFile' ] ] = > String
2012-05-07 23:41:39 +02:00
def file_to_path ( file )
2014-12-26 23:11:58 +01:00
relative_path = file . is_a? ( Pathname ) ? file . to_s : file [ :relative_path ] . to_s
2012-08-14 00:39:06 +02:00
2012-06-20 05:07:50 +02:00
# Replace a file name containing automatic_directory_matcher with a folder
2012-10-14 07:37:24 +02:00
unless @app . config [ :automatic_directory_matcher ] . nil?
2014-07-16 03:01:45 +02:00
relative_path = relative_path . gsub ( @app . config [ :automatic_directory_matcher ] , '/' )
2012-06-20 05:07:50 +02:00
end
2012-08-14 00:39:06 +02:00
2014-07-16 03:01:45 +02:00
extensionless_path ( relative_path )
2012-05-07 23:41:39 +02:00
end
2012-08-14 00:39:06 +02:00
2012-05-07 23:41:39 +02:00
# Get a path without templating extensions
# @param [String] file
# @return [String]
2014-07-03 04:04:34 +02:00
Contract String = > String
2012-05-07 23:41:39 +02:00
def extensionless_path ( file )
path = file . dup
2014-03-21 01:03:15 +01:00
remove_templating_extensions ( path )
2012-05-07 23:41:39 +02:00
end
2012-09-16 07:24:39 +02:00
# 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!
2012-10-21 06:19:13 +02:00
@lock . synchronize do
return unless @needs_sitemap_rebuild
@needs_sitemap_rebuild = false
2012-09-16 07:24:39 +02:00
2013-12-28 01:26:31 +01:00
@app . logger . debug '== Rebuilding resource list'
2012-09-16 07:24:39 +02:00
2015-09-17 18:41:17 +02:00
@resources = [ ]
@resource_list_manipulators . each do | m |
2015-09-15 02:37:35 +02:00
@app . logger . debug " == Running manipulator: #{ m [ :name ] } "
2015-09-17 18:41:17 +02:00
@resources = m [ :manipulator ] . send ( m [ :custom_name ] || :manipulate_resource_list , @resources )
2012-09-16 07:24:39 +02:00
2012-10-21 06:19:13 +02:00
# Reset lookup cache
reset_lookup_cache!
2015-05-04 02:11:49 +02:00
# Rebuild cache
2015-09-17 18:41:17 +02:00
@resources . each do | resource |
2012-10-21 06:19:13 +02:00
@_lookup_by_path [ resource . path ] = resource
@_lookup_by_destination_path [ resource . destination_path ] = resource
end
2012-09-16 07:24:39 +02:00
2015-09-17 18:41:17 +02:00
invalidate_resources_not_ignored_cache!
2012-10-21 06:19:13 +02:00
end
2013-05-23 09:11:09 +02:00
2014-06-29 00:07:43 +02:00
@update_count += 1
2012-09-16 07:24:39 +02:00
end
end
private
def reset_lookup_cache!
2012-10-21 06:19:13 +02:00
@lock . synchronize {
@_lookup_by_path = { }
@_lookup_by_destination_path = { }
}
2012-09-16 07:24:39 +02:00
end
2013-05-03 06:17:50 +02:00
# Removes the templating extensions, while keeping the others
# @param [String] path
# @return [String]
2014-07-03 04:04:34 +02:00
Contract String = > String
2013-05-03 06:17:50 +02:00
def remove_templating_extensions ( path )
2013-05-03 07:03:28 +02:00
# Strip templating extensions as long as Tilt knows them
2015-09-17 18:41:17 +02:00
path = path . sub ( / #{ :: Regexp . escape ( File . extname ( path ) ) } $ / , '' ) while :: Tilt [ path ]
2013-05-03 06:17:50 +02:00
path
end
# Remove the locale token from the end of the path
# @param [String] path
# @return [String]
2014-07-03 04:04:34 +02:00
Contract String = > String
2013-05-03 06:17:50 +02:00
def strip_away_locale ( path )
2014-07-06 01:50:19 +02:00
if @app . extensions [ :i18n ]
2013-05-03 07:03:28 +02:00
path_bits = path . split ( '.' )
lang = path_bits . last
2014-07-06 01:50:19 +02:00
return path_bits [ 0 .. - 2 ] . join ( '.' ) if @app . extensions [ :i18n ] . langs . include? ( lang . to_sym )
2013-05-03 06:17:50 +02:00
end
path
end
2012-04-20 00:47:42 +02:00
end
2011-11-21 02:05:29 +01:00
end
2012-07-11 07:46:18 +02:00
end