Merge pull request #1135 from middleman/config_context

Put all config.rb access into a jail.
This commit is contained in:
Thomas Reynolds 2014-01-01 13:27:09 -08:00
commit 572bf533f8
26 changed files with 553 additions and 613 deletions

View file

@ -1,12 +1,16 @@
set :layout, false set :layout, false
module ExtensionA class ExtensionA < ::Middleman::Extension
class << self helpers do
def registered(app, options={}) def get_option(key)
app.set :a_options, options extensions[:extension_a].options[key]
end end
alias :included :registered
end end
option :hello, '', ''
option :hola, '', ''
end end
activate ExtensionA, :hello => "world", :hola => "mundo" ExtensionA.register
activate :extension_a, :hello => "world", :hola => "mundo"

View file

@ -1,3 +1,2 @@
<% a_options.each do |k, v| %> hello: <%= get_option(:hello) %>
<%= k %>: <%= v%> hola: <%= get_option(:hola) %>
<% end %>

View file

@ -1,9 +1,17 @@
class ExtensionOne < ::Middleman::Extension class ExtensionOne < ::Middleman::Extension
helpers do
def extension_two_was_activated
extensions[:extension_one].extension_two_was_activated
end
end
attr_reader :extension_two_was_activated
def initialize(app, options_hash={}) def initialize(app, options_hash={})
super super
after_extension_activated :extension_two do after_extension_activated :extension_two do
app.set :extension_two_was_activated, true @extension_two_was_activated = true
end end
end end
end end
@ -11,11 +19,19 @@ end
ExtensionOne.register ExtensionOne.register
class ExtensionTwo < ::Middleman::Extension class ExtensionTwo < ::Middleman::Extension
helpers do
def extension_one_was_activated
extensions[:extension_two].extension_one_was_activated
end
end
attr_reader :extension_one_was_activated
def initialize(app, options_hash={}) def initialize(app, options_hash={})
super super
after_extension_activated :extension_one do after_extension_activated :extension_one do
app.set :extension_one_was_activated, true @extension_one_was_activated = true
end end
end end
end end

View file

@ -20,10 +20,13 @@ require 'vendored-middleman-deps/hooks-0.2.0/lib/hooks'
require 'middleman-core/logger' require 'middleman-core/logger'
require 'middleman-core/sitemap' require 'middleman-core/sitemap'
require 'middleman-core/sitemap/store'
require 'middleman-core/configuration' require 'middleman-core/configuration'
require 'middleman-core/core_extensions' require 'middleman-core/core_extensions'
require 'middleman-core/config_context'
# Core Middleman Class # Core Middleman Class
module Middleman module Middleman
class Application class Application
@ -150,48 +153,52 @@ module Middleman
# extensions see updated frontmatter! # extensions see updated frontmatter!
register Middleman::CoreExtensions::FrontMatter register Middleman::CoreExtensions::FrontMatter
# Sitemap # Sitemap Config options and public api
register Middleman::Sitemap register Middleman::Sitemap
# Setup external helpers # Setup external helpers
register Middleman::CoreExtensions::ExternalHelpers register Middleman::CoreExtensions::ExternalHelpers
# with_layout and page routing # Reference to Logger singleton
include Middleman::CoreExtensions::Routing
attr_reader :logger attr_reader :logger
# New container for config.rb commands
attr_reader :config_context
# Reference to Sitemap
attr_reader :sitemap
# Template cache
attr_reader :cache
# Initialize the Middleman project # Initialize the Middleman project
def initialize(&block) def initialize(&block)
@cache = ::Tilt::Cache.new
@logger = ::Middleman::Logger.singleton @logger = ::Middleman::Logger.singleton
@config_context = ConfigContext.new(self)
# Clear the static class cache
cache.clear
# Setup the default values from calls to set before initialization # Setup the default values from calls to set before initialization
self.class.config.load_settings(self.class.superclass.config.all_settings) self.class.config.load_settings(self.class.superclass.config.all_settings)
# Initialize the Sitemap
@sitemap = ::Middleman::Sitemap::Store.new(self)
if Object.const_defined?(:Encoding) if Object.const_defined?(:Encoding)
Encoding.default_internal = config[:encoding] Encoding.default_internal = config[:encoding]
Encoding.default_external = config[:encoding] Encoding.default_external = config[:encoding]
end end
# Evaluate a passed block if given # Evaluate a passed block if given
instance_exec(&block) if block_given? @config_context.instance_exec(&block) if block_given?
config[:source] = ENV['MM_SOURCE'] if ENV['MM_SOURCE'] config[:source] = ENV['MM_SOURCE'] if ENV['MM_SOURCE']
super super
end end
# Shared cache instance def add_to_config_context(name, &func)
# @config_context.define_singleton_method(name, &func)
# @private
# @return [Middleman::Util::Cache] The cache
def self.cache
@_cache ||= ::Tilt::Cache.new
end end
delegate :cache, :to => :"self.class"
# Whether we're in development mode # Whether we're in development mode
# @return [Boolean] If we're in dev mode # @return [Boolean] If we're in dev mode

View file

