Skip to content

Commit

Permalink
Refactoring async_aroundware, Part I. in ResponseReceiver, the defaul…
Browse files Browse the repository at this point in the history
…t pre_process returns Goliath::Connection::AsyncResponse; env is an attr_reader not attr_accessor; in examples/async_aroundware_demo.rb added a missing include. Otherwise, this commit is largely cosmetic.
  • Loading branch information
Philip (flip) Kromer committed Jul 30, 2011
1 parent 50fcd4d commit 72435e0
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 24 deletions.
1 change: 1 addition & 0 deletions examples/async_aroundware_demo.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
$: << File.dirname(__FILE__)+'/../lib'

require 'goliath'
require 'goliath/synchrony/response_receiver'
require 'em-synchrony/em-http'
require 'yajl/json_gem'

Expand Down
57 changes: 40 additions & 17 deletions lib/goliath/rack/async_aroundware.rb
Original file line number Diff line number Diff line change
@@ -1,55 +1,78 @@
module Goliath
module Rack
class AsyncAroundware
# Create a new AsyncAroundware
include Goliath::Rack::Validator

# Called by the framework to create the middleware. Any extra args passed
# to the use statement are sent to each response_receiver_klass as it is created.
#
# @example
# class MyResponseReceiver < Goliath::Rack::MultiReceiver
# def initialize(env, aq)
# @awesomeness_quotient = aq
# super(env)
# end
# # ... define pre_process and post_process ...
# end
#
# class AsyncAroundwareDemoMulti < Goliath::API
# use Goliath::Rack::AsyncAroundware, MyResponseReceiver
# class AwesomeApiWithShortening < Goliath::API
# use Goliath::Rack::AsyncAroundware, MyResponseReceiver, 3
# # ... stuff ...
# end
#
# @param app [#call] the downstream app
# @param response_receiver_klass a class that quacks like a
# Goliath::Rack::ResponseReceiver and an EM::Deferrable
# @param *args [Array] extra args to pass to the response_receiver
# @return [Goliath::Rack::AsyncMiddleware]
def initialize app, response_receiver_klass, *args
@app = app
@response_receiver_klass = response_receiver_klass
@response_receiver_args = args
end

# This coordinates a response_receiver to process a request. We hook the
# response_receiver in the middle of the async_callback chain:
# * send the downstream response to the barrier, whether received directly
# from @app.call or via async callback
# * have the upstream callback chain be invoked when the response_receiver completes
#
# @param env [Goliath::Env] The goliath environment
# @return [Array] The [status_code, headers, body] tuple
def call(env)
response_receiver = new_response_receiver(env)

# put response_receiver in the middle of the async_callback chain:
# * save the old callback chain;
# * put the response_receiver in as the new async_callback;
# * when the response_receiver completes, invoke the old callback chain
async_callback = env['async.callback']
env['async.callback'] = response_receiver
response_receiver.callback{ do_postprocess(env, async_callback, response_receiver) }
response_receiver.errback{ do_postprocess(env, async_callback, response_receiver) }
hook_into_callback_chain(env, response_receiver)

response_receiver.pre_process
response_receiver_resp = response_receiver.pre_process

response_receiver.call(@app.call(env))
downstream_resp = @app.call(env)
response_receiver.call(downstream_resp)
end

# Generate a response_receiver to process the request, using request env & any args
# passed to this Response_ReceiverMiddleware at creation
#
# @param env [Goliath::Env] The goliath environment
# @return [Goliath::Rack::AsyncResponse_Receiver] The response_receiver to process this request
def new_response_receiver(env)
@response_receiver_klass.new(env, *@response_receiver_args)
end

include Goliath::Rack::Validator
# put response_receiver in the middle of the async_callback chain:
# * save the old callback chain;
# * have the downstream callback send results to the response_receiver (possibly
# completing it)
# * set the old callback chain to fire when the response_receiver completes
def hook_into_callback_chain(env, response_receiver)
async_callback = env['async.callback']
env['async.callback'] = response_receiver
response_receiver.callback{ do_postprocess(env, async_callback, response_receiver) }
response_receiver.errback{ do_postprocess(env, async_callback, response_receiver) }
end

def do_postprocess(env, async_callback, response_receiver)
Goliath::Rack::Validator.safely(env) do
async_callback.call(response_receiver.post_process)
end
safely(env){ async_callback.call(response_receiver.post_process) }
end
end
end
Expand Down
18 changes: 11 additions & 7 deletions lib/goliath/synchrony/response_receiver.rb
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
module Goliath
module Synchrony

#
# FIXME: generalize this to work with any deferrable
module ResponseReceiver
attr_accessor :env, :status, :headers, :body
# The request environment, set in the initializer
attr_reader :env
# The response, set by the BarrierMiddleware's downstream
attr_accessor :status, :headers, :body

# Override this method in your middleware to perform any preprocessing
# (launching a deferred request, perhaps)
# (launching a deferred request, perhaps).
#
# @return [Array] array contains [status, headers, body]
def pre_process
Goliath::Connection::AsyncResponse
end

# Override this method in your middleware to perform any postprocessing. This
# will only be invoked when the deferrable and the response have been
# received.
# Override this method in your middleware to perform any postprocessing.
# This will only be invoked when all deferred requests (including the
# response) have completed.
#
# @return [Array] array contains [status, headers, body]
def post_process
Expand Down

0 comments on commit 72435e0

Please sign in to comment.