commit
5c75d26c86
|
@ -8,3 +8,4 @@ middleman-*/lib/**/*.rb
|
|||
--exclude middleman-cli/lib/middleman-cli/templates/extension/
|
||||
--no-private
|
||||
--hide-void-return
|
||||
--markup=markdown
|
|
@ -6,3 +6,4 @@ lib/**/*.rb
|
|||
--exclude lib/middleman-cli/templates/extension/
|
||||
--no-private
|
||||
--hide-void-return
|
||||
--markup=markdown
|
|
@ -3,3 +3,4 @@ lib/**/*.rb
|
|||
--exclude lib/middleman-core/step_definitions
|
||||
--no-private
|
||||
--hide-void-return
|
||||
--markup=markdown
|
55
middleman-core/lib/middleman-core/auto_gem_extensions.rb
Normal file
55
middleman-core/lib/middleman-core/auto_gem_extensions.rb
Normal file
|
@ -0,0 +1,55 @@
|
|||
# Add module methods to the Middleman module that allow automatically loading
|
||||
# extensions defined in gems based on the existance of a special file. This also
|
||||
# adds a method for iterating over all the Gems on a system.
|
||||
module Middleman
|
||||
class << self
|
||||
# Where to look in gems for extensions to auto-register. Since most extensions are
|
||||
# called out in a Gemfile, this is really only useful for template extensions that get
|
||||
# used by "middleman init".
|
||||
EXTENSION_FILE = File.join('lib', 'middleman_extension.rb') unless const_defined?(:EXTENSION_FILE)
|
||||
|
||||
# Automatically load extensions from available RubyGems
|
||||
# which contain the EXTENSION_FILE
|
||||
#
|
||||
# @private
|
||||
def load_extensions_in_path
|
||||
require 'rubygems'
|
||||
|
||||
extensions = rubygems_latest_specs.select do |spec|
|
||||
spec_has_file?(spec, EXTENSION_FILE)
|
||||
end
|
||||
|
||||
extensions.each do |spec|
|
||||
require spec.name
|
||||
end
|
||||
end
|
||||
|
||||
# Backwards compatible means of finding all the latest gemspecs
|
||||
# available on the system
|
||||
#
|
||||
# @private
|
||||
# @return [Array] Array of latest Gem::Specification
|
||||
def rubygems_latest_specs
|
||||
# If newer Rubygems
|
||||
if ::Gem::Specification.respond_to? :latest_specs
|
||||
::Gem::Specification.latest_specs(true)
|
||||
else
|
||||
::Gem.source_index.latest_specs
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Where a given Gem::Specification has a specific file. Used
|
||||
# to discover extensions.
|
||||
#
|
||||
# @private
|
||||
# @param [Gem::Specification] spec
|
||||
# @param [String] path Path to look for
|
||||
# @return [Boolean] Whether the file exists
|
||||
def spec_has_file?(spec, path)
|
||||
full_path = File.join(spec.full_gem_path, path)
|
||||
File.exist?(full_path)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -82,9 +82,11 @@ module Middleman
|
|||
# Define a new setting, with optional default and user-friendly description.
|
||||
# Once the configuration manager is finalized, no new settings may be defined.
|
||||
#
|
||||
# @param [Symbol] key
|
||||
# @param [Object] default
|
||||
# @param [String] description
|
||||
# @example
|
||||
# config.define_setting :compress, false, 'Whether to compress the output'
|
||||
# @param [Symbol] key The name of the option
|
||||
# @param [Object] default The default value for the option
|
||||
# @param [String] description A human-readable description of what the option does
|
||||
# @return [ConfigSetting]
|
||||
def define_setting(key, default=nil, description=nil)
|
||||
raise "Setting #{key} doesn't exist" if @finalized
|
||||
|
|
|
@ -1,34 +1,3 @@
|
|||
# Middleman provides an extension API which allows you to hook into the
|
||||
# lifecycle of a page request, or static build, and manipulate the output.
|
||||
# Internal to Middleman, these extensions are called "features," but we use
|
||||
# the exact same API as is made available to the public.
|
||||
#
|
||||
# A Middleman extension looks like this:
|
||||
#
|
||||
# module MyExtension
|
||||
# class << self
|
||||
# def registered(app)
|
||||
# # My Code
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# In your `config.rb`, you must load your extension (if it is not defined in
|
||||
# that file) and call `activate`.
|
||||
#
|
||||
# require "my_extension"
|
||||
# activate MyExtension
|
||||
#
|
||||
# This will call the `registered` method in your extension and provide you
|
||||
# with the `app` parameter which is a Middleman::Application context. From here
|
||||
# you can choose to respond to requests for certain paths or simply attach
|
||||
# Rack middleware to the stack.
|
||||
#
|
||||
# The built-in features cover a wide range of functions. Some provide helper
|
||||
# methods to use in your views. Some modify the output on-the-fly. And some
|
||||
# apply computationally-intensive changes to your final build files.
|
||||
|
||||
# Namespace extensions module
|
||||
module Middleman
|
||||
module CoreExtensions
|
||||
module Extensions
|
||||
|
@ -47,7 +16,6 @@ module Middleman
|
|||
app.config[:autoload_sprockets] = (ENV['AUTOLOAD_SPROCKETS'] == 'true') if ENV['AUTOLOAD_SPROCKETS']
|
||||
|
||||
app.extend ClassMethods
|
||||
app.send :include, InstanceMethods
|
||||
app.delegate :configure, to: :"self.class"
|
||||
end
|
||||
end
|
||||
|
@ -56,15 +24,13 @@ module Middleman
|
|||
module ClassMethods
|
||||
# Add a callback to run in a specific environment
|
||||
#
|
||||
# @param [String, Symbol] env The environment to run in
|
||||
# @param [String, Symbol] env The environment to run in (:build, :development)
|
||||
# @return [void]
|
||||
def configure(env, &block)
|
||||
send("#{env}_config", &block)
|
||||
end
|
||||
end
|
||||
|
||||
# Instance methods
|
||||
module InstanceMethods
|
||||
# This method is available in the project's `config.rb`.
|
||||
# It takes a underscore-separated symbol, finds the appropriate
|
||||
# feature module and includes it.
|
||||
|
@ -170,7 +136,7 @@ module Middleman
|
|||
|
||||
::Middleman::Extension.activated_extension(klass)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -56,8 +56,6 @@ module Middleman
|
|||
# Set the shared instance
|
||||
#
|
||||
# @private
|
||||
# @param [Middleman::Application] inst
|
||||
# @return [void]
|
||||
attr_writer :inst
|
||||
|
||||
# Return built Rack app
|
||||
|
@ -188,7 +186,6 @@ module Middleman
|
|||
# message.
|
||||
#
|
||||
# @param env
|
||||
# @param [Rack::Request] req
|
||||
# @param [Rack::Response] res
|
||||
def process_request(env, _, res)
|
||||
start_time = Time.now
|
||||
|
|
|
@ -2,64 +2,179 @@ require 'active_support/core_ext/module/delegation'
|
|||
require 'active_support/core_ext/class/attribute'
|
||||
|
||||
module Middleman
|
||||
# Middleman's Extension API provides the ability to add functionality to Middleman
|
||||
# and to customize existing features. Internally, most features in Middleman are
|
||||
# implemented as extensions. A good way to figure out how to write your own extension
|
||||
# is to look at the source of the built-in extensions or popular extension gems like
|
||||
# `middleman-blog` or `middleman-syntax`.
|
||||
#
|
||||
# The most basic extension looks like:
|
||||
#
|
||||
# class MyFeature < Middleman::Extension
|
||||
# def initialize(app, options_hash={}, &block)
|
||||
# super
|
||||
# end
|
||||
# end
|
||||
# ::Middleman::Extensions.register(:my_feature, MyFeature)
|
||||
#
|
||||
# A more complicated example might look like:
|
||||
#
|
||||
# class MyFeature < Middleman::Extension
|
||||
# option :my_option, 'cool', 'A very cool option'
|
||||
#
|
||||
# def initialize(app, options_hash={}, &block)
|
||||
# super
|
||||
# puts "My option is #{options.my_option}"
|
||||
# end
|
||||
#
|
||||
# def after_configuration
|
||||
# puts "The project has been configured"
|
||||
# end
|
||||
#
|
||||
# def manipulate_resource_list(resources)
|
||||
# resources.each do |resource|
|
||||
# # Make all .jpg's get built or served with a .jpeg extension.
|
||||
# if resource.ext == '.jpg'
|
||||
# resource.destination_path = resource.destination_path.sub('.jpg', '.jpeg')
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# ::Middleman::Extensions.register :my_feature do
|
||||
# MyFeature
|
||||
# end
|
||||
#
|
||||
# Extensions can add helpers (via {Extension.helpers}), add to the sitemap or change it (via {#manipulate_resource_list}), or run
|
||||
# arbitrary code at different parts of the Middleman application's lifecycle. They can have options (defined via {Extension.option} and accessed via {#options}).
|
||||
#
|
||||
# Common lifecycle events can be handled by extensions simply by implementing an appropriately-named method:
|
||||
#
|
||||
# * {#after_configuration}
|
||||
# * {#after_build}
|
||||
# * {#before_build}
|
||||
# * {#instance_available}
|
||||
#
|
||||
# There are also some less common hooks that can be listened to from within an extension's `initialize` method:
|
||||
#
|
||||
# * `app.before_render {|body, path, locs, template_class| ... }` - Manipulate template sources before they are rendered.
|
||||
# * `app.after_render {|content, path, locs, template_class| ... }` - Manipulate output text after a template has been rendered. It is also common to install a Rack middleware to do this instead.
|
||||
# * `app.ready { ... }` - Run code once Middleman is ready to serve or build files (after `after_configuration`).
|
||||
# * `app.compass_config { |compass_config| ... }` - Manipulate the Compass configuration after it has been set up.
|
||||
#
|
||||
# @see http://middlemanapp.com/advanced/custom/ Middleman Custom Extensions Documentation
|
||||
class Extension
|
||||
# @!attribute supports_multiple_instances
|
||||
# @!scope class
|
||||
# @return [Boolean] whether or not an extension can be activated multiple times, generating multiple instances of the extension.
|
||||
# By default extensions can only be activated once in a project. This is an advanced option.
|
||||
class_attribute :supports_multiple_instances, instance_reader: false, instance_writer: false
|
||||
|
||||
# @!attribute defined_helpers
|
||||
# @!scope class
|
||||
# @api private
|
||||
# @return [Array<Module>] a list of all the helper modules this extension provides. Set these using {#helpers}.
|
||||
class_attribute :defined_helpers, instance_reader: false, instance_writer: false
|
||||
|
||||
# @!attribute ext_name
|
||||
# @!scope class
|
||||
# @return [Symbol] the name this extension is registered under. This is the symbol used to activate the extension.
|
||||
class_attribute :ext_name, instance_reader: false, instance_writer: false
|
||||
|
||||
class << self
|
||||
# @api private
|
||||
# @return [Middleman::Configuration::ConfigurationManager] The defined options for this extension.
|
||||
def config
|
||||
@_config ||= ::Middleman::Configuration::ConfigurationManager.new
|
||||
end
|
||||
|
||||
# Add an option to this extension.
|
||||
# @see Middleman::Configuration::ConfigurationManager#define_setting
|
||||
# @example
|
||||
# option :compress, false, 'Whether to compress the output'
|
||||
# @param [Symbol] key The name of the option
|
||||
# @param [Object] default The default value for the option
|
||||
# @param [String] description A human-readable description of what the option does
|
||||
def option(key, default=nil, description=nil)
|
||||
config.define_setting(key, default, description)
|
||||
end
|
||||
|
||||
# Add helpers to the global Middleman application.
|
||||
# Declare helpers to be added the global Middleman application.
|
||||
# This accepts either a list of modules to add on behalf
|
||||
# of this extension, or a block whose contents will all
|
||||
# be used as helpers in a new module.
|
||||
def helpers(*m, &block)
|
||||
# @example With a block:
|
||||
# helpers do
|
||||
# def my_helper
|
||||
# "I helped!"
|
||||
# end
|
||||
# end
|
||||
# @example With modules:
|
||||
# helpers FancyHelpers, PlainHelpers
|
||||
# @param [Array<Module>] modules An optional list of modules to add as helpers
|
||||
# @param [Proc] block A block which will be evaluated to create a new helper module
|
||||
# @return [void]
|
||||
def helpers(*modules, &block)
|
||||
self.defined_helpers ||= []
|
||||
|
||||
if block_given?
|
||||
mod = Module.new
|
||||
mod.module_eval(&block)
|
||||
m = [mod]
|
||||
modules = [mod]
|
||||
end
|
||||
|
||||
self.defined_helpers += m
|
||||
end
|
||||
|
||||
def activate
|
||||
new(::Middleman::Application)
|
||||
self.defined_helpers += modules
|
||||
end
|
||||
|
||||
# Reset all {Extension.after_extension_activated} callbacks.
|
||||
# @api private
|
||||
# @return [void]
|
||||
def clear_after_extension_callbacks
|
||||
@_extension_activation_callbacks = {}
|
||||
end
|
||||
|
||||
# Register to run a block after a named extension is activated.
|
||||
# @param [Symbol] name The name the extension was registered under
|
||||
# @param [Proc] block A callback to run when the named extension is activated
|
||||
# @return [void]
|
||||
def after_extension_activated(name, &block)
|
||||
@_extension_activation_callbacks ||= {}
|
||||
@_extension_activation_callbacks[name] ||= []
|
||||
@_extension_activation_callbacks[name] << block if block_given?
|
||||
end
|
||||
|
||||
# Notify that a particular extension has been activated and run all
|
||||
# registered {Extension.after_extension_activated} callbacks.
|
||||
# @api private
|
||||
# @param [Middleman::Extension] instance Activated extension instance
|
||||
# @return [void]
|
||||
def activated_extension(instance)
|
||||
name = instance.class.ext_name
|
||||
return unless @_extension_activation_callbacks && @_extension_activation_callbacks[name]
|
||||
return unless @_extension_activation_callbacks && @_extension_activation_callbacks.has_key?(name)
|
||||
@_extension_activation_callbacks[name].each do |block|
|
||||
block.arity == 1 ? block.call(instance) : block.call
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
attr_accessor :options
|
||||
# @return [Middleman::Configuration::ConfigurationManager] options for this extension instance.
|
||||
attr_reader :options
|
||||
|
||||
# @return [Middleman::Application] the Middleman application instance.
|
||||
attr_reader :app
|
||||
|
||||
# @!method after_extension_activated(name, &block)
|
||||
# Register to run a block after a named extension is activated.
|
||||
# @param [Symbol] name The name the extension was registered under
|
||||
# @param [Proc] block A callback to run when the named extension is activated
|
||||
# @return [void]
|
||||
delegate :after_extension_activated, to: :"::Middleman::Extension"
|
||||
|
||||
# Extensions are instantiated when they are activated.
|
||||
# @param [Class] klass The Middleman::Application class
|
||||
# @param [Hash] options_hash The raw options hash. Subclasses should not manipulate this directly - it will be turned into {#options}.
|
||||
# @yield An optional block that can be used to customize options before the extension is activated.
|
||||
# @yieldparam [Middleman::Configuration::ConfigurationManager] options Extension options
|
||||
def initialize(klass, options_hash={}, &block)
|
||||
@_helpers = []
|
||||
@klass = klass
|
||||
|
@ -74,6 +189,39 @@ module Middleman
|
|||
bind_after_build
|
||||
end
|
||||
|
||||
# @!method before_configuration
|
||||
# Respond to the `before_configuration` event.
|
||||
# If a `before_configuration` method is implemented, that method will be run before `config.rb` is run.
|
||||
# @note Because most extensions are activated from within `config.rb`, they *will not run* any `before_configuration` hook.
|
||||
|
||||
# @!method after_configuration
|
||||
# Respond to the `after_configuration` event.
|
||||
# If an `after_configuration` method is implemented, that method will be run before `config.rb` is run.
|
||||
|
||||
# @!method before_build
|
||||
# Respond to the `before_build` event.
|
||||
# If an `before_build` method is implemented, that method will be run before the builder runs.
|
||||
|
||||
# @!method after_build
|
||||
# Respond to the `after_build` event.
|
||||
# If an `after_build` method is implemented, that method will be run after the builder runs.
|
||||
|
||||
# @!method instance_available
|
||||
# Respond to the `instance_available` event.
|
||||
# If an `instance_available` method is implemented, that method will be run after `config.rb` is run and after environment-specific config blocks have been run, but before any `after_configuration` callbacks.
|
||||
|
||||
# @!method manipulate_resource_list(resources)
|
||||
# Manipulate the resource list by transforming or adding {Sitemap::Resource}s.
|
||||
# Sitemap manipulation is a powerful way of interacting with a project, since it can modify each {Sitemap::Resource} or generate new {Sitemap::Resources}. This method is used in a pipeline where each sitemap manipulator is run in turn, with each one being fed the output of the previous manipulator. See the source of built-in Middleman extensions like {Middleman::Extensions::DirectoryIndexes} and {Middleman::Extensions::AssetHash} for examples of how to use this.
|
||||
# @note This method *must* return the full set of resources, because its return value will be used as the new sitemap.
|
||||
# @see http://middlemanapp.com/advanced/sitemap/ Sitemap Documentation
|
||||
# @see Sitemap::Store
|
||||
# @see Sitemap::Resource
|
||||
# @param [Array<Sitemap::Resource>] resources A list of all the resources known to the sitemap.
|
||||
# @return [Array<Sitemap::Resource>] The transformed list of resources.
|
||||
|
||||
# Assign the app instance. Used internally.
|
||||
# @api private
|
||||
def app=(app)
|
||||
@app = app
|
||||
|
||||
|
@ -85,8 +233,10 @@ module Middleman
|
|||
end
|
||||
end
|
||||
|
||||
protected
|
||||
private
|
||||
|
||||
# @yield An optional block that can be used to customize options before the extension is activated.
|
||||
# @yieldparam Middleman::Configuration::ConfigurationManager] options Extension options
|
||||
def setup_options(options_hash)
|
||||
@options = self.class.config.dup
|
||||
@options.finalize!
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
require 'middleman-core/extension'
|
||||
|
||||
module Middleman
|
||||
# The Extensions module is used to handle global registration and loading of Middleman Extensions.
|
||||
#
|
||||
# The application-facing extension API (activate, etc) is in Middleman::CoreExtensions::Extensions in
|
||||
# middleman-core/core_extensions/extensions.rb.
|
||||
module Extensions
|
||||
class << self
|
||||
def registered
|
||||
|
@ -66,55 +72,4 @@ module Middleman
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Where to look in gems for extensions to auto-register. Since most extensions are
|
||||
# called out in a Gemfile, this is really only useful for template extensions that get
|
||||
# used by "middleman init".
|
||||
EXTENSION_FILE = File.join('lib', 'middleman_extension.rb') unless const_defined?(:EXTENSION_FILE)
|
||||
|
||||
class << self
|
||||
# Automatically load extensions from available RubyGems
|
||||
# which contain the EXTENSION_FILE
|
||||
#
|
||||
# @private
|
||||
def load_extensions_in_path
|
||||
require 'rubygems'
|
||||
|
||||
extensions = rubygems_latest_specs.select do |spec|
|
||||
spec_has_file?(spec, EXTENSION_FILE)
|
||||
end
|
||||
|
||||
extensions.each do |spec|
|
||||
require spec.name
|
||||
end
|
||||
end
|
||||
|
||||
# Backwards compatible means of finding all the latest gemspecs
|
||||
# available on the system
|
||||
#
|
||||
# @private
|
||||
# @return [Array] Array of latest Gem::Specification
|
||||
def rubygems_latest_specs
|
||||
# If newer Rubygems
|
||||
if ::Gem::Specification.respond_to? :latest_specs
|
||||
::Gem::Specification.latest_specs(true)
|
||||
else
|
||||
::Gem.source_index.latest_specs
|
||||
end
|
||||
end
|
||||
|
||||
# Where a given Gem::Specification has a specific file. Used
|
||||
# to discover extensions.
|
||||
#
|
||||
# @private
|
||||
# @param [Gem::Specification] spec
|
||||
# @param [String] path Path to look for
|
||||
# @return [Boolean] Whether the file exists
|
||||
def spec_has_file?(spec, path)
|
||||
full_path = File.join(spec.full_gem_path, path)
|
||||
File.exist?(full_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
require 'middleman-core/extension'
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Core Pathname library used for traversal
|
||||
require 'pathname'
|
||||
require 'middleman-core/auto_gem_extensions'
|
||||
|
||||
module Middleman
|
||||
class << self
|
||||
|
|
|
@ -57,21 +57,20 @@ module Middleman
|
|||
app.config_context.class.send :delegate, :sitemap, to: :app
|
||||
end
|
||||
|
||||
# Register a klass which can manipulate the main site map list. Best to register
|
||||
# these in a before_configuration or after_configuration hook.
|
||||
# Register an object which can transform the sitemap resource list. Best to register
|
||||
# these in a `before_configuration` or `after_configuration` hook.
|
||||
#
|
||||
# @param [Symbol] name Name of the manipulator for debugging
|
||||
# @param [Class, Module] inst Abstract namespace which can update the resource list
|
||||
# @param [#manipulate_resource_list] manipulator Resource list manipulator
|
||||
# @return [void]
|
||||
def register_resource_list_manipulator(name, inst, *)
|
||||
@resource_list_manipulators << [name, inst]
|
||||
def register_resource_list_manipulator(name, manipulator, *)
|
||||
@resource_list_manipulators << [name, manipulator]
|
||||
rebuild_resource_list!(:registered_new)
|
||||
end
|
||||
|
||||
# Rebuild the list of resources from scratch, using registed manipulators
|
||||
# rubocop:disable UnusedMethodArgument
|
||||
# @return [void]
|
||||
def rebuild_resource_list!(reason=nil)
|
||||
def rebuild_resource_list!(_=nil)
|
||||
@lock.synchronize do
|
||||
@needs_sitemap_rebuild = true
|
||||
end
|
||||
|
|
|
@ -60,7 +60,6 @@ module Middleman
|
|||
# Sinatra/Padrino compatible render method signature referenced by some view
|
||||
# helpers. Especially partials.
|
||||
#
|
||||
# @param [String, Symbol] engine
|
||||
# @param [String, Symbol] data
|
||||
# @param [Hash] options
|
||||
# @return [String]
|
||||
|
|
|
@ -2,3 +2,4 @@ lib/**/*.rb
|
|||
--exclude lib/middleman-templates
|
||||
--no-private
|
||||
--hide-void-return
|
||||
--markup=markdown
|
Loading…
Reference in a new issue