@ -68,6 +68,7 @@ module Middleman::Cli
action BuildAction.new(self, opts) action BuildAction.new(self, opts)
self.class.shared_instance.run_hook :after_build, self self.class.shared_instance.run_hook :after_build, self
self.class.shared_instance.config_context.execute_after_build_callbacks(self)
if self.had_errors && !self.debugging if self.had_errors && !self.debugging
msg = 'There were errors during this build' msg = 'There were errors during this build'
@ -110,7 +111,7 @@ module Middleman::Cli
def initialize(base, config={}) def initialize(base, config={})
@app = base.class.shared_instance @app = base.class.shared_instance
@source_dir = Pathname(@app.source_dir) @source_dir = Pathname(@app.source_dir)
@build_dir = Pathname(@app.build_dir) @build_dir = Pathname(@app.config[:build_dir])
@to_clean = Set.new @to_clean = Set.new
@logger = @app.logger @logger = @app.logger
@ -190,7 +191,7 @@ module Middleman::Cli
logger.debug '== Checking for Compass sprites' logger.debug '== Checking for Compass sprites'
# Double-check for compass sprites # Double-check for compass sprites
@app.files.find_new_files((@source_dir + @app.images_dir).relative_path_from(@app.root_path)) @app.files.find_new_files((@source_dir + @app.config[:images_dir]).relative_path_from(@app.root_path))
@app.sitemap.ensure_resource_list_updated! @app.sitemap.ensure_resource_list_updated!
# Sort paths to be built by the above order. This is primarily so Compass can # Sort paths to be built by the above order. This is primarily so Compass can

View file

@ -29,7 +29,7 @@ module Middleman::Cli
@app =::Middleman::Application.server.inst do @app =::Middleman::Application.server.inst do
if opts[:environment] if opts[:environment]
set :environment, opts[:environment].to_sym config[:environment] = opts[:environment].to_sym
end end
logger(opts[:debug] ? 0 : 1, opts[:instrumenting] || false) logger(opts[:debug] ? 0 : 1, opts[:instrumenting] || false)

View file

@ -0,0 +1,66 @@
module Middleman
class ConfigContext
# with_layout and page routing
include Middleman::CoreExtensions::Routing
attr_reader :app
# Whitelist methods that can reach out.
delegate :config, :logger, :activate, :use, :map, :mime_type, :data, :helpers, :template_extensions, :root, :to => :app
def initialize(app)
@app = app
@ready_callbacks = []
@after_build_callbacks = []
@after_configuration_callbacks = []
@configure_callbacks = {}
end
def ready(&block)
@ready_callbacks << block
end
def execute_ready_callbacks
@ready_callbacks.each do |b|
instance_exec(&b)
end
end
def after_build(&block)
@after_build_callbacks << block
end
def execute_after_build_callbacks(*args)
@after_build_callbacks.each do |b|
instance_exec(*args, &b)
end
end
def after_configuration(&block)
@after_configuration_callbacks << block
end
def execute_after_configuration_callbacks
@after_configuration_callbacks.each do |b|
instance_exec(&b)
end
end
def configure(key, &block)
@configure_callbacks[key] ||= []
@configure_callbacks[key] << block
end
def execute_configure_callbacks(key)
@configure_callbacks[key] ||= []
@configure_callbacks[key].each do |b|
instance_exec(&b)
end
end
def set(key, default=nil, &block)
config.define_setting(key, default) unless config.defines_setting?(key)
@app.config[key] = block_given? ? block : default
end
end
end

View file

@ -5,6 +5,7 @@ module Middleman
module Global module Global
def self.included(app) def self.included(app)
app.send :extend, ClassMethods app.send :extend, ClassMethods
app.send :delegate, :config, :to => :"self.class"
end end
module ClassMethods module ClassMethods
@ -13,75 +14,6 @@ module Middleman
def config def config
@_config ||= ConfigurationManager.new @_config ||= ConfigurationManager.new
end end
# Set attributes (global variables)
#
# @deprecated Prefer accessing settings through "config".
#
# @param [Symbol] key Name of the attribue
# @param default Attribute value
# @return [void]
def set(key, default=nil, &block)
config.define_setting(key, default) unless config.defines_setting?(key)
@inst.set(key, default, &block) if @inst
end
# Access global settings as methods, to preserve compatibility with
# old Middleman.
#
# @deprecated Prefer accessing settings through "config".
def method_missing(method, *args)
if config.defines_setting? method
config[method]
else
super
end
end
# Needed so that method_missing makes sense
def respond_to?(method, include_private = false)
super || config.defines_setting?(method)
end
end
def config
self.class.config
end
# Backwards compatibilty with old Sinatra template interface
#
# @deprecated Prefer accessing settings through "config".
#
# @return [ConfigurationManager]
alias :settings :config
# Set attributes (global variables)
#
# @deprecated Prefer accessing settings through "config".
#
# @param [Symbol] key Name of the attribue
# @param value Attribute value
# @return [void]
def set(key, value=nil, &block)
value = block if block_given?
config[key] = value
end
# Access global settings as methods, to preserve compatibility with
# old Middleman.
#
# @deprecated Prefer accessing settings through "config".
def method_missing(method, *args)
if config.defines_setting? method
config[method]
else
super
end
end
# Needed so that method_missing makes sense
def respond_to?(method, include_private = false)
super || config.defines_setting?(method)
end end
end end

View file

@ -159,11 +159,18 @@ module Middleman
local_config = File.join(root, 'config.rb') local_config = File.join(root, 'config.rb')
if File.exists? local_config if File.exists? local_config
logger.debug '== Reading: Local config' logger.debug '== Reading: Local config'
instance_eval File.read(local_config), local_config, 1 config_context.instance_eval File.read(local_config), local_config, 1
end end
run_hook :build_config if build? if build?
run_hook :development_config if development? run_hook :build_config
config_context.execute_configure_callbacks(:build)
end
if development?
run_hook :development_config
config_context.execute_configure_callbacks(:development)
end
run_hook :instance_available run_hook :instance_available
@ -177,6 +184,7 @@ module Middleman
end end
run_hook :after_configuration run_hook :after_configuration
config_context.execute_after_configuration_callbacks
logger.debug 'Loaded extensions:' logger.debug 'Loaded extensions:'
self.extensions.each do |ext, klass| self.extensions.each do |ext, klass|

