1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
|
#--
# =============================================================================
# Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
# All rights reserved.
#
# This source file is distributed as part of the Needle dependency injection
# library for Ruby. This file (and the library as a whole) may be used only as
# allowed by either the BSD license, or the Ruby license (or, by association
# with the Ruby license, the GPL). See the "doc" subdirectory of the Needle
# distribution for the texts of these licenses.
# -----------------------------------------------------------------------------
# needle website : http://needle.rubyforge.org
# project website: http://rubyforge.org/projects/needle
# =============================================================================
#++
require 'needle/errors'
module Needle
# This represents the definition of an interceptor as it is attached to a
# service point. Instances of Interceptor are also used for configuring
# themselves programmatically.
#
# You will almost never instantiate an Interceptor object directly. Instead,
# use the Container#intercept method. You can then configure the new
# interceptor by chaining methods of the new object together, quite
# readably:
#
# container.intercept( :foo ).with! { some_interceptor }.
# with_options( :arg => :value )
#
# You can also create new interceptors on the fly via the Interceptor#doing
# method.
class Interceptor
# This is the wrapper for on-the-fly interceptors that have been created
# via Interceptor#doing. The callback registered with Interceptor#doing
# gets wrapped by an instance of this class, to comply with the interface
# required by InterceptorChainBuilder.
#
# This class should rarely (if ever) be instantiated directly. Instead,
# using the Interceptor#doing method to create dynamic interceptors.
class DynamicInterceptor
# Create a new DynamicInterceptor instance that wraps the given
# callback.
def initialize( callback )
@callback = callback
end
# This method is a concession to the required interceptor factory
# interface. It should return the new interceptor, configured to be
# attached to the given service point, and with the given options.
# It will always return +self+.
def new( point, opts )
@point = point
@options = opts
self
end
# Process this link in the interceptor chain. This will invoke the
# wrapped callback, passing in the chain and context parameters.
# Before invoking the callback, the options and service point
# references that were given in #new are assigned to context
# data members (so they can be referenced inside the callback).
def process( chain, context )
context.data[:options] = @options
context.data[:point] = @point
@callback.call( chain, context )
end
end
# The set of options that were given to this interceptor via the
# #with_options method.
attr_reader :options
# Create a new Interceptor definition. By default, it has no
# implementation and a priority of 0.
def initialize
@options = { :priority => 0 }
@doing = @with = nil
end
# Returns the action that was specified for this interceptor as a proc
# instance. This will either be the block passed to #with, or a proc
# that wraps the instantiation of a DynamicInterceptor (when #doing
# was used).
#
# If neither #with nor #doing were specified, an
# InterceptorConfigurationError is raised.
def action
return @with if @with
raise InterceptorConfigurationError,
"You must specify either 'with' or 'doing'" unless @doing
return proc { |c| DynamicInterceptor.new( @doing ) }
end
# Sets the action for this interceptor to be that defined by the
# interceptor returned when the block is executed. You can only
# invoke #with once, and never after previously invoking #doing on the
# same interceptor instance.
#
# Usage:
#
# container.intercept( :foo ).
# with { |c| c.logging_interceptor }
def with( &block )
if @with
raise InterceptorConfigurationError,
"you cannot redefine 'with' behavior"
end
if @doing
raise InterceptorConfigurationError,
"cannot specify 'with' after specifying 'doing'"
end
if block.nil?
raise InterceptorConfigurationError,
"you must specify a block to 'with'"
end
@with = block
self
end
# This is identical to #with, but it wraps the block in another proc that
# calls +instance_eval+ on the container, with the block.
#
# Usage:
#
# container.intercept( :foo ).
# with! { logging_interceptor }
def with!( &block )
with { |c| c.instance_eval( &block ) }
end
# This allows new interceptors to be defined "on-the-fly". The associated
# block must accept two parameters--an object representing the chain of
# interceptors, and the context of the current method invocation. The block
# should then invoke #process_next on the chain (passing the context as
# the lone parameter) when the next element of the chain should be invoked.
#
# You should only call #doing once per interceptor, and never after
# invoking #with on the same interceptor.
def doing( &block )
if @doing
raise InterceptorConfigurationError,
"you cannot redefine 'doing' behavior"
end
if @with
raise InterceptorConfigurationError,
"cannot specify 'doing' after specifying 'with'"
end
if block.nil?
raise InterceptorConfigurationError,
"you must specify a block to 'doing'"
end
@doing = block
self
end
# Merge the given +opts+ hash into the interceptors options hash.
def with_options( opts={} )
@options.update opts
self
end
# A convenience method for querying the options on an interceptor
# definition.
def []( name )
@options[ name ]
end
# A convenience method for setting the options on an interceptor
# definition.
def []=( name, value )
@options[ name ] = value
end
end
end
|