diff --git a/middleman-core/lib/middleman-core/config_context.rb b/middleman-core/lib/middleman-core/config_context.rb index 96f30d1d..90922ea9 100644 --- a/middleman-core/lib/middleman-core/config_context.rb +++ b/middleman-core/lib/middleman-core/config_context.rb @@ -1,10 +1,5 @@ -require 'middleman-core/core_extensions/routing' - module Middleman class ConfigContext - # page routing - include Middleman::CoreExtensions::Routing - attr_reader :app # Whitelist methods that can reach out. diff --git a/middleman-core/lib/middleman-core/core_extensions.rb b/middleman-core/lib/middleman-core/core_extensions.rb index e85afac3..4ff25339 100644 --- a/middleman-core/lib/middleman-core/core_extensions.rb +++ b/middleman-core/lib/middleman-core/core_extensions.rb @@ -45,6 +45,12 @@ Middleman::Extensions.register :lorem, auto_activate: :before_configuration do Middleman::Extensions::Lorem end +Middleman::Extensions.register :routing, auto_activate: :before_configuration do + require 'middleman-core/core_extensions/routing' + Middleman::CoreExtensions::Routing +end + + ### # Setup Optional Extensions ### diff --git a/middleman-core/lib/middleman-core/core_extensions/extensions.rb b/middleman-core/lib/middleman-core/core_extensions/extensions.rb index 7a76776c..2cfb6c41 100644 --- a/middleman-core/lib/middleman-core/core_extensions/extensions.rb +++ b/middleman-core/lib/middleman-core/core_extensions/extensions.rb @@ -105,6 +105,9 @@ module Middleman run_hook :before_configuration + # Evaluate a passed block if given + config_context.instance_exec(&block) if block_given? + # Check for and evaluate local configuration in `config.rb` local_config = File.join(root, 'config.rb') if File.exist? local_config diff --git a/middleman-core/lib/middleman-core/core_extensions/routing.rb b/middleman-core/lib/middleman-core/core_extensions/routing.rb index cc4d3e3c..07150f7e 100644 --- a/middleman-core/lib/middleman-core/core_extensions/routing.rb +++ b/middleman-core/lib/middleman-core/core_extensions/routing.rb @@ -1,16 +1,55 @@ # Routing extension module Middleman module CoreExtensions - module Routing - # The page method allows the layout to be set on a specific path + class Routing < Extension + # This should always run late, but not as late as :directory_indexes, + # so it can add metadata to any pages generated by other extensions + self.resource_list_manipulator_priority = 90 + + def initialize(app, options_hash={}, &block) + super + + @page_configs = [] + end + + def before_configuration + app.add_to_config_context :page, &method(:page) + end + + def manipulate_resource_list(resources) + resources.each do |resource| + @page_configs.each do |matcher, metadata| + case matcher + when Regexp + next unless resource.path =~ matcher + when String + next unless File.fnmatch('/' + Util.strip_leading_slash(matcher), "/#{resource.path}") + end + + resource.add_metadata metadata + end + end + end + + # The page method allows options to be set for a given source path, regex, or glob. + # Options that may be set include layout, locals, proxy, andx ignore. # - # page "/about.html", layout: false - # page "/", layout: :homepage_layout + # @example + # page '/about.html', layout: false + # @example + # page '/index.html', layout: :homepage_layout + # @example + # page '/foo.html', locals: { foo: 'bar' } # - # @param [String] url - # @param [Hash] opts + # @param [String, Regexp] path A source path, or a Regexp/glob that can match multiple resources. + # @params [Hash] opts Options to apply to all matching resources. Undocumented options are passed on as page metadata to be used by extensions. + # @option opts [Symbol, Boolean, String] layout The layout name to use (e.g. `:article`) or `false` to disable layout. + # @option opts [Boolean] directory_indexes Whether or not the `:directory_indexes` extension applies to these paths. + # @option opts [Hash] locals Local variables for the template. These will be available when the template renders. + # @option opts [String] proxy The source path for a template to proxy this path to. Only valid when a single path is provided. Prefer using the `proxy` method to do this. + # @option opts [Boolean] ignore Set to `true` to ignore the provided path(s). Only valid when a single path is provided. Prefer using the `ignore` method to do this. # @return [void] - def page(url, opts={}) + def page(path, opts={}) options = opts.dup # Default layout @@ -19,30 +58,26 @@ module Middleman # TODO: You can set options and locals, but not data metadata = { options: options, locals: options.delete(:locals) || {} } - # If the url is a regexp - unless url.is_a?(Regexp) || url.include?('*') + # If the path is a regexp + unless path.is_a?(Regexp) || path.include?('*') # Normalized path - url = '/' + Middleman::Util.normalize_path(url) - if url.end_with?('/') || File.directory?(File.join(@app.source_dir, url)) - url = File.join(url, @app.config[:index_file]) + path = '/' + Middleman::Util.normalize_path(path) + if path.end_with?('/') || File.directory?(File.join(@app.source_dir, path)) + path = File.join(path, @app.config[:index_file]) end # Setup proxy if target = options.delete(:proxy) # TODO: deprecate proxy through page? - @app.proxy(url, target, opts.dup) + @app.proxy(path, target, opts.dup) return elsif options.delete(:ignore) # TODO: deprecate ignore through page? - @app.ignore(url) + @app.ignore(path) end end - # Setup a metadata matcher for rendering those options - # TODO: How to get rid of this? Perhaps a separate extension that manipulates - # in this sort of data? - # This is harder because sitemap isn't available. - @app.sitemap.provides_metadata_for_path(url) { |_| metadata } + @page_configs << [path, metadata] end end end diff --git a/middleman-core/lib/middleman-core/sitemap/extensions/metadata_for_path.rb b/middleman-core/lib/middleman-core/sitemap/extensions/metadata_for_path.rb deleted file mode 100644 index 10463cab..00000000 --- a/middleman-core/lib/middleman-core/sitemap/extensions/metadata_for_path.rb +++ /dev/null @@ -1,17 +0,0 @@ -module Middleman - module Sitemap - module Extensions - - # Add metadata to Resources based on path matchers. This exists - # entirely to support the "page" method in config.rb. - - # TODO: This requires the concept of priority for sitemap manipulators - # in order for it to always run after all other manipulators. - class MetadataForPath - def initialize(sitemap) - @app = sitemap.app - end - end - end - end -end diff --git a/middleman-core/lib/middleman-core/sitemap/extensions/redirects.rb b/middleman-core/lib/middleman-core/sitemap/extensions/redirects.rb index 058595f2..60e5bdbb 100644 --- a/middleman-core/lib/middleman-core/sitemap/extensions/redirects.rb +++ b/middleman-core/lib/middleman-core/sitemap/extensions/redirects.rb @@ -53,7 +53,7 @@ module Middleman end def render(*) - url = ::Middleman::Util.url_for(store.app, @request_path, + url = ::Middleman::Util.url_for(@store.app, @request_path, relative: false, find_resource: true ) @@ -83,10 +83,6 @@ module Middleman false end - def metadata - @local_metadata.dup - end - def get_source_file '' end diff --git a/middleman-core/lib/middleman-core/sitemap/extensions/request_endpoints.rb b/middleman-core/lib/middleman-core/sitemap/extensions/request_endpoints.rb index cc980879..18bc996e 100644 --- a/middleman-core/lib/middleman-core/sitemap/extensions/request_endpoints.rb +++ b/middleman-core/lib/middleman-core/sitemap/extensions/request_endpoints.rb @@ -71,8 +71,8 @@ module Middleman false end - def metadata - @local_metadata.dup + def get_source_file + '' end end end diff --git a/middleman-core/lib/middleman-core/sitemap/resource.rb b/middleman-core/lib/middleman-core/sitemap/resource.rb index 5b039cc6..16ac10f0 100644 --- a/middleman-core/lib/middleman-core/sitemap/resource.rb +++ b/middleman-core/lib/middleman-core/sitemap/resource.rb @@ -42,7 +42,7 @@ module Middleman # 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. - @local_metadata = { options: {}, locals: {}, page: {} } + @metadata = { options: {}, locals: {}, page: {} } end # Whether this resource has a template file @@ -59,15 +59,12 @@ module Middleman # 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={}) - @local_metadata.deep_merge!(meta) + @metadata.deep_merge!(meta) end - # Get the metadata for both the current source_file and the current path + # The metadata for this resource # @return [Hash] - def metadata - # TODO: The only reason we call metadata_for_page is to power the "page" DSL - @store.metadata_for_path(path).deep_merge @local_metadata - end + attr_reader :metadata # Data about this resource, populated from frontmatter or extensions. # @return [HashWithIndifferentAccess] diff --git a/middleman-core/lib/middleman-core/sitemap/store.rb b/middleman-core/lib/middleman-core/sitemap/store.rb index 2de8ecf5..c12a1e6f 100644 --- a/middleman-core/lib/middleman-core/sitemap/store.rb +++ b/middleman-core/lib/middleman-core/sitemap/store.rb @@ -27,11 +27,9 @@ module Middleman def initialize(app) @app = app @resources = [] - @_cached_metadata = {} # TODO: Should this be a set or hash? @resource_list_manipulators = [] @needs_sitemap_rebuild = true - @provides_metadata_for_path = Set.new @lock = Monitor.new reset_lookup_cache! @@ -130,40 +128,6 @@ module Middleman @resources_not_ignored = nil end - # Register a handler to provide metadata on a url path - # Extensions authors should prefer adding metadata to Resources via a - # sitemap manipulator and Resource#add_metadata. - # - # @param [Regexp, String] matcher or glob string - def provides_metadata_for_path(matcher=nil, &block) - if block_given? - @provides_metadata_for_path << [block, matcher] - @_cached_metadata = {} - end - nil - end - - # Get the metadata for a specific source path - # @param [String] path Source path of a resource - # @return [Hash] - def metadata_for_path(path) - return @_cached_metadata[path] if @_cached_metadata.has_key?(path) - - blank_metadata = { options: {}, locals: {}, page: {} } - - @_cached_metadata[path] = @provides_metadata_for_path.inject(blank_metadata) do |result, (callback, matcher)| - case matcher - when Regexp - next result unless path =~ matcher - when String - next result unless File.fnmatch('/' + Util.strip_leading_slash(matcher), "/#{path}") - end - - metadata = callback.call(path) - result.deep_merge(metadata) - end - end - # Get the URL path for an on-disk file # @param [String] file # @return [String]