Name things :)

This commit is contained in:
Thomas Reynolds 2014-07-10 12:35:47 -07:00
parent 08b75f06ef
commit 6ccab8e071
13 changed files with 128 additions and 46 deletions

View file

@ -15,6 +15,8 @@ require 'hooks'
# Our custom logger
require 'middleman-core/logger'
require 'middleman-core/contracts'
require 'middleman-core/sitemap/store'
require 'middleman-core/configuration'
@ -30,6 +32,7 @@ require 'middleman-core/template_renderer'
module Middleman
class Application
extend Forwardable
include Contracts
class << self
# Global configuration for the whole Middleman project.
@ -165,7 +168,11 @@ module Middleman
attr_reader :config
attr_reader :generic_template_context
attr_reader :extensions
Contract None => SetOf['Middleman::Application::MiddlewareDescriptor']
attr_reader :middleware
Contract None => SetOf['Middleman::Application::MapDescriptor']
attr_reader :mappings
# Reference to Logger singleton
@ -179,8 +186,8 @@ module Middleman
# Search the root of the project for required files
$LOAD_PATH.unshift(root) unless $LOAD_PATH.include?(root)
@middleware = []
@mappings = []
@middleware = Set.new
@mappings = Set.new
@template_context_class = Class.new(Middleman::TemplateContext)
@generic_template_context = @template_context_class.new(self)
@ -296,20 +303,26 @@ module Middleman
File.join(root, config[:source])
end
MiddlewareDescriptor = Struct.new(:class, :options, :block)
# Use Rack middleware
#
# @param [Class] middleware Middleware module
# @return [void]
Contract Any, Args[Any], Proc => Any
def use(middleware, *args, &block)
@middleware << [middleware, args, block]
@middleware << MiddlewareDescriptor.new(middleware, args, block)
end
MapDescriptor = Struct.new(:path, :block)
# Add Rack App mapped to specific path
#
# @param [String] map Path to map
# @return [void]
Contract String, Proc => Any
def map(map, &block)
@mappings << [map, block]
@mappings << MapDescriptor.new(map, block)
end
# Work around this bug: http://bugs.ruby-lang.org/issues/4521

View file

@ -1,22 +1,56 @@
if ENV['TEST'] || ENV['CONTRACTS'] == 'true'
require 'contracts'
class IsA
def self.[](val)
@lookup ||= {}
@lookup[val] ||= new(val)
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
def initialize(val)
@val = val
class ArrayOf
def initialize(contract)
@contract = contract.is_a?(String) ? IsA[contract] : contract
end
end
def valid?(val)
val.is_a? @val.constantize
class SetOf < CallableClass
def initialize(contract)
@contract = contract.is_a?(String) ? IsA[contract] : contract
end
def valid?(vals)
return false unless vals.is_a?(Set)
vals.all? do |val|
res, _ = Contract.valid?(val, @contract)
res
end
end
def to_s
"a set of #{@contract}"
end
def testable?
Testable.testable? @contract
end
def test_data
Set.new([], [Testable.test_data(@contract)], [Testable.test_data(@contract), Testable.test_data(@contract)])
end
end
ResourceList = Contracts::ArrayOf[IsA['Middleman::Sitemap::Resource']]
end
ResourceList = Contracts::ArrayOf[IsA['Middleman::Sitemap::Resource']]
else
module Contracts
def self.included(base)
@ -91,5 +125,12 @@ else
class IsA < Callable
end
class SetOf < Callable
end
end
end
module Contracts
PATH_MATCHER = Or[String, RespondTo[:match], RespondTo[:call], RespondTo[:to_s]]
end

View file

@ -140,7 +140,7 @@ class Middleman::CoreExtensions::DefaultHelpers < ::Middleman::Extension
path << index_file if path.end_with?('/')
path = ::Middleman::Util.strip_leading_slash(path)
classes = []
classes = Set.new
parts = path.split('.').first.split('/')
parts.each_with_index { |_, i| classes << parts.first(i + 1).join('_') }

