actionwebservice 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/ChangeLog +47 -0
- data/MIT-LICENSE +21 -0
- data/README +238 -0
- data/Rakefile +144 -0
- data/TODO +13 -0
- data/examples/googlesearch/README +143 -0
- data/examples/googlesearch/autoloading/google_search_api.rb +50 -0
- data/examples/googlesearch/autoloading/google_search_controller.rb +57 -0
- data/examples/googlesearch/delegated/google_search_service.rb +108 -0
- data/examples/googlesearch/delegated/search_controller.rb +7 -0
- data/examples/googlesearch/direct/google_search_api.rb +50 -0
- data/examples/googlesearch/direct/search_controller.rb +58 -0
- data/examples/metaWeblog/README +16 -0
- data/examples/metaWeblog/blog_controller.rb +127 -0
- data/lib/action_web_service.rb +60 -0
- data/lib/action_web_service/api.rb +2 -0
- data/lib/action_web_service/api/abstract.rb +192 -0
- data/lib/action_web_service/api/action_controller.rb +92 -0
- data/lib/action_web_service/base.rb +41 -0
- data/lib/action_web_service/client.rb +3 -0
- data/lib/action_web_service/client/base.rb +39 -0
- data/lib/action_web_service/client/soap_client.rb +88 -0
- data/lib/action_web_service/client/xmlrpc_client.rb +77 -0
- data/lib/action_web_service/container.rb +85 -0
- data/lib/action_web_service/dispatcher.rb +2 -0
- data/lib/action_web_service/dispatcher/abstract.rb +150 -0
- data/lib/action_web_service/dispatcher/action_controller_dispatcher.rb +299 -0
- data/lib/action_web_service/invocation.rb +205 -0
- data/lib/action_web_service/protocol.rb +4 -0
- data/lib/action_web_service/protocol/abstract.rb +128 -0
- data/lib/action_web_service/protocol/registry.rb +55 -0
- data/lib/action_web_service/protocol/soap_protocol.rb +484 -0
- data/lib/action_web_service/protocol/xmlrpc_protocol.rb +168 -0
- data/lib/action_web_service/struct.rb +55 -0
- data/lib/action_web_service/support/class_inheritable_options.rb +26 -0
- data/lib/action_web_service/support/signature.rb +100 -0
- data/setup.rb +1360 -0
- data/test/abstract_client.rb +131 -0
- data/test/abstract_soap.rb +58 -0
- data/test/abstract_unit.rb +9 -0
- data/test/api_test.rb +52 -0
- data/test/base_test.rb +42 -0
- data/test/client_soap_test.rb +93 -0
- data/test/client_xmlrpc_test.rb +92 -0
- data/test/container_test.rb +53 -0
- data/test/dispatcher_action_controller_test.rb +186 -0
- data/test/gencov +3 -0
- data/test/invocation_test.rb +149 -0
- data/test/protocol_registry_test.rb +53 -0
- data/test/protocol_soap_test.rb +252 -0
- data/test/protocol_xmlrpc_test.rb +147 -0
- data/test/run +5 -0
- data/test/struct_test.rb +40 -0
- metadata +131 -0
@@ -0,0 +1,299 @@
|
|
1
|
+
module ActionWebService # :nodoc:
|
2
|
+
module Dispatcher # :nodoc:
|
3
|
+
module ActionController # :nodoc:
|
4
|
+
def self.append_features(base) # :nodoc:
|
5
|
+
super
|
6
|
+
base.class_eval do
|
7
|
+
class << self
|
8
|
+
alias_method :inherited_without_action_controller, :inherited
|
9
|
+
end
|
10
|
+
alias_method :before_direct_invoke_without_action_controller, :before_direct_invoke
|
11
|
+
alias_method :after_direct_invoke_without_action_controller, :after_direct_invoke
|
12
|
+
end
|
13
|
+
base.add_web_service_api_callback do |klass, api|
|
14
|
+
if klass.web_service_dispatching_mode == :direct
|
15
|
+
klass.class_eval <<-EOS
|
16
|
+
def api
|
17
|
+
controller_dispatch_web_service_request
|
18
|
+
end
|
19
|
+
EOS
|
20
|
+
end
|
21
|
+
end
|
22
|
+
base.add_web_service_definition_callback do |klass, name, info|
|
23
|
+
if klass.web_service_dispatching_mode == :delegated
|
24
|
+
klass.class_eval <<-EOS
|
25
|
+
def #{name}
|
26
|
+
controller_dispatch_web_service_request
|
27
|
+
end
|
28
|
+
EOS
|
29
|
+
end
|
30
|
+
end
|
31
|
+
base.extend(ClassMethods)
|
32
|
+
base.send(:include, ActionWebService::Dispatcher::ActionController::Invocation)
|
33
|
+
end
|
34
|
+
|
35
|
+
module ClassMethods # :nodoc:
|
36
|
+
def inherited(child)
|
37
|
+
inherited_without_action_controller(child)
|
38
|
+
child.send(:include, ActionWebService::Dispatcher::ActionController::WsdlGeneration)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
module Invocation # :nodoc:
|
43
|
+
private
|
44
|
+
def controller_dispatch_web_service_request
|
45
|
+
request, response, elapsed, exception = dispatch_web_service_request(@request)
|
46
|
+
if response
|
47
|
+
begin
|
48
|
+
log_request(request)
|
49
|
+
log_error(exception) if exception && logger
|
50
|
+
log_response(response, elapsed)
|
51
|
+
response_options = { :type => response.content_type, :disposition => 'inline' }
|
52
|
+
send_data(response.raw_body, response_options)
|
53
|
+
rescue Exception => e
|
54
|
+
log_error(e) unless logger.nil?
|
55
|
+
render_text("Internal protocol error", "500 Internal Server Error")
|
56
|
+
end
|
57
|
+
else
|
58
|
+
logger.error("No response available") unless logger.nil?
|
59
|
+
render_text("Internal protocol error", "500 Internal Server Error")
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def before_direct_invoke(request)
|
64
|
+
before_direct_invoke_without_action_controller(request)
|
65
|
+
@params ||= {}
|
66
|
+
signature = request.signature
|
67
|
+
if signature && (expects = request.signature[:expects])
|
68
|
+
(0..(@method_params.size-1)).each do |i|
|
69
|
+
if expects[i].is_a?(Hash)
|
70
|
+
@params[expects[i].keys[0].to_s] = @method_params[i]
|
71
|
+
else
|
72
|
+
@params['param%d' % i] = @method_params[i]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
@params['action'] = request.method_name.to_s
|
77
|
+
@session ||= {}
|
78
|
+
@assigns ||= {}
|
79
|
+
return nil if before_action == false
|
80
|
+
true
|
81
|
+
end
|
82
|
+
|
83
|
+
def after_direct_invoke(request)
|
84
|
+
after_direct_invoke_without_action_controller(request)
|
85
|
+
after_action
|
86
|
+
end
|
87
|
+
|
88
|
+
def log_request(request)
|
89
|
+
unless logger.nil? || request.nil?
|
90
|
+
logger.debug("\nWeb Service Request:")
|
91
|
+
indented = request.raw_body.split(/\n/).map{|x| " #{x}"}.join("\n")
|
92
|
+
logger.debug(indented)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def log_response(response, elapsed)
|
97
|
+
unless logger.nil? || response.nil?
|
98
|
+
logger.debug("\nWeb Service Response (%f):" % elapsed)
|
99
|
+
indented = response.raw_body.split(/\n/).map{|x| " #{x}"}.join("\n")
|
100
|
+
logger.debug(indented)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
unless method_defined?(:logger)
|
105
|
+
def logger; @logger; end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
module WsdlGeneration # :nodoc:
|
110
|
+
XsdNs = 'http://www.w3.org/2001/XMLSchema'
|
111
|
+
WsdlNs = 'http://schemas.xmlsoap.org/wsdl/'
|
112
|
+
SoapNs = 'http://schemas.xmlsoap.org/wsdl/soap/'
|
113
|
+
SoapEncodingNs = 'http://schemas.xmlsoap.org/soap/encoding/'
|
114
|
+
SoapHttpTransport = 'http://schemas.xmlsoap.org/soap/http'
|
115
|
+
|
116
|
+
def wsdl
|
117
|
+
case @request.method
|
118
|
+
when :get
|
119
|
+
begin
|
120
|
+
host_name = @request.env['HTTP_HOST'] || @request.env['SERVER_NAME']
|
121
|
+
uri = "http://#{host_name}/#{controller_name}/"
|
122
|
+
soap_action_base = "/#{controller_name}"
|
123
|
+
xml = to_wsdl(self, uri, soap_action_base)
|
124
|
+
send_data(xml, :type => 'text/xml', :disposition => 'inline')
|
125
|
+
rescue Exception => e
|
126
|
+
log_error e unless logger.nil?
|
127
|
+
render_text('', "500 #{e.message}")
|
128
|
+
end
|
129
|
+
when :post
|
130
|
+
render_text('', "500 POST not supported")
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
private
|
135
|
+
def to_wsdl(container, uri, soap_action_base)
|
136
|
+
wsdl = ""
|
137
|
+
|
138
|
+
web_service_dispatching_mode = container.web_service_dispatching_mode
|
139
|
+
mapper = container.class.soap_mapper
|
140
|
+
namespace = mapper.custom_namespace
|
141
|
+
wsdl_service_name = namespace.split(/:/)[1]
|
142
|
+
|
143
|
+
services = {}
|
144
|
+
mapper.map_container_services(container) do |name, api, api_methods|
|
145
|
+
services[name] = [api, api_methods]
|
146
|
+
end
|
147
|
+
custom_types = mapper.custom_types
|
148
|
+
|
149
|
+
|
150
|
+
xm = Builder::XmlMarkup.new(:target => wsdl, :indent => 2)
|
151
|
+
xm.instruct!
|
152
|
+
|
153
|
+
xm.definitions('name' => wsdl_service_name,
|
154
|
+
'targetNamespace' => namespace,
|
155
|
+
'xmlns:typens' => namespace,
|
156
|
+
'xmlns:xsd' => XsdNs,
|
157
|
+
'xmlns:soap' => SoapNs,
|
158
|
+
'xmlns:soapenc' => SoapEncodingNs,
|
159
|
+
'xmlns:wsdl' => WsdlNs,
|
160
|
+
'xmlns' => WsdlNs) do
|
161
|
+
|
162
|
+
# Custom type XSD generation
|
163
|
+
if custom_types.size > 0
|
164
|
+
xm.types do
|
165
|
+
xm.xsd(:schema, 'xmlns' => XsdNs, 'targetNamespace' => namespace) do
|
166
|
+
custom_types.each do |klass, mapping|
|
167
|
+
case
|
168
|
+
when mapping.is_a?(ActionWebService::Protocol::Soap::SoapArrayMapping)
|
169
|
+
xm.xsd(:complexType, 'name' => mapping.type_name) do
|
170
|
+
xm.xsd(:complexContent) do
|
171
|
+
xm.xsd(:restriction, 'base' => 'soapenc:Array') do
|
172
|
+
xm.xsd(:attribute, 'ref' => 'soapenc:arrayType',
|
173
|
+
'wsdl:arrayType' => mapping.element_mapping.qualified_type_name + '[]')
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
when mapping.is_a?(ActionWebService::Protocol::Soap::SoapMapping)
|
178
|
+
xm.xsd(:complexType, 'name' => mapping.type_name) do
|
179
|
+
xm.xsd(:all) do
|
180
|
+
mapping.each_attribute do |name, type_name|
|
181
|
+
xm.xsd(:element, 'name' => name, 'type' => type_name)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
else
|
186
|
+
raise(WsdlError, "unsupported mapping type #{mapping.class.name}")
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
services.each do |service_name, service_values|
|
194
|
+
service_api, api_methods = service_values
|
195
|
+
# Parameter list message definitions
|
196
|
+
api_methods.each do |method_name, method_signature|
|
197
|
+
gen = lambda do |msg_name, direction|
|
198
|
+
xm.message('name' => msg_name) do
|
199
|
+
sym = nil
|
200
|
+
if direction == :out
|
201
|
+
if method_signature[:returns]
|
202
|
+
xm.part('name' => 'return', 'type' => method_signature[:returns][0].qualified_type_name)
|
203
|
+
end
|
204
|
+
else
|
205
|
+
mapping_list = method_signature[:expects]
|
206
|
+
i = 1
|
207
|
+
mapping_list.each do |mapping|
|
208
|
+
if mapping.is_a?(Hash)
|
209
|
+
param_name = mapping.keys.shift
|
210
|
+
mapping = mapping.values.shift
|
211
|
+
else
|
212
|
+
param_name = "param#{i}"
|
213
|
+
end
|
214
|
+
xm.part('name' => param_name, 'type' => mapping.qualified_type_name)
|
215
|
+
i += 1
|
216
|
+
end if mapping_list
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
public_name = service_api.public_api_method_name(method_name)
|
221
|
+
gen.call(public_name, :in)
|
222
|
+
gen.call("#{public_name}Response", :out)
|
223
|
+
end
|
224
|
+
|
225
|
+
# Declare the port
|
226
|
+
port_name = port_name_for(wsdl_service_name, service_name)
|
227
|
+
xm.portType('name' => port_name) do
|
228
|
+
api_methods.each do |method_name, method_signature|
|
229
|
+
public_name = service_api.public_api_method_name(method_name)
|
230
|
+
xm.operation('name' => public_name) do
|
231
|
+
xm.input('message' => "typens:#{public_name}")
|
232
|
+
xm.output('message' => "typens:#{public_name}Response")
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
# Bind the port to SOAP
|
238
|
+
binding_name = binding_name_for(wsdl_service_name, service_name)
|
239
|
+
xm.binding('name' => binding_name, 'type' => "typens:#{port_name}") do
|
240
|
+
xm.soap(:binding, 'style' => 'rpc', 'transport' => SoapHttpTransport)
|
241
|
+
api_methods.each do |method_name, method_signature|
|
242
|
+
public_name = service_api.public_api_method_name(method_name)
|
243
|
+
xm.operation('name' => public_name) do
|
244
|
+
case web_service_dispatching_mode
|
245
|
+
when :direct
|
246
|
+
soap_action = soap_action_base + "/api/" + public_name
|
247
|
+
when :delegated
|
248
|
+
soap_action = soap_action_base \
|
249
|
+
+ "/" + service_name.to_s \
|
250
|
+
+ "/" + public_name
|
251
|
+
end
|
252
|
+
xm.soap(:operation, 'soapAction' => soap_action)
|
253
|
+
xm.input do
|
254
|
+
xm.soap(:body,
|
255
|
+
'use' => 'encoded',
|
256
|
+
'namespace' => namespace,
|
257
|
+
'encodingStyle' => SoapEncodingNs)
|
258
|
+
end
|
259
|
+
xm.output do
|
260
|
+
xm.soap(:body,
|
261
|
+
'use' => 'encoded',
|
262
|
+
'namespace' => namespace,
|
263
|
+
'encodingStyle' => SoapEncodingNs)
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
# Define the service
|
271
|
+
xm.service('name' => "#{wsdl_service_name}Service") do
|
272
|
+
services.each do |service_name, service_values|
|
273
|
+
port_name = port_name_for(wsdl_service_name, service_name)
|
274
|
+
binding_name = binding_name_for(wsdl_service_name, service_name)
|
275
|
+
case web_service_dispatching_mode
|
276
|
+
when :direct
|
277
|
+
binding_target = 'api'
|
278
|
+
when :delegated
|
279
|
+
binding_target = service_name.to_s
|
280
|
+
end
|
281
|
+
xm.port('name' => port_name, 'binding' => "typens:#{binding_name}") do
|
282
|
+
xm.soap(:address, 'location' => "#{uri}#{binding_target}")
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
def port_name_for(wsdl_service_name, service_name)
|
290
|
+
"#{wsdl_service_name}#{service_name.to_s.camelize}Port"
|
291
|
+
end
|
292
|
+
|
293
|
+
def binding_name_for(wsdl_service_name, service_name)
|
294
|
+
"#{wsdl_service_name}#{service_name.to_s.camelize}Binding"
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
@@ -0,0 +1,205 @@
|
|
1
|
+
module ActionWebService # :nodoc:
|
2
|
+
module Invocation # :nodoc:
|
3
|
+
class InvocationError < ActionWebService::ActionWebServiceError # :nodoc:
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.append_features(base) # :nodoc:
|
7
|
+
super
|
8
|
+
base.extend(ClassMethods)
|
9
|
+
base.send(:include, ActionWebService::Invocation::InstanceMethods)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Invocation interceptors provide a means to execute custom code before
|
13
|
+
# and after method invocations on ActionWebService::Base objects.
|
14
|
+
#
|
15
|
+
# When running in _Direct_ dispatching mode, ActionController filters
|
16
|
+
# should be used for this functionality instead.
|
17
|
+
#
|
18
|
+
# The semantics of invocation interceptors are the same as ActionController
|
19
|
+
# filters, and accept the same parameters and options.
|
20
|
+
#
|
21
|
+
# A _before_ interceptor can also cancel execution by returning +false+,
|
22
|
+
# or returning a <tt>[false, "cancel reason"]</tt> array if it wishes to supply
|
23
|
+
# a reason for canceling the request.
|
24
|
+
#
|
25
|
+
# === Example
|
26
|
+
#
|
27
|
+
# class CustomService < ActionWebService::Base
|
28
|
+
# before_invocation :intercept_add, :only => [:add]
|
29
|
+
#
|
30
|
+
# def add(a, b)
|
31
|
+
# a + b
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# private
|
35
|
+
# def intercept_add
|
36
|
+
# return [false, "permission denied"] # cancel it
|
37
|
+
# end
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# Options:
|
41
|
+
# [<tt>:except</tt>] A list of methods for which the interceptor will NOT be called
|
42
|
+
# [<tt>:only</tt>] A list of methods for which the interceptor WILL be called
|
43
|
+
module ClassMethods
|
44
|
+
# Appends the given +interceptors+ to be called
|
45
|
+
# _before_ method invocation.
|
46
|
+
def append_before_invocation(*interceptors, &block)
|
47
|
+
conditions = extract_conditions!(interceptors)
|
48
|
+
interceptors << block if block_given?
|
49
|
+
add_interception_conditions(interceptors, conditions)
|
50
|
+
append_interceptors_to_chain("before", interceptors)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Prepends the given +interceptors+ to be called
|
54
|
+
# _before_ method invocation.
|
55
|
+
def prepend_before_invocation(*interceptors, &block)
|
56
|
+
conditions = extract_conditions!(interceptors)
|
57
|
+
interceptors << block if block_given?
|
58
|
+
add_interception_conditions(interceptors, conditions)
|
59
|
+
prepend_interceptors_to_chain("before", interceptors)
|
60
|
+
end
|
61
|
+
|
62
|
+
alias :before_invocation :append_before_invocation
|
63
|
+
|
64
|
+
# Appends the given +interceptors+ to be called
|
65
|
+
# _after_ method invocation.
|
66
|
+
def append_after_invocation(*interceptors, &block)
|
67
|
+
conditions = extract_conditions!(interceptors)
|
68
|
+
interceptors << block if block_given?
|
69
|
+
add_interception_conditions(interceptors, conditions)
|
70
|
+
append_interceptors_to_chain("after", interceptors)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Prepends the given +interceptors+ to be called
|
74
|
+
# _after_ method invocation.
|
75
|
+
def prepend_after_invocation(*interceptors, &block)
|
76
|
+
conditions = extract_conditions!(interceptors)
|
77
|
+
interceptors << block if block_given?
|
78
|
+
add_interception_conditions(interceptors, conditions)
|
79
|
+
prepend_interceptors_to_chain("after", interceptors)
|
80
|
+
end
|
81
|
+
|
82
|
+
alias :after_invocation :append_after_invocation
|
83
|
+
|
84
|
+
def before_invocation_interceptors # :nodoc:
|
85
|
+
read_inheritable_attribute("before_invocation_interceptors")
|
86
|
+
end
|
87
|
+
|
88
|
+
def after_invocation_interceptors # :nodoc:
|
89
|
+
read_inheritable_attribute("after_invocation_interceptors")
|
90
|
+
end
|
91
|
+
|
92
|
+
def included_intercepted_methods # :nodoc:
|
93
|
+
read_inheritable_attribute("included_intercepted_methods") || {}
|
94
|
+
end
|
95
|
+
|
96
|
+
def excluded_intercepted_methods # :nodoc:
|
97
|
+
read_inheritable_attribute("excluded_intercepted_methods") || {}
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
def append_interceptors_to_chain(condition, interceptors)
|
102
|
+
write_inheritable_array("#{condition}_invocation_interceptors", interceptors)
|
103
|
+
end
|
104
|
+
|
105
|
+
def prepend_interceptors_to_chain(condition, interceptors)
|
106
|
+
interceptors = interceptors + read_inheritable_attribute("#{condition}_invocation_interceptors")
|
107
|
+
write_inheritable_attribute("#{condition}_invocation_interceptors", interceptors)
|
108
|
+
end
|
109
|
+
|
110
|
+
def extract_conditions!(interceptors)
|
111
|
+
return nil unless interceptors.last.is_a? Hash
|
112
|
+
interceptors.pop
|
113
|
+
end
|
114
|
+
|
115
|
+
def add_interception_conditions(interceptors, conditions)
|
116
|
+
return unless conditions
|
117
|
+
included, excluded = conditions[:only], conditions[:except]
|
118
|
+
write_inheritable_hash("included_intercepted_methods", condition_hash(interceptors, included)) && return if included
|
119
|
+
write_inheritable_hash("excluded_intercepted_methods", condition_hash(interceptors, excluded)) if excluded
|
120
|
+
end
|
121
|
+
|
122
|
+
def condition_hash(interceptors, *methods)
|
123
|
+
interceptors.inject({}) {|hash, interceptor| hash.merge(interceptor => methods.flatten.map {|method| method.to_s})}
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
module InstanceMethods # :nodoc:
|
128
|
+
def self.append_features(base)
|
129
|
+
super
|
130
|
+
base.class_eval do
|
131
|
+
alias_method :perform_invocation_without_interception, :perform_invocation
|
132
|
+
alias_method :perform_invocation, :perform_invocation_with_interception
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def perform_invocation_with_interception(method_name, params, &block)
|
137
|
+
return if before_invocation(method_name, params, &block) == false
|
138
|
+
return_value = perform_invocation_without_interception(method_name, params)
|
139
|
+
after_invocation(method_name, params, return_value)
|
140
|
+
return_value
|
141
|
+
end
|
142
|
+
|
143
|
+
def perform_invocation(method_name, params)
|
144
|
+
send(method_name, *params)
|
145
|
+
end
|
146
|
+
|
147
|
+
def before_invocation(name, args, &block)
|
148
|
+
call_interceptors(self.class.before_invocation_interceptors, [name, args], &block)
|
149
|
+
end
|
150
|
+
|
151
|
+
def after_invocation(name, args, result)
|
152
|
+
call_interceptors(self.class.after_invocation_interceptors, [name, args, result])
|
153
|
+
end
|
154
|
+
|
155
|
+
private
|
156
|
+
|
157
|
+
def call_interceptors(interceptors, interceptor_args, &block)
|
158
|
+
if interceptors and not interceptors.empty?
|
159
|
+
interceptors.each do |interceptor|
|
160
|
+
next if method_exempted?(interceptor, interceptor_args[0].to_s)
|
161
|
+
result = case
|
162
|
+
when interceptor.is_a?(Symbol)
|
163
|
+
self.send(interceptor, *interceptor_args)
|
164
|
+
when interceptor_block?(interceptor)
|
165
|
+
interceptor.call(self, *interceptor_args)
|
166
|
+
when interceptor_class?(interceptor)
|
167
|
+
interceptor.intercept(self, *interceptor_args)
|
168
|
+
else
|
169
|
+
raise(
|
170
|
+
InvocationError,
|
171
|
+
"Interceptors need to be either a symbol, proc/method, or a class implementing a static intercept method"
|
172
|
+
)
|
173
|
+
end
|
174
|
+
reason = nil
|
175
|
+
if result.is_a?(Array)
|
176
|
+
reason = result[1] if result[1]
|
177
|
+
result = result[0]
|
178
|
+
end
|
179
|
+
if result == false
|
180
|
+
block.call(reason) if block && reason
|
181
|
+
return false
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def interceptor_block?(interceptor)
|
188
|
+
interceptor.respond_to?("call") && (interceptor.arity == 3 || interceptor.arity == -1)
|
189
|
+
end
|
190
|
+
|
191
|
+
def interceptor_class?(interceptor)
|
192
|
+
interceptor.respond_to?("intercept")
|
193
|
+
end
|
194
|
+
|
195
|
+
def method_exempted?(interceptor, method_name)
|
196
|
+
case
|
197
|
+
when self.class.included_intercepted_methods[interceptor]
|
198
|
+
!self.class.included_intercepted_methods[interceptor].include?(method_name)
|
199
|
+
when self.class.excluded_intercepted_methods[interceptor]
|
200
|
+
self.class.excluded_intercepted_methods[interceptor].include?(method_name)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|