Make our Rack pipeline a simple class abstracted from the App.
This commit is contained in:
parent
c94470d33f
commit
6752a86b83
12 changed files with 243 additions and 395 deletions
|
@ -53,6 +53,7 @@ module Middleman::Cli
|
|||
|
||||
require 'middleman-core'
|
||||
require 'middleman-core/logger'
|
||||
require 'middleman-core/rack'
|
||||
|
||||
require 'rack'
|
||||
require 'rack/mock'
|
||||
|
@ -66,7 +67,7 @@ module Middleman::Cli
|
|||
verbose = options['verbose'] ? 0 : 1
|
||||
instrument = options['instrument']
|
||||
|
||||
app = ::Middleman::Application.server.inst do
|
||||
app = ::Middleman::Application.new do
|
||||
config[:mode] = :build
|
||||
config[:environment] = env
|
||||
::Middleman::Logger.singleton(verbose, instrument)
|
||||
|
@ -118,7 +119,8 @@ module Middleman::Cli
|
|||
@to_clean = Set.new
|
||||
|
||||
@logger = @app.logger
|
||||
@rack = ::Rack::MockRequest.new(@app.class.to_rack_app)
|
||||
rack_app = ::Middleman::Rack.new(@app).to_app
|
||||
@rack = ::Rack::MockRequest.new(rack_app)
|
||||
|
||||
super(base, @build_dir, config)
|
||||
end
|
||||
|
|
|
@ -26,21 +26,38 @@ require 'middleman-core/config_context'
|
|||
require 'middleman-core/file_renderer'
|
||||
require 'middleman-core/template_renderer'
|
||||
|
||||
# Rack Request
|
||||
require 'middleman-core/core_extensions/request'
|
||||
|
||||
# Core Middleman Class
|
||||
module Middleman
|
||||
class Application
|
||||
extend Forwardable
|
||||
|
||||
# Global configuration
|
||||
include Configuration::Global
|
||||
class << self
|
||||
# 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
|
||||
ENV['MM_ROOT'] || Dir.pwd
|
||||
end
|
||||
|
||||
# Pathname-addressed root
|
||||
def root_path
|
||||
Pathname(root)
|
||||
end
|
||||
end
|
||||
|
||||
# Uses callbacks
|
||||
include Hooks
|
||||
include Hooks::InstanceHooks
|
||||
|
||||
define_hook :initialized
|
||||
define_hook :after_configuration
|
||||
define_hook :before_configuration
|
||||
|
||||
# Before request hook
|
||||
define_hook :before
|
||||
|
||||
|
@ -56,19 +73,6 @@ module Middleman
|
|||
define_hook :before_render
|
||||
define_hook :after_render
|
||||
|
||||
# Root project directory (overwritten in middleman build/server)
|
||||
# @return [String]
|
||||
def self.root
|
||||
ENV['MM_ROOT'] || Dir.pwd
|
||||
end
|
||||
def_delegator :"self.class", :root
|
||||
|
||||
# Pathname-addressed root
|
||||
def self.root_path
|
||||
Pathname(root)
|
||||
end
|
||||
def_delegator :"self.class", :root_path
|
||||
|
||||
# Name of the source directory
|
||||
# @return [String]
|
||||
config.define_setting :source, 'source', 'Name of the source directory'
|
||||
|
@ -158,44 +162,33 @@ module Middleman
|
|||
}
|
||||
}, 'Callbacks that can exclude paths from the sitemap'
|
||||
|
||||
define_hook :initialized
|
||||
define_hook :instance_available
|
||||
define_hook :after_configuration
|
||||
define_hook :before_configuration
|
||||
|
||||
config.define_setting :autoload_sprockets, true, 'Automatically load sprockets at startup?'
|
||||
config[:autoload_sprockets] = (ENV['AUTOLOAD_SPROCKETS'] == 'true') if ENV['AUTOLOAD_SPROCKETS']
|
||||
|
||||
# Basic Rack Request Handling
|
||||
include Middleman::CoreExtensions::Request
|
||||
attr_reader :config_context
|
||||
attr_reader :sitemap
|
||||
attr_reader :cache
|
||||
attr_reader :template_context_class
|
||||
attr_reader :config
|
||||
attr_reader :generic_template_context
|
||||
attr_reader :extensions
|
||||
attr_reader :middleware
|
||||
attr_reader :mappings
|
||||
|
||||
# Reference to Logger singleton
|
||||
def_delegator :"::Middleman::Logger", :singleton, :logger
|
||||
|
||||
# New container for config.rb commands
|
||||
attr_reader :config_context
|
||||
|
||||
# Reference to Sitemap
|
||||
attr_reader :sitemap
|
||||
|
||||
# Template cache
|
||||
attr_reader :cache
|
||||
|
||||
attr_reader :template_context_class
|
||||
|
||||
# Hack to get a sandboxed copy of these helpers for overriding similar methods inside Markdown renderers.
|
||||
attr_reader :generic_template_context
|
||||
def_delegator :"::Middleman::Util", :instrument
|
||||
def_delegators :"self.class", :root, :root_path
|
||||
def_delegators :@generic_template_context, :link_to, :image_tag, :asset_path
|
||||
|
||||
attr_reader :extensions
|
||||
|
||||
|
||||
# Initialize the Middleman project
|
||||
def initialize(&block)
|
||||
self.class.inst = self
|
||||
|
||||
# Search the root of the project for required files
|
||||
$LOAD_PATH.unshift(root) unless $LOAD_PATH.include?(root)
|
||||
|
||||
@middleware = []
|
||||
@mappings = []
|
||||
|
||||
@template_context_class = Class.new(Middleman::TemplateContext)
|
||||
@generic_template_context = @template_context_class.new(self)
|
||||
@config_context = ConfigContext.new(self, @template_context_class)
|
||||
|
@ -204,7 +197,8 @@ module Middleman
|
|||
::Middleman::TemplateRenderer.cache.clear
|
||||
|
||||
# Setup the default values from calls to set before initialization
|
||||
self.class.config.load_settings(self.class.superclass.config.all_settings)
|
||||
@config = ::Middleman::Configuration::ConfigurationManager.new
|
||||
@config.load_settings(self.class.config.all_settings)
|
||||
|
||||
@extensions = ::Middleman::ExtensionManager.new(self)
|
||||
@extensions.auto_activate(:before_sitemap)
|
||||
|
@ -238,8 +232,6 @@ module Middleman
|
|||
|
||||
evaluate_configuration(&block)
|
||||
|
||||
run_hook :instance_available
|
||||
|
||||
# This is for making the tests work - since the tests
|
||||
# don't completely reload middleman, I18n.load_path can get
|
||||
# polluted with paths from other test app directories that don't
|
||||
|
@ -262,6 +254,9 @@ module Middleman
|
|||
config_context.execute_after_configuration_callbacks
|
||||
|
||||
@extensions.activate_all
|
||||
|
||||
run_hook :ready
|
||||
@config_context.execute_ready_callbacks
|
||||
end
|
||||
|
||||
def evaluate_configuration(&block)
|
||||
|
@ -317,7 +312,21 @@ module Middleman
|
|||
File.join(root, config[:source])
|
||||
end
|
||||
|
||||
def_delegator ::Middleman::Util, :instrument
|
||||
# Use Rack middleware
|
||||
#
|
||||
# @param [Class] middleware Middleware module
|
||||
# @return [void]
|
||||
def use(middleware, *args, &block)
|
||||
@middleware << [middleware, args, block]
|
||||
end
|
||||
|
||||
# Add Rack App mapped to specific path
|
||||
#
|
||||
# @param [String] map Path to map
|
||||
# @return [void]
|
||||
def map(map, &block)
|
||||
@mappings << [map, block]
|
||||
end
|
||||
|
||||
# Work around this bug: http://bugs.ruby-lang.org/issues/4521
|
||||
# where Ruby will call to_s/inspect while printing exception
|
||||
|
@ -328,9 +337,5 @@ module Middleman
|
|||
end
|
||||
alias_method :inspect, :to_s # Ruby 2.0 calls inspect for NoMethodError instead of to_s
|
||||
|
||||
# Hooks clones _hooks from the class to the instance.
|
||||
# https://github.com/apotonick/hooks/blob/master/lib/hooks/instance_hooks.rb#L10
|
||||
# Middleman expects the same list of hooks for class and instance hooks:
|
||||
def_delegator :"self.class", :_hooks
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
require 'rack/mime'
|
||||
|
||||
module Middleman
|
||||
class ConfigContext
|
||||
extend Forwardable
|
||||
|
@ -87,5 +89,15 @@ module Middleman
|
|||
config.define_setting(key, default) unless config.defines_setting?(key)
|
||||
@app.config[key] = block_given? ? block : default
|
||||
end
|
||||
|
||||
# Add a new mime-type for a specific extension
|
||||
#
|
||||
# @param [Symbol] type File extension
|
||||
# @param [String] value Mime type
|
||||
# @return [void]
|
||||
def mime_type(type, value)
|
||||
type = ".#{type}" unless type.to_s[0] == '.'
|
||||
::Rack::Mime::MIME_TYPES[type] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,21 +1,5 @@
|
|||
module Middleman
|
||||
module Configuration
|
||||
# Access to a global configuration manager for the whole Middleman project,
|
||||
# plus backwards compatibility mechanisms for older Middleman projects.
|
||||
module Global
|
||||
def self.included(app)
|
||||
app.send :extend, ClassMethods
|
||||
app.send :def_delegator, :"self.class", :config
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
# Global configuration for the whole Middleman project.
|
||||
# @return [ConfigurationManager]
|
||||
def config
|
||||
@_config ||= ConfigurationManager.new
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# A class that manages a collection of documented settings.
|
||||
# Can be used by extensions as well as the main Middleman
|
||||
|
|
|
@ -1,263 +0,0 @@
|
|||
# Built on Rack
|
||||
require 'rack'
|
||||
require 'rack/file'
|
||||
require 'rack/lint'
|
||||
require 'rack/head'
|
||||
|
||||
require 'middleman-core/util'
|
||||
require 'middleman-core/template_renderer'
|
||||
|
||||
module Middleman
|
||||
module CoreExtensions
|
||||
# Base helper to manipulate asset paths
|
||||
module Request
|
||||
# Extension registered
|
||||
class << self
|
||||
# @private
|
||||
def included(app)
|
||||
# CSSPIE HTC File
|
||||
::Rack::Mime::MIME_TYPES['.htc'] = 'text/x-component'
|
||||
|
||||
# Let's serve all HTML as UTF-8
|
||||
::Rack::Mime::MIME_TYPES['.html'] = 'text/html; charset=utf-8'
|
||||
::Rack::Mime::MIME_TYPES['.htm'] = 'text/html; charset=utf-8'
|
||||
|
||||
app.extend ClassMethods
|
||||
app.extend ServerMethods
|
||||
|
||||
Middleman.extend CompatibleClassMethods
|
||||
|
||||
# Include instance methods
|
||||
app.send :include, InstanceMethods
|
||||
end
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
# Reset Rack setup
|
||||
#
|
||||
# @private
|
||||
def reset!
|
||||
@rack_app = nil
|
||||
end
|
||||
|
||||
# Get the static instance
|
||||
#
|
||||
# @private
|
||||
# @return [Middleman::Application]
|
||||
def inst(&block)
|
||||
@inst ||= begin
|
||||
mm = new(&block)
|
||||
mm.run_hook :ready
|
||||
mm.config_context.execute_ready_callbacks
|
||||
mm
|
||||
end
|
||||
end
|
||||
|
||||
# Set the shared instance
|
||||
#
|
||||
# @private
|
||||
attr_writer :inst
|
||||
|
||||
# Return built Rack app
|
||||
#
|
||||
# @private
|
||||
# @return [Rack::Builder]
|
||||
def to_rack_app(&block)
|
||||
@rack_app ||= begin
|
||||
app = ::Rack::Builder.new
|
||||
app.use Rack::Lint
|
||||
app.use Rack::Head
|
||||
|
||||
Array(@middleware).each do |klass, options, middleware_block|
|
||||
app.use(klass, *options, &middleware_block)
|
||||
end
|
||||
|
||||
inner_app = inst(&block)
|
||||
app.map('/') { run inner_app }
|
||||
|
||||
Array(@mappings).each do |path, map_block|
|
||||
app.map(path, &map_block)
|
||||
end
|
||||
|
||||
app
|
||||
end
|
||||
end
|
||||
|
||||
# Prototype app. Used in config.ru
|
||||
#
|
||||
# @private
|
||||
# @return [Rack::Builder]
|
||||
def prototype
|
||||
reset!
|
||||
to_rack_app
|
||||
end
|
||||
|
||||
# Call prototype, use in config.ru
|
||||
#
|
||||
# @private
|
||||
def call(env)
|
||||
prototype.call(env)
|
||||
end
|
||||
|
||||
# Use Rack middleware
|
||||
#
|
||||
# @param [Class] middleware Middleware module
|
||||
# @return [void]
|
||||
def use(middleware, *args, &block)
|
||||
@middleware ||= []
|
||||
@middleware << [middleware, args, block]
|
||||
end
|
||||
|
||||
# Add Rack App mapped to specific path
|
||||
#
|
||||
# @param [String] map Path to map
|
||||
# @return [void]
|
||||
def map(map, &block)
|
||||
@mappings ||= []
|
||||
@mappings << [map, block]
|
||||
end
|
||||
end
|
||||
|
||||
module ServerMethods
|
||||
# Create a new Class which is based on Middleman::Application
|
||||
# Used to create a safe sandbox into which extensions and
|
||||
# configuration can be included later without impacting
|
||||
# other classes and instances.
|
||||
#
|
||||
# @return [Class]
|
||||
def server(&block)
|
||||
@servercounter ||= 0
|
||||
@servercounter += 1
|
||||
const_set("MiddlemanApplication#{@servercounter}", Class.new(Middleman::Application, &block))
|
||||
end
|
||||
end
|
||||
|
||||
module CompatibleClassMethods
|
||||
# Create a new Class which is based on Middleman::Application
|
||||
# Used to create a safe sandbox into which extensions and
|
||||
# configuration can be included later without impacting
|
||||
# other classes and instances.
|
||||
#
|
||||
# @return [Class]
|
||||
def server(&block)
|
||||
::Middleman::Application.server(&block)
|
||||
end
|
||||
end
|
||||
|
||||
# Methods to be mixed-in to Middleman::Application
|
||||
module InstanceMethods
|
||||
def self.included(app)
|
||||
app.send :def_delegators, :"self.class", :use, :map
|
||||
end
|
||||
|
||||
def call(env)
|
||||
dup.call!(env)
|
||||
end
|
||||
|
||||
# Rack Interface
|
||||
#
|
||||
# @param env Rack environment
|
||||
def call!(env)
|
||||
# Store environment, request and response for later
|
||||
req = ::Rack::Request.new(env)
|
||||
res = ::Rack::Response.new
|
||||
|
||||
logger.debug "== Request: #{env['PATH_INFO']}"
|
||||
|
||||
# Catch :halt exceptions and use that response if given
|
||||
catch(:halt) do
|
||||
process_request(env, req, res)
|
||||
|
||||
res.status = 404
|
||||
|
||||
res.finish
|
||||
end
|
||||
end
|
||||
|
||||
# Halt the current request and return a response
|
||||
#
|
||||
# @param [String] response Response value
|
||||
def halt(response)
|
||||
throw :halt, response
|
||||
end
|
||||
|
||||
# Core response method. We process the request, check with
|
||||
# the sitemap, and return the correct file, response or status
|
||||
# message.
|
||||
#
|
||||
# @param env
|
||||
# @param [Rack::Response] res
|
||||
def process_request(env, _, res)
|
||||
start_time = Time.now
|
||||
|
||||
request_path = URI.decode(env['PATH_INFO'].dup)
|
||||
if request_path.respond_to? :force_encoding
|
||||
request_path.force_encoding('UTF-8')
|
||||
end
|
||||
request_path = ::Middleman::Util.full_path(request_path, self)
|
||||
|
||||
# Run before callbacks
|
||||
run_hook :before
|
||||
|
||||
# Get the resource object for this path
|
||||
resource = sitemap.find_resource_by_destination_path(request_path.gsub(' ', '%20'))
|
||||
|
||||
# Return 404 if not in sitemap
|
||||
return not_found(res, request_path) unless resource && !resource.ignored?
|
||||
|
||||
# If this path is a binary file, send it immediately
|
||||
return send_file(resource, env) if resource.binary?
|
||||
|
||||
res['Content-Type'] = resource.content_type || 'text/plain'
|
||||
|
||||
begin
|
||||
# Write out the contents of the page
|
||||
res.write resource.render
|
||||
|
||||
# Valid content is a 200 status
|
||||
res.status = 200
|
||||
rescue Middleman::TemplateRenderer::TemplateNotFound => e
|
||||
res.write "Error: #{e.message}"
|
||||
res.status = 500
|
||||
end
|
||||
|
||||
# End the request
|
||||
logger.debug "== Finishing Request: #{resource.destination_path} (#{(Time.now - start_time).round(2)}s)"
|
||||
halt res.finish
|
||||
end
|
||||
|
||||
# Add a new mime-type for a specific extension
|
||||
#
|
||||
# @param [Symbol] type File extension
|
||||
# @param [String] value Mime type
|
||||
# @return [void]
|
||||
def mime_type(type, value)
|
||||
type = ".#{type}" unless type.to_s[0] == '.'
|
||||
::Rack::Mime::MIME_TYPES[type] = value
|
||||
end
|
||||
|
||||
# Halt request and return 404
|
||||
def not_found(res, path)
|
||||
res.status = 404
|
||||
res.write "<html><head></head><body><h1>File Not Found</h1><p>#{path}</p></body></html>"
|
||||
res.finish
|
||||
end
|
||||
|
||||
# Immediately send static file
|
||||
def send_file(resource, env)
|
||||
file = ::Rack::File.new nil
|
||||
file.path = resource.source_file
|
||||
response = file.serving(env)
|
||||
status = response[0]
|
||||
response[1]['Content-Encoding'] = 'gzip' if %w(.svgz .gz).include?(resource.ext)
|
||||
# Do not set Content-Type if status is 1xx, 204, 205 or 304, otherwise
|
||||
# Rack will throw an error (500)
|
||||
if !(100..199).include?(status) && ![204, 205, 304].include?(status)
|
||||
response[1]['Content-Type'] = resource.content_type || 'application/octet-stream'
|
||||
end
|
||||
halt response
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -53,7 +53,6 @@ module Middleman
|
|||
# * {#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:
|
||||
#
|
||||
|
@ -181,16 +180,15 @@ module Middleman
|
|||
def_delegator :"@app.extensions[:file_watcher]", :api, :file_watcher
|
||||
|
||||
# Extensions are instantiated when they are activated.
|
||||
# @param [Class] klass The Middleman::Application class
|
||||
# @param [Middleman::Application] app The Middleman::Application instance
|
||||
# @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)
|
||||
def initialize(app, options_hash={}, &block)
|
||||
@_helpers = []
|
||||
@klass = klass
|
||||
@app = app
|
||||
|
||||
setup_options(options_hash, &block)
|
||||
setup_app_reference_when_available
|
||||
|
||||
# Bind app hooks to local methods
|
||||
bind_before_configuration
|
||||
|
@ -216,10 +214,6 @@ module Middleman
|
|||
# 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.
|
||||
|
@ -230,20 +224,6 @@ module Middleman
|
|||
# @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
|
||||
|
||||
ext = self
|
||||
|
||||
return unless ext.respond_to?(:instance_available)
|
||||
|
||||
@klass.instance_available do
|
||||
ext.instance_available
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# @yield An optional block that can be used to customize options before the extension is activated.
|
||||
|
@ -259,30 +239,14 @@ module Middleman
|
|||
yield @options if block_given?
|
||||
end
|
||||
|
||||
def setup_app_reference_when_available
|
||||
ext = self
|
||||
|
||||
@klass.initialized do
|
||||
ext.app = self
|
||||
end
|
||||
|
||||
@klass.instance_available do
|
||||
ext.app ||= self
|
||||
end
|
||||
end
|
||||
|
||||
def bind_before_configuration
|
||||
ext = self
|
||||
return unless ext.respond_to?(:before_configuration)
|
||||
|
||||
@klass.before_configuration do
|
||||
ext.before_configuration
|
||||
end
|
||||
return unless respond_to?(:before_configuration)
|
||||
@app.before_configuration(&method(:before_configuration))
|
||||
end
|
||||
|
||||
def bind_after_configuration
|
||||
ext = self
|
||||
@klass.after_configuration do
|
||||
@app.after_configuration do
|
||||
ext.after_configuration if ext.respond_to?(:after_configuration)
|
||||
|
||||
if ext.respond_to?(:manipulate_resource_list)
|
||||
|
@ -295,7 +259,7 @@ module Middleman
|
|||
ext = self
|
||||
return unless ext.respond_to?(:before_build)
|
||||
|
||||
@klass.before_build do |builder|
|
||||
@app.before_build do |builder|
|
||||
if ext.method(:before_build).arity == 1
|
||||
ext.before_build(builder)
|
||||
else
|
||||
|
@ -308,7 +272,7 @@ module Middleman
|
|||
ext = self
|
||||
return unless ext.respond_to?(:after_build)
|
||||
|
||||
@klass.after_build do |builder|
|
||||
@app.after_build do |builder|
|
||||
if ext.method(:after_build).arity == 1
|
||||
ext.after_build(builder)
|
||||
else
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
module Middleman
|
||||
class ExtensionManager
|
||||
extend Forwardable
|
||||
|
||||
def_delegator :@app, :logger
|
||||
def_delegators :@activated, :[]
|
||||
|
||||
|
@ -44,11 +45,11 @@ module Middleman
|
|||
if extension.supports_multiple_instances?
|
||||
@activated[ext_name] ||= {}
|
||||
key = "instance_#{@activated[ext_name].keys.length}"
|
||||
@activated[ext_name][key] = extension.new(@app.class, options, &block)
|
||||
@activated[ext_name][key] = extension.new(@app, options, &block)
|
||||
elsif @activated.key?(ext_name)
|
||||
raise "#{ext_name} has already been activated and cannot be re-activated."
|
||||
else
|
||||
@activated[ext_name] = extension.new(@app.class, options, &block)
|
||||
@activated[ext_name] = extension.new(@app, options, &block)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -45,7 +45,10 @@ class Middleman::Extensions::AssetHash < ::Middleman::Extension
|
|||
# Update the main sitemap resource list
|
||||
# @return [void]
|
||||
def manipulate_resource_list(resources)
|
||||
@rack_client = ::Rack::MockRequest.new(app.class.to_rack_app)
|
||||
@rack_client ||= begin
|
||||
rack_app = ::Middleman::Rack.new(app).to_app
|
||||
::Rack::MockRequest.new(rack_app)
|
||||
end
|
||||
|
||||
# Process resources in order: binary images and fonts, then SVG, then JS/CSS.
|
||||
# This is so by the time we get around to the text files (which may reference
|
||||
|
|
|
@ -17,9 +17,9 @@ module Middleman
|
|||
@middleman = middleman
|
||||
|
||||
meta_pages = self
|
||||
@rack_app = Rack::Builder.new do
|
||||
@rack_app = ::Rack::Builder.new do
|
||||
# Serve assets from metadata/assets
|
||||
use Rack::Static, urls: ['/assets'], root: File.join(File.dirname(__FILE__), 'meta_pages')
|
||||
use ::Rack::Static, urls: ['/assets'], root: File.join(File.dirname(__FILE__), 'meta_pages')
|
||||
|
||||
map '/' do
|
||||
run meta_pages.method(:index)
|
||||
|
@ -46,7 +46,7 @@ module Middleman
|
|||
|
||||
# Inspect the sitemap
|
||||
def sitemap(_)
|
||||
resources = @middleman.inst.sitemap.resources(true)
|
||||
resources = @middleman.sitemap.resources(true)
|
||||
|
||||
sitemap_tree = SitemapTree.new
|
||||
|
||||
|
@ -59,10 +59,10 @@ module Middleman
|
|||
|
||||
# Inspect configuration
|
||||
def config(_)
|
||||
global_config = @middleman.inst.config.all_settings.map { |c| ConfigSetting.new(c) }
|
||||
global_config = @middleman.config.all_settings.map { |c| ConfigSetting.new(c) }
|
||||
extension_config = {}
|
||||
|
||||
@middleman.inst.extensions.each do |ext_name, extension|
|
||||
@middleman.extensions.each do |ext_name, extension|
|
||||
next if ::Middleman::Extension.auto_activated.include? ext_name
|
||||
|
||||
if extension.is_a?(Hash)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
require 'webrick'
|
||||
require 'middleman-core/meta_pages'
|
||||
require 'middleman-core/logger'
|
||||
require 'middleman-core/rack'
|
||||
|
||||
# rubocop:disable GlobalVars
|
||||
module Middleman
|
||||
|
@ -100,17 +101,17 @@ module Middleman
|
|||
opts[:instrumenting] || false
|
||||
)
|
||||
|
||||
server = ::Middleman::Application.server
|
||||
app = ::Middleman::Application.new do
|
||||
config[:environment] = opts[:environment].to_sym if opts[:environment]
|
||||
end
|
||||
|
||||
# Add in the meta pages application
|
||||
meta_app = Middleman::MetaPages::Application.new(server)
|
||||
server.map '/__middleman' do
|
||||
meta_app = Middleman::MetaPages::Application.new(app)
|
||||
app.map '/__middleman' do
|
||||
run meta_app
|
||||
end
|
||||
|
||||
@app = server.inst do
|
||||
config[:environment] = opts[:environment].to_sym if opts[:environment]
|
||||
end
|
||||
app
|
||||
end
|
||||
|
||||
def start_file_watcher
|
||||
|
@ -197,7 +198,7 @@ module Middleman
|
|||
|
||||
start_file_watcher
|
||||
|
||||
rack_app = app.class.to_rack_app
|
||||
rack_app = ::Middleman::Rack.new(@app).to_app
|
||||
@webrick.mount '/', ::Rack::Handler::WEBrick, rack_app
|
||||
end
|
||||
|
||||
|
|
138
middleman-core/lib/middleman-core/rack.rb
Normal file
138
middleman-core/lib/middleman-core/rack.rb
Normal file
|
@ -0,0 +1,138 @@
|
|||
require 'rack'
|
||||
require 'rack/file'
|
||||
require 'rack/lint'
|
||||
require 'rack/head'
|
||||
|
||||
require 'middleman-core/util'
|
||||
require 'middleman-core/template_renderer'
|
||||
|
||||
# CSSPIE HTC File
|
||||
::Rack::Mime::MIME_TYPES['.htc'] = 'text/x-component'
|
||||
|
||||
# Let's serve all HTML as UTF-8
|
||||
::Rack::Mime::MIME_TYPES['.html'] = 'text/html; charset=utf-8'
|
||||
::Rack::Mime::MIME_TYPES['.htm'] = 'text/html; charset=utf-8'
|
||||
|
||||
module Middleman
|
||||
class Rack
|
||||
extend Forwardable
|
||||
|
||||
def to_app
|
||||
app = ::Rack::Builder.new
|
||||
|
||||
app.use ::Rack::Lint
|
||||
app.use ::Rack::Head
|
||||
|
||||
@middleman.middleware.each do |klass, options, middleware_block|
|
||||
app.use(klass, *options, &middleware_block)
|
||||
end
|
||||
|
||||
inner_app = self
|
||||
app.map('/') { run inner_app }
|
||||
|
||||
@middleman.mappings.each do |path, map_block|
|
||||
app.map(path, &map_block)
|
||||
end
|
||||
|
||||
app
|
||||
end
|
||||
|
||||
def_delegator :"::Middleman::Logger", :singleton, :logger
|
||||
|
||||
def initialize(middleman)
|
||||
@middleman = middleman
|
||||
end
|
||||
|
||||
# Rack Interface
|
||||
#
|
||||
# @param env Rack environment
|
||||
def call(env)
|
||||
# Store environment, request and response for later
|
||||
req = ::Rack::Request.new(env)
|
||||
res = ::Rack::Response.new
|
||||
|
||||
logger.debug "== Request: #{env['PATH_INFO']}"
|
||||
|
||||
# Catch :halt exceptions and use that response if given
|
||||
catch(:halt) do
|
||||
process_request(env, req, res)
|
||||
res.status = 404
|
||||
res.finish
|
||||
end
|
||||
end
|
||||
|
||||
# Halt the current request and return a response
|
||||
#
|
||||
# @param [String] response Response value
|
||||
def halt(response)
|
||||
throw :halt, response
|
||||
end
|
||||
|
||||
# Core response method. We process the request, check with
|
||||
# the sitemap, and return the correct file, response or status
|
||||
# message.
|
||||
#
|
||||
# @param env
|
||||
# @param [Rack::Response] res
|
||||
def process_request(env, _, res)
|
||||
start_time = Time.now
|
||||
|
||||
request_path = URI.decode(env['PATH_INFO'].dup)
|
||||
if request_path.respond_to? :force_encoding
|
||||
request_path.force_encoding('UTF-8')
|
||||
end
|
||||
request_path = ::Middleman::Util.full_path(request_path, @middleman)
|
||||
|
||||
# Run before callbacks
|
||||
@middleman.run_hook :before
|
||||
|
||||
# Get the resource object for this path
|
||||
resource = @middleman.sitemap.find_resource_by_destination_path(request_path.gsub(' ', '%20'))
|
||||
|
||||
# Return 404 if not in sitemap
|
||||
return not_found(res, request_path) unless resource && !resource.ignored?
|
||||
|
||||
# If this path is a binary file, send it immediately
|
||||
return send_file(resource, env) if resource.binary?
|
||||
|
||||
res['Content-Type'] = resource.content_type || 'text/plain'
|
||||
|
||||
begin
|
||||
# Write out the contents of the page
|
||||
res.write resource.render
|
||||
|
||||
# Valid content is a 200 status
|
||||
res.status = 200
|
||||
rescue Middleman::TemplateRenderer::TemplateNotFound => e
|
||||
res.write "Error: #{e.message}"
|
||||
res.status = 500
|
||||
end
|
||||
|
||||
# End the request
|
||||
logger.debug "== Finishing Request: #{resource.destination_path} (#{(Time.now - start_time).round(2)}s)"
|
||||
halt res.finish
|
||||
end
|
||||
|
||||
# Halt request and return 404
|
||||
def not_found(res, path)
|
||||
res.status = 404
|
||||
res.write "<html><head></head><body><h1>File Not Found</h1><p>#{path}</p></body></html>"
|
||||
res.finish
|
||||
end
|
||||
|
||||
# Immediately send static file
|
||||
def send_file(resource, env)
|
||||
file = ::Rack::File.new nil
|
||||
file.path = resource.source_file
|
||||
response = file.serving(env)
|
||||
status = response[0]
|
||||
response[1]['Content-Encoding'] = 'gzip' if %w(.svgz .gz).include?(resource.ext)
|
||||
# Do not set Content-Type if status is 1xx, 204, 205 or 304, otherwise
|
||||
# Rack will throw an error (500)
|
||||
if !(100..199).include?(status) && ![204, 205, 304].include?(status)
|
||||
response[1]['Content-Type'] = resource.content_type || 'application/octet-stream'
|
||||
end
|
||||
halt response
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,6 +1,7 @@
|
|||
# encoding: UTF-8
|
||||
|
||||
require 'rack/mock'
|
||||
require 'middleman-core/rack'
|
||||
|
||||
Given /^a clean server$/ do
|
||||
@initialize_commands = []
|
||||
|
@ -43,14 +44,14 @@ Given /^the Server is running$/ do
|
|||
initialize_commands = @initialize_commands || []
|
||||
initialize_commands.unshift lambda { config[:show_exceptions] = false }
|
||||
|
||||
@server_inst = Middleman::Application.server.inst do
|
||||
@server_inst = ::Middleman::Application.new do
|
||||
initialize_commands.each do |p|
|
||||
instance_exec(&p)
|
||||
end
|
||||
end
|
||||
|
||||
app_rack = @server_inst.class.to_rack_app
|
||||
@browser = ::Rack::MockRequest.new(app_rack)
|
||||
rack = ::Middleman::Rack.new(@server_inst)
|
||||
@browser = ::Rack::MockRequest.new(rack.to_app)
|
||||
end
|
||||
|
||||
Given /^the Server is running at "([^\"]*)"$/ do |app_path|
|
||||
|
|
Loading…
Reference in a new issue