diff --git a/middleman-cli/lib/middleman-cli/build.rb b/middleman-cli/lib/middleman-cli/build.rb index e6837604..87b8ea35 100644 --- a/middleman-cli/lib/middleman-cli/build.rb +++ b/middleman-cli/lib/middleman-cli/build.rb @@ -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 diff --git a/middleman-core/lib/middleman-core/application.rb b/middleman-core/lib/middleman-core/application.rb index a40158e9..fe262672 100644 --- a/middleman-core/lib/middleman-core/application.rb +++ b/middleman-core/lib/middleman-core/application.rb @@ -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 diff --git a/middleman-core/lib/middleman-core/config_context.rb b/middleman-core/lib/middleman-core/config_context.rb index caeebd51..5c5ee42f 100644 --- a/middleman-core/lib/middleman-core/config_context.rb +++ b/middleman-core/lib/middleman-core/config_context.rb @@ -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 diff --git a/middleman-core/lib/middleman-core/configuration.rb b/middleman-core/lib/middleman-core/configuration.rb index 9678801d..a17382ce 100644 --- a/middleman-core/lib/middleman-core/configuration.rb +++ b/middleman-core/lib/middleman-core/configuration.rb @@ -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 diff --git a/middleman-core/lib/middleman-core/core_extensions/request.rb b/middleman-core/lib/middleman-core/core_extensions/request.rb deleted file mode 100644 index c833a1ec..00000000 --- a/middleman-core/lib/middleman-core/core_extensions/request.rb +++ /dev/null @@ -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 "

File Not Found

#{path}

" - 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 diff --git a/middleman-core/lib/middleman-core/extension.rb b/middleman-core/lib/middleman-core/extension.rb index 96186848..713a12c0 100644 --- a/middleman-core/lib/middleman-core/extension.rb +++ b/middleman-core/lib/middleman-core/extension.rb @@ -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] resources A list of all the resources known to the sitemap. # @return [Array] 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 diff --git a/middleman-core/lib/middleman-core/extension_manager.rb b/middleman-core/lib/middleman-core/extension_manager.rb index e05a5d24..0356f0bd 100644 --- a/middleman-core/lib/middleman-core/extension_manager.rb +++ b/middleman-core/lib/middleman-core/extension_manager.rb @@ -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 diff --git a/middleman-core/lib/middleman-core/extensions/asset_hash.rb b/middleman-core/lib/middleman-core/extensions/asset_hash.rb index 3fa5e9ea..fb277adf 100644 --- a/middleman-core/lib/middleman-core/extensions/asset_hash.rb +++ b/middleman-core/lib/middleman-core/extensions/asset_hash.rb @@ -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 diff --git a/middleman-core/lib/middleman-core/meta_pages.rb b/middleman-core/lib/middleman-core/meta_pages.rb index c21f25d7..cd789ef1 100644 --- a/middleman-core/lib/middleman-core/meta_pages.rb +++ b/middleman-core/lib/middleman-core/meta_pages.rb @@ -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) diff --git a/middleman-core/lib/middleman-core/preview_server.rb b/middleman-core/lib/middleman-core/preview_server.rb index 301b1b69..577a0c1b 100644 --- a/middleman-core/lib/middleman-core/preview_server.rb +++ b/middleman-core/lib/middleman-core/preview_server.rb @@ -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 diff --git a/middleman-core/lib/middleman-core/rack.rb b/middleman-core/lib/middleman-core/rack.rb new file mode 100644 index 00000000..3d540685 --- /dev/null +++ b/middleman-core/lib/middleman-core/rack.rb @@ -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 "

File Not Found

#{path}

" + 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 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 94905abe..470a46b1 100644 --- a/middleman-core/lib/middleman-core/step_definitions/server_steps.rb +++ b/middleman-core/lib/middleman-core/step_definitions/server_steps.rb @@ -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|