2014-07-04 05:41:44 +02:00
|
|
|
require 'rack/mime'
|
2013-12-28 01:26:31 +01:00
|
|
|
require 'middleman-core/sitemap/extensions/traversal'
|
2014-01-03 22:15:02 +01:00
|
|
|
require 'middleman-core/file_renderer'
|
|
|
|
require 'middleman-core/template_renderer'
|
2012-05-07 23:41:39 +02:00
|
|
|
|
|
|
|
module Middleman
|
2012-08-14 00:39:06 +02:00
|
|
|
# Sitemap namespace
|
2012-05-07 23:41:39 +02:00
|
|
|
module Sitemap
|
|
|
|
# Sitemap Resource class
|
|
|
|
class Resource
|
|
|
|
include Middleman::Sitemap::Extensions::Traversal
|
2012-08-14 00:39:06 +02:00
|
|
|
|
2012-05-07 23:41:39 +02:00
|
|
|
# The source path of this resource (relative to the source directory,
|
|
|
|
# without template extensions)
|
|
|
|
# @return [String]
|
|
|
|
attr_reader :path
|
2012-08-14 00:39:06 +02:00
|
|
|
|
2014-04-28 07:50:19 +02:00
|
|
|
# The output path in the build directory for this resource
|
2013-11-27 09:19:59 +01:00
|
|
|
# @return [String]
|
|
|
|
attr_accessor :destination_path
|
2014-05-31 07:46:15 +02:00
|
|
|
|
|
|
|
# The path to use when requesting this resource. Normally it's
|
|
|
|
# the same as {#destination_path} but it can be overridden in subclasses.
|
|
|
|
# @return [String]
|
|
|
|
alias_method :request_path, :destination_path
|
2013-11-27 09:19:59 +01:00
|
|
|
|
2014-07-04 05:41:44 +02:00
|
|
|
# Get the on-disk source file for this resource
|
|
|
|
# @return [String]
|
|
|
|
def source_file
|
|
|
|
if @source_file
|
|
|
|
@source_file
|
|
|
|
elsif proxy?
|
|
|
|
proxied_to_resource.source_file
|
|
|
|
else
|
|
|
|
nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-05-07 23:41:39 +02:00
|
|
|
# Initialize resource with parent store and URL
|
|
|
|
# @param [Middleman::Sitemap::Store] store
|
|
|
|
# @param [String] path
|
|
|
|
# @param [String] source_file
|
|
|
|
def initialize(store, path, source_file=nil)
|
|
|
|
@store = store
|
|
|
|
@app = @store.app
|
2013-08-16 06:12:29 +02:00
|
|
|
@path = path.gsub(' ', '%20') # handle spaces in filenames
|
2012-05-07 23:41:39 +02:00
|
|
|
@source_file = source_file
|
2013-11-27 09:19:59 +01:00
|
|
|
@destination_path = @path
|
2012-04-30 07:32:52 +02:00
|
|
|
|
2014-04-28 06:54:53 +02:00
|
|
|
# Options are generally rendering/sitemap options
|
|
|
|
# Locals are local variables for rendering this resource's template
|
|
|
|
# Page are data that is exposed through this resource's data member.
|
|
|
|
# Note: It is named 'page' for backwards compatibility with older MM.
|
2014-05-28 09:00:36 +02:00
|
|
|
@metadata = { options: {}, locals: {}, page: {} }
|
2012-05-07 23:41:39 +02:00
|
|
|
end
|
2012-08-14 00:39:06 +02:00
|
|
|
|
2014-07-04 19:38:25 +02:00
|
|
|
# Set the on-disk source file for this resource
|
|
|
|
# @return [String]
|
|
|
|
def source_file
|
|
|
|
# TODO: Make this work when get_source_file doesn't exist
|
|
|
|
@source_file || get_source_file
|
|
|
|
end
|
|
|
|
|
2012-05-07 23:41:39 +02:00
|
|
|
# Whether this resource has a template file
|
|
|
|
# @return [Boolean]
|
|
|
|
def template?
|
|
|
|
return false if source_file.nil?
|
|
|
|
!::Tilt[source_file].nil?
|
|
|
|
end
|
2012-08-14 00:39:06 +02:00
|
|
|
|
2014-04-28 08:21:12 +02:00
|
|
|
# Merge in new metadata specific to this resource.
|
|
|
|
# @param [Hash] meta A metadata block with keys :options, :locals, :page.
|
|
|
|
# Options are generally rendering/sitemap options
|
|
|
|
# Locals are local variables for rendering this resource's template
|
|
|
|
# Page are data that is exposed through this resource's data member.
|
|
|
|
# Note: It is named 'page' for backwards compatibility with older MM.
|
|
|
|
def add_metadata(meta={})
|
2014-05-28 09:00:36 +02:00
|
|
|
@metadata.deep_merge!(meta)
|
2014-04-28 08:21:12 +02:00
|
|
|
end
|
|
|
|
|
2014-05-28 09:00:36 +02:00
|
|
|
# The metadata for this resource
|
2012-05-07 23:41:39 +02:00
|
|
|
# @return [Hash]
|
2014-05-28 09:00:36 +02:00
|
|
|
attr_reader :metadata
|
2012-04-30 07:32:52 +02:00
|
|
|
|
2014-04-28 06:54:53 +02:00
|
|
|
# Data about this resource, populated from frontmatter or extensions.
|
|
|
|
# @return [HashWithIndifferentAccess]
|
|
|
|
def data
|
2014-04-28 08:21:12 +02:00
|
|
|
# TODO: Should this really be a HashWithIndifferentAccess?
|
|
|
|
::Middleman::Util.recursively_enhance(metadata[:page]).freeze
|
2014-04-28 06:54:53 +02:00
|
|
|
end
|
|
|
|
|
2014-04-28 08:21:12 +02:00
|
|
|
# Options about how this resource is rendered, such as its :layout,
|
|
|
|
# :renderer_options, and whether or not to use :directory_indexes.
|
|
|
|
# @return [Hash]
|
|
|
|
def options
|
|
|
|
metadata[:options]
|
|
|
|
end
|
|
|
|
|
|
|
|
# Local variable mappings that are used when rendering the template for this resource.
|
|
|
|
# @return [Hash]
|
|
|
|
def locals
|
|
|
|
metadata[:locals]
|
2012-04-30 07:32:52 +02:00
|
|
|
end
|
2012-08-14 00:39:06 +02:00
|
|
|
|
2012-05-07 23:41:39 +02:00
|
|
|
# Extension of the path (i.e. '.js')
|
|
|
|
# @return [String]
|
|
|
|
def ext
|
|
|
|
File.extname(path)
|
|
|
|
end
|
2012-08-14 00:39:06 +02:00
|
|
|
|
2012-05-07 23:41:39 +02:00
|
|
|
# Render this resource
|
|
|
|
# @return [String]
|
2014-01-04 00:49:54 +01:00
|
|
|
def render(opts={}, locs={})
|
2014-04-29 20:43:05 +02:00
|
|
|
return ::Middleman::FileRenderer.new(@app, source_file).template_data_for_file unless template?
|
2012-04-04 19:26:07 +02:00
|
|
|
|
2014-04-28 07:24:27 +02:00
|
|
|
relative_source = Pathname(source_file).relative_path_from(Pathname(@app.root))
|
2012-07-29 19:22:57 +02:00
|
|
|
|
2014-04-28 07:24:27 +02:00
|
|
|
@app.instrument 'render.resource', path: relative_source, destination_path: destination_path do
|
2014-04-28 08:02:20 +02:00
|
|
|
md = metadata
|
2012-07-15 20:04:45 +02:00
|
|
|
opts = md[:options].deep_merge(opts)
|
|
|
|
locs = md[:locals].deep_merge(locs)
|
2014-04-29 19:50:21 +02:00
|
|
|
locs[:current_path] ||= destination_path
|
2013-06-24 22:39:14 +02:00
|
|
|
|
|
|
|
# Certain output file types don't use layouts
|
2014-04-29 19:50:21 +02:00
|
|
|
unless opts.key?(:layout)
|
|
|
|
opts[:layout] = false if %w(.js .json .css .txt).include?(ext)
|
2013-06-24 22:39:14 +02:00
|
|
|
end
|
|
|
|
|
2014-01-03 22:15:02 +01:00
|
|
|
renderer = ::Middleman::TemplateRenderer.new(@app, source_file)
|
2014-01-04 00:49:54 +01:00
|
|
|
renderer.render(locs, opts)
|
2012-07-15 20:04:45 +02:00
|
|
|
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
|
|
|
# A path without the directory index - so foo/index.html becomes
|
|
|
|
# just foo. Best for linking.
|
|
|
|
# @return [String]
|
|
|
|
def url
|
2012-07-13 03:27:05 +02:00
|
|
|
url_path = destination_path
|
2014-04-28 07:24:27 +02:00
|
|
|
if @app.config[:strip_index_file]
|
|
|
|
url_path = url_path.sub(/(^|\/)#{Regexp.escape(@app.config[:index_file])}$/,
|
|
|
|
@app.config[:trailing_slash] ? '/' : '')
|
2012-07-13 03:27:05 +02:00
|
|
|
end
|
2014-04-28 07:24:27 +02:00
|
|
|
File.join(@app.config[:http_prefix], url_path)
|
2012-05-07 23:41:39 +02:00
|
|
|
end
|
2012-12-31 05:36:06 +01:00
|
|
|
|
|
|
|
# Whether the source file is binary.
|
|
|
|
#
|
2013-03-28 19:15:33 +01:00
|
|
|
# @return [Boolean]
|
2012-12-31 05:36:06 +01:00
|
|
|
def binary?
|
2014-07-04 04:44:26 +02:00
|
|
|
source_file && ::Middleman::Util.binary?(source_file)
|
2012-12-31 05:36:06 +01:00
|
|
|
end
|
2014-07-04 05:41:44 +02:00
|
|
|
|
|
|
|
# Ignore a resource directly, without going through the whole
|
|
|
|
# ignore filter stuff.
|
|
|
|
# @return [void]
|
|
|
|
def ignore!
|
|
|
|
@ignored = true
|
|
|
|
end
|
|
|
|
|
|
|
|
# Whether the Resource is ignored
|
|
|
|
# @return [Boolean]
|
|
|
|
def ignored?
|
|
|
|
return true if @ignored
|
|
|
|
# Ignore based on the source path (without template extensions)
|
|
|
|
return true if @app.sitemap.ignored?(path)
|
|
|
|
# This allows files to be ignored by their source file name (with template extensions)
|
|
|
|
!proxy? && @app.sitemap.ignored?(source_file.sub("#{@app.source_dir}/", ''))
|
|
|
|
end
|
|
|
|
|
|
|
|
# The preferred MIME content type for this resource based on extension or metadata
|
|
|
|
# @return [String] MIME type for this resource
|
|
|
|
def content_type
|
|
|
|
mime_type = options[:content_type] || ::Rack::Mime.mime_type(ext, nil)
|
|
|
|
return mime_type if mime_type
|
|
|
|
|
|
|
|
if proxy?
|
|
|
|
proxied_to_resource.content_type
|
|
|
|
else
|
|
|
|
nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# Whether this page is a proxy
|
|
|
|
# @return [Boolean]
|
|
|
|
def proxy?
|
|
|
|
@proxied_to
|
|
|
|
end
|
|
|
|
|
|
|
|
# Set this page to proxy to a target path
|
|
|
|
# @param [String] target
|
|
|
|
# @return [void]
|
|
|
|
def proxy_to(target)
|
|
|
|
target = ::Middleman::Util.normalize_path(target)
|
|
|
|
raise "You can't proxy #{path} to itself!" if target == path
|
|
|
|
@proxied_to = target
|
|
|
|
end
|
|
|
|
|
|
|
|
# The path of the page this page is proxied to, or nil if it's not proxied.
|
|
|
|
# @return [String]
|
|
|
|
attr_reader :proxied_to
|
|
|
|
|
|
|
|
# The resource for the page this page is proxied to. Throws an exception
|
|
|
|
# if there is no resource.
|
|
|
|
# @return [Sitemap::Resource]
|
|
|
|
def proxied_to_resource
|
|
|
|
proxy_resource = @store.find_resource_by_path(proxied_to)
|
|
|
|
|
|
|
|
unless proxy_resource
|
|
|
|
raise "Path #{path} proxies to unknown file #{proxied_to}:#{@store.resources.map(&:path)}"
|
|
|
|
end
|
|
|
|
|
|
|
|
if proxy_resource.proxy?
|
|
|
|
raise "You can't proxy #{path} to #{proxied_to} which is itself a proxy."
|
|
|
|
end
|
|
|
|
|
|
|
|
proxy_resource
|
|
|
|
end
|
2012-04-04 19:26:07 +02:00
|
|
|
end
|
|
|
|
end
|
2012-05-19 22:28:59 +02:00
|
|
|
end
|