View file

@ -51,6 +51,7 @@ module Middleman
@inst ||= begin @inst ||= begin
mm = new(&block) mm = new(&block)
mm.run_hook :ready mm.run_hook :ready
mm.config_context.execute_ready_callbacks
mm mm
end end
end end

View file

@ -2,69 +2,72 @@
module Middleman module Middleman
module CoreExtensions module CoreExtensions
module Routing module Routing
# Takes a block which allows many pages to have the same layout
#
# with_layout :admin do
# page "/admin/"
# page "/admin/login.html"
# end
#
# @param [String, Symbol] layout_name
# @return [void]
def with_layout(layout_name, &block)
old_layout = config[:layout]
config[:layout] = layout_name # Sandboxed layout to implement temporary overriding of layout.
instance_exec(&block) if block_given? class LayoutBlock
ensure attr_reader :scope
config[:layout] = old_layout
def initialize(scope, layout_name)
@scope = scope
@layout_name = layout_name
end end
# The page method allows the layout to be set on a specific path
#
# page "/about.html", :layout => false
# page "/", :layout => :homepage_layout
#
# @param [String] url
# @param [Hash] opts
# @return [void]
def page(url, opts={}, &block) def page(url, opts={}, &block)
blocks = Array(block) opts[:layout] ||= @layout_name
@scope.page(url, opts, &block)
end
# Default layout delegate :proxy, :to => :scope
opts[:layout] = config[:layout] if opts[:layout].nil? end
# If the url is a regexp # Takes a block which allows many pages to have the same layout
if url.is_a?(Regexp) || url.include?('*') #
# with_layout :admin do
# page "/admin/"
# page "/admin/login.html"
# end
#
# @param [String, Symbol] layout_name
# @return [void]
def with_layout(layout_name, &block)
LayoutBlock.new(self, layout_name).instance_eval(&block)
end
# Use the metadata loop for matching against paths at runtime # The page method allows the layout to be set on a specific path
sitemap.provides_metadata_for_path(url) do |_| #
{ :options => opts, :blocks => blocks } # page "/about.html", :layout => false
end # page "/", :layout => :homepage_layout
#
return # @param [String] url
end # @param [Hash] opts
# @return [void]
def page(url, opts={}, &block)
# Default layout
opts[:layout] = @app.config[:layout] if opts[:layout].nil?
metadata = { :options => opts, :blocks => Array(block) }
# If the url is a regexp
unless url.is_a?(Regexp) || url.include?('*')
# Normalized path # Normalized path
url = '/' + Middleman::Util.normalize_path(url) url = '/' + Middleman::Util.normalize_path(url)
if url.end_with?('/') || File.directory?(File.join(source_dir, url)) if url.end_with?('/') || File.directory?(File.join(@app.source_dir, url))
url = File.join(url, config[:index_file]) url = File.join(url, @app.config[:index_file])
end end
# Setup proxy # Setup proxy
if target = opts.delete(:proxy) if target = opts.delete(:proxy)
# TODO: deprecate proxy through page? # TODO: deprecate proxy through page?
proxy(url, target, opts, &block) and return @app.proxy(url, target, opts, &block)
return
elsif opts.delete(:ignore) elsif opts.delete(:ignore)
# TODO: deprecate ignore through page? # TODO: deprecate ignore through page?
ignore(url) @app.ignore(url)
end
# Setup a metadata matcher for rendering those options
sitemap.provides_metadata_for_path(url) do |_|
{ :options => opts, :blocks => blocks }
end end
end end
# Setup a metadata matcher for rendering those options
@app.sitemap.provides_metadata_for_path(url) { |_| metadata }
end
end end
end end
end end

View file

@ -12,8 +12,9 @@ module Middleman
# Once registered # Once registered
def registered(app) def registered(app)
# Default less options # Default stylus options
app.set :styl, {} app.config.define_setting :styl, {}, 'Stylus config options'
app.before_configuration do app.before_configuration do
template_extensions :styl => :css template_extensions :styl => :css

View file

@ -1,12 +1,3 @@
require 'middleman-core/sitemap/store'
require 'middleman-core/sitemap/resource'
require 'middleman-core/sitemap/extensions/on_disk'
require 'middleman-core/sitemap/extensions/redirects'
require 'middleman-core/sitemap/extensions/request_endpoints'
require 'middleman-core/sitemap/extensions/proxies'
require 'middleman-core/sitemap/extensions/ignores'
# Core Sitemap Extensions # Core Sitemap Extensions
module Middleman module Middleman
@ -18,11 +9,6 @@ module Middleman
# Once registered # Once registered
def registered(app) def registered(app)
app.register Middleman::Sitemap::Extensions::RequestEndpoints
app.register Middleman::Sitemap::Extensions::Proxies
app.register Middleman::Sitemap::Extensions::Ignores
app.register Middleman::Sitemap::Extensions::Redirects
# Set to automatically convert some characters into a directory # Set to automatically convert some characters into a directory
app.config.define_setting :automatic_directory_matcher, nil, 'Set to automatically convert some characters into a directory' app.config.define_setting :automatic_directory_matcher, nil, 'Set to automatically convert some characters into a directory'
@ -46,11 +32,6 @@ module Middleman
# Include instance methods # Include instance methods
app.send :include, InstanceMethods app.send :include, InstanceMethods
# Initialize Sitemap
app.before_configuration do
sitemap
end
end end
alias :included :registered alias :included :registered
@ -59,12 +40,6 @@ module Middleman
# Sitemap instance methods # Sitemap instance methods
module InstanceMethods module InstanceMethods
# Get the sitemap class instance
# @return [Middleman::Sitemap::Store]
def sitemap
@_sitemap ||= Store.new(self)
end
# Get the resource object for the current path # Get the resource object for the current path
# @return [Middleman::Sitemap::Resource] # @return [Middleman::Sitemap::Resource]
def current_page def current_page

