pelle-actionwebservice 2.3.3

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.
Files changed (79) hide show
  1. data/CHANGELOG +320 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README +381 -0
  4. data/Rakefile +173 -0
  5. data/TODO +32 -0
  6. data/examples/googlesearch/README +143 -0
  7. data/examples/googlesearch/autoloading/google_search_api.rb +50 -0
  8. data/examples/googlesearch/autoloading/google_search_controller.rb +57 -0
  9. data/examples/googlesearch/delegated/google_search_service.rb +108 -0
  10. data/examples/googlesearch/delegated/search_controller.rb +7 -0
  11. data/examples/googlesearch/direct/google_search_api.rb +50 -0
  12. data/examples/googlesearch/direct/search_controller.rb +58 -0
  13. data/examples/metaWeblog/README +17 -0
  14. data/examples/metaWeblog/apis/blogger_api.rb +60 -0
  15. data/examples/metaWeblog/apis/blogger_service.rb +34 -0
  16. data/examples/metaWeblog/apis/meta_weblog_api.rb +67 -0
  17. data/examples/metaWeblog/apis/meta_weblog_service.rb +48 -0
  18. data/examples/metaWeblog/controllers/xmlrpc_controller.rb +16 -0
  19. data/generators/web_service/USAGE +28 -0
  20. data/generators/web_service/templates/api_definition.rb +5 -0
  21. data/generators/web_service/templates/controller.rb +8 -0
  22. data/generators/web_service/templates/functional_test.rb +19 -0
  23. data/generators/web_service/web_service_generator.rb +29 -0
  24. data/lib/action_web_service/api.rb +297 -0
  25. data/lib/action_web_service/base.rb +38 -0
  26. data/lib/action_web_service/casting.rb +149 -0
  27. data/lib/action_web_service/client/base.rb +28 -0
  28. data/lib/action_web_service/client/soap_client.rb +113 -0
  29. data/lib/action_web_service/client/xmlrpc_client.rb +58 -0
  30. data/lib/action_web_service/client.rb +3 -0
  31. data/lib/action_web_service/container/action_controller_container.rb +93 -0
  32. data/lib/action_web_service/container/delegated_container.rb +86 -0
  33. data/lib/action_web_service/container/direct_container.rb +69 -0
  34. data/lib/action_web_service/container.rb +3 -0
  35. data/lib/action_web_service/dispatcher/abstract.rb +207 -0
  36. data/lib/action_web_service/dispatcher/action_controller_dispatcher.rb +379 -0
  37. data/lib/action_web_service/dispatcher.rb +2 -0
  38. data/lib/action_web_service/invocation.rb +202 -0
  39. data/lib/action_web_service/protocol/abstract.rb +112 -0
  40. data/lib/action_web_service/protocol/discovery.rb +37 -0
  41. data/lib/action_web_service/protocol/soap_protocol/marshaler.rb +242 -0
  42. data/lib/action_web_service/protocol/soap_protocol.rb +176 -0
  43. data/lib/action_web_service/protocol/xmlrpc_protocol.rb +122 -0
  44. data/lib/action_web_service/protocol.rb +4 -0
  45. data/lib/action_web_service/scaffolding.rb +281 -0
  46. data/lib/action_web_service/struct.rb +64 -0
  47. data/lib/action_web_service/support/class_inheritable_options.rb +26 -0
  48. data/lib/action_web_service/support/signature_types.rb +226 -0
  49. data/lib/action_web_service/templates/scaffolds/layout.html.erb +65 -0
  50. data/lib/action_web_service/templates/scaffolds/methods.html.erb +6 -0
  51. data/lib/action_web_service/templates/scaffolds/parameters.html.erb +29 -0
  52. data/lib/action_web_service/templates/scaffolds/result.html.erb +30 -0
  53. data/lib/action_web_service/test_invoke.rb +110 -0
  54. data/lib/action_web_service/version.rb +9 -0
  55. data/lib/action_web_service.rb +66 -0
  56. data/lib/actionwebservice.rb +1 -0
  57. data/setup.rb +1379 -0
  58. data/test/abstract_client.rb +183 -0
  59. data/test/abstract_dispatcher.rb +548 -0
  60. data/test/abstract_unit.rb +39 -0
  61. data/test/api_test.rb +102 -0
  62. data/test/apis/auto_load_api.rb +3 -0
  63. data/test/apis/broken_auto_load_api.rb +2 -0
  64. data/test/base_test.rb +42 -0
  65. data/test/casting_test.rb +94 -0
  66. data/test/client_soap_test.rb +155 -0
  67. data/test/client_xmlrpc_test.rb +153 -0
  68. data/test/container_test.rb +73 -0
  69. data/test/dispatcher_action_controller_soap_test.rb +138 -0
  70. data/test/dispatcher_action_controller_xmlrpc_test.rb +59 -0
  71. data/test/fixtures/db_definitions/mysql.sql +8 -0
  72. data/test/fixtures/users.yml +12 -0
  73. data/test/gencov +3 -0
  74. data/test/invocation_test.rb +185 -0
  75. data/test/run +6 -0
  76. data/test/scaffolded_controller_test.rb +146 -0
  77. data/test/struct_test.rb +52 -0
  78. data/test/test_invoke_test.rb +112 -0
  79. metadata +171 -0
