475fa41de0
I’ve attempted to make support for .htm and .xhtml consistent. Extensions are in alphabetical order to make it easier to compare them.
434 lines
14 KiB
Ruby
434 lines
14 KiB
Ruby
require 'active_support/core_ext/integer/inflections'
|
|
|
|
require 'middleman-core/contracts'
|
|
require 'middleman-core/callback_manager'
|
|
require 'middleman-core/logger'
|
|
require 'middleman-core/sitemap/store'
|
|
require 'middleman-core/configuration'
|
|
require 'middleman-core/extension_manager'
|
|
require 'middleman-core/core_extensions'
|
|
require 'middleman-core/config_context'
|
|
require 'middleman-core/file_renderer'
|
|
require 'middleman-core/template_renderer'
|
|
|
|
# Core Middleman Class
|
|
module Middleman
|
|
MiddlewareDescriptor = Struct.new(:class, :options, :block)
|
|
MapDescriptor = Struct.new(:path, :block)
|
|
|
|
class Application
|
|
extend Forwardable
|
|
include Contracts
|
|
|
|
class << self
|
|
extend Forwardable
|
|
def_delegator :config, :define_setting
|
|
|
|
# Global configuration for the whole Middleman project.
|
|
# @return [ConfigurationManager]
|
|
def config
|
|
@config ||= ::Middleman::Configuration::ConfigurationManager.new
|
|
end
|
|
|
|
# Root project directory (overwritten in middleman build/server)
|
|
# @return [String]
|
|
def root
|
|
r = ENV['MM_ROOT'] ? ENV['MM_ROOT'].dup : ::Middleman::Util.current_directory
|
|
r.encode!('UTF-8', 'UTF-8-MAC') if RUBY_PLATFORM =~ /darwin/
|
|
r
|
|
end
|
|
|
|
# Pathname-addressed root
|
|
def root_path
|
|
Pathname(root)
|
|
end
|
|
end
|
|
|
|
Contract ::Middleman::ConfigContext
|
|
attr_reader :config_context
|
|
|
|
Contract ::Middleman::Sitemap::Store
|
|
attr_reader :sitemap
|
|
|
|
# An anonymous subclass of ::Middleman::TemplateContext
|
|
attr_reader :template_context_class
|
|
|
|
# An instance of the above anonymouse class.
|
|
attr_reader :generic_template_context
|
|
|
|
Contract ::Middleman::Configuration::ConfigurationManager
|
|
attr_reader :config
|
|
|
|
Contract ::Middleman::ExtensionManager
|
|
attr_reader :extensions
|
|
|
|
Contract SetOf[MiddlewareDescriptor]
|
|
attr_reader :middleware
|
|
|
|
Contract SetOf[MapDescriptor]
|
|
attr_reader :mappings
|
|
|
|
# Which port preview should start on.
|
|
# @return [Fixnum]
|
|
define_setting :port, 4567, 'The preview server port'
|
|
|
|
# Which server name should be used
|
|
# @return [NilClass, String]
|
|
define_setting :server_name, nil, 'The server name of preview server'
|
|
|
|
# Which bind address the preview server should use
|
|
# @return [NilClass, String]
|
|
define_setting :bind_address, nil, 'The bind address of the preview server'
|
|
|
|
# Whether to serve the preview server over HTTPS.
|
|
# @return [Boolean]
|
|
define_setting :https, false, 'Serve the preview server over SSL/TLS'
|
|
|
|
# The (optional) path to the SSL cert to use for the preview server.
|
|
# @return [String]
|
|
define_setting :ssl_certificate, nil, 'Path to an X.509 certificate to use for the preview server'
|
|
|
|
# The (optional) private key for the certificate in :ssl_certificate.
|
|
# @return [String]
|
|
define_setting :ssl_private_key, nil, "Path to an RSA private key for the preview server's certificate"
|
|
|
|
# Name of the source directory
|
|
# @return [String]
|
|
define_setting :source, 'source', 'Name of the source directory'
|
|
|
|
# If we should exit before ready event.
|
|
# @return [Boolean]
|
|
define_setting :exit_before_ready, false, 'If we should exit before ready event.'
|
|
|
|
# Middleman mode. Defaults to :server, set to :build by the build process
|
|
# @return [String]
|
|
define_setting :mode, :server, 'Middleman mode. Defaults to :server'
|
|
|
|
# Middleman environment. Defaults to :development
|
|
# @return [String]
|
|
define_setting :environment, ((ENV['MM_ENV'] && ENV['MM_ENV'].to_sym) || :development), 'Middleman environment. Defaults to :development'
|
|
|
|
# Which file should be used for directory indexes
|
|
# @return [String]
|
|
define_setting :index_file, 'index.html', 'Which file should be used for directory indexes'
|
|
|
|
# Whether to strip the index file name off links to directory indexes
|
|
# @return [Boolean]
|
|
define_setting :strip_index_file, true, 'Whether to strip the index file name off links to directory indexes'
|
|
|
|
# Whether to include a trailing slash when stripping the index file
|
|
# @return [Boolean]
|
|
define_setting :trailing_slash, true, 'Whether to include a trailing slash when stripping the index file'
|
|
|
|
# Location of javascripts within source.
|
|
# @return [String]
|
|
define_setting :js_dir, 'javascripts', 'Location of javascripts within source'
|
|
|
|
# Location of stylesheets within source.
|
|
# @return [String]
|
|
define_setting :css_dir, 'stylesheets', 'Location of stylesheets within source'
|
|
|
|
# Location of images within source. Used by HTML helpers.
|
|
# @return [String]
|
|
define_setting :images_dir, 'images', 'Location of images within source'
|
|
|
|
# Location of fonts within source.
|
|
# @return [String]
|
|
define_setting :fonts_dir, 'fonts', 'Location of fonts within source'
|
|
|
|
# Location of layouts within source. Used by renderers.
|
|
# @return [String]
|
|
define_setting :layouts_dir, 'layouts', 'Location of layouts within source'
|
|
|
|
# Where to build output files
|
|
# @return [String]
|
|
define_setting :build_dir, 'build', 'Where to build output files'
|
|
|
|
# Default prefix for building paths. Used by HTML helpers.
|
|
# @return [String]
|
|
define_setting :http_prefix, '/', 'Default prefix for building paths'
|
|
|
|
# Default layout name
|
|
# @return [String]
|
|
define_setting :layout, :_auto_layout, 'Default layout name'
|
|
|
|
# Which file extensions have a layout by default.
|
|
# @return [Array.<String>]
|
|
define_setting :extensions_with_layout, %w(.htm .html .xhtml .php), 'Which file extensions have a layout by default.'
|
|
|
|
# Default string encoding for templates and output.
|
|
# @return [String]
|
|
define_setting :encoding, 'utf-8', 'Default string encoding for templates and output'
|
|
|
|
# Should Padrino include CRSF tag
|
|
# @return [Boolean]
|
|
define_setting :protect_from_csrf, false, 'Should Padrino include CRSF tag'
|
|
|
|
# Set to automatically convert some characters into a directory
|
|
define_setting :automatic_directory_matcher, nil, 'Set to automatically convert some characters into a directory'
|
|
|
|
# Setup callbacks which can exclude paths from the sitemap
|
|
define_setting :ignored_sitemap_matchers, {
|
|
# Files starting with an underscore, but not a double-underscore
|
|
partials: proc do |file|
|
|
ignored = false
|
|
|
|
file[:relative_path].ascend do |f|
|
|
if f.basename.to_s =~ %r{^_[^_]}
|
|
ignored = true
|
|
break
|
|
end
|
|
end
|
|
|
|
ignored
|
|
end,
|
|
|
|
layout: proc do |file, _sitemap_app|
|
|
file[:relative_path].to_s.start_with?('layout.', 'layouts/')
|
|
end
|
|
}, 'Callbacks that can exclude paths from the sitemap'
|
|
|
|
define_setting :skip_build_clean, proc { |p| [/\.git/].any? { |r| p =~ r } }, 'Whether some paths should not be removed during a clean build.'
|
|
|
|
define_setting :watcher_disable, false, 'If the Listen watcher should not run'
|
|
define_setting :watcher_force_polling, false, 'If the Listen watcher should run in polling mode'
|
|
define_setting :watcher_latency, nil, 'The Listen watcher latency'
|
|
|
|
# Delegate convenience methods off to their implementations
|
|
def_delegator :"::Middleman::Logger", :singleton, :logger
|
|
def_delegator :"::Middleman::Util", :instrument
|
|
def_delegators :"self.class", :root, :root_path
|
|
def_delegators :@generic_template_context, :link_to, :image_tag, :asset_path
|
|
def_delegators :@extensions, :activate
|
|
def_delegators :config, :define_setting
|
|
|
|
# Initialize the Middleman project
|
|
def initialize(&block)
|
|
# Search the root of the project for required files
|
|
$LOAD_PATH.unshift(root) unless $LOAD_PATH.include?(root)
|
|
|
|
::Middleman::Util.instrument 'application.setup' do
|
|
@callbacks = ::Middleman::CallbackManager.new
|
|
@callbacks.install_methods!(self, [
|
|
:initialized,
|
|
:configure,
|
|
:before_extensions,
|
|
:before_instance_block,
|
|
:before_sitemap,
|
|
:before_configuration,
|
|
:after_configuration,
|
|
:after_configuration_eval,
|
|
:ready,
|
|
:before_build,
|
|
:after_build,
|
|
:before_shutdown,
|
|
:before, # Before Rack requests
|
|
:before_render,
|
|
:after_render,
|
|
:before_server
|
|
])
|
|
|
|
@middleware = Set.new
|
|
@mappings = Set.new
|
|
|
|
@template_context_class = Class.new(Middleman::TemplateContext)
|
|
@generic_template_context = @template_context_class.new(self)
|
|
@config_context = ConfigContext.new(self, @template_context_class)
|
|
|
|
# Setup the default values from calls to set before initialization
|
|
@config = ::Middleman::Configuration::ConfigurationManager.new
|
|
@config.load_settings(self.class.config.all_settings)
|
|
|
|
config[:source] = ENV['MM_SOURCE'] if ENV['MM_SOURCE']
|
|
|
|
# TODO, make this less global
|
|
::Middleman::FileRenderer.cache.clear
|
|
::Middleman::TemplateRenderer.cache.clear
|
|
end
|
|
|
|
execute_callbacks(:before_extensions)
|
|
|
|
@extensions = ::Middleman::ExtensionManager.new(self)
|
|
|
|
execute_callbacks(:before_instance_block)
|
|
|
|
# Evaluate a passed block if given
|
|
config_context.instance_exec(&block) if block_given?
|
|
|
|
execute_callbacks(:before_sitemap)
|
|
|
|
# Initialize the Sitemap
|
|
@sitemap = ::Middleman::Sitemap::Store.new(self)
|
|
|
|
::Middleman::Extension.clear_after_extension_callbacks
|
|
|
|
# Before config is parsed, before extensions get to it.
|
|
execute_callbacks(:initialized)
|
|
|
|
# Before config is parsed. Mostly used for extensions.
|
|
execute_callbacks(:before_configuration)
|
|
|
|
# Eval config.
|
|
evaluate_configuration!
|
|
|
|
# Run any `configure` blocks for the current environment.
|
|
execute_callbacks([:configure, config[:environment]])
|
|
|
|
# Run any `configure` blocks for the current mode.
|
|
execute_callbacks([:configure, config[:mode]])
|
|
|
|
# Post parsing, pre-extension callback
|
|
execute_callbacks(:after_configuration_eval)
|
|
|
|
if Object.const_defined?(:Encoding)
|
|
Encoding.default_external = config[:encoding]
|
|
end
|
|
|
|
prune_tilt_templates!
|
|
|
|
# After extensions have worked after_config
|
|
execute_callbacks(:after_configuration)
|
|
|
|
# Everything is stable
|
|
execute_callbacks(:ready) unless config[:exit_before_ready]
|
|
end
|
|
|
|
# Eval config
|
|
def evaluate_configuration!
|
|
# Check for and evaluate local configuration in `config.rb`
|
|
config_rb = File.join(root, 'config.rb')
|
|
if File.exist? config_rb
|
|
logger.debug '== Reading: Local config: config.rb'
|
|
config_context.instance_eval File.read(config_rb), config_rb, 1
|
|
else
|
|
# Check for and evaluate local configuration in `middleman.rb`
|
|
middleman_rb = File.join(root, 'middleman.rb')
|
|
if File.exist? middleman_rb
|
|
logger.debug '== Reading: Local middleman: middleman.rb'
|
|
config_context.instance_eval File.read(middleman_rb), middleman_rb, 1
|
|
end
|
|
end
|
|
|
|
env_config = File.join(root, 'environments', "#{config[:environment]}.rb")
|
|
return unless File.exist? env_config
|
|
|
|
logger.debug "== Reading: #{config[:environment]} config"
|
|
config_context.instance_eval File.read(env_config), env_config, 1
|
|
end
|
|
|
|
# Clean up missing Tilt exts
|
|
def prune_tilt_templates!
|
|
::Tilt.mappings.each_key do |key|
|
|
begin
|
|
::Tilt[".#{key}"]
|
|
rescue LoadError, NameError
|
|
::Tilt.mappings.delete(key)
|
|
end
|
|
end
|
|
end
|
|
|
|
# Whether we're in a specific mode
|
|
# @param [Symbol] key
|
|
# @return [Boolean]
|
|
Contract Symbol => Bool
|
|
def mode?(key)
|
|
config[:mode] == key
|
|
end
|
|
|
|
# Whether we're in server mode
|
|
# @return [Boolean] If we're in dev mode
|
|
Contract Bool
|
|
def server?
|
|
mode?(:server)
|
|
end
|
|
|
|
# Whether we're in build mode
|
|
# @return [Boolean] If we're in dev mode
|
|
Contract Bool
|
|
def build?
|
|
mode?(:build)
|
|
end
|
|
|
|
# Whether we're in a specific environment
|
|
# @param [Symbol] key
|
|
# @return [Boolean]
|
|
Contract Symbol => Bool
|
|
def environment?(key)
|
|
config[:environment] == key
|
|
end
|
|
|
|
# Backwards compatible helper. What the current environment is.
|
|
# @return [Symbol]
|
|
Contract Symbol
|
|
def environment
|
|
config[:environment]
|
|
end
|
|
|
|
# Backwards compatible helper. Whether we're in dev mode.
|
|
# @return [Boolean]
|
|
Contract Bool
|
|
def development?
|
|
environment?(:development)
|
|
end
|
|
|
|
# Backwards compatible helper. Whether we're in production mode.
|
|
# @return [Boolean]
|
|
Contract Bool
|
|
def production?
|
|
environment?(:production)
|
|
end
|
|
|
|
# Backwards compatible helper. The full path to the default source dir.
|
|
Contract Pathname
|
|
def source_dir
|
|
Pathname(File.join(root, config[:source]))
|
|
end
|
|
|
|
# Use Rack middleware
|
|
#
|
|
# @param [Class] middleware Middleware module
|
|
# @return [void]
|
|
# Contract Any, Args[Any], Maybe[Proc] => Any
|
|
def use(middleware, *args, &block)
|
|
@middleware << MiddlewareDescriptor.new(middleware, args, block)
|
|
end
|
|
|
|
# Add Rack App mapped to specific path
|
|
#
|
|
# @param [String] map Path to map
|
|
# @return [void]
|
|
Contract String, Proc => Any
|
|
def map(map, &block)
|
|
@mappings << MapDescriptor.new(map, block)
|
|
end
|
|
|
|
# Let everyone know we're shutting down.
|
|
def shutdown!
|
|
execute_callbacks(:before_shutdown)
|
|
end
|
|
|
|
# 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)
|
|
logger.warn "Warning: `set :#{key}` is deprecated. Use `config[:#{key}] =` instead."
|
|
|
|
value = block if block_given?
|
|
config[key] = value
|
|
end
|
|
|
|
# Work around this bug: http://bugs.ruby-lang.org/issues/4521
|
|
# where Ruby will call to_s/inspect while printing exception
|
|
# messages, which can take a long time (minutes at full CPU)
|
|
# if the object is huge or has cyclic references, like this.
|
|
def to_s
|
|
"#<Middleman::Application:0x#{object_id}>"
|
|
end
|
|
alias inspect to_s # Ruby 2.0 calls inspect for NoMethodError instead of to_s
|
|
end
|
|
end
|