2013-12-28 01:26:31 +01:00
require 'active_support/core_ext/integer/inflections'
2012-04-14 22:51:02 +02:00
2014-07-10 21:35:47 +02:00
require 'middleman-core/contracts'
2015-05-04 00:38:18 +02:00
require 'middleman-core/callback_manager'
require 'middleman-core/logger'
2014-01-01 03:21:30 +01:00
require 'middleman-core/sitemap/store'
2013-12-28 01:26:31 +01:00
require 'middleman-core/configuration'
2014-07-05 21:51:41 +02:00
require 'middleman-core/extension_manager'
2013-12-28 01:26:31 +01:00
require 'middleman-core/core_extensions'
2014-01-01 03:21:30 +01:00
require 'middleman-core/config_context'
2014-01-03 22:15:02 +01:00
require 'middleman-core/file_renderer'
require 'middleman-core/template_renderer'
2014-01-01 03:21:30 +01:00
2011-11-24 06:59:53 +01:00
# Core Middleman Class
2012-04-14 22:51:02 +02:00
module Middleman
2015-04-24 19:26:42 +02:00
MiddlewareDescriptor = Struct . new ( :class , :options , :block )
MapDescriptor = Struct . new ( :path , :block )
2012-04-14 23:04:10 +02:00
class Application
2014-07-05 20:17:41 +02:00
extend Forwardable
2014-07-10 21:35:47 +02:00
include Contracts
2014-07-05 20:17:41 +02:00
2014-07-05 21:14:58 +02:00
class << self
2015-05-04 00:38:18 +02:00
extend Forwardable
def_delegator :config , :define_setting
2014-07-05 21:14:58 +02:00
# 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
2015-09-17 22:53:43 +02:00
r = ENV [ 'MM_ROOT' ] ? ENV [ 'MM_ROOT' ] . dup : :: Middleman :: Util . current_directory
r . encode! ( 'UTF-8' , 'UTF-8-MAC' ) if RUBY_PLATFORM =~ / darwin /
r
2014-07-05 21:14:58 +02:00
end
# Pathname-addressed root
def root_path
Pathname ( root )
end
end
2012-10-14 02:57:48 +02:00
2015-05-04 00:38:18 +02:00
Contract :: Middleman :: ConfigContext
attr_reader :config_context
2012-08-14 00:39:06 +02:00
2015-05-04 00:38:18 +02:00
Contract :: Middleman :: Sitemap :: Store
attr_reader :sitemap
2014-07-05 21:14:58 +02:00
2015-05-04 00:38:18 +02:00
# An anonymous subclass of ::Middleman::TemplateContext
attr_reader :template_context_class
2012-06-09 20:30:22 +02:00
2015-05-04 00:38:18 +02:00
# An instance of the above anonymouse class.
attr_reader :generic_template_context
2012-06-09 20:30:22 +02:00
2015-05-04 00:38:18 +02:00
Contract :: Middleman :: Configuration :: ConfigurationManager
attr_reader :config
2014-02-05 06:03:24 +01:00
2015-05-04 00:38:18 +02:00
Contract :: Middleman :: ExtensionManager
attr_reader :extensions
2013-04-10 07:42:04 +02:00
2015-05-04 00:38:18 +02:00
Contract SetOf [ MiddlewareDescriptor ]
attr_reader :middleware
2014-07-16 03:01:45 +02:00
2015-05-04 00:38:18 +02:00
Contract SetOf [ MapDescriptor ]
attr_reader :mappings
2014-07-05 22:41:59 +02:00
2015-03-02 03:17:22 +01:00
# Which port preview should start on.
# @return [Fixnum]
2016-01-13 01:03:23 +01:00
define_setting :port , 4567 , 'The preview server port'
2015-03-02 03:17:22 +01:00
2015-05-07 09:21:11 +02:00
# Which server name should be used
# @return [NilClass, String]
2016-01-13 01:03:23 +01:00
define_setting :server_name , nil , 'The server name of preview server'
2015-06-25 15:39:56 +02:00
# Which bind address the preview server should use
# @return [NilClass, String]
2016-01-13 01:03:23 +01:00
define_setting :bind_address , nil , 'The bind address of the preview server'
2015-03-02 03:17:22 +01:00
2015-05-03 05:54:58 +02:00
# Whether to serve the preview server over HTTPS.
# @return [Boolean]
2016-01-13 01:03:23 +01:00
define_setting :https , false , 'Serve the preview server over SSL/TLS'
2015-05-03 05:54:58 +02:00
# The (optional) path to the SSL cert to use for the preview server.
# @return [String]
2016-01-13 01:03:23 +01:00
define_setting :ssl_certificate , nil , 'Path to an X.509 certificate to use for the preview server'
2015-05-03 05:54:58 +02:00
# The (optional) private key for the certificate in :ssl_certificate.
# @return [String]
2016-01-13 01:03:23 +01:00
define_setting :ssl_private_key , nil , " Path to an RSA private key for the preview server's certificate "
2015-05-03 05:54:58 +02:00
2012-04-14 22:51:02 +02:00
# Name of the source directory
# @return [String]
2015-05-04 00:38:18 +02:00
define_setting :source , 'source' , 'Name of the source directory'
2012-06-09 20:30:22 +02:00
2016-03-17 21:23:36 +01:00
# If we should not run the sitemap.
# @return [Boolean]
define_setting :disable_sitemap , false , 'If we should not run the sitemap.'
2015-08-21 02:45:18 +02:00
# If we should exit before ready event.
# @return [Boolean]
define_setting :exit_before_ready , false , 'If we should exit before ready event.'
2014-06-07 00:32:00 +02:00
# Middleman mode. Defaults to :server, set to :build by the build process
# @return [String]
2015-09-01 18:58:13 +02:00
define_setting :mode , :server , 'Middleman mode. Defaults to :server'
2014-06-07 00:32:00 +02:00
# Middleman environment. Defaults to :development
2012-04-14 22:51:02 +02:00
# @return [String]
2016-03-12 22:55:25 +01:00
define_setting :environment , ( ( ENV [ 'MM_ENV' ] && ENV [ 'MM_ENV' ] . to_sym ) || :development ) , 'Middleman environment. Defaults to :development' , import : proc { | s | s . to_sym }
2012-06-09 20:30:22 +02:00
2012-04-14 22:51:02 +02:00
# Which file should be used for directory indexes
# @return [String]
2015-05-04 00:38:18 +02:00
define_setting :index_file , 'index.html' , 'Which file should be used for directory indexes'
2011-11-18 04:56:55 +01:00
2012-07-13 03:27:05 +02:00
# Whether to strip the index file name off links to directory indexes
# @return [Boolean]
2015-05-04 00:38:18 +02:00
define_setting :strip_index_file , true , 'Whether to strip the index file name off links to directory indexes'
2012-07-13 03:27:05 +02:00
# Whether to include a trailing slash when stripping the index file
2012-07-12 04:33:33 +02:00
# @return [Boolean]
2015-05-04 00:38:18 +02:00
define_setting :trailing_slash , true , 'Whether to include a trailing slash when stripping the index file'
2012-07-12 04:33:33 +02:00
2012-05-24 23:29:29 +02:00
# Location of javascripts within source.
2012-04-14 22:51:02 +02:00
# @return [String]
2015-05-04 00:38:18 +02:00
define_setting :js_dir , 'javascripts' , 'Location of javascripts within source'
2012-06-09 20:30:22 +02:00
2015-12-01 02:14:02 +01:00
# Location of stylesheets within source.
2012-04-14 22:51:02 +02:00
# @return [String]
2015-05-04 00:38:18 +02:00
define_setting :css_dir , 'stylesheets' , 'Location of stylesheets within source'
2012-06-09 20:30:22 +02:00
2015-12-01 02:14:02 +01:00
# Location of images within source. Used by HTML helpers.
2012-04-14 22:51:02 +02:00
# @return [String]
2015-05-04 00:38:18 +02:00
define_setting :images_dir , 'images' , 'Location of images within source'
2011-11-18 04:56:55 +01:00
2015-12-01 02:14:02 +01:00
# Location of fonts within source.
2012-06-09 20:30:22 +02:00
# @return [String]
2015-05-04 00:38:18 +02:00
define_setting :fonts_dir , 'fonts' , 'Location of fonts within source'
2012-06-09 20:30:22 +02:00
2013-06-02 00:25:44 +02:00
# Location of layouts within source. Used by renderers.
# @return [String]
2015-05-04 00:38:18 +02:00
define_setting :layouts_dir , 'layouts' , 'Location of layouts within source'
2013-06-02 00:25:44 +02:00
2012-04-14 22:51:02 +02:00
# Where to build output files
# @return [String]
2015-05-04 00:38:18 +02:00
define_setting :build_dir , 'build' , 'Where to build output files'
2012-06-09 20:30:22 +02:00
2015-12-01 02:14:02 +01:00
# Default prefix for building paths. Used by HTML helpers.
2012-04-14 22:51:02 +02:00
# @return [String]
2015-05-04 00:38:18 +02:00
define_setting :http_prefix , '/' , 'Default prefix for building paths'
2011-11-24 06:59:53 +01:00
2012-04-14 22:51:02 +02:00
# Default layout name
2016-01-26 22:21:39 +01:00
# @return [String]
2015-05-04 00:38:18 +02:00
define_setting :layout , :_auto_layout , 'Default layout name'
2012-06-09 20:30:22 +02:00
2016-01-26 22:21:39 +01:00
# Which file extensions have a layout by default.
# @return [Array.<String>]
2016-01-27 10:48:36 +01:00
define_setting :extensions_with_layout , %w( .htm .html .xhtml .php ) , 'Which file extensions have a layout by default.'
2016-01-26 22:21:39 +01:00
2016-03-22 23:43:48 +01:00
# Which file extensions are "assets."
# @return [Array.<String>]
define_setting :asset_extensions , %w( .css .png .jpg .jpeg .webp .svg .svgz .js .gif .ttf .otf .woff .woff2 .eot .ico .map ) , 'Which file extensions are treated as assets.'
2013-04-10 07:42:04 +02:00
# Default string encoding for templates and output.
# @return [String]
2015-05-04 00:38:18 +02:00
define_setting :encoding , 'utf-8' , 'Default string encoding for templates and output'
2013-04-10 07:42:04 +02:00
2013-11-20 03:10:26 +01:00
# Should Padrino include CRSF tag
# @return [Boolean]
2015-05-04 00:38:18 +02:00
define_setting :protect_from_csrf , false , 'Should Padrino include CRSF tag'
2013-11-20 03:10:26 +01:00
2014-07-05 19:42:03 +02:00
# Set to automatically convert some characters into a directory
2015-05-04 00:38:18 +02:00
define_setting :automatic_directory_matcher , nil , 'Set to automatically convert some characters into a directory'
2014-07-05 19:42:03 +02:00
# Setup callbacks which can exclude paths from the sitemap
2015-05-04 00:38:18 +02:00
define_setting :ignored_sitemap_matchers , {
2014-07-05 19:42:03 +02:00
# Files starting with an underscore, but not a double-underscore
2015-09-17 22:53:43 +02:00
partials : proc do | file |
2014-08-18 23:53:15 +02:00
ignored = false
file [ :relative_path ] . ascend do | f |
2016-01-14 20:21:42 +01:00
if f . basename . to_s =~ %r{ ^_[^_] }
2014-08-18 23:53:15 +02:00
ignored = true
break
end
end
ignored
2015-09-17 22:53:43 +02:00
end ,
2014-07-05 19:42:03 +02:00
2016-02-19 22:24:16 +01:00
layout : proc do | file , app |
file [ :relative_path ] . to_s . start_with? ( 'layout.' , app . config [ :layouts_dir ] + '/' )
2015-09-17 22:53:43 +02:00
end
2014-07-05 19:42:03 +02:00
} , 'Callbacks that can exclude paths from the sitemap'
2016-01-23 00:57:07 +01:00
define_setting :skip_build_clean , proc { | p | [ / \ .git / ] . any? { | r | p =~ r } } , 'Whether some paths should not be removed during a clean build.'
2016-01-11 02:14:41 +01:00
2016-03-21 00:44:20 +01:00
define_setting :cli_options , { } , 'Options from the Command Line.'
2015-05-04 00:38:18 +02:00
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'
2016-10-31 18:50:03 +01:00
define_setting :watcher_wait_for_delay , 0 . 5 , 'The Listen watcher delay between calls when changes exist'
2014-07-05 21:51:41 +02:00
2015-05-04 00:38:18 +02:00
# Delegate convenience methods off to their implementations
2014-07-05 21:14:58 +02:00
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
2015-03-27 18:56:09 +01:00
def_delegators :@extensions , :activate
2015-05-04 00:38:18 +02:00
def_delegators :config , :define_setting
2014-07-09 02:02:02 +02:00
2012-04-14 22:51:02 +02:00
# Initialize the Middleman project
2014-07-05 21:51:41 +02:00
def initialize ( & block )
# Search the root of the project for required files
$LOAD_PATH . unshift ( root ) unless $LOAD_PATH . include? ( root )
2016-01-14 20:21:42 +01:00
:: Middleman :: Util . instrument 'application.setup' do
2016-01-14 02:16:36 +01:00
@callbacks = :: Middleman :: CallbackManager . new
@callbacks . install_methods! ( self , [
2016-01-14 20:21:42 +01:00
: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 ,
2016-04-12 19:00:09 +02:00
:before_server ,
:reload
2016-01-14 20:21:42 +01:00
] )
2016-01-14 02:16:36 +01:00
@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
2015-05-04 00:38:18 +02:00
2016-01-13 01:03:23 +01:00
execute_callbacks ( :before_extensions )
2014-07-05 21:51:41 +02:00
@extensions = :: Middleman :: ExtensionManager . new ( self )
2014-07-16 03:01:45 +02:00
2016-01-14 02:16:36 +01:00
execute_callbacks ( :before_instance_block )
2014-07-16 03:01:45 +02:00
# Evaluate a passed block if given
config_context . instance_exec ( & block ) if block_given?
2016-05-31 22:07:58 +02:00
apply_cli_options
2015-05-04 00:38:18 +02:00
execute_callbacks ( :before_sitemap )
2014-03-30 01:17:00 +01:00
2014-01-01 03:21:30 +01:00
# Initialize the Sitemap
@sitemap = :: Middleman :: Sitemap :: Store . new ( self )
2014-07-05 21:51:41 +02:00
:: Middleman :: Extension . clear_after_extension_callbacks
2015-05-04 00:38:18 +02:00
# Before config is parsed, before extensions get to it.
execute_callbacks ( :initialized )
2014-07-05 21:51:41 +02:00
2015-05-04 00:38:18 +02:00
# Before config is parsed. Mostly used for extensions.
execute_callbacks ( :before_configuration )
2014-07-05 21:51:41 +02:00
2015-05-04 00:38:18 +02:00
# Eval config.
evaluate_configuration!
2015-01-04 21:23:35 +01:00
2015-05-04 18:58:29 +02:00
# 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 ] ] )
2016-03-21 01:33:44 +01:00
apply_cli_options
2016-03-21 00:44:20 +01:00
2015-05-04 00:38:18 +02:00
# Post parsing, pre-extension callback
execute_callbacks ( :after_configuration_eval )
2014-07-05 22:41:59 +02:00
2016-01-13 01:03:23 +01:00
if Object . const_defined? ( :Encoding )
Encoding . default_external = config [ :encoding ]
end
prune_tilt_templates!
2015-05-04 00:38:18 +02:00
# After extensions have worked after_config
execute_callbacks ( :after_configuration )
2014-07-05 21:51:41 +02:00
2015-05-04 00:38:18 +02:00
# Everything is stable
2015-08-21 02:45:18 +02:00
execute_callbacks ( :ready ) unless config [ :exit_before_ready ]
2014-07-05 21:51:41 +02:00
end
2016-03-21 01:33:44 +01:00
def apply_cli_options
config [ :cli_options ] . each do | k , v |
setting = config . setting ( k . to_sym )
next unless setting
v = setting . options [ :import ] . call ( v ) if setting . options [ :import ]
config [ k . to_sym ] = v
end
end
2015-05-04 00:38:18 +02:00
# Eval config
def evaluate_configuration!
2014-07-05 21:51:41 +02:00
# Check for and evaluate local configuration in `config.rb`
2014-08-15 05:34:31 +02:00
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
2014-07-05 21:51:41 +02:00
end
env_config = File . join ( root , 'environments' , " #{ config [ :environment ] } .rb " )
2015-05-04 20:05:00 +02:00
return unless File . exist? env_config
logger . debug " == Reading: #{ config [ :environment ] } config "
config_context . instance_eval File . read ( env_config ) , env_config , 1
2015-05-04 00:38:18 +02:00
end
# Clean up missing Tilt exts
2016-01-13 01:03:23 +01:00
def prune_tilt_templates!
2016-09-09 00:18:34 +02:00
:: Tilt . default_mapping . lazy_map . each_key do | key |
2015-05-04 00:38:18 +02:00
begin
:: Tilt [ " . #{ key } " ]
rescue LoadError , NameError
2016-09-09 00:18:34 +02:00
:: Tilt . default_mapping . lazy_map . delete ( key )
2015-05-04 00:38:18 +02:00
end
end
2012-04-14 22:51:02 +02:00
end
2012-06-09 20:30:22 +02:00
2016-01-11 23:24:28 +01:00
# Whether we're in a specific mode
# @param [Symbol] key
# @return [Boolean]
Contract Symbol = > Bool
def mode? ( key )
config [ :mode ] == key
end
2014-06-07 00:32:00 +02:00
# Whether we're in server mode
2012-04-14 22:51:02 +02:00
# @return [Boolean] If we're in dev mode
2015-05-04 00:38:18 +02:00
Contract Bool
2014-06-07 00:32:00 +02:00
def server?
2016-01-11 23:24:28 +01:00
mode? ( :server )
2013-12-31 23:41:17 +01:00
end
2012-06-09 20:30:22 +02:00
2012-04-14 22:51:02 +02:00
# Whether we're in build mode
2014-06-07 00:32:00 +02:00
# @return [Boolean] If we're in dev mode
2015-05-04 00:38:18 +02:00
Contract Bool
2013-12-31 23:41:17 +01:00
def build?
2016-01-11 23:24:28 +01:00
mode? ( :build )
2014-06-07 00:32:00 +02:00
end
# Whether we're in a specific environment
2015-09-19 23:07:42 +02:00
# @param [Symbol] key
2014-06-07 00:32:00 +02:00
# @return [Boolean]
2015-09-19 23:07:42 +02:00
Contract Symbol = > Bool
2014-06-07 00:32:00 +02:00
def environment? ( key )
config [ :environment ] == key
2013-12-31 23:41:17 +01:00
end
2011-11-18 04:56:55 +01:00
2014-12-23 23:54:21 +01:00
# Backwards compatible helper. What the current environment is.
# @return [Symbol]
2015-05-04 00:38:18 +02:00
Contract Symbol
2014-12-23 23:54:21 +01:00
def environment
config [ :environment ]
end
# Backwards compatible helper. Whether we're in dev mode.
# @return [Boolean]
2015-05-04 00:38:18 +02:00
Contract Bool
2014-12-23 23:54:21 +01:00
def development?
environment? ( :development )
end
# Backwards compatible helper. Whether we're in production mode.
# @return [Boolean]
2015-05-04 00:38:18 +02:00
Contract Bool
2014-12-23 23:54:21 +01:00
def production?
environment? ( :production )
end
# Backwards compatible helper. The full path to the default source dir.
2015-05-04 00:38:18 +02:00
Contract Pathname
2014-12-23 23:54:21 +01:00
def source_dir
Pathname ( File . join ( root , config [ :source ] ) )
end
2014-07-05 21:14:58 +02:00
# Use Rack middleware
#
# @param [Class] middleware Middleware module
# @return [void]
2015-04-24 19:26:42 +02:00
# Contract Any, Args[Any], Maybe[Proc] => Any
2014-07-05 21:14:58 +02:00
def use ( middleware , * args , & block )
2014-07-10 21:35:47 +02:00
@middleware << MiddlewareDescriptor . new ( middleware , args , block )
2014-07-05 21:14:58 +02:00
end
# Add Rack App mapped to specific path
#
# @param [String] map Path to map
# @return [void]
2014-07-10 21:35:47 +02:00
Contract String , Proc = > Any
2014-07-05 21:14:58 +02:00
def map ( map , & block )
2014-07-10 21:35:47 +02:00
@mappings << MapDescriptor . new ( map , block )
2014-07-05 21:14:58 +02:00
end
2012-06-09 20:30:22 +02:00
2015-05-04 00:38:18 +02:00
# Let everyone know we're shutting down.
2014-07-16 03:01:45 +02:00
def shutdown!
2015-05-04 00:38:18 +02:00
execute_callbacks ( :before_shutdown )
2014-07-16 03:01:45 +02:00
end
2015-10-12 21:37:07 +02:00
# 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
2012-07-03 08:39:30 +02:00
# 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
2012-09-13 19:51:16 +02:00
" # <Middleman::Application:0x #{ object_id } > "
2012-07-03 08:39:30 +02:00
end
2016-01-14 20:21:42 +01:00
alias inspect to_s # Ruby 2.0 calls inspect for NoMethodError instead of to_s
2011-11-18 04:56:55 +01:00
end
2012-05-17 06:19:03 +02:00
end