Skip to content

Commit

Permalink
Easier way of defining custom middleware.
Browse files Browse the repository at this point in the history
Instead of requiring developers to monkey-patch the Innate module in order to
add their own middleware people can now use `Innate.middleware` similar to the
traditional `Innate.middleware!` method.

Example use:

    Innate.middleware(:dev) do
      use Rack::Head
      use Rack::Reloader, 2

      run Innate.core
    end

Signed-off-by: Yorick Peterse <yorickpeterse@gmail.com>
  • Loading branch information
Yorick Peterse committed Dec 5, 2012
1 parent 42e6a33 commit 1746fe4
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 44 deletions.
20 changes: 12 additions & 8 deletions example/custom_middleware.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
require 'rubygems'
require 'innate'
require File.expand_path('../../lib/innate', __FILE__)

class Demo
Innate.node '/'
Expand All @@ -14,22 +14,26 @@ def empty
end
end

Innate.start do |mw|
Innate.options.mode = :dev

Innate.middleware(:dev) do
# Makes sure all requests and responses conform to Rack protocol
mw.use Rack::Lint
use Rack::Lint

# Avoid showing empty failure pages, give information when it happens.
mw.use Rack::ShowStatus
use Rack::ShowStatus

# Catch exceptions inside Innate and give nice status info
mw.use Rack::ShowExceptions
use Rack::ShowExceptions

# Log access
mw.use Rack::CommonLogger
use Rack::CommonLogger

# Reload modified files before request
mw.use Rack::Reloader
use Rack::Reloader

# Start up the application
mw.innate
run Innate.core
end

Innate.start
106 changes: 78 additions & 28 deletions lib/innate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,22 @@ module Innate
module SingletonMethods
PROXY_OPTIONS = { :port => 'adapter.port', :host => 'adapter.host',
:adapter => 'adapter.handler' }

##
# Hash that will contain the middleware for each defined mode.
#
# @return [Hash]
#
MIDDLEWARE = {}

##
# Returns an instance of `Rack::Builder` that can be used to start a Innate
# application.
#
# @return [Rack::Builder]
#
attr_accessor :app

# The method that starts the whole business.
#
# Call Innate.start after you defined your application.
Expand All @@ -77,8 +93,6 @@ module SingletonMethods
# Innate.start :adapter => :mongrel, :mode => :live
#
# @return [nil] if options.started is true
# @yield [MiddlewareCompiler]
# @param [Proc] block will be passed to {middleware!}
#
# @option param :host [String] ('0.0.0.0')
# IP address or hostname that we respond to - 0.0.0.0 for all
Expand Down Expand Up @@ -113,17 +127,30 @@ def start(options = {})
setup_dependencies

return if innate_options.started

innate_options.started = true

signal = innate_options.trap

trap(signal){ stop(10) } if signal

mode = self.options[:mode].to_sym

# While Rack itself will spit out errors for invalid instances of
# Rack::Builder these errors are typically not very user friendly.
if !Innate.app or !MIDDLEWARE[mode]
raise(
ArgumentError,
"The mode \"#{mode}\" does not have a set of middleware defined. " \
"You can define these middleware using " \
"#{self}.middleware(:#{mode}) { ... }"
)
end

start!
end

def start!(mode = options[:mode])
recompile_middleware unless app

Adapter.start(app)
end

Expand Down Expand Up @@ -159,38 +186,59 @@ def call(env)
app.call(env)
end

attr_accessor :app

##
# Updates `Innate.app` based on the current mode.
#
# @param [#to_sym] mode The mode to use.
#
def recompile_middleware(mode = options[:mode])
Innate.app = send("middleware_#{mode}")
mode = mode.to_sym

if MIDDLEWARE[mode]
Innate.app = Rack::Builder.new(&MIDDLEWARE[mode])
end
end

def middleware_core
##
# Returns an instance of `Rack::Cascade` for running Innate applications.
# This method should be called using `Rack::Builder#run`:
#
# Innate.middleware(:dev) do
# run Innate.core
# end
#
# @return [Rack::Cascade]
#
def core
roots, publics = options[:roots], options[:publics]
joined = roots.map{|root| publics.map{|public| ::File.join(root, public)}}

Rack::Cascade.new(
joined.flatten.map{|public_root| Rack::File.new(public_root) } <<
Current.new(Route.new(DynaMap), Rewrite.new(DynaMap)), [404, 405])
end
joined = roots.map { |root| publics.map { |p| File.join(root, p) } }
joined = joined.flatten.map { |p| Rack::File.new(p) }
current = Current.new(Route.new(DynaMap), Rewrite.new(DynaMap))

def middleware_dev
Rack::Builder.new do
[ Rack::Lint, Rack::Head, Rack::ContentLength, Rack::CommonLogger,
Rack::ShowExceptions, Rack::ShowStatus, Rack::ConditionalGet,
[Rack::Reloader, 2],
].each{|m| use(*m) }
run Innate.middleware_core
end
return Rack::Cascade.new(joined << current, [404, 405])
end

def middleware_live
Rack::Builder.new do
[ Rack::Head, Rack::ContentLength, Rack::CommonLogger,
Rack::ShowStatus, Rack::ConditionalGet,
].each{|*m| use(*m) }
run Innate.middleware_core
end
##
# Sets the middleware for the given mode.
#
# @example
# Innate.middleware(:dev) do
# use Rack::Head
# use Rack::Reloader
#
# run Innate.core
# end
#
# @param [#to_sym] mode The mode that the middleware belong to.
# @param [Proc] block Block containing the middleware. This block will be
# passed to an instance of `Rack::Builder` and can thus contain everything
# this class allows you to use.
#
def middleware(mode, &block)
MIDDLEWARE[mode.to_sym] = block

recompile_middleware
end

# @example Innate can be started by:
Expand Down Expand Up @@ -219,4 +267,6 @@ def go_figure_root(backtrace, options)
end

extend SingletonMethods

require 'innate/default_middleware'
end
22 changes: 22 additions & 0 deletions lib/innate/default_middleware.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
Innate.middleware(:dev) do
use Rack::Lint
use Rack::Head
use Rack::ContentLength
use Rack::CommonLogger
use Rack::ShowExceptions
use Rack::ShowStatus
use Rack::ConditionalGet
use Rack::Reloader, 2

run Innate.core
end

Innate.middleware(:live) do
use Rack::Head
use Rack::ContentLength
use Rack::CommonLogger
use Rack::ShowStatus
use Rack::ConditionalGet

run Innate.core
end
11 changes: 3 additions & 8 deletions lib/innate/spec/bacon.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,10 @@

ENV['RACK_ENV'] = 'TEST'

module Innate
def self.middleware_spec
Rack::Builder.new { run Innate.middleware_core }
end
Innate.middleware(:spec) { run Innate.core }

# skip starting adapter
options.started = true
options.mode = :spec
end
Innate.options.started = true
Innate.options.mode = :spec

shared :rack_test do
Innate.setup_dependencies
Expand Down

0 comments on commit 1746fe4

Please sign in to comment.