diff --git a/middleman-core/fixtures/feature-params-app/config.rb b/middleman-core/fixtures/feature-params-app/config.rb index f30e6074..fda7e5a0 100644 --- a/middleman-core/fixtures/feature-params-app/config.rb +++ b/middleman-core/fixtures/feature-params-app/config.rb @@ -1,12 +1,16 @@ set :layout, false -module ExtensionA - class << self - def registered(app, options={}) - app.set :a_options, options +class ExtensionA < ::Middleman::Extension + helpers do + def get_option(key) + extensions[:extension_a].options[key] end - alias :included :registered end + + option :hello, '', '' + option :hola, '', '' end -activate ExtensionA, :hello => "world", :hola => "mundo" +ExtensionA.register + +activate :extension_a, :hello => "world", :hola => "mundo" diff --git a/middleman-core/fixtures/feature-params-app/source/index.html.erb b/middleman-core/fixtures/feature-params-app/source/index.html.erb index 0435545e..62193af1 100644 --- a/middleman-core/fixtures/feature-params-app/source/index.html.erb +++ b/middleman-core/fixtures/feature-params-app/source/index.html.erb @@ -1,3 +1,2 @@ -<% a_options.each do |k, v| %> - <%= k %>: <%= v%> -<% end %> \ No newline at end of file +hello: <%= get_option(:hello) %> +hola: <%= get_option(:hola) %> \ No newline at end of file diff --git a/middleman-core/fixtures/v4-extension-callbacks/config.rb b/middleman-core/fixtures/v4-extension-callbacks/config.rb index 174bbec3..c9c1c5d4 100644 --- a/middleman-core/fixtures/v4-extension-callbacks/config.rb +++ b/middleman-core/fixtures/v4-extension-callbacks/config.rb @@ -1,9 +1,17 @@ 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={}) super after_extension_activated :extension_two do - app.set :extension_two_was_activated, true + @extension_two_was_activated = true end end end @@ -11,11 +19,19 @@ end ExtensionOne.register 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={}) super after_extension_activated :extension_one do - app.set :extension_one_was_activated, true + @extension_one_was_activated = true end end end diff --git a/middleman-core/lib/middleman-core/application.rb b/middleman-core/lib/middleman-core/application.rb index 74c35285..a1dc0092 100644 --- a/middleman-core/lib/middleman-core/application.rb +++ b/middleman-core/lib/middleman-core/application.rb @@ -20,10 +20,13 @@ require 'vendored-middleman-deps/hooks-0.2.0/lib/hooks' require 'middleman-core/logger' require 'middleman-core/sitemap' +require 'middleman-core/sitemap/store' require 'middleman-core/configuration' require 'middleman-core/core_extensions' +require 'middleman-core/config_context' + # Core Middleman Class module Middleman class Application @@ -150,48 +153,52 @@ module Middleman # extensions see updated frontmatter! register Middleman::CoreExtensions::FrontMatter - # Sitemap + # Sitemap Config options and public api register Middleman::Sitemap # Setup external helpers register Middleman::CoreExtensions::ExternalHelpers - # with_layout and page routing - include Middleman::CoreExtensions::Routing - + # Reference to Logger singleton 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 def initialize(&block) + @cache = ::Tilt::Cache.new @logger = ::Middleman::Logger.singleton - - # Clear the static class cache - cache.clear + @config_context = ConfigContext.new(self) # Setup the default values from calls to set before initialization 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) Encoding.default_internal = config[:encoding] Encoding.default_external = config[:encoding] end # 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'] super end - # Shared cache instance - # - # @private - # @return [Middleman::Util::Cache] The cache - def self.cache - @_cache ||= ::Tilt::Cache.new + def add_to_config_context(name, &func) + @config_context.define_singleton_method(name, &func) end - delegate :cache, :to => :"self.class" # Whether we're in development mode # @return [Boolean] If we're in dev mode diff --git a/middleman-core/lib/middleman-core/cli/build.rb b/middleman-core/lib/middleman-core/cli/build.rb index 33365949..c4d20923 100644 --- a/middleman-core/lib/middleman-core/cli/build.rb +++ b/middleman-core/lib/middleman-core/cli/build.rb @@ -68,6 +68,7 @@ module Middleman::Cli action BuildAction.new(self, opts) 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 msg = 'There were errors during this build' @@ -110,7 +111,7 @@ module Middleman::Cli def initialize(base, config={}) @app = base.class.shared_instance @source_dir = Pathname(@app.source_dir) - @build_dir = Pathname(@app.build_dir) + @build_dir = Pathname(@app.config[:build_dir]) @to_clean = Set.new @logger = @app.logger @@ -190,7 +191,7 @@ module Middleman::Cli logger.debug '== Checking 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! # Sort paths to be built by the above order. This is primarily so Compass can diff --git a/middleman-core/lib/middleman-core/cli/console.rb b/middleman-core/lib/middleman-core/cli/console.rb index d647db2f..21f4c42c 100644 --- a/middleman-core/lib/middleman-core/cli/console.rb +++ b/middleman-core/lib/middleman-core/cli/console.rb @@ -29,7 +29,7 @@ module Middleman::Cli @app =::Middleman::Application.server.inst do if opts[:environment] - set :environment, opts[:environment].to_sym + config[:environment] = opts[:environment].to_sym end logger(opts[:debug] ? 0 : 1, opts[:instrumenting] || false) diff --git a/middleman-core/lib/middleman-core/config_context.rb b/middleman-core/lib/middleman-core/config_context.rb new file mode 100644 index 00000000..358dc9f2 --- /dev/null +++ b/middleman-core/lib/middleman-core/config_context.rb @@ -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 \ No newline at end of file diff --git a/middleman-core/lib/middleman-core/configuration.rb b/middleman-core/lib/middleman-core/configuration.rb index 4340fa16..ea6d9037 100644 --- a/middleman-core/lib/middleman-core/configuration.rb +++ b/middleman-core/lib/middleman-core/configuration.rb @@ -5,6 +5,7 @@ module Middleman module Global def self.included(app) app.send :extend, ClassMethods + app.send :delegate, :config, :to => :"self.class" end module ClassMethods @@ -13,75 +14,6 @@ module Middleman def config @_config ||= ConfigurationManager.new 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 diff --git a/middleman-core/lib/middleman-core/core_extensions/extensions.rb b/middleman-core/lib/middleman-core/core_extensions/extensions.rb index 9f7fe708..3f028c08 100644 --- a/middleman-core/lib/middleman-core/core_extensions/extensions.rb +++ b/middleman-core/lib/middleman-core/core_extensions/extensions.rb @@ -159,11 +159,18 @@ module Middleman local_config = File.join(root, 'config.rb') if File.exists? 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 - run_hook :build_config if build? - run_hook :development_config if development? + if build? + 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 @@ -177,6 +184,7 @@ module Middleman end run_hook :after_configuration + config_context.execute_after_configuration_callbacks logger.debug 'Loaded extensions:' self.extensions.each do |ext, klass| diff --git a/middleman-core/lib/middleman-core/core_extensions/request.rb b/middleman-core/lib/middleman-core/core_extensions/request.rb index 09380ab8..fde29797 100644 --- a/middleman-core/lib/middleman-core/core_extensions/request.rb +++ b/middleman-core/lib/middleman-core/core_extensions/request.rb @@ -51,6 +51,7 @@ module Middleman @inst ||= begin mm = new(&block) mm.run_hook :ready + mm.config_context.execute_ready_callbacks mm end end diff --git a/middleman-core/lib/middleman-core/core_extensions/routing.rb b/middleman-core/lib/middleman-core/core_extensions/routing.rb index ea53efeb..c4171b78 100644 --- a/middleman-core/lib/middleman-core/core_extensions/routing.rb +++ b/middleman-core/lib/middleman-core/core_extensions/routing.rb @@ -2,69 +2,72 @@ module Middleman module CoreExtensions 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 - instance_exec(&block) if block_given? - ensure - config[:layout] = old_layout + # Sandboxed layout to implement temporary overriding of layout. + class LayoutBlock + attr_reader :scope + + def initialize(scope, layout_name) + @scope = scope + @layout_name = layout_name 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) - blocks = Array(block) + opts[:layout] ||= @layout_name + @scope.page(url, opts, &block) + end - # Default layout - opts[:layout] = config[:layout] if opts[:layout].nil? + delegate :proxy, :to => :scope + end - # If the url is a regexp - if url.is_a?(Regexp) || url.include?('*') + # 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) + LayoutBlock.new(self, layout_name).instance_eval(&block) + end - # Use the metadata loop for matching against paths at runtime - sitemap.provides_metadata_for_path(url) do |_| - { :options => opts, :blocks => blocks } - end - - return - 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) + # 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 url = '/' + Middleman::Util.normalize_path(url) - if url.end_with?('/') || File.directory?(File.join(source_dir, url)) - url = File.join(url, config[:index_file]) + if url.end_with?('/') || File.directory?(File.join(@app.source_dir, url)) + url = File.join(url, @app.config[:index_file]) end # Setup proxy if target = opts.delete(:proxy) # TODO: deprecate proxy through page? - proxy(url, target, opts, &block) and return + @app.proxy(url, target, opts, &block) + return elsif opts.delete(:ignore) # TODO: deprecate ignore through page? - ignore(url) - end - - # Setup a metadata matcher for rendering those options - sitemap.provides_metadata_for_path(url) do |_| - { :options => opts, :blocks => blocks } + @app.ignore(url) end end + + # Setup a metadata matcher for rendering those options + @app.sitemap.provides_metadata_for_path(url) { |_| metadata } + end end end end diff --git a/middleman-core/lib/middleman-core/renderers/stylus.rb b/middleman-core/lib/middleman-core/renderers/stylus.rb index 87109104..72a4cba8 100644 --- a/middleman-core/lib/middleman-core/renderers/stylus.rb +++ b/middleman-core/lib/middleman-core/renderers/stylus.rb @@ -12,8 +12,9 @@ module Middleman # Once registered def registered(app) - # Default less options - app.set :styl, {} + # Default stylus options + app.config.define_setting :styl, {}, 'Stylus config options' + app.before_configuration do template_extensions :styl => :css diff --git a/middleman-core/lib/middleman-core/sitemap.rb b/middleman-core/lib/middleman-core/sitemap.rb index 61f5de97..389ef5c4 100644 --- a/middleman-core/lib/middleman-core/sitemap.rb +++ b/middleman-core/lib/middleman-core/sitemap.rb @@ -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 module Middleman @@ -18,11 +9,6 @@ module Middleman # Once registered 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 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 app.send :include, InstanceMethods - - # Initialize Sitemap - app.before_configuration do - sitemap - end end alias :included :registered @@ -59,12 +40,6 @@ module Middleman # Sitemap instance methods 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 # @return [Middleman::Sitemap::Resource] def current_page diff --git a/middleman-core/lib/middleman-core/sitemap/extensions/ignores.rb b/middleman-core/lib/middleman-core/sitemap/extensions/ignores.rb index 59553626..3c5d3872 100644 --- a/middleman-core/lib/middleman-core/sitemap/extensions/ignores.rb +++ b/middleman-core/lib/middleman-core/sitemap/extensions/ignores.rb @@ -4,87 +4,61 @@ module Middleman 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 - class << self + # Array of callbacks which can ass ignored + @ignored_callbacks = [] - # Once registered - def registered(app) - # Include methods - app.send :include, InstanceMethods - - ::Middleman::Sitemap::Resource.send :include, ResourceInstanceMethods - end - - alias :included :registered + sitemap.define_singleton_method(:ignored?, &method(:ignored?)) + ::Middleman::Sitemap::Resource.send :include, IgnoreResourceInstanceMethods end - # Helpers methods for Resources - module ResourceInstanceMethods - - # Whether the Resource is ignored - # @return [Boolean] - def ignored? - @app.ignore_manager.ignored?(path) || - (!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) + # Ignore a path or add an ignore callback + # @param [String, Regexp] path Path glob expression, or path regex + # @return [void] + def create_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) - @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 + + @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 diff --git a/middleman-core/lib/middleman-core/sitemap/extensions/proxies.rb b/middleman-core/lib/middleman-core/sitemap/extensions/proxies.rb index 18076943..933e7312 100644 --- a/middleman-core/lib/middleman-core/sitemap/extensions/proxies.rb +++ b/middleman-core/lib/middleman-core/sitemap/extensions/proxies.rb @@ -4,165 +4,143 @@ module Middleman 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 - class << self + @proxy_configs = Set.new - # Once registered - def registered(app) - ::Middleman::Sitemap::Resource.send :include, ResourceInstanceMethods - - # Include methods - app.send :include, InstanceMethods - end - - alias :included :registered + ::Middleman::Sitemap::Resource.send :include, ProxyResourceInstanceMethods end - module ResourceInstanceMethods - # Whether this page is a proxy - # @return [Boolean] - def proxy? - !!@proxied_to + # Setup a proxy from a path to a target + # @param [String] path + # @param [String] target + # @param [Hash] opts options to apply to the proxy, including things like + # :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 - # 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 + # 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 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 existing sitemap path that this will proxy to + attr_reader :target + def target=(t) + @target = ::Middleman::Util.normalize_path(t) + 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) + # Additional metadata like blocks and locals to apply to the proxy + attr_accessor :metadata - unless proxy_resource - raise "Path #{path} proxies to unknown file #{proxied_to}:#{store.resources.map(&:path)}" - end - - 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 + # Create a new proxy configuration from hash options + def initialize(options={}) + options.each do |key, value| + send "#{key}=", value end end - module InstanceMethods - def proxy_manager - @_proxy_manager ||= ProxyManager.new(self) + # 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 + + 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 - def proxy(*args, &block) - proxy_manager.proxy(*args, &block) + 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 - # Manages the list of proxy configurations and manipulates the sitemap - # to include new resources based on those configurations - class ProxyManager - def initialize(app) - @app = app - @proxy_configs = Set.new - end + def content_type + mime_type = super + return mime_type if mime_type - # Setup a proxy from a path to a target - # @param [String] path - # @param [String] target - # @param [Hash] opts options to apply to the proxy, including things like - # :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 + if proxy? + proxied_to_resource.content_type + else + nil end end end diff --git a/middleman-core/lib/middleman-core/sitemap/extensions/redirects.rb b/middleman-core/lib/middleman-core/sitemap/extensions/redirects.rb index 9e67cd4d..6dfdb2b0 100644 --- a/middleman-core/lib/middleman-core/sitemap/extensions/redirects.rb +++ b/middleman-core/lib/middleman-core/sitemap/extensions/redirects.rb @@ -1,126 +1,105 @@ +require 'middleman-core/sitemap/resource' + module Middleman module Sitemap 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 - class << self - - # Once registered - def registered(app) - # Include methods - app.send :include, InstanceMethods - end - - alias :included :registered + @redirects = {} end - module InstanceMethods - def redirect_manager - @_redirect_manager ||= RedirectManager.new(self) + # Setup a redirect from a path to a target + # @param [String] path + # @param [Hash] opts The :to value gives a target path + def create_redirect(path, opts={}, &block) + if block_given? + opts[:template] = block end - def redirect(*args, &block) - redirect_manager.create_redirect(*args, &block) + @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 + + 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 + + + + + + + + + + END end end - # Manages the list of proxy configurations and manipulates the sitemap - # to include new resources based on those configurations - class RedirectManager - def initialize(app) - @app = app - @redirects = {} - end + # def request_path + # @request_path + # end - # Setup a redirect from a path to a target - # @param [String] path - # @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 + def binary? + false 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 - - - - - - - - - - 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 - + def raw_data + {} end + + def ignored? + false + end + + def metadata + @local_metadata.dup + end + end 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 84eee061..ba3ad732 100644 --- a/middleman-core/lib/middleman-core/sitemap/extensions/request_endpoints.rb +++ b/middleman-core/lib/middleman-core/sitemap/extensions/request_endpoints.rb @@ -4,110 +4,87 @@ module Middleman module Extensions - module 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 + class RequestEndpoints # Manages the list of proxy configurations and manipulates the sitemap # to include new resources based on those configurations - class EndpointManager - def initialize(app) - @app = app - @endpoints = {} - end + def initialize(sitemap) + @app = sitemap.app + @app.add_to_config_context :endpoint, &method(:create_endpoint) - # 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 - 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 + @endpoints = {} end - class EndpointResource < ::Middleman::Sitemap::Resource - attr_accessor :output + # 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 + def create_endpoint(path, opts={}, &block) + endpoint = { + :request_path => path + } - def initialize(store, path, source_file) - @request_path = ::Middleman::Util.normalize_path(source_file) - - super(store, path) + if block_given? + endpoint[:output] = block + else + endpoint[:request_path] = opts[:path] if opts.has_key?(:path) end - def template? - true - end + @endpoints[path] = endpoint - def render(*args, &block) - return self.output.call if self.output - end + @app.sitemap.rebuild_resource_list!(:added_endpoint) + end - def request_path - @request_path + # 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 - def binary? - false - end + class EndpointResource < ::Middleman::Sitemap::Resource + attr_accessor :output - def raw_data - {} - end + def initialize(store, path, source_file) + @request_path = ::Middleman::Util.normalize_path(source_file) - def ignored? - false - end + super(store, path) + end - def metadata - @local_metadata.dup - end + def template? + true + 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 diff --git a/middleman-core/lib/middleman-core/sitemap/extensions/traversal.rb b/middleman-core/lib/middleman-core/sitemap/extensions/traversal.rb index 78f8eb28..65c43030 100644 --- a/middleman-core/lib/middleman-core/sitemap/extensions/traversal.rb +++ b/middleman-core/lib/middleman-core/sitemap/extensions/traversal.rb @@ -9,12 +9,12 @@ module Middleman # @return [Middleman::Sitemap::Resource, nil] def parent 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 parts.pop - parts << app.index_file + parts << app.config[:index_file] parent_path = '/' + parts.join('/') @@ -30,7 +30,7 @@ module Middleman base_path = eponymous_directory_path prefix = %r|^#{base_path.sub("/", "\\/")}| else - base_path = path.sub("#{app.index_file}", '') + base_path = path.sub("#{app.config[:index_file]}", '') prefix = %r|^#{base_path.sub("/", "\\/")}| end @@ -43,7 +43,7 @@ module Middleman if parts.length == 1 true elsif parts.length == 2 - parts.last == app.index_file + parts.last == app.config[:index_file] else false 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 # @return [Boolean] def directory_index? - path.include?(app.index_file) || path =~ /\/$/ || eponymous_directory? + path.include?(app.config[:index_file]) || path =~ /\/$/ || eponymous_directory? end # 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) # @return [Boolean] 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 end full_path = File.join(app.source_dir, eponymous_directory_path) diff --git a/middleman-core/lib/middleman-core/sitemap/resource.rb b/middleman-core/lib/middleman-core/sitemap/resource.rb index ae3f7ae7..d0d39483 100644 --- a/middleman-core/lib/middleman-core/sitemap/resource.rb +++ b/middleman-core/lib/middleman-core/sitemap/resource.rb @@ -149,11 +149,11 @@ module Middleman # @return [String] def url url_path = destination_path - if app.strip_index_file - url_path = url_path.sub(/(^|\/)#{Regexp.escape(app.index_file)}$/, - app.trailing_slash ? '/' : '') + if app.config[:strip_index_file] + url_path = url_path.sub(/(^|\/)#{Regexp.escape(app.config[:index_file])}$/, + app.config[:trailing_slash] ? '/' : '') end - File.join(app.respond_to?(:http_prefix) ? app.http_prefix : '/', url_path) + File.join(app.config[:http_prefix], url_path) end # Whether the source file is binary. diff --git a/middleman-core/lib/middleman-core/sitemap/store.rb b/middleman-core/lib/middleman-core/sitemap/store.rb index 190da9b7..a8fcdd22 100644 --- a/middleman-core/lib/middleman-core/sitemap/store.rb +++ b/middleman-core/lib/middleman-core/sitemap/store.rb @@ -3,6 +3,13 @@ require 'active_support/core_ext/hash/deep_merge' require 'monitor' 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 # Sitemap namespace @@ -29,21 +36,31 @@ module Middleman @_cached_metadata = {} @resource_list_manipulators = [] @needs_sitemap_rebuild = true + @lock = Monitor.new - reset_lookup_cache! - # Register classes which can manipulate the main site map list - register_resource_list_manipulator(:on_disk, Middleman::Sitemap::Extensions::OnDisk.new(self)) + # Handle ignore commands + Middleman::Sitemap::Extensions::Ignores.new(self) - # Request Endpoints - register_resource_list_manipulator(:request_endpoints, @app.endpoint_manager) + # Extensions + { + # Register classes which can manipulate the main site map list + on_disk: Middleman::Sitemap::Extensions::OnDisk, - # Proxies - register_resource_list_manipulator(:proxies, @app.proxy_manager) + # Request Endpoints + request_endpoints: Middleman::Sitemap::Extensions::RequestEndpoints, - # Redirects - register_resource_list_manipulator(:redirects, @app.redirect_manager) + # Proxies + 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 # Register a klass which can manipulate the main site map list. Best to register diff --git a/middleman-core/lib/middleman-core/step_definitions/server_steps.rb b/middleman-core/lib/middleman-core/step_definitions/server_steps.rb index 0dda0772..7f51de6e 100644 --- a/middleman-core/lib/middleman-core/step_definitions/server_steps.rb +++ b/middleman-core/lib/middleman-core/step_definitions/server_steps.rb @@ -24,7 +24,9 @@ end Given /^"([^\"]*)" is set to "([^\"]*)"$/ do |variable, value| @initialize_commands ||= [] - @initialize_commands << lambda { set(variable.to_sym, value) } + @initialize_commands << lambda { + config[variable.to_sym] = value + } end Given /^current environment is "([^\"]*)"$/ do |env| @@ -44,8 +46,8 @@ Given /^the Server is running$/ do initialize_commands = @initialize_commands || [] initialize_commands.unshift lambda { - set :environment, @current_env || :development - set :show_exceptions, false + config[:environment] = @current_env || :development + config[:show_exceptions] = false } @server_inst = Middleman::Application.server.inst do diff --git a/middleman-core/lib/middleman-more/core_extensions/default_helpers.rb b/middleman-core/lib/middleman-more/core_extensions/default_helpers.rb index ce6d245d..b3120bb6 100644 --- a/middleman-core/lib/middleman-more/core_extensions/default_helpers.rb +++ b/middleman-core/lib/middleman-more/core_extensions/default_helpers.rb @@ -105,8 +105,8 @@ class Middleman::CoreExtensions::DefaultHelpers < ::Middleman::Extension def auto_tag(asset_ext, asset_dir=nil) if asset_dir.nil? asset_dir = case asset_ext - when :js then js_dir - when :css then css_dir + when :js then config[:js_dir] + when :css then config[:css_dir] end end @@ -153,10 +153,10 @@ class Middleman::CoreExtensions::DefaultHelpers < ::Middleman::Extension def asset_path(kind, source) return source if source.to_s.include?('//') || source.to_s.start_with?('data:') asset_folder = case kind - when :css then css_dir - when :js then js_dir - when :images then images_dir - when :fonts then fonts_dir + when :css then config[:css_dir] + when :js then config[:js_dir] + when :images then config[:images_dir] + when :fonts then config[:fonts_dir] else kind.to_s end source = source.to_s.tr(' ', '') diff --git a/middleman-core/lib/middleman-more/extensions/automatic_image_sizes.rb b/middleman-core/lib/middleman-more/extensions/automatic_image_sizes.rb index 1b600e4e..2fb37abb 100644 --- a/middleman-core/lib/middleman-more/extensions/automatic_image_sizes.rb +++ b/middleman-core/lib/middleman-more/extensions/automatic_image_sizes.rb @@ -20,7 +20,7 @@ class Middleman::Extensions::AutomaticImageSizes < ::Middleman::Extension params[:alt] ||= '' 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) if File.exists?(full_path) diff --git a/middleman-core/lib/middleman-more/extensions/cache_buster.rb b/middleman-core/lib/middleman-more/extensions/cache_buster.rb index 02ded8af..8d1788ce 100644 --- a/middleman-core/lib/middleman-more/extensions/cache_buster.rb +++ b/middleman-core/lib/middleman-more/extensions/cache_buster.rb @@ -8,7 +8,7 @@ class Middleman::Extensions::CacheBuster < ::Middleman::Extension app.compass_config do |config| config.asset_cache_buster do |path, real_path| 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) File.mtime(real_path).strftime('%s') else @@ -35,7 +35,7 @@ class Middleman::Extensions::CacheBuster < ::Middleman::Extension real_path_static = File.join(prefix, path) 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) 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) diff --git a/middleman-core/lib/middleman-more/extensions/directory_indexes.rb b/middleman-core/lib/middleman-more/extensions/directory_indexes.rb index 18818f5e..5c14f723 100644 --- a/middleman-core/lib/middleman-more/extensions/directory_indexes.rb +++ b/middleman-core/lib/middleman-more/extensions/directory_indexes.rb @@ -3,7 +3,7 @@ class Middleman::Extensions::DirectoryIndexes < ::Middleman::Extension # Update the main sitemap resource list # @return [void] def manipulate_resource_list(resources) - index_file = app.index_file + index_file = app.config[:index_file] new_index_path = "/#{index_file}" resources.each do |resource| diff --git a/middleman-core/lib/middleman-more/extensions/gzip.rb b/middleman-core/lib/middleman-more/extensions/gzip.rb index d52e8cfc..6ca5e6d1 100644 --- a/middleman-core/lib/middleman-more/extensions/gzip.rb +++ b/middleman-core/lib/middleman-more/extensions/gzip.rb @@ -23,7 +23,7 @@ class Middleman::Extensions::Gzip < ::Middleman::Extension def after_build(builder) 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 # Fill a queue with inputs