diff --git a/middleman-core/lib/middleman-core/contracts.rb b/middleman-core/lib/middleman-core/contracts.rb index 3afedbfe..e99d0d59 100644 --- a/middleman-core/lib/middleman-core/contracts.rb +++ b/middleman-core/lib/middleman-core/contracts.rb @@ -1,112 +1,23 @@ -if ENV['TEST'] || ENV['CONTRACTS'] == 'true' - require 'contracts' - require 'hamster' - - module Contracts - class IsA - def self.[](val) - @lookup ||= {} - @lookup[val] ||= new(val) - end - - def initialize(val) - @val = val - end - - def valid?(val) - val.is_a? @val.constantize - end - end - - VectorOf = Contracts::CollectionOf::Factory.new(::Hamster::Vector) - ResourceList = Contracts::ArrayOf[IsA['Middleman::Sitemap::Resource']] - end -else - module Contracts - def self.included(base) - base.extend self - end - - # rubocop:disable MethodName - def Contract(*) - end - - class Callable - def self.[](*) - end - end - - class Bool - end - - class Num - end - - class Pos - end - - class Neg - end - - class Any - end - - class None - end - - class Or < Callable - end - - class Xor < Callable - end - - class And < Callable - end - - class Not < Callable - end - - class RespondTo < Callable - end - - class Send < Callable - end - - class Exactly < Callable - end - - class ArrayOf < Callable - end - - class ResourceList < Callable - end - - class Args < Callable - end - - class HashOf < Callable - end - - class Bool - end - - class Maybe < Callable - end - - class IsA < Callable - end - - class SetOf < Callable - end - - class Frozen < Callable - end - - class VectorOf < Callable - end - end -end +require 'contracts' +require 'hamster' module Contracts + class IsA + def self.[](val) + @lookup ||= {} + @lookup[val] ||= new(val) + end + + def initialize(val) + @val = val + end + + def valid?(val) + val.is_a? @val.constantize + end + end + + VectorOf = ::Contracts::CollectionOf::Factory.new(::Hamster::Vector) + ResourceList = ::Contracts::ArrayOf[IsA['Middleman::Sitemap::Resource']] PATH_MATCHER = Or[String, RespondTo[:match], RespondTo[:call], RespondTo[:to_s]] end diff --git a/middleman-core/lib/middleman-core/core_extensions/routing.rb b/middleman-core/lib/middleman-core/core_extensions/routing.rb index dcbea0ec..cf024d7d 100644 --- a/middleman-core/lib/middleman-core/core_extensions/routing.rb +++ b/middleman-core/lib/middleman-core/core_extensions/routing.rb @@ -1,7 +1,7 @@ # Routing extension module Middleman module CoreExtensions - class Routing < Extension + class Routing < ConfigExtension # 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 = 10 @@ -9,24 +9,28 @@ module Middleman # Expose the `page` method to config. expose_to_config :page - def initialize(app, options_hash={}, &block) - super + PageDescriptor = Struct.new(:path, :metadata) do + def execute_descriptor(app, resources) + normalized_path = path.dup - @page_configs = Set.new - end - - # @return Array - Contract ResourceList => ResourceList - def manipulate_resource_list(resources) - resources.each do |resource| - @page_configs.each do |p| - resource.add_metadata(p[:metadata]) if Middleman::Util.path_match(p[:path], "/#{resource.path}") + if normalized_path.is_a?(String) && !normalized_path.include?('*') + # Normalize path + normalized_path = ::Middleman::Util.normalize_path(normalized_path) + if normalized_path.end_with?('/') || app.files.by_type(:source).watchers.any? { |w| (w.directory + Pathname(normalized_path)).directory? } + normalized_path = ::File.join(normalized_path, app.config[:index_file]) + end end + + normalized_path = '/' + ::Middleman::Util.strip_leading_slash(normalized_path) if normalized_path.is_a?(String) + + resources + .select { |r| ::Middleman::Util.path_match(normalized_path, "/#{r.path}")} + .each { |r| r.add_metadata(metadata) } + + resources end end - PageDescriptor = Struct.new(:path, :metadata) - # The page method allows options to be set for a given source path, regex, or glob. # Options that may be set include layout, locals, andx ignore. # @@ -44,28 +48,18 @@ module Middleman # @option opts [Hash] locals Local variables for the template. These will be available when the template renders. # @option opts [Hash] data Extra metadata to add to the page. This is the same as frontmatter, though frontmatter will take precedence over metadata defined here. Available via {Resource#data}. # @return [void] - Contract Or[String, Regexp], Hash => Any + Contract Or[String, Regexp], Hash => PageDescriptor def page(path, opts={}) options = opts.dup # Default layout metadata = { - options: options, locals: options.delete(:locals) || {}, - page: options.delete(:data) || {} + page: options.delete(:data) || {}, + options: options } - if path.is_a?(String) && !path.include?('*') - # Normalize path - path = Middleman::Util.normalize_path(path) - if path.end_with?('/') || app.files.by_type(:source).watchers.any? { |w| (w.directory + Pathname(path)).directory? } - path = File.join(path, @app.config[:index_file]) - end - end - - path = '/' + Util.strip_leading_slash(path) if path.is_a?(String) - - @page_configs << PageDescriptor.new(path, metadata) + PageDescriptor.new(path, metadata) end end end diff --git a/middleman-core/lib/middleman-core/sitemap/extensions/ignores.rb b/middleman-core/lib/middleman-core/sitemap/extensions/ignores.rb index 3a0fa464..4caa2156 100644 --- a/middleman-core/lib/middleman-core/sitemap/extensions/ignores.rb +++ b/middleman-core/lib/middleman-core/sitemap/extensions/ignores.rb @@ -5,13 +5,13 @@ module Middleman class Ignores < ConfigExtension self.resource_list_manipulator_priority = 0 - expose_to_config ignore: :create_ignore + expose_to_config :ignore # Ignore a path or add an ignore callback # @param [String, Regexp] path Path glob expression, or path regex # @return [IgnoreDescriptor] Contract Maybe[Or[String, Regexp]], Maybe[Proc] => RespondTo[:execute_descriptor] - def create_ignore(path=nil, &block) + def ignore(path=nil, &block) @app.sitemap.invalidate_resources_not_ignored_cache! IgnoreDescriptor.new(path, block) end diff --git a/middleman-core/lib/middleman-core/sitemap/extensions/import.rb b/middleman-core/lib/middleman-core/sitemap/extensions/import.rb index eb7bd4e5..63b06e13 100644 --- a/middleman-core/lib/middleman-core/sitemap/extensions/import.rb +++ b/middleman-core/lib/middleman-core/sitemap/extensions/import.rb @@ -7,11 +7,8 @@ module Middleman class Import < ConfigExtension self.resource_list_manipulator_priority = 1 - # Expose `create_import_file` to config as `import_file` - expose_to_config import_file: :create_import_file - - # Expose `create_import_path` to config as `import_path` - expose_to_config import_path: :create_import_path + # Expose methods + expose_to_config :import_file, :import_path ImportFileDescriptor = Struct.new(:from, :to) do def execute_descriptor(app, resources) @@ -42,7 +39,7 @@ module Middleman # @param [String] to The new path. # @return [void] Contract String, String => ImportFileDescriptor - def create_import_file(from, to) + def import_file(from, to) ImportFileDescriptor.new( File.expand_path(from, @app.root), ::Middleman::Util.normalize_path(to) @@ -54,7 +51,7 @@ module Middleman # @param [Proc] block Renaming method # @return [void] Contract String, Maybe[Proc] => ImportPathDescriptor - def create_import_path(from, &block) + def import_path(from, &block) ImportPathDescriptor.new( from, block_given? ? block : proc { |path| path } diff --git a/middleman-core/lib/middleman-core/sitemap/extensions/move_file.rb b/middleman-core/lib/middleman-core/sitemap/extensions/move_file.rb index 7f8a11bb..97c6d6f9 100644 --- a/middleman-core/lib/middleman-core/sitemap/extensions/move_file.rb +++ b/middleman-core/lib/middleman-core/sitemap/extensions/move_file.rb @@ -9,8 +9,8 @@ module Middleman class MoveFile < ConfigExtension self.resource_list_manipulator_priority = 101 - # Expose `create_move_file` to config as `move_file` - expose_to_config move_file: :create_move_file + # Expose `move_file` + expose_to_config :move_file MoveFileDescriptor = Struct.new(:from, :to) do def execute_descriptor(_app, resources) @@ -27,7 +27,7 @@ module Middleman # @param [String] to The new path. # @return [void] Contract String, String => MoveFileDescriptor - def create_move_file(from, to) + def move_file(from, to) MoveFileDescriptor.new( ::Middleman::Util.normalize_path(from), ::Middleman::Util.normalize_path(to) diff --git a/middleman-core/lib/middleman-core/sitemap/extensions/proxies.rb b/middleman-core/lib/middleman-core/sitemap/extensions/proxies.rb index 8ff8087f..ca0b7895 100644 --- a/middleman-core/lib/middleman-core/sitemap/extensions/proxies.rb +++ b/middleman-core/lib/middleman-core/sitemap/extensions/proxies.rb @@ -9,8 +9,8 @@ module Middleman class Proxies < ConfigExtension self.resource_list_manipulator_priority = 0 - # Expose `create_proxy` to config as `proxy` - expose_to_config proxy: :create_proxy + # Expose `proxy` + expose_to_config :proxy # Setup a proxy from a path to a target # @param [String] path The new, proxied path to create @@ -22,7 +22,7 @@ module Middleman # @option opts [Hash] data Extra metadata to add to the page. This is the same as frontmatter, though frontmatter will take precedence over metadata defined here. Available via {Resource#data}. # @return [ProxyDescriptor] Contract String, String, Maybe[Hash] => RespondTo[:execute_descriptor] - def create_proxy(path, target, opts={}) + def proxy(path, target, opts={}) ProxyDescriptor.new( ::Middleman::Util.normalize_path(path), ::Middleman::Util.normalize_path(target), diff --git a/middleman-core/lib/middleman-core/sitemap/extensions/redirects.rb b/middleman-core/lib/middleman-core/sitemap/extensions/redirects.rb index f6ac7bcc..47ffd726 100644 --- a/middleman-core/lib/middleman-core/sitemap/extensions/redirects.rb +++ b/middleman-core/lib/middleman-core/sitemap/extensions/redirects.rb @@ -6,43 +6,31 @@ module Middleman module Extensions # Manages the list of proxy configurations and manipulates the sitemap # to include new resources based on those configurations - class Redirects < Extension + class Redirects < ConfigExtension self.resource_list_manipulator_priority = 0 - # Expose `create_redirect` to config as `redirect` - expose_to_config redirect: :create_redirect + # Expose `redirect` + expose_to_config :redirect - def initialize(app, config={}, &block) - super + RedirectDescriptor = Struct.new(:path, :to, :template) do + def execute_descriptor(app, resources) + r = RedirectResource.new( + app.sitemap, + path, + to + ) + r.output = template if template - @redirects = {} + resources + [r] + end end # Setup a redirect from a path to a target # @param [String] path # @param [Hash] opts The :to value gives a target path - Contract String, ({ to: Or[String, IsA['Middleman::Sitemap::Resource']] }), Maybe[Proc] => Any - def create_redirect(path, opts={}, &block) - opts[:template] = block if block_given? - - @redirects[path] = opts - - @app.sitemap.rebuild_resource_list!(:added_redirect) - end - - # Update the main sitemap resource list - # @return Array - Contract ResourceList => ResourceList - def manipulate_resource_list(resources) - resources + @redirects.map do |path, opts| - r = RedirectResource.new( - @app.sitemap, - path, - opts[:to] - ) - r.output = opts[:template] if opts[:template] - r - end + Contract String, ({ to: Or[String, ::Middleman::Sitemap::Resource] }), Maybe[Proc] => RedirectDescriptor + def redirect(path, opts={}, &block) + RedirectDescriptor.new(path, opts[:to], block_given? ? block : nil) end 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 245df873..ede1cc64 100644 --- a/middleman-core/lib/middleman-core/sitemap/extensions/request_endpoints.rb +++ b/middleman-core/lib/middleman-core/sitemap/extensions/request_endpoints.rb @@ -3,53 +3,35 @@ require 'middleman-core/sitemap/resource' module Middleman module Sitemap module Extensions - class RequestEndpoints < Extension + class RequestEndpoints < ConfigExtension self.resource_list_manipulator_priority = 0 - # Expose `create_endpoint` to config as `endpoint` - expose_to_config endpoint: :create_endpoint + # Expose `endpoint` + expose_to_config :endpoint - # Manages the list of proxy configurations and manipulates the sitemap - # to include new resources based on those configurations - def initialize(app, config={}, &block) - super + EndpointDescriptor = Struct.new(:path, :request_path, :block) do + def execute_descriptor(app, resources) + r = EndpointResource.new( + app.sitemap, + path, + request_path + ) + r.output = block if block - @endpoints = {} + resources + [r] + end end # Setup a proxy from a path to a target # @param [String] path # @param [Hash] opts The :path value gives a request path if it # differs from the output path - Contract String, Or[({ path: String }), Proc] => Any - def create_endpoint(path, opts={}, &block) - endpoint = { - request_path: path - } - + Contract String, Or[{ path: String }, Proc] => EndpointDescriptor + def endpoint(path, opts={}, &block) if block_given? - endpoint[:output] = block + EndpointDescriptor.new(path, path, block) else - endpoint[:request_path] = opts[:path] if opts.key?(:path) - end - - @endpoints[path] = endpoint - - @app.sitemap.rebuild_resource_list!(:added_endpoint) - end - - # Update the main sitemap resource list - # @return Array - Contract ResourceList => ResourceList - def manipulate_resource_list(resources) - resources + @endpoints.map do |path, config| - r = EndpointResource.new( - @app.sitemap, - path, - config[:request_path] - ) - r.output = config[:output] if config.key?(:output) - r + EndpointDescriptor.new(path, opts[:path] || path, nil) end end end diff --git a/middleman-core/lib/middleman-core/sitemap/store.rb b/middleman-core/lib/middleman-core/sitemap/store.rb index b433417f..985b3ae4 100644 --- a/middleman-core/lib/middleman-core/sitemap/store.rb +++ b/middleman-core/lib/middleman-core/sitemap/store.rb @@ -3,46 +3,48 @@ require 'active_support/core_ext/hash/deep_merge' require 'monitor' require 'hamster' +require 'middleman-core/extensions' + # Files on Disk -Middleman::Extensions.register :sitemap_ondisk, auto_activate: :before_configuration do +::Middleman::Extensions.register :sitemap_ondisk, auto_activate: :before_configuration do require 'middleman-core/sitemap/extensions/on_disk' - Middleman::Sitemap::Extensions::OnDisk + ::Middleman::Sitemap::Extensions::OnDisk end # Files on Disk (outside the project root) -Middleman::Extensions.register :sitemap_import, auto_activate: :before_configuration do +::Middleman::Extensions.register :sitemap_import, auto_activate: :before_configuration do require 'middleman-core/sitemap/extensions/import' - Middleman::Sitemap::Extensions::Import + ::Middleman::Sitemap::Extensions::Import end # Endpoints -Middleman::Extensions.register :sitemap_endpoint, auto_activate: :before_configuration do +::Middleman::Extensions.register :sitemap_endpoint, auto_activate: :before_configuration do require 'middleman-core/sitemap/extensions/request_endpoints' - Middleman::Sitemap::Extensions::RequestEndpoints + ::Middleman::Sitemap::Extensions::RequestEndpoints end # Proxies -Middleman::Extensions.register :sitemap_proxies, auto_activate: :before_configuration do +::Middleman::Extensions.register :sitemap_proxies, auto_activate: :before_configuration do require 'middleman-core/sitemap/extensions/proxies' - Middleman::Sitemap::Extensions::Proxies + ::Middleman::Sitemap::Extensions::Proxies end # Redirects -Middleman::Extensions.register :sitemap_redirects, auto_activate: :before_configuration do +::Middleman::Extensions.register :sitemap_redirects, auto_activate: :before_configuration do require 'middleman-core/sitemap/extensions/redirects' - Middleman::Sitemap::Extensions::Redirects + ::Middleman::Sitemap::Extensions::Redirects end # Move Files -Middleman::Extensions.register :sitemap_move_files, auto_activate: :before_configuration do +::Middleman::Extensions.register :sitemap_move_files, auto_activate: :before_configuration do require 'middleman-core/sitemap/extensions/move_file' - Middleman::Sitemap::Extensions::MoveFile + ::Middleman::Sitemap::Extensions::MoveFile end # Ignores -Middleman::Extensions.register :sitemap_ignore, auto_activate: :before_configuration do +::Middleman::Extensions.register :sitemap_ignore, auto_activate: :before_configuration do require 'middleman-core/sitemap/extensions/ignores' - Middleman::Sitemap::Extensions::Ignores + ::Middleman::Sitemap::Extensions::Ignores end require 'middleman-core/contracts' diff --git a/middleman-core/lib/middleman-core/util.rb b/middleman-core/lib/middleman-core/util.rb index e994e09c..c4210a0e 100644 --- a/middleman-core/lib/middleman-core/util.rb +++ b/middleman-core/lib/middleman-core/util.rb @@ -10,6 +10,9 @@ require 'rack/mime' # DbC require 'middleman-core/contracts' +require 'middleman-core/application' +require 'middleman-core/sources' +require 'middleman-core/sitemap/resource' # Indifferent Access require 'hashie' @@ -104,7 +107,7 @@ module Middleman Contract String => String def normalize_path(path) # The tr call works around a bug in Ruby's Unicode handling - URI.decode(path).sub(%r{^/}, '').tr('', '') + ::URI.decode(path).sub(%r{^/}, '').tr('', '') end # This is a separate method from normalize_path in case we @@ -167,7 +170,7 @@ module Middleman # @param [String, Symbol] source The path to the file # @param [Hash] options Data to pass through. # @return [String] - Contract IsA['Middleman::Application'], Symbol, Or[String, Symbol], Hash => String + Contract ::Middleman::Application, Symbol, Or[String, Symbol], Hash => String def asset_path(app, kind, source, options={}) return source if source.to_s.include?('//') || source.to_s.start_with?('data:') @@ -198,7 +201,7 @@ module Middleman # @param [String] prefix The type prefix (such as "images") # @param [Hash] options Data to pass through. # @return [String] The fully qualified asset url - Contract IsA['Middleman::Application'], String, String, Hash => String + Contract ::Middleman::Application, String, String, Hash => String def asset_url(app, path, prefix='', options={}) # Don't touch assets which already have a full path return path if path.include?('//') || path.start_with?('data:') @@ -221,7 +224,7 @@ module Middleman end end - final_result = URI.encode(relative_path_from_resource(options[:current_resource], result, options[:relative])) + final_result = ::URI.encode(relative_path_from_resource(options[:current_resource], result, options[:relative])) result_uri = URI(final_result) result_uri.query = uri.query @@ -232,7 +235,7 @@ module Middleman # Given a source path (referenced either absolutely or relatively) # or a Resource, this will produce the nice URL configured for that # path, respecting :relative_links, directory indexes, etc. - Contract IsA['Middleman::Application'], Or[String, IsA['Middleman::Sitemap::Resource']], Hash => String + Contract ::Middleman::Application, Or[String, ::Middleman::Sitemap::Resource], Hash => String def url_for(app, path_or_resource, options={}) # Handle Resources and other things which define their own url method url = if path_or_resource.respond_to?(:url) @@ -244,7 +247,7 @@ module Middleman # Try to parse URL begin uri = URI(url) - rescue URI::InvalidURIError + rescue ::URI::InvalidURIError # Nothing we can do with it, it's not really a URI return url end @@ -287,7 +290,7 @@ module Middleman if resource uri.path = if this_resource - URI.encode(relative_path_from_resource(this_resource, resource_url, effective_relative)) + ::URI.encode(relative_path_from_resource(this_resource, resource_url, effective_relative)) else resource_url end @@ -311,7 +314,7 @@ module Middleman # @param [String] path Request path/ # @param [Middleman::Application] app The requesting app. # @return [String] Path with index file if necessary. - Contract String, IsA['Middleman::Application'] => String + Contract String, ::Middleman::Application => String def full_path(path, app) resource = app.sitemap.find_resource_by_destination_path(path) @@ -422,7 +425,7 @@ module Middleman # @param [String] resource_url The target url. # @param [Boolean] relative If the path should be relative. # @return [String] - Contract IsA['Middleman::Sitemap::Resource'], String, Bool => String + Contract ::Middleman::Sitemap::Resource, String, Bool => String def relative_path_from_resource(curr_resource, resource_url, relative) # Switch to the relative path between resource and the given resource # if we've been asked to. @@ -480,7 +483,7 @@ module Middleman # # @param [Pathname] path The path. # @return [Middleman::SourceFile] - Contract Pathname, Pathname, Symbol, Bool => IsA['Middleman::SourceFile'] + Contract Pathname, Pathname, Symbol, Bool => ::Middleman::SourceFile def path_to_source_file(path, directory, type, destination_dir) types = Set.new([type]) @@ -496,7 +499,7 @@ module Middleman # @param [Middleman::Application] app The app. # @param [Pathname] files The original touched file paths. # @return [Middleman::SourceFile] All related file paths, not including the source file paths. - Contract IsA['Middleman::Application'], ArrayOf[Pathname] => ArrayOf[IsA['Middleman::SourceFile']] + Contract ::Middleman::Application, ArrayOf[Pathname] => ArrayOf[::Middleman::SourceFile] def find_related_files(app, files) all_extensions = files.flat_map { |f| collect_extensions(f.to_s) } @@ -546,7 +549,7 @@ module Middleman tmpl_src = tmpl_src.gsub(/:([A-Za-z0-9]+)/, '{\1}') end - Addressable::Template.new ::Middleman::Util.normalize_path(tmpl_src) + ::Addressable::Template.new ::Middleman::Util.normalize_path(tmpl_src) end # Apply a URI template with the given data, producing a normalized @@ -556,7 +559,7 @@ module Middleman # @param [Hash] data # @return [String] normalized path def apply_uri_template(template, data) - ::Middleman::Util.normalize_path Addressable::URI.unencode(template.expand(data)).to_s + ::Middleman::Util.normalize_path ::Addressable::URI.unencode(template.expand(data)).to_s end # Use a template to extract parameters from a path, and validate some special (date)