View file

@ -4,87 +4,61 @@ module Middleman
module Extensions module Extensions
module Ignores # Class to handle managing ignores
class Ignores
def initialize(sitemap)
@app = sitemap.app
@app.add_to_config_context :ignore, &method(:create_ignore)
@app.define_singleton_method(:ignore, &method(:create_ignore))
# Setup extension # Array of callbacks which can ass ignored
class << self @ignored_callbacks = []
# Once registered sitemap.define_singleton_method(:ignored?, &method(:ignored?))
def registered(app) ::Middleman::Sitemap::Resource.send :include, IgnoreResourceInstanceMethods
# Include methods
app.send :include, InstanceMethods
::Middleman::Sitemap::Resource.send :include, ResourceInstanceMethods
end
alias :included :registered
end end
# Helpers methods for Resources # Ignore a path or add an ignore callback
module ResourceInstanceMethods # @param [String, Regexp] path Path glob expression, or path regex
# @return [void]
# Whether the Resource is ignored def create_ignore(path=nil, &block)
# @return [Boolean] if path.is_a? Regexp
def ignored? @ignored_callbacks << Proc.new {|p| p =~ path }
@app.ignore_manager.ignored?(path) || elsif path.is_a? String
(!proxy? &&
@app.ignore_manager.ignored?(source_file.sub("#{@app.source_dir}/", ''))
)
end
end
# Ignore-related instance methods
module InstanceMethods
def ignore_manager
@_ignore_manager ||= IgnoreManager.new(self)
end
# Ignore a path or add an ignore callback
# @param [String, Regexp] path Path glob expression, or path regex
# @return [void]
def ignore(path=nil, &block)
ignore_manager.ignore(path, &block)
end
end
# Class to handle managing ignores
class IgnoreManager
def initialize(app)
@app = app
# Array of callbacks which can ass ignored
@ignored_callbacks = []
end
# Ignore a path or add an ignore callback
# @param [String, Regexp] path Path glob expression, or path regex
# @return [void]
def ignore(path=nil, &block)
if path.is_a? Regexp
@ignored_callbacks << Proc.new {|p| p =~ path }
elsif path.is_a? String
path_clean = ::Middleman::Util.normalize_path(path)
if path_clean.include?('*') # It's a glob
@ignored_callbacks << Proc.new {|p| File.fnmatch(path_clean, p) }
else
# Add a specific-path ignore unless that path is already covered
return if ignored?(path_clean)
@ignored_callbacks << Proc.new {|p| p == path_clean }
end
elsif block_given?
@ignored_callbacks << block
end
@app.sitemap.invalidate_resources_not_ignored_cache!
end
# Whether a path is ignored
# @param [String] path
# @return [Boolean]
def ignored?(path)
path_clean = ::Middleman::Util.normalize_path(path) path_clean = ::Middleman::Util.normalize_path(path)
@ignored_callbacks.any? { |b| b.call(path_clean) } if path_clean.include?('*') # It's a glob
@ignored_callbacks << Proc.new {|p| File.fnmatch(path_clean, p) }
else
# Add a specific-path ignore unless that path is already covered
return if ignored?(path_clean)
@ignored_callbacks << Proc.new {|p| p == path_clean }
end
elsif block_given?
@ignored_callbacks << block
end end
@app.sitemap.invalidate_resources_not_ignored_cache!
end
# Whether a path is ignored
# @param [String] path
# @return [Boolean]
def ignored?(path)
path_clean = ::Middleman::Util.normalize_path(path)
@ignored_callbacks.any? { |b| b.call(path_clean) }
end
end
# Helpers methods for Resources
module IgnoreResourceInstanceMethods
# Whether the Resource is ignored
# @return [Boolean]
def ignored?
@app.sitemap.ignored?(path) ||
(!proxy? &&
@app.sitemap.ignored?(source_file.sub("#{@app.source_dir}/", ''))
)
end end
end end
end end

View file

