Convert more basic config commands to ConfigExtensions

This commit is contained in:
Thomas Reynolds 2016-01-12 10:35:12 -08:00
parent 56d71fe7d6
commit 95c7a53291
10 changed files with 118 additions and 241 deletions

View file

@ -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

View file

@ -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<Middleman::Sitemap::Resource>
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

View file

@ -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

View file

@ -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 }

View file

@ -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)

View file

@ -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),

View file

@ -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<Middleman::Sitemap::Resource>
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

View file

@ -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<Middleman::Sitemap::Resource>
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

View file

@ -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'

View file

@ -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)