Module: Needle::InterceptorChainBuilder

Defined in:
lib/needle/interceptor-chain.rb

Overview

This module encapsulates the functionality for building interceptor chains.

Defined Under Namespace

Classes: InterceptedServiceProxy, InterceptorChainElement, InvocationContext, ProxyObjectChainElement

Class Method Summary collapse

Class Method Details

.build(point, service, interceptors) ⇒ Object

This will apply the given interceptors to the given service by first ordering the interceptors based on their relative priorities, and then dynamically modifying the service's methods so that the chain of interceptors sits in front of each of them.

The modified service is returned.


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
# File 'lib/needle/interceptor-chain.rb', line 103

def build( point, service, interceptors )
  return service if interceptors.nil? || interceptors.empty?

  ordered_list =
    interceptors.sort { |a,b|
      a.options[:priority] <=> b.options[:priority] }

  chain = ProxyObjectChainElement.new( service )

  ordered_list.reverse.each do |interceptor|
    factory = interceptor.action.call( point.container )
    instance = factory.new( point, interceptor.options )
    element = InterceptorChainElement.new( instance )
    element.next = chain
    chain = element
  end

  # FIXME: should inherited methods of "Object" be interceptable?
  methods_to_intercept = ( service.class.instance_methods( true ) -
                           Object.instance_methods +
                           service.class.instance_methods( false ) ).uniq

  service = InterceptedServiceProxy.new( chain )
  singleton = class << service; self; end

  methods_to_intercept.each do |method|
    next if method =~ /^__/

    if singleton.instance_methods(false).include? method
      singleton.send( :remove_method, method )
    end

    singleton.class_eval <<-EOF
      def #{method}( *args, &block )
        context = InvocationContext.new( :#{method}, args, block, Hash.new )
        @chain.process_next( context )
      end
    EOF
  end

  # allow the interceptor to intercept methods not explicitly
  # declared on the reciever.
  if singleton.instance_methods(false).include? "method_missing"
    singleton.send( :remove_method, :method_missing )
  end

  singleton.class_eval <<-EOF
    def method_missing( sym, *args, &block )
      context = InvocationContext.new( sym, args, block, Hash.new )
      @chain.process_next( context )
    end
  EOF

  return service
end