@ -4,165 +4,143 @@ module Middleman
module Extensions module Extensions
module Proxies # Manages the list of proxy configurations and manipulates the sitemap
# to include new resources based on those configurations
class Proxies
def initialize(sitemap)
@app = sitemap.app
@app.add_to_config_context :proxy, &method(:create_proxy)
@app.define_singleton_method(:proxy, &method(:create_proxy))
# Setup extension @proxy_configs = Set.new
class << self
# Once registered ::Middleman::Sitemap::Resource.send :include, ProxyResourceInstanceMethods
def registered(app)
::Middleman::Sitemap::Resource.send :include, ResourceInstanceMethods
# Include methods
app.send :include, InstanceMethods
end
alias :included :registered
end end
module ResourceInstanceMethods # Setup a proxy from a path to a target
# Whether this page is a proxy # @param [String] path
# @return [Boolean] # @param [String] target
def proxy? # @param [Hash] opts options to apply to the proxy, including things like
!!@proxied_to # :locals, :ignore to hide the proxy target, :layout, and :directory_indexes.
# @return [void]
def create_proxy(path, target, opts={}, &block)
metadata = { :options => {}, :locals => {}, :blocks => [] }
metadata[:blocks] << block if block_given?
metadata[:locals] = opts.delete(:locals) || {}
@app.ignore(target) if opts.delete(:ignore)
metadata[:options] = opts
@proxy_configs << ProxyConfiguration.new(:path => path, :target => target, :metadata => metadata)
@app.sitemap.rebuild_resource_list!(:added_proxy)
end
# Update the main sitemap resource list
# @return [void]
def manipulate_resource_list(resources)
resources + @proxy_configs.map do |config|
p = ::Middleman::Sitemap::Resource.new(
@app.sitemap,
config.path
)
p.proxy_to(config.target)
p.add_metadata(config.metadata)
p
end end
end
end
# Set this page to proxy to a target path # Configuration for a proxy instance
# @param [String] target class ProxyConfiguration
# @return [void] # The path that this proxy will appear at in the sitemap
def proxy_to(target) attr_reader :path
target = ::Middleman::Util.normalize_path(target) def path=(p)
raise "You can't proxy #{path} to itself!" if target == path @path = ::Middleman::Util.normalize_path(p)
@proxied_to = target end
end
# The path of the page this page is proxied to, or nil if it's not proxied. # The existing sitemap path that this will proxy to
# @return [String] attr_reader :target
def proxied_to def target=(t)
@proxied_to @target = ::Middleman::Util.normalize_path(t)
end end
# The resource for the page this page is proxied to. Throws an exception # Additional metadata like blocks and locals to apply to the proxy
# if there is no resource. attr_accessor :metadata
# @return [Sitemap::Resource]
def proxied_to_resource
proxy_resource = store.find_resource_by_path(proxied_to)
unless proxy_resource # Create a new proxy configuration from hash options
raise "Path #{path} proxies to unknown file #{proxied_to}:#{store.resources.map(&:path)}" def initialize(options={})
end options.each do |key, value|
send "#{key}=", value
if proxy_resource.proxy?
raise "You can't proxy #{path} to #{proxied_to} which is itself a proxy."
end
proxy_resource
end
def get_source_file
if proxy?
proxied_to_resource.source_file
else
super
end
end
def content_type
mime_type = super
return mime_type if mime_type
if proxy?
proxied_to_resource.content_type
else
nil
end
end end
end end
module InstanceMethods # Two configurations are equal if they reference the same path
def proxy_manager def eql?(other)
@_proxy_manager ||= ProxyManager.new(self) other.path == path
end
# Two configurations are equal if they reference the same path
def hash
path.hash
end
end
module ProxyResourceInstanceMethods
# 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]
def proxied_to
@proxied_to
end
# 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 end
def proxy(*args, &block) if proxy_resource.proxy?
proxy_manager.proxy(*args, &block) raise "You can't proxy #{path} to #{proxied_to} which is itself a proxy."
end
proxy_resource
end
def get_source_file
if proxy?
proxied_to_resource.source_file
else
super
end end
end end
# Manages the list of proxy configurations and manipulates the sitemap def content_type
# to include new resources based on those configurations mime_type = super
class ProxyManager return mime_type if mime_type
def initialize(app)
@app = app
@proxy_configs = Set.new
end
# Setup a proxy from a path to a target if proxy?
# @param [String] path proxied_to_resource.content_type
# @param [String] target else
# @param [Hash] opts options to apply to the proxy, including things like nil
# :locals, :ignore to hide the proxy target, :layout, and :directory_indexes.
# @return [void]
def proxy(path, target, opts={}, &block)
metadata = { :options => {}, :locals => {}, :blocks => [] }
metadata[:blocks] << block if block_given?
metadata[:locals] = opts.delete(:locals) || {}
@app.ignore(target) if opts.delete(:ignore)
metadata[:options] = opts
@proxy_configs << ProxyConfiguration.new(:path => path, :target => target, :metadata => metadata)
@app.sitemap.rebuild_resource_list!(:added_proxy)
end
# Update the main sitemap resource list
# @return [void]
def manipulate_resource_list(resources)
resources + @proxy_configs.map do |config|
p = ::Middleman::Sitemap::Resource.new(
@app.sitemap,
config.path
)
p.proxy_to(config.target)
p.add_metadata(config.metadata)
p
end
end
end
# Configuration for a proxy instance
class ProxyConfiguration
# The path that this proxy will appear at in the sitemap
attr_reader :path
def path=(p)
@path = ::Middleman::Util.normalize_path(p)
end
# The existing sitemap path that this will proxy to
attr_reader :target
def target=(t)
@target = ::Middleman::Util.normalize_path(t)
end
# Additional metadata like blocks and locals to apply to the proxy
attr_accessor :metadata
# Create a new proxy configuration from hash options
def initialize(options={})
options.each do |key, value|
send "#{key}=", value
end
end
# Two configurations are equal if they reference the same path
def eql?(other)
other.path == path
end
# Two configurations are equal if they reference the same path
def hash
path.hash
end end
end end
end end

View file

