rubyjedi-actionwebservice 2.3.5.20100615120735
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 +335 -0
- data/MIT-LICENSE +21 -0
- data/README +381 -0
- data/Rakefile +180 -0
- data/TODO +32 -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 +17 -0
- data/examples/metaWeblog/apis/blogger_api.rb +60 -0
- data/examples/metaWeblog/apis/blogger_service.rb +34 -0
- data/examples/metaWeblog/apis/meta_weblog_api.rb +67 -0
- data/examples/metaWeblog/apis/meta_weblog_service.rb +48 -0
- data/examples/metaWeblog/controllers/xmlrpc_controller.rb +16 -0
- data/generators/web_service/USAGE +28 -0
- data/generators/web_service/templates/api_definition.rb +5 -0
- data/generators/web_service/templates/controller.rb +8 -0
- data/generators/web_service/templates/functional_test.rb +19 -0
- data/generators/web_service/web_service_generator.rb +29 -0
- data/lib/action_web_service/acts_as_web_service.rb +24 -0
- data/lib/action_web_service/api.rb +297 -0
- data/lib/action_web_service/base.rb +38 -0
- data/lib/action_web_service/casting.rb +151 -0
- data/lib/action_web_service/client/base.rb +28 -0
- data/lib/action_web_service/client/soap_client.rb +113 -0
- data/lib/action_web_service/client/xmlrpc_client.rb +58 -0
- data/lib/action_web_service/client.rb +3 -0
- data/lib/action_web_service/container/action_controller_container.rb +93 -0
- data/lib/action_web_service/container/delegated_container.rb +86 -0
- data/lib/action_web_service/container/direct_container.rb +69 -0
- data/lib/action_web_service/container.rb +3 -0
- data/lib/action_web_service/dispatcher/abstract.rb +208 -0
- data/lib/action_web_service/dispatcher/action_controller_dispatcher.rb +396 -0
- data/lib/action_web_service/dispatcher.rb +2 -0
- data/lib/action_web_service/invocation.rb +202 -0
- data/lib/action_web_service/protocol/abstract.rb +112 -0
- data/lib/action_web_service/protocol/discovery.rb +37 -0
- data/lib/action_web_service/protocol/soap_protocol/marshaler.rb +242 -0
- data/lib/action_web_service/protocol/soap_protocol.rb +176 -0
- data/lib/action_web_service/protocol/xmlrpc_protocol.rb +123 -0
- data/lib/action_web_service/protocol.rb +4 -0
- data/lib/action_web_service/scaffolding.rb +281 -0
- data/lib/action_web_service/simple.rb +53 -0
- data/lib/action_web_service/string_to_datetime_for_soap.rb +16 -0
- data/lib/action_web_service/struct.rb +68 -0
- data/lib/action_web_service/support/class_inheritable_options.rb +26 -0
- data/lib/action_web_service/support/signature_types.rb +261 -0
- data/lib/action_web_service/templates/scaffolds/layout.html.erb +65 -0
- data/lib/action_web_service/templates/scaffolds/methods.html.erb +6 -0
- data/lib/action_web_service/templates/scaffolds/parameters.html.erb +29 -0
- data/lib/action_web_service/templates/scaffolds/result.html.erb +30 -0
- data/lib/action_web_service/test_invoke.rb +110 -0
- data/lib/action_web_service/version.rb +9 -0
- data/lib/action_web_service.rb +60 -0
- data/lib/actionwebservice.rb +1 -0
- data/setup.rb +1379 -0
- data/test/abstract_client.rb +184 -0
- data/test/abstract_dispatcher.rb +549 -0
- data/test/abstract_unit.rb +43 -0
- data/test/actionwebservice_unittest.db +0 -0
- data/test/api_test.rb +102 -0
- data/test/apis/auto_load_api.rb +3 -0
- data/test/apis/broken_auto_load_api.rb +2 -0
- data/test/base_test.rb +42 -0
- data/test/casting_test.rb +95 -0
- data/test/client_soap_test.rb +156 -0
- data/test/client_xmlrpc_test.rb +154 -0
- data/test/container_test.rb +75 -0
- data/test/debug.log +12305 -0
- data/test/dispatcher_action_controller_soap_test.rb +139 -0
- data/test/dispatcher_action_controller_xmlrpc_test.rb +59 -0
- data/test/fixtures/db_definitions/mysql.sql +8 -0
- data/test/fixtures/db_definitions/sqlite3.sql +8 -0
- data/test/fixtures/users.yml +12 -0
- data/test/gencov +3 -0
- data/test/invocation_test.rb +186 -0
- data/test/run +6 -0
- data/test/scaffolded_controller_test.rb +147 -0
- data/test/struct_test.rb +84 -0
- data/test/test_invoke_test.rb +113 -0
- metadata +182 -0
@@ -0,0 +1,396 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
require 'builder/xmlmarkup'
|
3
|
+
|
4
|
+
module ActionWebService # :nodoc:
|
5
|
+
module Dispatcher # :nodoc:
|
6
|
+
module ActionController # :nodoc:
|
7
|
+
def self.included(base) # :nodoc:
|
8
|
+
class << base
|
9
|
+
include ClassMethods
|
10
|
+
alias_method_chain :inherited, :action_controller
|
11
|
+
end
|
12
|
+
base.class_eval do
|
13
|
+
alias_method :web_service_direct_invoke_without_controller, :web_service_direct_invoke
|
14
|
+
end
|
15
|
+
base.add_web_service_api_callback do |klass, api|
|
16
|
+
if klass.web_service_dispatching_mode == :direct
|
17
|
+
klass.class_eval 'def api; dispatch_web_service_request; end'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
base.add_web_service_definition_callback do |klass, name, info|
|
21
|
+
if klass.web_service_dispatching_mode == :delegated
|
22
|
+
klass.class_eval "def #{name}; dispatch_web_service_request; end"
|
23
|
+
elsif klass.web_service_dispatching_mode == :layered
|
24
|
+
klass.class_eval 'def api; dispatch_web_service_request; end'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
base.send(:include, ActionWebService::Dispatcher::ActionController::InstanceMethods)
|
28
|
+
end
|
29
|
+
|
30
|
+
module ClassMethods # :nodoc:
|
31
|
+
def inherited_with_action_controller(child)
|
32
|
+
inherited_without_action_controller(child)
|
33
|
+
child.send(:include, ActionWebService::Dispatcher::ActionController::WsdlAction)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
module InstanceMethods # :nodoc:
|
38
|
+
private
|
39
|
+
def dispatch_web_service_request
|
40
|
+
method = request.method.to_s.upcase
|
41
|
+
allowed_methods = self.class.web_service_api ? (self.class.web_service_api.allowed_http_methods || []) : [ :post ]
|
42
|
+
allowed_methods = allowed_methods.map{|m| m.to_s.upcase }
|
43
|
+
if !allowed_methods.include?(method)
|
44
|
+
render :text => "#{method} not supported", :status=>500
|
45
|
+
return
|
46
|
+
end
|
47
|
+
exception = nil
|
48
|
+
begin
|
49
|
+
ws_request = discover_web_service_request(request)
|
50
|
+
rescue Exception => e
|
51
|
+
exception = e
|
52
|
+
end
|
53
|
+
if ws_request
|
54
|
+
ws_response = nil
|
55
|
+
exception = nil
|
56
|
+
bm = Benchmark.measure do
|
57
|
+
begin
|
58
|
+
ws_response = invoke_web_service_request(ws_request)
|
59
|
+
rescue Exception => e
|
60
|
+
exception = e
|
61
|
+
end
|
62
|
+
end
|
63
|
+
log_request(ws_request, request.raw_post)
|
64
|
+
if exception
|
65
|
+
log_error(exception) unless logger.nil?
|
66
|
+
send_web_service_error_response(ws_request, exception)
|
67
|
+
else
|
68
|
+
send_web_service_response(ws_response, bm.real)
|
69
|
+
end
|
70
|
+
else
|
71
|
+
exception ||= DispatcherError.new("Malformed SOAP or XML-RPC protocol message")
|
72
|
+
log_error(exception) unless logger.nil?
|
73
|
+
send_web_service_error_response(ws_request, exception)
|
74
|
+
end
|
75
|
+
rescue Exception => e
|
76
|
+
log_error(e) unless logger.nil?
|
77
|
+
send_web_service_error_response(ws_request, e)
|
78
|
+
end
|
79
|
+
|
80
|
+
def send_web_service_response(ws_response, elapsed=nil)
|
81
|
+
log_response(ws_response, elapsed)
|
82
|
+
options = { :type => ws_response.content_type, :disposition => 'inline' }
|
83
|
+
send_data(ws_response.body, options)
|
84
|
+
end
|
85
|
+
|
86
|
+
def send_web_service_error_response(ws_request, exception)
|
87
|
+
if ws_request
|
88
|
+
unless self.class.web_service_exception_reporting
|
89
|
+
exception = DispatcherError.new("Internal server error (exception raised)")
|
90
|
+
end
|
91
|
+
api_method = ws_request.api_method
|
92
|
+
public_method_name = api_method ? api_method.public_name : ws_request.method_name
|
93
|
+
return_type = ActionWebService::SignatureTypes.canonical_signature_entry(Exception, 0)
|
94
|
+
ws_response = ws_request.protocol.encode_response(public_method_name + 'Response', exception, return_type, ws_request.protocol_options)
|
95
|
+
send_web_service_response(ws_response)
|
96
|
+
else
|
97
|
+
if self.class.web_service_exception_reporting
|
98
|
+
message = exception.message
|
99
|
+
backtrace = "\nBacktrace:\n#{exception.backtrace.join("\n")}"
|
100
|
+
else
|
101
|
+
message = "Exception raised"
|
102
|
+
backtrace = ""
|
103
|
+
end
|
104
|
+
render :status => 500, :text => "Internal protocol error: #{message}#{backtrace}"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def web_service_direct_invoke(invocation)
|
109
|
+
invocation.method_named_params.each do |name, value|
|
110
|
+
params[name] = value
|
111
|
+
end
|
112
|
+
web_service_direct_invoke_without_controller(invocation)
|
113
|
+
end
|
114
|
+
|
115
|
+
def log_request(ws_request, body)
|
116
|
+
unless logger.nil?
|
117
|
+
name = ws_request.method_name
|
118
|
+
api_method = ws_request.api_method
|
119
|
+
params = ws_request.method_params
|
120
|
+
if api_method && api_method.expects
|
121
|
+
params = api_method.expects.zip(params).map{ |type, param| "#{type.name}=>#{param.inspect}" }
|
122
|
+
else
|
123
|
+
params = params.map{ |param| param.inspect }
|
124
|
+
end
|
125
|
+
service = ws_request.service_name
|
126
|
+
logger.debug("\nWeb Service Request: #{name}(#{params.join(", ")}) Entrypoint: #{service}")
|
127
|
+
logger.debug(indent(body))
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def log_response(ws_response, elapsed=nil)
|
132
|
+
unless logger.nil?
|
133
|
+
elapsed = (elapsed ? " (%f):" % elapsed : ":")
|
134
|
+
logger.debug("\nWeb Service Response" + elapsed + " => #{ws_response.return_value.inspect}")
|
135
|
+
logger.debug(indent(ws_response.body))
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def indent(body)
|
140
|
+
body.split(/\n/).map{|x| " #{x}"}.join("\n")
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
module WsdlAction # :nodoc:
|
145
|
+
XsdNs = 'http://www.w3.org/2001/XMLSchema'
|
146
|
+
WsdlNs = 'http://schemas.xmlsoap.org/wsdl/'
|
147
|
+
SoapNs = 'http://schemas.xmlsoap.org/wsdl/soap/'
|
148
|
+
SoapEncodingNs = 'http://schemas.xmlsoap.org/soap/encoding/'
|
149
|
+
SoapHttpTransport = 'http://schemas.xmlsoap.org/soap/http'
|
150
|
+
|
151
|
+
def wsdl
|
152
|
+
case request.method
|
153
|
+
when :get
|
154
|
+
begin
|
155
|
+
options = { :type => 'text/xml', :disposition => 'inline' }
|
156
|
+
send_data(to_wsdl, options)
|
157
|
+
rescue Exception => e
|
158
|
+
log_error(e) unless logger.nil?
|
159
|
+
end
|
160
|
+
when :post
|
161
|
+
render :status => 500, :text => 'POST not supported'
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
private
|
166
|
+
def base_uri
|
167
|
+
host = request.host_with_port
|
168
|
+
relative_url_root = ::ActionController::Base.relative_url_root
|
169
|
+
scheme = request.ssl? ? 'https' : 'http'
|
170
|
+
'%s://%s%s/%s/' % [scheme, host, relative_url_root, self.class.controller_path]
|
171
|
+
end
|
172
|
+
|
173
|
+
def to_wsdl
|
174
|
+
xml = ''
|
175
|
+
inflect = web_service_inflect_type
|
176
|
+
dispatching_mode = web_service_dispatching_mode
|
177
|
+
global_service_name = wsdl_service_name
|
178
|
+
namespace = wsdl_namespace || 'urn:ActionWebService'
|
179
|
+
soap_action_base = "/#{controller_name}"
|
180
|
+
|
181
|
+
marshaler = ActionWebService::Protocol::Soap::SoapMarshaler.new(namespace)
|
182
|
+
apis = {}
|
183
|
+
case dispatching_mode
|
184
|
+
when :direct
|
185
|
+
api = self.class.web_service_api
|
186
|
+
web_service_name = controller_class_name.sub(/Controller$/, '').underscore
|
187
|
+
apis[web_service_name] = [api, register_api(api, marshaler)]
|
188
|
+
when :delegated, :layered
|
189
|
+
self.class.web_services.each do |web_service_name, info|
|
190
|
+
service = web_service_object(web_service_name)
|
191
|
+
api = service.class.web_service_api
|
192
|
+
apis[web_service_name] = [api, register_api(api, marshaler)]
|
193
|
+
end
|
194
|
+
end
|
195
|
+
custom_types = []
|
196
|
+
apis.values.each do |api, bindings|
|
197
|
+
bindings.each do |b|
|
198
|
+
custom_types << b unless custom_types.include?(b)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
xm = Builder::XmlMarkup.new(:target => xml, :indent => 2)
|
203
|
+
xm.instruct!
|
204
|
+
xm.definitions('name' => wsdl_service_name,
|
205
|
+
'targetNamespace' => namespace,
|
206
|
+
'xmlns:typens' => namespace,
|
207
|
+
'xmlns:xsd' => XsdNs,
|
208
|
+
'xmlns:soap' => SoapNs,
|
209
|
+
'xmlns:soapenc' => SoapEncodingNs,
|
210
|
+
'xmlns:wsdl' => WsdlNs,
|
211
|
+
'xmlns' => WsdlNs) do
|
212
|
+
# Generate XSD
|
213
|
+
if custom_types.size > 0
|
214
|
+
xm.types do
|
215
|
+
xm.xsd(:schema, 'xmlns' => XsdNs, 'targetNamespace' => namespace) do
|
216
|
+
simple_types, array_types, complex_types = [], [], []
|
217
|
+
custom_types.each do |binding|
|
218
|
+
case
|
219
|
+
when binding.type.simple? then simple_types.push binding
|
220
|
+
when binding.type.array? then array_types.push binding
|
221
|
+
when binding.type.structured? then complex_types.push binding
|
222
|
+
end
|
223
|
+
end
|
224
|
+
simple_types.each do |binding|
|
225
|
+
xm.xsd(:simpleType, 'name' => inflect ? binding.type_name.camelize(:lower) : binding.type_name) do
|
226
|
+
xm.xsd(:restriction, 'base' => "xsd:#{binding.type.base}") do
|
227
|
+
binding.type.restrictions do |name, value|
|
228
|
+
xm.xsd(name.to_sym, 'value' => value)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
array_types.each do |binding|
|
234
|
+
xm.xsd(:complexType, 'name' => inflect ? binding.type_name.camelize(:lower) : binding.type_name) do
|
235
|
+
xm.xsd(:complexContent) do
|
236
|
+
xm.xsd(:restriction, 'base' => 'soapenc:Array') do
|
237
|
+
xm.xsd(:attribute, 'ref' => 'soapenc:arrayType',
|
238
|
+
'wsdl:arrayType' => binding.element_binding.qualified_type_name('typens') + '[]')
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
complex_types.each do |binding|
|
244
|
+
xm.xsd(:complexType, 'name' => inflect ? binding.type_name.camelize(:lower) : binding.type_name) do
|
245
|
+
xm.xsd(:all) do
|
246
|
+
binding.type.each_member do |name, type, options|
|
247
|
+
options ||= {}
|
248
|
+
b = marshaler.register_type(type)
|
249
|
+
xm.xsd(:element, {'name' => inflect ? name.to_s.camelize : name, 'type' => b.qualified_type_name('typens')}.merge(options))
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
# APIs
|
259
|
+
apis.each do |api_name, values|
|
260
|
+
api = values[0]
|
261
|
+
api.api_methods.each do |name, method|
|
262
|
+
gen = lambda do |msg_name, direction|
|
263
|
+
xm.message('name' => message_name_for(api_name, msg_name)) do
|
264
|
+
sym = nil
|
265
|
+
if direction == :out
|
266
|
+
returns = method.returns
|
267
|
+
if returns
|
268
|
+
binding = marshaler.register_type(returns[0])
|
269
|
+
xm.part('name' => 'return', 'type' => binding.qualified_type_name('typens'))
|
270
|
+
end
|
271
|
+
else
|
272
|
+
expects = method.expects
|
273
|
+
expects.each do |type|
|
274
|
+
binding = marshaler.register_type(type)
|
275
|
+
xm.part('name' => type.name, 'type' => binding.qualified_type_name('typens'))
|
276
|
+
end if expects
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
280
|
+
public_name = method.public_name
|
281
|
+
gen.call(public_name, :in)
|
282
|
+
gen.call("#{public_name}Response", :out)
|
283
|
+
end
|
284
|
+
|
285
|
+
# Port
|
286
|
+
port_name = port_name_for(global_service_name, api_name)
|
287
|
+
xm.portType('name' => port_name) do
|
288
|
+
api.api_methods.each do |name, method|
|
289
|
+
xm.operation('name' => method.public_name) do
|
290
|
+
xm.input('message' => "typens:" + message_name_for(api_name, method.public_name))
|
291
|
+
xm.output('message' => "typens:" + message_name_for(api_name, "#{method.public_name}Response"))
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
# Bind it
|
297
|
+
binding_name = binding_name_for(global_service_name, api_name)
|
298
|
+
xm.binding('name' => binding_name, 'type' => "typens:#{port_name}") do
|
299
|
+
xm.soap(:binding, 'style' => 'rpc', 'transport' => SoapHttpTransport)
|
300
|
+
api.api_methods.each do |name, method|
|
301
|
+
xm.operation('name' => method.public_name) do
|
302
|
+
case web_service_dispatching_mode
|
303
|
+
when :direct
|
304
|
+
soap_action = soap_action_base + "/api/" + method.public_name
|
305
|
+
when :delegated, :layered
|
306
|
+
soap_action = soap_action_base \
|
307
|
+
+ "/" + api_name.to_s \
|
308
|
+
+ "/" + method.public_name
|
309
|
+
end
|
310
|
+
xm.soap(:operation, 'soapAction' => soap_action)
|
311
|
+
xm.input do
|
312
|
+
xm.soap(:body,
|
313
|
+
'use' => 'encoded',
|
314
|
+
'namespace' => namespace,
|
315
|
+
'encodingStyle' => SoapEncodingNs)
|
316
|
+
end
|
317
|
+
xm.output do
|
318
|
+
xm.soap(:body,
|
319
|
+
'use' => 'encoded',
|
320
|
+
'namespace' => namespace,
|
321
|
+
'encodingStyle' => SoapEncodingNs)
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
# Define it
|
329
|
+
xm.service('name' => "#{global_service_name}Service") do
|
330
|
+
apis.each do |api_name, values|
|
331
|
+
port_name = port_name_for(global_service_name, api_name)
|
332
|
+
binding_name = binding_name_for(global_service_name, api_name)
|
333
|
+
case web_service_dispatching_mode
|
334
|
+
when :direct, :layered
|
335
|
+
binding_target = 'api'
|
336
|
+
when :delegated
|
337
|
+
binding_target = api_name.to_s
|
338
|
+
end
|
339
|
+
xm.port('name' => port_name, 'binding' => "typens:#{binding_name}") do
|
340
|
+
xm.soap(:address, 'location' => "#{base_uri}#{binding_target}")
|
341
|
+
end
|
342
|
+
end
|
343
|
+
end
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
def port_name_for(global_service, service)
|
348
|
+
"#{global_service}#{service.to_s.camelize}Port"
|
349
|
+
end
|
350
|
+
|
351
|
+
def binding_name_for(global_service, service)
|
352
|
+
"#{global_service}#{service.to_s.camelize}Binding"
|
353
|
+
end
|
354
|
+
|
355
|
+
def message_name_for(api_name, message_name)
|
356
|
+
mode = web_service_dispatching_mode
|
357
|
+
if mode == :layered || mode == :delegated
|
358
|
+
api_name.to_s + '-' + message_name
|
359
|
+
else
|
360
|
+
message_name
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
def register_api(api, marshaler)
|
365
|
+
bindings = {}
|
366
|
+
traverse_custom_types(api, marshaler, bindings) do |binding|
|
367
|
+
bindings[binding] = nil unless bindings.has_key?(binding)
|
368
|
+
element_binding = binding.element_binding
|
369
|
+
bindings[element_binding] = nil if element_binding && !bindings.has_key?(element_binding)
|
370
|
+
end
|
371
|
+
bindings.keys
|
372
|
+
end
|
373
|
+
|
374
|
+
def traverse_custom_types(api, marshaler, bindings, &block)
|
375
|
+
api.api_methods.each do |name, method|
|
376
|
+
expects, returns = method.expects, method.returns
|
377
|
+
expects.each{ |type| traverse_type(marshaler, type, bindings, &block) if type.custom? } if expects
|
378
|
+
returns.each{ |type| traverse_type(marshaler, type, bindings, &block) if type.custom? } if returns
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
def traverse_type(marshaler, type, bindings, &block)
|
383
|
+
binding = marshaler.register_type(type)
|
384
|
+
return if bindings.has_key?(binding)
|
385
|
+
bindings[binding] = nil
|
386
|
+
yield binding
|
387
|
+
if type.array?
|
388
|
+
yield marshaler.register_type(type.element_type)
|
389
|
+
type = type.element_type
|
390
|
+
end
|
391
|
+
type.each_member{ |name, type| traverse_type(marshaler, type, bindings, &block) } if type.structured?
|
392
|
+
end
|
393
|
+
end
|
394
|
+
end
|
395
|
+
end
|
396
|
+
end
|
@@ -0,0 +1,202 @@
|
|
1
|
+
module ActionWebService # :nodoc:
|
2
|
+
module Invocation # :nodoc:
|
3
|
+
class InvocationError < ActionWebService::ActionWebServiceError # :nodoc:
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.included(base) # :nodoc:
|
7
|
+
base.extend(ClassMethods)
|
8
|
+
base.send(:include, ActionWebService::Invocation::InstanceMethods)
|
9
|
+
end
|
10
|
+
|
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
|
51
|
+
|
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
|
60
|
+
|
61
|
+
alias :before_invocation :append_before_invocation
|
62
|
+
|
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
|
71
|
+
|
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
|
80
|
+
|
81
|
+
alias :after_invocation :append_after_invocation
|
82
|
+
|
83
|
+
def before_invocation_interceptors # :nodoc:
|
84
|
+
read_inheritable_attribute("before_invocation_interceptors")
|
85
|
+
end
|
86
|
+
|
87
|
+
def after_invocation_interceptors # :nodoc:
|
88
|
+
read_inheritable_attribute("after_invocation_interceptors")
|
89
|
+
end
|
90
|
+
|
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
|
98
|
+
|
99
|
+
private
|
100
|
+
def append_interceptors_to_chain(condition, interceptors)
|
101
|
+
write_inheritable_array("#{condition}_invocation_interceptors", interceptors)
|
102
|
+
end
|
103
|
+
|
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
|
108
|
+
|
109
|
+
def extract_conditions!(interceptors)
|
110
|
+
return nil unless interceptors.last.is_a? Hash
|
111
|
+
interceptors.pop
|
112
|
+
end
|
113
|
+
|
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
|
120
|
+
|
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
|
125
|
+
|
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
|
132
|
+
|
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
|
139
|
+
|
140
|
+
def perform_invocation(method_name, params)
|
141
|
+
send(method_name, *params)
|
142
|
+
end
|
143
|
+
|
144
|
+
def before_invocation(name, args, &block)
|
145
|
+
call_interceptors(self.class.before_invocation_interceptors, [name, args], &block)
|
146
|
+
end
|
147
|
+
|
148
|
+
def after_invocation(name, args, result)
|
149
|
+
call_interceptors(self.class.after_invocation_interceptors, [name, args, result])
|
150
|
+
end
|
151
|
+
|
152
|
+
private
|
153
|
+
|
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
|
183
|
+
|
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
|
191
|
+
|
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
|
202
|
+
end
|