removing log dir from .gitignore
[monkeycharger.git] / vendor / rails / actionwebservice / lib / action_web_service / invocation.rb
blob2a9121ee26f075b39d286bdd611b5cac249b1b0f
1 module ActionWebService # :nodoc:
2   module Invocation # :nodoc:
3     class InvocationError < ActionWebService::ActionWebServiceError # :nodoc:
4     end
6     def self.included(base) # :nodoc:
7       base.extend(ClassMethods)
8       base.send(:include, ActionWebService::Invocation::InstanceMethods)
9     end
11     # Invocation interceptors provide a means to execute custom code before
12     # and after method invocations on ActionWebService::Base objects.
13     #
14     # When running in _Direct_ dispatching mode, ActionController filters
15     # should be used for this functionality instead.
16     #
17     # The semantics of invocation interceptors are the same as ActionController
18     # filters, and accept the same parameters and options.
19     #
20     # A _before_ interceptor can also cancel execution by returning +false+,
21     # or returning a <tt>[false, "cancel reason"]</tt> array if it wishes to supply
22     # a reason for canceling the request.
23     #
24     # === Example
25     #
26     #   class CustomService < ActionWebService::Base
27     #     before_invocation :intercept_add, :only => [:add]
28     #
29     #     def add(a, b)
30     #       a + b
31     #     end
32     #
33     #     private
34     #       def intercept_add
35     #         return [false, "permission denied"] # cancel it
36     #       end
37     #   end
38     #
39     # Options:
40     # [<tt>:except</tt>]  A list of methods for which the interceptor will NOT be called
41     # [<tt>:only</tt>]    A list of methods for which the interceptor WILL be called
42     module ClassMethods
43       # Appends the given +interceptors+ to be called
44       # _before_ method invocation.
45       def append_before_invocation(*interceptors, &block)
46         conditions = extract_conditions!(interceptors)
47         interceptors << block if block_given?
48         add_interception_conditions(interceptors, conditions)
49         append_interceptors_to_chain("before", interceptors)
50       end
52       # Prepends the given +interceptors+ to be called
53       # _before_ method invocation.
54       def prepend_before_invocation(*interceptors, &block)
55         conditions = extract_conditions!(interceptors)
56         interceptors << block if block_given?
57         add_interception_conditions(interceptors, conditions)
58         prepend_interceptors_to_chain("before", interceptors)
59       end
61       alias :before_invocation :append_before_invocation
63       # Appends the given +interceptors+ to be called
64       # _after_ method invocation.
65       def append_after_invocation(*interceptors, &block)
66         conditions = extract_conditions!(interceptors)
67         interceptors << block if block_given?
68         add_interception_conditions(interceptors, conditions)
69         append_interceptors_to_chain("after", interceptors)
70       end
72       # Prepends the given +interceptors+ to be called
73       # _after_ method invocation.
74       def prepend_after_invocation(*interceptors, &block)
75         conditions = extract_conditions!(interceptors)
76         interceptors << block if block_given?
77         add_interception_conditions(interceptors, conditions)
78         prepend_interceptors_to_chain("after", interceptors)
79       end
81       alias :after_invocation :append_after_invocation
83       def before_invocation_interceptors # :nodoc:
84         read_inheritable_attribute("before_invocation_interceptors")
85       end
87       def after_invocation_interceptors # :nodoc:
88         read_inheritable_attribute("after_invocation_interceptors")
89       end
91       def included_intercepted_methods # :nodoc:
92         read_inheritable_attribute("included_intercepted_methods") || {}
93       end
94       
95       def excluded_intercepted_methods # :nodoc:
96         read_inheritable_attribute("excluded_intercepted_methods") || {}
97       end
99       private
100         def append_interceptors_to_chain(condition, interceptors)
101           write_inheritable_array("#{condition}_invocation_interceptors", interceptors)
102         end
104         def prepend_interceptors_to_chain(condition, interceptors)
105           interceptors = interceptors + read_inheritable_attribute("#{condition}_invocation_interceptors")
106           write_inheritable_attribute("#{condition}_invocation_interceptors", interceptors)
107         end
109         def extract_conditions!(interceptors)
110           return nil unless interceptors.last.is_a? Hash
111           interceptors.pop
112         end
114         def add_interception_conditions(interceptors, conditions)
115           return unless conditions
116           included, excluded = conditions[:only], conditions[:except]
117           write_inheritable_hash("included_intercepted_methods", condition_hash(interceptors, included)) && return if included
118           write_inheritable_hash("excluded_intercepted_methods", condition_hash(interceptors, excluded)) if excluded
119         end
121         def condition_hash(interceptors, *methods)
122           interceptors.inject({}) {|hash, interceptor| hash.merge(interceptor => methods.flatten.map {|method| method.to_s})}
123         end
124     end
126     module InstanceMethods # :nodoc:
127       def self.included(base)
128         base.class_eval do
129           alias_method_chain :perform_invocation, :interception
130         end
131       end
133       def perform_invocation_with_interception(method_name, params, &block)
134         return if before_invocation(method_name, params, &block) == false
135         return_value = perform_invocation_without_interception(method_name, params)
136         after_invocation(method_name, params, return_value)
137         return_value
138       end
140       def perform_invocation(method_name, params)
141         send(method_name, *params)
142       end
144       def before_invocation(name, args, &block)
145         call_interceptors(self.class.before_invocation_interceptors, [name, args], &block)
146       end
148       def after_invocation(name, args, result)
149         call_interceptors(self.class.after_invocation_interceptors, [name, args, result])
150       end
152       private
154         def call_interceptors(interceptors, interceptor_args, &block)
155           if interceptors and not interceptors.empty?
156             interceptors.each do |interceptor|
157               next if method_exempted?(interceptor, interceptor_args[0].to_s)
158               result = case
159                 when interceptor.is_a?(Symbol)
160                   self.send(interceptor, *interceptor_args)
161                 when interceptor_block?(interceptor)
162                   interceptor.call(self, *interceptor_args)
163                 when interceptor_class?(interceptor)
164                   interceptor.intercept(self, *interceptor_args)
165                 else
166                   raise(
167                     InvocationError,
168                     "Interceptors need to be either a symbol, proc/method, or a class implementing a static intercept method"
169                   )
170               end
171               reason = nil
172               if result.is_a?(Array)
173                 reason = result[1] if result[1]
174                 result = result[0]
175               end
176               if result == false
177                 block.call(reason) if block && reason
178                 return false
179               end
180             end
181           end
182         end
184         def interceptor_block?(interceptor)
185           interceptor.respond_to?("call") && (interceptor.arity == 3 || interceptor.arity == -1)
186         end
187         
188         def interceptor_class?(interceptor)
189           interceptor.respond_to?("intercept")
190         end
192         def method_exempted?(interceptor, method_name)
193           case
194             when self.class.included_intercepted_methods[interceptor]
195               !self.class.included_intercepted_methods[interceptor].include?(method_name)
196             when self.class.excluded_intercepted_methods[interceptor] 
197               self.class.excluded_intercepted_methods[interceptor].include?(method_name)
198           end
199         end
200     end
201   end