@ -1,126 +1,105 @@
require 'middleman-core/sitemap/resource'
module Middleman module Middleman
module Sitemap module Sitemap
module Extensions module Extensions
module Redirects # Manages the list of proxy configurations and manipulates the sitemap
# to include new resources based on those configurations
class Redirects
def initialize(sitemap)
@app = sitemap.app
@app.add_to_config_context :redirect, &method(:create_redirect)
# Setup extension @redirects = {}
class << self
# Once registered
def registered(app)
# Include methods
app.send :include, InstanceMethods
end
alias :included :registered
end end
module InstanceMethods # Setup a redirect from a path to a target
def redirect_manager # @param [String] path
@_redirect_manager ||= RedirectManager.new(self) # @param [Hash] opts The :to value gives a target path
def create_redirect(path, opts={}, &block)
if block_given?
opts[:template] = block
end end
def redirect(*args, &block) @redirects[path] = opts
redirect_manager.create_redirect(*args, &block)
@app.sitemap.rebuild_resource_list!(:added_redirect)
end
# Update the main sitemap resource list
# @return [void]
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
end
end
class RedirectResource < ::Middleman::Sitemap::Resource
attr_accessor :output
def initialize(store, path, target)
@request_path = target
super(store, path)
end
def template?
true
end
def render(*args, &block)
url = ::Middleman::Util.url_for(store.app, @request_path, {
:relative => false,
:find_resource => true
})
if output
output.call(path, url)
else
<<-END
<html>
<head>
<meta http-equiv=refresh content="0; url=#{url}" />
<meta name="robots" content="noindex,follow" />
<meta http-equiv="cache-control" content="no-cache" />
</head>
<body>
</body>
</html>
END
end end
end end
# Manages the list of proxy configurations and manipulates the sitemap # def request_path
# to include new resources based on those configurations # @request_path
class RedirectManager # end
def initialize(app)
@app = app
@redirects = {}
end
# Setup a redirect from a path to a target def binary?
# @param [String] path false
# @param [Hash] opts The :to value gives a target path
def create_redirect(path, opts={}, &block)
if block_given?
opts[:template] = block
end
@redirects[path] = opts
@app.sitemap.rebuild_resource_list!(:added_redirect)
end
# Update the main sitemap resource list
# @return [void]
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
end
end end
class RedirectResource < ::Middleman::Sitemap::Resource def raw_data
attr_accessor :output {}
def initialize(store, path, target)
@request_path = target
super(store, path)
end
def template?
true
end
def render(*args, &block)
url = ::Middleman::Util.url_for(store.app, @request_path, {
:relative => false,
:find_resource => true
})
if output
output.call(path, url)
else
<<-END
<html>
<head>
<meta http-equiv=refresh content="0; url=#{url}" />
<meta name="robots" content="noindex,follow" />
<meta http-equiv="cache-control" content="no-cache" />
</head>
<body>
</body>
</html>
END
end
end
# def request_path
# @request_path
# end
def binary?
false
end
def raw_data
{}
end
def ignored?
false
end
def metadata
@local_metadata.dup
end
end end
def ignored?
false
end
def metadata
@local_metadata.dup
end
end end
end end
end end

View file

@ -4,110 +4,87 @@ module Middleman
module Extensions module Extensions
module RequestEndpoints class RequestEndpoints
# Setup extension
class << self
# Once registered
def registered(app)
# Include methods
app.send :include, InstanceMethods
end
alias :included :registered
end
module InstanceMethods
def endpoint_manager
@_endpoint_manager ||= EndpointManager.new(self)
end
def endpoint(*args, &block)
endpoint_manager.create_endpoint(*args, &block)
end
end
# Manages the list of proxy configurations and manipulates the sitemap # Manages the list of proxy configurations and manipulates the sitemap
# to include new resources based on those configurations # to include new resources based on those configurations
class EndpointManager def initialize(sitemap)
def initialize(app) @app = sitemap.app
@app = app @app.add_to_config_context :endpoint, &method(:create_endpoint)
@endpoints = {}
end
# Setup a proxy from a path to a target @endpoints = {}
# @param [String] path
# @param [Hash] opts The :path value gives a request path if it
# differs from the output path
def create_endpoint(path, opts={}, &block)
endpoint = {
:request_path => path
}
if block_given?
endpoint[:output] = block
else
endpoint[:request_path] = opts[:path] if opts.has_key?(:path)
end
@endpoints[path] = endpoint
@app.sitemap.rebuild_resource_list!(:added_endpoint)
end
# Update the main sitemap resource list
# @return [void]
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.has_key?(:output)
r
end
end
end end
class EndpointResource < ::Middleman::Sitemap::Resource # Setup a proxy from a path to a target
attr_accessor :output # @param [String] path
# @param [Hash] opts The :path value gives a request path if it
# differs from the output path
def create_endpoint(path, opts={}, &block)
endpoint = {
:request_path => path
}
def initialize(store, path, source_file) if block_given?
@request_path = ::Middleman::Util.normalize_path(source_file) endpoint[:output] = block
else
super(store, path) endpoint[:request_path] = opts[:path] if opts.has_key?(:path)
end end
def template? @endpoints[path] = endpoint
true
end
def render(*args, &block) @app.sitemap.rebuild_resource_list!(:added_endpoint)
return self.output.call if self.output end
end
def request_path # Update the main sitemap resource list
@request_path # @return [void]
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.has_key?(:output)
r
end end
end
end
def binary? class EndpointResource < ::Middleman::Sitemap::Resource
false attr_accessor :output
end
def raw_data def initialize(store, path, source_file)
{} @request_path = ::Middleman::Util.normalize_path(source_file)
end
def ignored? super(store, path)
false end
end
def metadata def template?
@local_metadata.dup true
end end
def render(*args, &block)
return self.output.call if self.output
end
def request_path
@request_path
end
def binary?
false
end
def raw_data
{}
end
def ignored?
false
end
def metadata
@local_metadata.dup
end end
end end
end end