View file

@ -55,28 +55,30 @@ module Middleman
@app = app
@known_paths = Set.new
@_changed = []
@_deleted = []
@on_change_callbacks = Set.new
@on_delete_callbacks = Set.new
end
CallbackDescriptor = Struct.new(:proc, :matcher)
# Add callback to be run on file change
#
# @param [nil,Regexp] matcher A Regexp to match the change path against
# @return [Array<Proc>]
Contract Or[Regexp, Proc] => ArrayOf[ArrayOf[Or[Proc, Regexp, nil]]]
Contract Or[Regexp, Proc] => SetOf['Middleman::CoreExtensions::FileWatcher::API::CallbackDescriptor']
def changed(matcher=nil, &block)
@_changed << [block, matcher] if block_given?
@_changed
@on_change_callbacks << CallbackDescriptor.new(block, matcher) if block_given?
@on_change_callbacks
end
# Add callback to be run on file deletion
#
# @param [nil,Regexp] matcher A Regexp to match the deleted path against
# @return [Array<Proc>]
Contract Or[Regexp, Proc] => ArrayOf[ArrayOf[Or[Proc, Regexp, nil]]]
Contract Or[Regexp, Proc] => SetOf['Middleman::CoreExtensions::FileWatcher::API::CallbackDescriptor']
def deleted(matcher=nil, &block)
@_deleted << [block, matcher] if block_given?
@_deleted
@on_delete_callbacks << CallbackDescriptor.new(block, matcher) if block_given?
@on_delete_callbacks
end
# Notify callbacks that a file changed
@ -159,9 +161,9 @@ module Middleman
# @return [void]
def run_callbacks(path, callbacks_name)
path = path.to_s
send(callbacks_name).each do |callback, matcher|
next unless matcher.nil? || path.match(matcher)
@app.instance_exec(path, &callback)
send(callbacks_name).each do |callback|
next unless callback[:matcher].nil? || path.match(callback[:matcher])
@app.instance_exec(path, &callback[:proc])
end
end
end

View file

@ -9,7 +9,7 @@ module Middleman
def initialize(app, options_hash={}, &block)
super
@page_configs = []
@page_configs = Set.new
end
def before_configuration
@ -20,12 +20,14 @@ module Middleman
Contract ResourceList => ResourceList
def manipulate_resource_list(resources)
resources.each do |resource|
@page_configs.each do |matcher, metadata|
resource.add_metadata(metadata) if Middleman::Util.path_match(matcher, "/#{resource.path}")
@page_configs.each do |p|
resource.add_metadata(p[:metadata]) if Middleman::Util.path_match(p[:path], "/#{resource.path}")
end
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, proxy, andx ignore.
#
@ -43,6 +45,7 @@ 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 String, Hash => Any
def page(path, opts={})
options = opts.dup
@ -63,7 +66,7 @@ module Middleman
path = '/' + Util.strip_leading_slash(path) if path.is_a?(String)
@page_configs << [path, metadata]
@page_configs << PageDescriptor.new(path, metadata)
end
end
end

View file

@ -9,9 +9,9 @@ module Middleman
@registered = {}
@auto_activate = {
# Activate before the Sitemap is instantiated
before_sitemap: [],
before_sitemap: Set.new,
# Activate the extension before `config.rb` and the `:before_configuration` hook.
before_configuration: []
before_configuration: Set.new
}
AutoActivation = Struct.new(:name, :modes)

View file

@ -32,6 +32,11 @@ class Middleman::Extensions::MinifyCss < ::Middleman::Extension
# Init
# @param [Class] app
# @param [Hash] options
Contract RespondTo[:call], ({
ignore: ArrayOf[PATH_MATCHER],
inline: Bool,
compressor: Or[Proc, RespondTo[:to_proc], RespondTo[:compress]]
}) => Any
def initialize(app, options={})
@app = app
@ignore = options.fetch(:ignore)