@@ -0,0 +1,379 @@
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
+ dispatching_mode = web_service_dispatching_mode
176
+ global_service_name = wsdl_service_name
177
+ namespace = wsdl_namespace || 'urn:ActionWebService'
178
+ soap_action_base = "/#{controller_name}"
179
+
180
+ marshaler = ActionWebService::Protocol::Soap::SoapMarshaler.new(namespace)
181
+ apis = {}
182
+ case dispatching_mode
183
+ when :direct
184
+ api = self.class.web_service_api
185
+ web_service_name = controller_class_name.sub(/Controller$/, '').underscore
186
+ apis[web_service_name] = [api, register_api(api, marshaler)]
187
+ when :delegated, :layered
188
+ self.class.web_services.each do |web_service_name, info|
189
+ service = web_service_object(web_service_name)
190
+ api = service.class.web_service_api
191
+ apis[web_service_name] = [api, register_api(api, marshaler)]
192
+ end
193
+ end
194
+ custom_types = []
195
+ apis.values.each do |api, bindings|
196
+ bindings.each do |b|
197
+ custom_types << b unless custom_types.include?(b)
198
+ end
199
+ end
200
+
201
+ xm = Builder::XmlMarkup.new(:target => xml, :indent => 2)
202
+ xm.instruct!
203
+ xm.definitions('name' => wsdl_service_name,
204
+ 'targetNamespace' => namespace,
205
+ 'xmlns:typens' => namespace,
206
+ 'xmlns:xsd' => XsdNs,
207
+ 'xmlns:soap' => SoapNs,
208
+ 'xmlns:soapenc' => SoapEncodingNs,
209
+ 'xmlns:wsdl' => WsdlNs,
210
+ 'xmlns' => WsdlNs) do
211
+ # Generate XSD
212
+ if custom_types.size > 0
213
+ xm.types do
214
+ xm.xsd(:schema, 'xmlns' => XsdNs, 'targetNamespace' => namespace) do
215
+ custom_types.each do |binding|
216
+ case
217
+ when binding.type.array?
218
+ xm.xsd(:complexType, 'name' => binding.type_name) do
219
+ xm.xsd(:complexContent) do
220
+ xm.xsd(:restriction, 'base' => 'soapenc:Array') do
221
+ xm.xsd(:attribute, 'ref' => 'soapenc:arrayType',
222
+ 'wsdl:arrayType' => binding.element_binding.qualified_type_name('typens') + '[]')
223
+ end
224
+ end
225
+ end
226
+ when binding.type.structured?
227
+ xm.xsd(:complexType, 'name' => binding.type_name) do
228
+ xm.xsd(:all) do
229
+ binding.type.each_member do |name, type|
230
+ b = marshaler.register_type(type)
231
+ xm.xsd(:element, 'name' => name, 'type' => b.qualified_type_name('typens'))
232
+ end
233
+ end
234
+ end
235
+ end
236
+ end
237
+ end
238
+ end
239
+ end
240
+
241
+ # APIs
242
+ apis.each do |api_name, values|
243
+ api = values[0]
244
+ api.api_methods.each do |name, method|
245
+ gen = lambda do |msg_name, direction|
246
+ xm.message('name' => message_name_for(api_name, msg_name)) do
247
+ sym = nil
248
+ if direction == :out
249
+ returns = method.returns
250
+ if returns
251
+ binding = marshaler.register_type(returns[0])
252
+ xm.part('name' => 'return', 'type' => binding.qualified_type_name('typens'))
253
+ end
254
+ else
255
+ expects = method.expects
256
+ expects.each do |type|
257
+ binding = marshaler.register_type(type)
258
+ xm.part('name' => type.name, 'type' => binding.qualified_type_name('typens'))
259
+ end if expects
260
+ end
261
+ end
262
+ end
263
+ public_name = method.public_name
264
+ gen.call(public_name, :in)
265
+ gen.call("#{public_name}Response", :out)
266
+ end
267
+
268
+ # Port
269
+ port_name = port_name_for(global_service_name, api_name)
270
+ xm.portType('name' => port_name) do
271
+ api.api_methods.each do |name, method|
272
+ xm.operation('name' => method.public_name) do
273
+ xm.input('message' => "typens:" + message_name_for(api_name, method.public_name))
274
+ xm.output('message' => "typens:" + message_name_for(api_name, "#{method.public_name}Response"))
275
+ end
276
+ end
277
+ end
278
+
279
+ # Bind it
280
+ binding_name = binding_name_for(global_service_name, api_name)
281
+ xm.binding('name' => binding_name, 'type' => "typens:#{port_name}") do
282
+ xm.soap(:binding, 'style' => 'rpc', 'transport' => SoapHttpTransport)
283
+ api.api_methods.each do |name, method|
284
+ xm.operation('name' => method.public_name) do
285
+ case web_service_dispatching_mode
286
+ when :direct
287
+ soap_action = soap_action_base + "/api/" + method.public_name
288
+ when :delegated, :layered
289
+ soap_action = soap_action_base \
290
+ + "/" + api_name.to_s \
291
+ + "/" + method.public_name
292
+ end
293
+ xm.soap(:operation, 'soapAction' => soap_action)
294
+ xm.input do
295
+ xm.soap(:body,
296
+ 'use' => 'encoded',
297
+ 'namespace' => namespace,
298
+ 'encodingStyle' => SoapEncodingNs)
299
+ end
300
+ xm.output do
301
+ xm.soap(:body,
302
+ 'use' => 'encoded',
303
+ 'namespace' => namespace,
304
+ 'encodingStyle' => SoapEncodingNs)
305
+ end
306
+ end
307
+ end
308
+ end
309
+ end
310
+
311
+ # Define it
312
+ xm.service('name' => "#{global_service_name}Service") do
313
+ apis.each do |api_name, values|
314
+ port_name = port_name_for(global_service_name, api_name)
315
+ binding_name = binding_name_for(global_service_name, api_name)
316
+ case web_service_dispatching_mode
317
+ when :direct, :layered
318
+ binding_target = 'api'
319
+ when :delegated
320
+ binding_target = api_name.to_s
321
+ end
322
+ xm.port('name' => port_name, 'binding' => "typens:#{binding_name}") do
323
+ xm.soap(:address, 'location' => "#{base_uri}#{binding_target}")
324
+ end
325
+ end
326
+ end
327
+ end
328
+ end
329
+
330
+ def port_name_for(global_service, service)
331
+ "#{global_service}#{service.to_s.camelize}Port"
332
+ end
333
+
334
+ def binding_name_for(global_service, service)
335
+ "#{global_service}#{service.to_s.camelize}Binding"
336
+ end
337
+
338
+ def message_name_for(api_name, message_name)
339
+ mode = web_service_dispatching_mode
340
+ if mode == :layered || mode == :delegated
341
+ api_name.to_s + '-' + message_name
342
+ else
343
+ message_name
344
+ end
345
+ end
346
+
347
+ def register_api(api, marshaler)
348
+ bindings = {}
349
+ traverse_custom_types(api, marshaler, bindings) do |binding|
350
+ bindings[binding] = nil unless bindings.has_key?(binding)
351
+ element_binding = binding.element_binding
352
+ bindings[element_binding] = nil if element_binding && !bindings.has_key?(element_binding)
353
+ end
354
+ bindings.keys
355
+ end
356
+
357
+ def traverse_custom_types(api, marshaler, bindings, &block)
358
+ api.api_methods.each do |name, method|
359
+ expects, returns = method.expects, method.returns
360
+ expects.each{ |type| traverse_type(marshaler, type, bindings, &block) if type.custom? } if expects
361
+ returns.each{ |type| traverse_type(marshaler, type, bindings, &block) if type.custom? } if returns
362
+ end
363
+ end
364
+
365
+ def traverse_type(marshaler, type, bindings, &block)
366
+ binding = marshaler.register_type(type)
367
+ return if bindings.has_key?(binding)
368
+ bindings[binding] = nil
369
+ yield binding
370
+ if type.array?
371
+ yield marshaler.register_type(type.element_type)
372
+ type = type.element_type
373
+ end
374
+ type.each_member{ |name, type| traverse_type(marshaler, type, bindings, &block) } if type.structured?
375
+ end
376
+ end
377
+ end
378
+ end
379
+ end
@@ -0,0 +1,2 @@
1
+ require 'action_web_service/dispatcher/abstract'
2
+ require 'action_web_service/dispatcher/action_controller_dispatcher'
@@ -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
@@ -0,0 +1,112 @@
1
+ module ActionWebService # :nodoc:
2
+ module Protocol # :nodoc:
3
+ class ProtocolError < ActionWebServiceError # :nodoc:
4
+ end
5
+
6
+ class AbstractProtocol # :nodoc:
7
+ def setup(controller)
8
+ end
9
+
10
+ def decode_action_pack_request(action_pack_request)
11
+ end
12
+
13
+ def encode_action_pack_request(service_name, public_method_name, raw_body, options={})
14
+ klass = options[:request_class] || SimpleActionPackRequest
15
+ request = klass.new({})
16
+ request.request_parameters['action'] = service_name.to_s
17
+ request.env['RAW_POST_DATA'] = raw_body
18
+ request.env['REQUEST_METHOD'] = 'POST'
19
+ request.env['HTTP_CONTENT_TYPE'] = 'text/xml'
20
+ request
21
+ end
22
+
23
+ def decode_request(raw_request, service_name, protocol_options={})
24
+ end
25
+
26
+ def encode_request(method_name, params, param_types)
27
+ end
28
+
29
+ def decode_response(raw_response)
30
+ end
31
+
32
+ def encode_response(method_name, return_value, return_type, protocol_options={})
33
+ end
34
+
35
+ def protocol_client(api, protocol_name, endpoint_uri, options)
36
+ end
37
+
38
+ def register_api(api)
39
+ end
40
+ end
41
+
42
+ class Request # :nodoc:
43
+ attr :protocol
44
+ attr_accessor :method_name
45
+ attr_accessor :method_params
46
+ attr :service_name
47
+ attr_accessor :api
48
+ attr_accessor :api_method
49
+ attr :protocol_options
50
+
51
+ def initialize(protocol, method_name, method_params, service_name, api=nil, api_method=nil, protocol_options=nil)
52
+ @protocol = protocol
53
+ @method_name = method_name
54
+ @method_params = method_params
55
+ @service_name = service_name
56
+ @api = api
57
+ @api_method = api_method
58
+ @protocol_options = protocol_options || {}
59
+ end
60
+ end
61
+
62
+ class Response # :nodoc:
63
+ attr :body
64
+ attr :content_type
65
+ attr :return_value
66
+
67
+ def initialize(body, content_type, return_value)
68
+ @body = body
69
+ @content_type = content_type
70
+ @return_value = return_value
71
+ end
72
+ end
73
+
74
+ class SimpleActionPackRequest < ActionController::Request # :nodoc:
75
+ def initialize(env = {})
76
+ @env = env
77
+ @qparams = {}
78
+ @rparams = {}
79
+ @cookies = {}
80
+ reset_session
81
+ end
82
+
83
+ def query_parameters
84
+ @qparams
85
+ end
86
+
87
+ def request_parameters
88
+ @rparams
89
+ end
90
+
91
+ def env
92
+ @env
93
+ end
94
+
95
+ def host
96
+ ''
97
+ end
98
+
99
+ def cookies
100
+ @cookies
101
+ end
102
+
103
+ def session
104
+ @session
105
+ end
106
+
107
+ def reset_session
108
+ @session = {}
109
+ end
110
+ end
111
+ end
112
+ end