View file

@ -9,12 +9,12 @@ module Middleman
# @return [Middleman::Sitemap::Resource, nil] # @return [Middleman::Sitemap::Resource, nil]
def parent def parent
parts = path.split('/') parts = path.split('/')
parts.pop if path.include?(app.index_file) parts.pop if path.include?(app.config[:index_file])
return nil if parts.length < 1 return nil if parts.length < 1
parts.pop parts.pop
parts << app.index_file parts << app.config[:index_file]
parent_path = '/' + parts.join('/') parent_path = '/' + parts.join('/')
@ -30,7 +30,7 @@ module Middleman
base_path = eponymous_directory_path base_path = eponymous_directory_path
prefix = %r|^#{base_path.sub("/", "\\/")}| prefix = %r|^#{base_path.sub("/", "\\/")}|
else else
base_path = path.sub("#{app.index_file}", '') base_path = path.sub("#{app.config[:index_file]}", '')
prefix = %r|^#{base_path.sub("/", "\\/")}| prefix = %r|^#{base_path.sub("/", "\\/")}|
end end
@ -43,7 +43,7 @@ module Middleman
if parts.length == 1 if parts.length == 1
true true
elsif parts.length == 2 elsif parts.length == 2
parts.last == app.index_file parts.last == app.config[:index_file]
else else
false false
end end
@ -61,14 +61,14 @@ module Middleman
# Whether this resource either a directory index, or has the same name as an existing directory in the source # Whether this resource either a directory index, or has the same name as an existing directory in the source
# @return [Boolean] # @return [Boolean]
def directory_index? def directory_index?
path.include?(app.index_file) || path =~ /\/$/ || eponymous_directory? path.include?(app.config[:index_file]) || path =~ /\/$/ || eponymous_directory?
end end
# Whether the resource has the same name as a directory in the source # Whether the resource has the same name as a directory in the source
# (e.g., if the resource is named 'gallery.html' and a path exists named 'gallery/', this would return true) # (e.g., if the resource is named 'gallery.html' and a path exists named 'gallery/', this would return true)
# @return [Boolean] # @return [Boolean]
def eponymous_directory? def eponymous_directory?
if !path.end_with?("/#{app.index_file}") && destination_path.end_with?("/#{app.index_file}") if !path.end_with?("/#{app.config[:index_file]}") && destination_path.end_with?("/#{app.config[:index_file]}")
return true return true
end end
full_path = File.join(app.source_dir, eponymous_directory_path) full_path = File.join(app.source_dir, eponymous_directory_path)

View file

@ -149,11 +149,11 @@ module Middleman
# @return [String] # @return [String]
def url def url
url_path = destination_path url_path = destination_path
if app.strip_index_file if app.config[:strip_index_file]
url_path = url_path.sub(/(^|\/)#{Regexp.escape(app.index_file)}$/, url_path = url_path.sub(/(^|\/)#{Regexp.escape(app.config[:index_file])}$/,
app.trailing_slash ? '/' : '') app.config[:trailing_slash] ? '/' : '')
end end
File.join(app.respond_to?(:http_prefix) ? app.http_prefix : '/', url_path) File.join(app.config[:http_prefix], url_path)
end end
# Whether the source file is binary. # Whether the source file is binary.

View file

@ -3,6 +3,13 @@ require 'active_support/core_ext/hash/deep_merge'
require 'monitor' require 'monitor'
require 'middleman-core/sitemap/queryable' require 'middleman-core/sitemap/queryable'
# Extensions
require 'middleman-core/sitemap/extensions/on_disk'
require 'middleman-core/sitemap/extensions/redirects'
require 'middleman-core/sitemap/extensions/request_endpoints'
require 'middleman-core/sitemap/extensions/proxies'
require 'middleman-core/sitemap/extensions/ignores'
module Middleman module Middleman
# Sitemap namespace # Sitemap namespace
@ -29,21 +36,31 @@ module Middleman
@_cached_metadata = {} @_cached_metadata = {}
@resource_list_manipulators = [] @resource_list_manipulators = []
@needs_sitemap_rebuild = true @needs_sitemap_rebuild = true
@lock = Monitor.new @lock = Monitor.new
reset_lookup_cache! reset_lookup_cache!
# Register classes which can manipulate the main site map list # Handle ignore commands
register_resource_list_manipulator(:on_disk, Middleman::Sitemap::Extensions::OnDisk.new(self)) Middleman::Sitemap::Extensions::Ignores.new(self)
# Request Endpoints # Extensions
register_resource_list_manipulator(:request_endpoints, @app.endpoint_manager) {
# Register classes which can manipulate the main site map list
on_disk: Middleman::Sitemap::Extensions::OnDisk,
# Proxies # Request Endpoints
register_resource_list_manipulator(:proxies, @app.proxy_manager) request_endpoints: Middleman::Sitemap::Extensions::RequestEndpoints,
# Redirects # Proxies
register_resource_list_manipulator(:redirects, @app.redirect_manager) proxies: Middleman::Sitemap::Extensions::Proxies,
# Redirects
redirects: Middleman::Sitemap::Extensions::Redirects
}.each do |k, m|
register_resource_list_manipulator(k, m.new(self))
end
app.config_context.class.send :delegate, :sitemap, :to => :app
end end
# Register a klass which can manipulate the main site map list. Best to register # Register a klass which can manipulate the main site map list. Best to register