View file

@ -23,6 +23,11 @@ class Middleman::Extensions::MinifyJavascript < ::Middleman::Extension
# Init
# @param [Class] app
# @param [Hash] options
Contract RespondTo[:call], ({
ignore: ArrayOf[PATH_MATCHER],
inline: Bool,
compressor: Or[Proc, RespondTo[:to_proc], RespondTo[:compress]]
}) => Any
def initialize(app, options={})
@app = app
@ignore = options.fetch(:ignore)

View file

@ -8,21 +8,31 @@ module Middleman
class InlineURLRewriter
include Contracts
IGNORE_DESCRIPTOR = Or[Regexp, RespondTo[:call], String]
Contract RespondTo[:call], ({
middleman_app: IsA['Middleman::Application'],
id: Maybe[Symbol],
proc: Or[Proc, Method],
url_extensions: ArrayOf[String],
source_extensions: ArrayOf[String],
ignore: ArrayOf[IGNORE_DESCRIPTOR]
}) => Any
def initialize(app, options={})
@rack_app = app
@middleman_app = options[:middleman_app]
@middleman_app = options.fetch(:middleman_app)
@uid = options[:id]
@proc = options[:proc]
@uid = options.fetch(:id, nil)
@proc = options.fetch(:proc)
raise 'InlineURLRewriter requires a :proc to call with inline URL results' unless @proc
@exts = options[:url_extensions]
@exts = options.fetch(:url_extensions)
@source_exts = options[:source_extensions]
@source_exts = options.fetch(:source_extensions)
@source_exts_regex_text = Regexp.union(@source_exts).to_s
@ignore = options[:ignore]
@ignore = options.fetch(:ignore)
end
def call(env)
@ -66,7 +76,7 @@ module Middleman
[status, headers, response]
end
Contract Or[Regexp, RespondTo[:call], String] => Bool
Contract IGNORE_DESCRIPTOR => Bool
def should_ignore?(validator, value)
if validator.is_a? Regexp
# Treat as Regexp

View file

@ -23,15 +23,15 @@ module Middleman
app.use ::Rack::Lint
app.use ::Rack::Head
@middleman.middleware.each do |klass, options, middleware_block|
app.use(klass, *options, &middleware_block)
@middleman.middleware.each do |middleware|
app.use(middleware[:class], *middleware[:options], &middleware[:block])
end
inner_app = self
app.map('/') { run inner_app }
@middleman.mappings.each do |path, map_block|
app.map(path, &map_block)
@middleman.mappings.each do |mapping|
app.map(mapping[:path], &mapping[:block])
end
app

View file

@ -10,7 +10,7 @@ module Middleman
@app.define_singleton_method :ignore, &method(:create_ignore)
# Array of callbacks which can ass ignored
@ignored_callbacks = []
@ignored_callbacks = Set.new
@app.sitemap.define_singleton_method :ignored?, &method(:ignored?)
end

View file

@ -30,8 +30,11 @@ module Middleman
# @return [String]
alias_method :request_path, :destination_path
METADATA_HASH = ({ options: Maybe[Hash], locals: Maybe[Hash], page: Maybe[Hash] })
# The metadata for this resource
# @return [Hash]
Contract None => METADATA_HASH
attr_reader :metadata
# Initialize resource with parent store and URL
@ -66,7 +69,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.
Contract Hash => Hash
Contract METADATA_HASH => METADATA_HASH
def add_metadata(meta={})
@metadata.deep_merge!(meta)
end

View file

@ -124,7 +124,7 @@ module Middleman
# @param [String, #match, #call] matcher A matcher String, RegExp, Proc, etc.
# @param [String] path A path as a string
# @return [Boolean] Whether the path matches the matcher
Contract Or[String, RespondTo[:match], RespondTo[:call], RespondTo[:to_s]], String => Bool
Contract PATH_MATCHER, String => Bool
def self.path_match(matcher, path)
case
when matcher.is_a?(String)