View file

@ -24,7 +24,9 @@ end
Given /^"([^\"]*)" is set to "([^\"]*)"$/ do |variable, value| Given /^"([^\"]*)" is set to "([^\"]*)"$/ do |variable, value|
@initialize_commands ||= [] @initialize_commands ||= []
@initialize_commands << lambda { set(variable.to_sym, value) } @initialize_commands << lambda {
config[variable.to_sym] = value
}
end end
Given /^current environment is "([^\"]*)"$/ do |env| Given /^current environment is "([^\"]*)"$/ do |env|
@ -44,8 +46,8 @@ Given /^the Server is running$/ do
initialize_commands = @initialize_commands || [] initialize_commands = @initialize_commands || []
initialize_commands.unshift lambda { initialize_commands.unshift lambda {
set :environment, @current_env || :development config[:environment] = @current_env || :development
set :show_exceptions, false config[:show_exceptions] = false
} }
@server_inst = Middleman::Application.server.inst do @server_inst = Middleman::Application.server.inst do

View file

@ -105,8 +105,8 @@ class Middleman::CoreExtensions::DefaultHelpers < ::Middleman::Extension
def auto_tag(asset_ext, asset_dir=nil) def auto_tag(asset_ext, asset_dir=nil)
if asset_dir.nil? if asset_dir.nil?
asset_dir = case asset_ext asset_dir = case asset_ext
when :js then js_dir when :js then config[:js_dir]
when :css then css_dir when :css then config[:css_dir]
end end
end end
@ -153,10 +153,10 @@ class Middleman::CoreExtensions::DefaultHelpers < ::Middleman::Extension
def asset_path(kind, source) def asset_path(kind, source)
return source if source.to_s.include?('//') || source.to_s.start_with?('data:') return source if source.to_s.include?('//') || source.to_s.start_with?('data:')
asset_folder = case kind asset_folder = case kind
when :css then css_dir when :css then config[:css_dir]
when :js then js_dir when :js then config[:js_dir]
when :images then images_dir when :images then config[:images_dir]
when :fonts then fonts_dir when :fonts then config[:fonts_dir]
else kind.to_s else kind.to_s
end end
source = source.to_s.tr(' ', '') source = source.to_s.tr(' ', '')

View file

@ -20,7 +20,7 @@ class Middleman::Extensions::AutomaticImageSizes < ::Middleman::Extension
params[:alt] ||= '' params[:alt] ||= ''
real_path = path real_path = path
real_path = File.join(images_dir, real_path) unless real_path.start_with?('/') real_path = File.join(config[:images_dir], real_path) unless real_path.start_with?('/')
full_path = File.join(source_dir, real_path) full_path = File.join(source_dir, real_path)
if File.exists?(full_path) if File.exists?(full_path)

View file

@ -8,7 +8,7 @@ class Middleman::Extensions::CacheBuster < ::Middleman::Extension
app.compass_config do |config| app.compass_config do |config|
config.asset_cache_buster do |path, real_path| config.asset_cache_buster do |path, real_path|
real_path = real_path.path if real_path.is_a? File real_path = real_path.path if real_path.is_a? File
real_path = real_path.gsub(File.join(root, build_dir), source) real_path = real_path.gsub(File.join(app.root, app.config[:build_dir]), app.config[:source])
if File.readable?(real_path) if File.readable?(real_path)
File.mtime(real_path).strftime('%s') File.mtime(real_path).strftime('%s')
else else
@ -35,7 +35,7 @@ class Middleman::Extensions::CacheBuster < ::Middleman::Extension
real_path_static = File.join(prefix, path) real_path_static = File.join(prefix, path)
if build? if build?
real_path_dynamic = File.join(build_dir, prefix, path) real_path_dynamic = File.join(config[:build_dir], prefix, path)
real_path_dynamic = File.expand_path(real_path_dynamic, root) real_path_dynamic = File.expand_path(real_path_dynamic, root)
http_path << '?' + File.mtime(real_path_dynamic).strftime('%s') if File.readable?(real_path_dynamic) http_path << '?' + File.mtime(real_path_dynamic).strftime('%s') if File.readable?(real_path_dynamic)
elsif resource = sitemap.find_resource_by_path(real_path_static) elsif resource = sitemap.find_resource_by_path(real_path_static)

View file

@ -3,7 +3,7 @@ class Middleman::Extensions::DirectoryIndexes < ::Middleman::Extension
# Update the main sitemap resource list # Update the main sitemap resource list
# @return [void] # @return [void]
def manipulate_resource_list(resources) def manipulate_resource_list(resources)
index_file = app.index_file index_file = app.config[:index_file]
new_index_path = "/#{index_file}" new_index_path = "/#{index_file}"
resources.each do |resource| resources.each do |resource|

View file

@ -23,7 +23,7 @@ class Middleman::Extensions::Gzip < ::Middleman::Extension
def after_build(builder) def after_build(builder)
num_threads = 4 num_threads = 4
paths = ::Middleman::Util.all_files_under(app.build_dir) paths = ::Middleman::Util.all_files_under(app.config[:build_dir])
total_savings = 0 total_savings = 0
# Fill a queue with inputs # Fill a queue with inputs