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.
Files changed (85) hide show
  1. data/CHANGELOG +335 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README +381 -0
  4. data/Rakefile +180 -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/acts_as_web_service.rb +24 -0
  25. data/lib/action_web_service/api.rb +297 -0
  26. data/lib/action_web_service/base.rb +38 -0
  27. data/lib/action_web_service/casting.rb +151 -0
  28. data/lib/action_web_service/client/base.rb +28 -0
  29. data/lib/action_web_service/client/soap_client.rb +113 -0
  30. data/lib/action_web_service/client/xmlrpc_client.rb +58 -0
  31. data/lib/action_web_service/client.rb +3 -0
  32. data/lib/action_web_service/container/action_controller_container.rb +93 -0
  33. data/lib/action_web_service/container/delegated_container.rb +86 -0
  34. data/lib/action_web_service/container/direct_container.rb +69 -0
  35. data/lib/action_web_service/container.rb +3 -0
  36. data/lib/action_web_service/dispatcher/abstract.rb +208 -0
  37. data/lib/action_web_service/dispatcher/action_controller_dispatcher.rb +396 -0
  38. data/lib/action_web_service/dispatcher.rb +2 -0
  39. data/lib/action_web_service/invocation.rb +202 -0
  40. data/lib/action_web_service/protocol/abstract.rb +112 -0
  41. data/lib/action_web_service/protocol/discovery.rb +37 -0
  42. data/lib/action_web_service/protocol/soap_protocol/marshaler.rb +242 -0
  43. data/lib/action_web_service/protocol/soap_protocol.rb +176 -0
  44. data/lib/action_web_service/protocol/xmlrpc_protocol.rb +123 -0
  45. data/lib/action_web_service/protocol.rb +4 -0
  46. data/lib/action_web_service/scaffolding.rb +281 -0
  47. data/lib/action_web_service/simple.rb +53 -0
  48. data/lib/action_web_service/string_to_datetime_for_soap.rb +16 -0
  49. data/lib/action_web_service/struct.rb +68 -0
  50. data/lib/action_web_service/support/class_inheritable_options.rb +26 -0
  51. data/lib/action_web_service/support/signature_types.rb +261 -0
  52. data/lib/action_web_service/templates/scaffolds/layout.html.erb +65 -0
  53. data/lib/action_web_service/templates/scaffolds/methods.html.erb +6 -0
  54. data/lib/action_web_service/templates/scaffolds/parameters.html.erb +29 -0
  55. data/lib/action_web_service/templates/scaffolds/result.html.erb +30 -0
  56. data/lib/action_web_service/test_invoke.rb +110 -0
  57. data/lib/action_web_service/version.rb +9 -0
  58. data/lib/action_web_service.rb +60 -0
  59. data/lib/actionwebservice.rb +1 -0
  60. data/setup.rb +1379 -0
  61. data/test/abstract_client.rb +184 -0
  62. data/test/abstract_dispatcher.rb +549 -0
  63. data/test/abstract_unit.rb +43 -0
  64. data/test/actionwebservice_unittest.db +0 -0
  65. data/test/api_test.rb +102 -0
  66. data/test/apis/auto_load_api.rb +3 -0
  67. data/test/apis/broken_auto_load_api.rb +2 -0
  68. data/test/base_test.rb +42 -0
  69. data/test/casting_test.rb +95 -0
  70. data/test/client_soap_test.rb +156 -0
  71. data/test/client_xmlrpc_test.rb +154 -0
  72. data/test/container_test.rb +75 -0
  73. data/test/debug.log +12305 -0
  74. data/test/dispatcher_action_controller_soap_test.rb +139 -0
  75. data/test/dispatcher_action_controller_xmlrpc_test.rb +59 -0
  76. data/test/fixtures/db_definitions/mysql.sql +8 -0
  77. data/test/fixtures/db_definitions/sqlite3.sql +8 -0
  78. data/test/fixtures/users.yml +12 -0
  79. data/test/gencov +3 -0
  80. data/test/invocation_test.rb +186 -0
  81. data/test/run +6 -0
  82. data/test/scaffolded_controller_test.rb +147 -0
  83. data/test/struct_test.rb +84 -0
  84. data/test/test_invoke_test.rb +113 -0
  85. metadata +182 -0
@@ -0,0 +1,28 @@
1
+ module ActionWebService # :nodoc:
2
+ module Client # :nodoc:
3
+ class ClientError < StandardError # :nodoc:
4
+ end
5
+
6
+ class Base # :nodoc:
7
+ def initialize(api, endpoint_uri)
8
+ @api = api
9
+ @endpoint_uri = endpoint_uri
10
+ end
11
+
12
+ def method_missing(name, *args) # :nodoc:
13
+ call_name = method_name(name)
14
+ return super(name, *args) if call_name.nil?
15
+ self.perform_invocation(call_name, args)
16
+ end
17
+
18
+ private
19
+ def method_name(name)
20
+ if @api.has_api_method?(name.to_sym)
21
+ name.to_s
22
+ elsif @api.has_public_api_method?(name.to_s)
23
+ @api.api_method_name(name.to_s).to_s
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,113 @@
1
+ require 'soap/rpc/driver'
2
+ require 'uri'
3
+
4
+ module ActionWebService # :nodoc:
5
+ module Client # :nodoc:
6
+
7
+ # Implements SOAP client support (using RPC encoding for the messages).
8
+ #
9
+ # ==== Example Usage
10
+ #
11
+ # class PersonAPI < ActionWebService::API::Base
12
+ # api_method :find_all, :returns => [[Person]]
13
+ # end
14
+ #
15
+ # soap_client = ActionWebService::Client::Soap.new(PersonAPI, "http://...")
16
+ # persons = soap_client.find_all
17
+ #
18
+ class Soap < Base
19
+ # provides access to the underlying soap driver
20
+ attr_reader :driver
21
+
22
+ # Creates a new web service client using the SOAP RPC protocol.
23
+ #
24
+ # +api+ must be an ActionWebService::API::Base derivative, and
25
+ # +endpoint_uri+ must point at the relevant URL to which protocol requests
26
+ # will be sent with HTTP POST.
27
+ #
28
+ # Valid options:
29
+ # [<tt>:namespace</tt>] If the remote server has used a custom namespace to
30
+ # declare its custom types, you can specify it here. This would
31
+ # be the namespace declared with a [WebService(Namespace = "http://namespace")] attribute
32
+ # in .NET, for example.
33
+ # [<tt>:driver_options</tt>] If you want to supply any custom SOAP RPC driver
34
+ # options, you can provide them as a Hash here
35
+ #
36
+ # The <tt>:driver_options</tt> option can be used to configure the backend SOAP
37
+ # RPC driver. An example of configuring the SOAP backend to do
38
+ # client-certificate authenticated SSL connections to the server:
39
+ #
40
+ # opts = {}
41
+ # opts['protocol.http.ssl_config.verify_mode'] = 'OpenSSL::SSL::VERIFY_PEER'
42
+ # opts['protocol.http.ssl_config.client_cert'] = client_cert_file_path
43
+ # opts['protocol.http.ssl_config.client_key'] = client_key_file_path
44
+ # opts['protocol.http.ssl_config.ca_file'] = ca_cert_file_path
45
+ # client = ActionWebService::Client::Soap.new(api, 'https://some/service', :driver_options => opts)
46
+ def initialize(api, endpoint_uri, options={})
47
+ super(api, endpoint_uri)
48
+ @namespace = options[:namespace] || 'urn:ActionWebService'
49
+ @driver_options = options[:driver_options] || {}
50
+ @protocol = ActionWebService::Protocol::Soap::SoapProtocol.new @namespace
51
+ @soap_action_base = options[:soap_action_base]
52
+ @soap_action_base ||= URI.parse(endpoint_uri).path
53
+ @driver = create_soap_rpc_driver(api, endpoint_uri)
54
+ @driver_options.each do |name, value|
55
+ @driver.options[name.to_s] = value
56
+ end
57
+ end
58
+
59
+ protected
60
+ def perform_invocation(method_name, args)
61
+ method = @api.api_methods[method_name.to_sym]
62
+ args = method.cast_expects(args.dup) rescue args
63
+ return_value = @driver.send(method_name, *args)
64
+ method.cast_returns(return_value.dup) rescue return_value
65
+ end
66
+
67
+ def soap_action(method_name)
68
+ "#{@soap_action_base}/#{method_name}"
69
+ end
70
+
71
+ private
72
+ def create_soap_rpc_driver(api, endpoint_uri)
73
+ @protocol.register_api(api)
74
+ driver = SoapDriver.new(endpoint_uri, nil)
75
+ driver.mapping_registry = @protocol.marshaler.registry
76
+ api.api_methods.each do |name, method|
77
+ qname = XSD::QName.new(@namespace, method.public_name)
78
+ action = soap_action(method.public_name)
79
+ expects = method.expects
80
+ returns = method.returns
81
+ param_def = []
82
+ if expects
83
+ expects.each do |type|
84
+ type_binding = @protocol.marshaler.lookup_type(type)
85
+ if SOAP::Version >= "1.5.5"
86
+ param_def << ['in', type.name.to_s, [type_binding.type.type_class.to_s]]
87
+ else
88
+ param_def << ['in', type.name, type_binding.mapping]
89
+ end
90
+ end
91
+ end
92
+ if returns
93
+ type_binding = @protocol.marshaler.lookup_type(returns[0])
94
+ if SOAP::Version >= "1.5.5"
95
+ param_def << ['retval', 'return', [type_binding.type.type_class.to_s]]
96
+ else
97
+ param_def << ['retval', 'return', type_binding.mapping]
98
+ end
99
+ end
100
+ driver.add_method(qname, action, method.name.to_s, param_def)
101
+ end
102
+ driver
103
+ end
104
+
105
+ class SoapDriver < SOAP::RPC::Driver # :nodoc:
106
+ def add_method(qname, soapaction, name, param_def)
107
+ @proxy.add_rpc_method(qname, soapaction, name, param_def)
108
+ add_rpc_method_interface(name, param_def)
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,58 @@
1
+ require 'uri'
2
+ require 'xmlrpc/client'
3
+
4
+ module ActionWebService # :nodoc:
5
+ module Client # :nodoc:
6
+
7
+ # Implements XML-RPC client support
8
+ #
9
+ # ==== Example Usage
10
+ #
11
+ # class BloggerAPI < ActionWebService::API::Base
12
+ # inflect_names false
13
+ # api_method :getRecentPosts, :returns => [[Blog::Post]]
14
+ # end
15
+ #
16
+ # blog = ActionWebService::Client::XmlRpc.new(BloggerAPI, "http://.../RPC", :handler_name => "blogger")
17
+ # posts = blog.getRecentPosts
18
+ class XmlRpc < Base
19
+
20
+ # Creates a new web service client using the XML-RPC protocol.
21
+ #
22
+ # +api+ must be an ActionWebService::API::Base derivative, and
23
+ # +endpoint_uri+ must point at the relevant URL to which protocol requests
24
+ # will be sent with HTTP POST.
25
+ #
26
+ # Valid options:
27
+ # [<tt>:handler_name</tt>] If the remote server defines its services inside special
28
+ # handler (the Blogger API uses a <tt>"blogger"</tt> handler name for example),
29
+ # provide it here, or your method calls will fail
30
+ def initialize(api, endpoint_uri, options={})
31
+ @api = api
32
+ @handler_name = options[:handler_name]
33
+ @protocol = ActionWebService::Protocol::XmlRpc::XmlRpcProtocol.new
34
+ @client = XMLRPC::Client.new2(endpoint_uri, options[:proxy], options[:timeout])
35
+ end
36
+
37
+ protected
38
+ def perform_invocation(method_name, args)
39
+ method = @api.api_methods[method_name.to_sym]
40
+ if method.expects && method.expects.length != args.length
41
+ raise(ArgumentError, "#{method.public_name}: wrong number of arguments (#{args.length} for #{method.expects.length})")
42
+ end
43
+ args = method.cast_expects(args.dup) rescue args
44
+ if method.expects
45
+ method.expects.each_with_index{ |type, i| args[i] = @protocol.value_to_xmlrpc_wire_format(args[i], type) }
46
+ end
47
+ ok, return_value = @client.call2(public_name(method_name), *args)
48
+ return (method.cast_returns(return_value.dup) rescue return_value) if ok
49
+ raise(ClientError, "#{return_value.faultCode}: #{return_value.faultString}")
50
+ end
51
+
52
+ def public_name(method_name)
53
+ public_name = @api.public_api_method_name(method_name)
54
+ @handler_name ? "#{@handler_name}.#{public_name}" : public_name
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,3 @@
1
+ require 'action_web_service/client/base'
2
+ require 'action_web_service/client/soap_client'
3
+ require 'action_web_service/client/xmlrpc_client'
@@ -0,0 +1,93 @@
1
+ module ActionWebService # :nodoc:
2
+ module Container # :nodoc:
3
+ module ActionController # :nodoc:
4
+ def self.included(base) # :nodoc:
5
+ class << base
6
+ include ClassMethods
7
+ alias_method_chain :inherited, :api
8
+ alias_method_chain :web_service_api, :require
9
+ end
10
+ end
11
+
12
+ module ClassMethods
13
+ # Creates a client for accessing remote web services, using the
14
+ # given +protocol+ to communicate with the +endpoint_uri+.
15
+ #
16
+ # ==== Example
17
+ #
18
+ # class MyController < ActionController::Base
19
+ # web_client_api :blogger, :xmlrpc, "http://blogger.com/myblog/api/RPC2", :handler_name => 'blogger'
20
+ # end
21
+ #
22
+ # In this example, a protected method named <tt>blogger</tt> will
23
+ # now exist on the controller, and calling it will return the
24
+ # XML-RPC client object for working with that remote service.
25
+ #
26
+ # +options+ is the set of protocol client specific options (see
27
+ # a protocol client class for details).
28
+ #
29
+ # If your API definition does not exist on the load path with the
30
+ # correct rules for it to be found using +name+, you can pass in
31
+ # the API definition class via +options+, using a key of <tt>:api</tt>
32
+ def web_client_api(name, protocol, endpoint_uri, options={})
33
+ unless method_defined?(name)
34
+ api_klass = options.delete(:api) || require_web_service_api(name)
35
+ class_eval do
36
+ define_method(name) do
37
+ create_web_service_client(api_klass, protocol, endpoint_uri, options)
38
+ end
39
+ protected name
40
+ end
41
+ end
42
+ end
43
+
44
+ def web_service_api_with_require(definition=nil) # :nodoc:
45
+ return web_service_api_without_require if definition.nil?
46
+ case definition
47
+ when String, Symbol
48
+ klass = require_web_service_api(definition)
49
+ else
50
+ klass = definition
51
+ end
52
+ web_service_api_without_require(klass)
53
+ end
54
+
55
+ def require_web_service_api(name) # :nodoc:
56
+ case name
57
+ when String, Symbol
58
+ file_name = name.to_s.underscore + "_api"
59
+ class_name = file_name.camelize
60
+ class_names = [class_name, class_name.sub(/Api$/, 'API')]
61
+ begin
62
+ require_dependency(file_name)
63
+ rescue LoadError => load_error
64
+ requiree = / -- (.*?)(\.rb)?$/.match(load_error.message).to_a[1]
65
+ msg = requiree == file_name ? "Missing API definition file in apis/#{file_name}.rb" : "Can't load file: #{requiree}"
66
+ raise LoadError.new(msg).copy_blame!(load_error)
67
+ end
68
+ klass = nil
69
+ class_names.each do |name|
70
+ klass = name.constantize rescue nil
71
+ break unless klass.nil?
72
+ end
73
+ unless klass
74
+ raise(NameError, "neither #{class_names[0]} or #{class_names[1]} found")
75
+ end
76
+ klass
77
+ else
78
+ raise(ArgumentError, "expected String or Symbol argument")
79
+ end
80
+ end
81
+
82
+ private
83
+ def inherited_with_api(child)
84
+ inherited_without_api(child)
85
+ begin child.web_service_api(child.controller_path)
86
+ rescue MissingSourceFile => e
87
+ raise unless e.is_missing?("apis/#{child.controller_path}_api")
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,86 @@
1
+ module ActionWebService # :nodoc:
2
+ module Container # :nodoc:
3
+ module Delegated # :nodoc:
4
+ class ContainerError < ActionWebServiceError # :nodoc:
5
+ end
6
+
7
+ def self.included(base) # :nodoc:
8
+ base.extend(ClassMethods)
9
+ base.send(:include, ActionWebService::Container::Delegated::InstanceMethods)
10
+ end
11
+
12
+ module ClassMethods
13
+ # Declares a web service that will provide access to the API of the given
14
+ # +object+. +object+ must be an ActionWebService::Base derivative.
15
+ #
16
+ # Web service object creation can either be _immediate_, where the object
17
+ # instance is given at class definition time, or _deferred_, where
18
+ # object instantiation is delayed until request time.
19
+ #
20
+ # ==== Immediate web service object example
21
+ #
22
+ # class ApiController < ApplicationController
23
+ # web_service_dispatching_mode :delegated
24
+ #
25
+ # web_service :person, PersonService.new
26
+ # end
27
+ #
28
+ # For deferred instantiation, a block should be given instead of an
29
+ # object instance. This block will be executed in controller instance
30
+ # context, so it can rely on controller instance variables being present.
31
+ #
32
+ # ==== Deferred web service object example
33
+ #
34
+ # class ApiController < ApplicationController
35
+ # web_service_dispatching_mode :delegated
36
+ #
37
+ # web_service(:person) { PersonService.new(request.env) }
38
+ # end
39
+ def web_service(name, object=nil, &block)
40
+ if (object && block_given?) || (object.nil? && block.nil?)
41
+ raise(ContainerError, "either service, or a block must be given")
42
+ end
43
+ name = name.to_sym
44
+ if block_given?
45
+ info = { name => { :block => block } }
46
+ else
47
+ info = { name => { :object => object } }
48
+ end
49
+ write_inheritable_hash("web_services", info)
50
+ call_web_service_definition_callbacks(self, name, info)
51
+ end
52
+
53
+ # Whether this service contains a service with the given +name+
54
+ def has_web_service?(name)
55
+ web_services.has_key?(name.to_sym)
56
+ end
57
+
58
+ def web_services # :nodoc:
59
+ read_inheritable_attribute("web_services") || {}
60
+ end
61
+
62
+ def add_web_service_definition_callback(&block) # :nodoc:
63
+ write_inheritable_array("web_service_definition_callbacks", [block])
64
+ end
65
+
66
+ private
67
+ def call_web_service_definition_callbacks(container_class, web_service_name, service_info)
68
+ (read_inheritable_attribute("web_service_definition_callbacks") || []).each do |block|
69
+ block.call(container_class, web_service_name, service_info)
70
+ end
71
+ end
72
+ end
73
+
74
+ module InstanceMethods # :nodoc:
75
+ def web_service_object(web_service_name)
76
+ info = self.class.web_services[web_service_name.to_sym]
77
+ unless info
78
+ raise(ContainerError, "no such web service '#{web_service_name}'")
79
+ end
80
+ service = info[:block]
81
+ service ? self.instance_eval(&service) : info[:object]
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,69 @@
1
+ module ActionWebService # :nodoc:
2
+ module Container # :nodoc:
3
+ module Direct # :nodoc:
4
+ class ContainerError < ActionWebServiceError # :nodoc:
5
+ end
6
+
7
+ def self.included(base) # :nodoc:
8
+ base.extend(ClassMethods)
9
+ end
10
+
11
+ module ClassMethods
12
+ # Attaches ActionWebService API +definition+ to the calling class.
13
+ #
14
+ # Action Controllers can have a default associated API, removing the need
15
+ # to call this method if you follow the Action Web Service naming conventions.
16
+ #
17
+ # A controller with a class name of GoogleSearchController will
18
+ # implicitly load <tt>app/apis/google_search_api.rb</tt>, and expect the
19
+ # API definition class to be named <tt>GoogleSearchAPI</tt> or
20
+ # <tt>GoogleSearchApi</tt>.
21
+ #
22
+ # ==== Service class example
23
+ #
24
+ # class MyService < ActionWebService::Base
25
+ # web_service_api MyAPI
26
+ # end
27
+ #
28
+ # class MyAPI < ActionWebService::API::Base
29
+ # ...
30
+ # end
31
+ #
32
+ # ==== Controller class example
33
+ #
34
+ # class MyController < ActionController::Base
35
+ # web_service_api MyAPI
36
+ # end
37
+ #
38
+ # class MyAPI < ActionWebService::API::Base
39
+ # ...
40
+ # end
41
+ def web_service_api(definition=nil)
42
+ if definition.nil?
43
+ read_inheritable_attribute("web_service_api")
44
+ else
45
+ if definition.is_a?(Symbol)
46
+ raise(ContainerError, "symbols can only be used for #web_service_api inside of a controller")
47
+ end
48
+ unless definition.respond_to?(:ancestors) && definition.ancestors.include?(ActionWebService::API::Base)
49
+ raise(ContainerError, "#{definition.to_s} is not a valid API definition")
50
+ end
51
+ write_inheritable_attribute("web_service_api", definition)
52
+ call_web_service_api_callbacks(self, definition)
53
+ end
54
+ end
55
+
56
+ def add_web_service_api_callback(&block) # :nodoc:
57
+ write_inheritable_array("web_service_api_callbacks", [block])
58
+ end
59
+
60
+ private
61
+ def call_web_service_api_callbacks(container_class, definition)
62
+ (read_inheritable_attribute("web_service_api_callbacks") || []).each do |block|
63
+ block.call(container_class, definition)
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,3 @@
1
+ require 'action_web_service/container/direct_container'
2
+ require 'action_web_service/container/delegated_container'
3
+ require 'action_web_service/container/action_controller_container'
@@ -0,0 +1,208 @@
1
+ require 'benchmark'
2
+
3
+ module ActionWebService # :nodoc:
4
+ module Dispatcher # :nodoc:
5
+ class DispatcherError < ActionWebService::ActionWebServiceError # :nodoc:
6
+ def initialize(*args)
7
+ super
8
+ set_backtrace(caller)
9
+ end
10
+ end
11
+
12
+ def self.included(base) # :nodoc:
13
+ base.class_inheritable_option(:web_service_dispatching_mode, :direct)
14
+ base.class_inheritable_option(:web_service_inflect_type, false)
15
+ base.class_inheritable_option(:web_service_exception_reporting, true)
16
+ base.send(:include, ActionWebService::Dispatcher::InstanceMethods)
17
+ end
18
+
19
+ module InstanceMethods # :nodoc:
20
+ private
21
+ def invoke_web_service_request(protocol_request)
22
+ invocation = web_service_invocation(protocol_request)
23
+ if invocation.is_a?(Array) && protocol_request.protocol.is_a?(Protocol::XmlRpc::XmlRpcProtocol)
24
+ xmlrpc_multicall_invoke(invocation)
25
+ else
26
+ web_service_invoke(invocation)
27
+ end
28
+ end
29
+
30
+ def web_service_direct_invoke(invocation)
31
+ @method_params = invocation.method_ordered_params
32
+ arity = method(invocation.api_method.name).arity rescue 0
33
+ if arity < 0 || arity > 0
34
+ params = @method_params
35
+ else
36
+ params = []
37
+ end
38
+ web_service_filtered_invoke(invocation, params)
39
+ end
40
+
41
+ def web_service_delegated_invoke(invocation)
42
+ web_service_filtered_invoke(invocation, invocation.method_ordered_params)
43
+ end
44
+
45
+ def web_service_filtered_invoke(invocation, params)
46
+ cancellation_reason = nil
47
+ return_value = invocation.service.perform_invocation(invocation.api_method.name, params) do |x|
48
+ cancellation_reason = x
49
+ end
50
+ if cancellation_reason
51
+ raise(DispatcherError, "request canceled: #{cancellation_reason}")
52
+ end
53
+ return_value
54
+ end
55
+
56
+ def web_service_invoke(invocation)
57
+ case web_service_dispatching_mode
58
+ when :direct
59
+ return_value = web_service_direct_invoke(invocation)
60
+ when :delegated, :layered
61
+ return_value = web_service_delegated_invoke(invocation)
62
+ end
63
+ web_service_create_response(invocation.protocol, invocation.protocol_options, invocation.api, invocation.api_method, return_value)
64
+ end
65
+
66
+ def xmlrpc_multicall_invoke(invocations)
67
+ responses = []
68
+ invocations.each do |invocation|
69
+ if invocation.is_a?(Hash)
70
+ responses << [invocation, nil]
71
+ next
72
+ end
73
+ begin
74
+ case web_service_dispatching_mode
75
+ when :direct
76
+ return_value = web_service_direct_invoke(invocation)
77
+ when :delegated, :layered
78
+ return_value = web_service_delegated_invoke(invocation)
79
+ end
80
+ api_method = invocation.api_method
81
+ if invocation.api.has_api_method?(api_method.name)
82
+ response_type = (api_method.returns ? api_method.returns[0] : nil)
83
+ return_value = api_method.cast_returns(return_value)
84
+ else
85
+ response_type = ActionWebService::SignatureTypes.canonical_signature_entry(return_value.class, 0)
86
+ end
87
+ responses << [return_value, response_type]
88
+ rescue Exception => e
89
+ responses << [{ 'faultCode' => 3, 'faultString' => e.message }, nil]
90
+ end
91
+ end
92
+ invocation = invocations[0]
93
+ invocation.protocol.encode_multicall_response(responses, invocation.protocol_options)
94
+ end
95
+
96
+ def web_service_invocation(request, level = 0)
97
+ public_method_name = request.method_name
98
+ invocation = Invocation.new
99
+ invocation.protocol = request.protocol
100
+ invocation.protocol_options = request.protocol_options
101
+ invocation.service_name = request.service_name
102
+ if web_service_dispatching_mode == :layered
103
+ case invocation.protocol
104
+ when Protocol::Soap::SoapProtocol
105
+ soap_action = request.protocol_options[:soap_action]
106
+ if soap_action && soap_action =~ /^\/\w+\/(\w+)\//
107
+ invocation.service_name = $1
108
+ end
109
+ when Protocol::XmlRpc::XmlRpcProtocol
110
+ if request.method_name =~ /^([^\.]+)\.(.*)$/
111
+ public_method_name = $2
112
+ invocation.service_name = $1
113
+ end
114
+ end
115
+ end
116
+ if invocation.protocol.is_a? Protocol::XmlRpc::XmlRpcProtocol
117
+ if public_method_name == 'multicall' && invocation.service_name == 'system'
118
+ if level > 0
119
+ raise(DispatcherError, "Recursive system.multicall invocations not allowed")
120
+ end
121
+ multicall = request.method_params.dup
122
+ unless multicall.is_a?(Array) && multicall[0].is_a?(Array)
123
+ raise(DispatcherError, "Malformed multicall (expected array of Hash elements)")
124
+ end
125
+ multicall = multicall[0]
126
+ return multicall.map do |item|
127
+ raise(DispatcherError, "Multicall elements must be Hash") unless item.is_a?(Hash)
128
+ raise(DispatcherError, "Multicall elements must contain a 'methodName' key") unless item.has_key?('methodName')
129
+ method_name = item['methodName']
130
+ params = item.has_key?('params') ? item['params'] : []
131
+ multicall_request = request.dup
132
+ multicall_request.method_name = method_name
133
+ multicall_request.method_params = params
134
+ begin
135
+ web_service_invocation(multicall_request, level + 1)
136
+ rescue Exception => e
137
+ {'faultCode' => 4, 'faultMessage' => e.message}
138
+ end
139
+ end
140
+ end
141
+ end
142
+ case web_service_dispatching_mode
143
+ when :direct
144
+ invocation.api = self.class.web_service_api
145
+ invocation.service = self
146
+ when :delegated, :layered
147
+ invocation.service = web_service_object(invocation.service_name)
148
+ invocation.api = invocation.service.class.web_service_api
149
+ end
150
+ if invocation.api.nil?
151
+ raise(DispatcherError, "no API attached to #{invocation.service.class}")
152
+ end
153
+ invocation.protocol.register_api(invocation.api)
154
+ request.api = invocation.api
155
+ if invocation.api.has_public_api_method?(public_method_name)
156
+ invocation.api_method = invocation.api.public_api_method_instance(public_method_name)
157
+ else
158
+ if invocation.api.default_api_method.nil?
159
+ raise(DispatcherError, "no such method '#{public_method_name}' on API #{invocation.api}")
160
+ else
161
+ invocation.api_method = invocation.api.default_api_method_instance
162
+ end
163
+ end
164
+ if invocation.service.nil?
165
+ raise(DispatcherError, "no service available for service name #{invocation.service_name}")
166
+ end
167
+ unless invocation.service.respond_to?(invocation.api_method.name)
168
+ raise(DispatcherError, "no such method '#{public_method_name}' on API #{invocation.api} (#{invocation.api_method.name})")
169
+ end
170
+ request.api_method = invocation.api_method
171
+ begin
172
+ invocation.method_ordered_params = invocation.api_method.cast_expects(request.method_params.dup)
173
+ rescue
174
+ logger.warn "Casting of method parameters failed" unless logger.nil?
175
+ invocation.method_ordered_params = request.method_params
176
+ end
177
+ request.method_params = invocation.method_ordered_params
178
+ invocation.method_named_params = {}
179
+ invocation.api_method.param_names.inject(0) do |m, n|
180
+ invocation.method_named_params[n] = invocation.method_ordered_params[m]
181
+ m + 1
182
+ end
183
+ invocation
184
+ end
185
+
186
+ def web_service_create_response(protocol, protocol_options, api, api_method, return_value)
187
+ if api.has_api_method?(api_method.name)
188
+ return_type = api_method.returns ? api_method.returns[0] : nil
189
+ return_value = api_method.cast_returns(return_value)
190
+ else
191
+ return_type = ActionWebService::SignatureTypes.canonical_signature_entry(return_value.class, 0)
192
+ end
193
+ protocol.encode_response(api_method.public_name + 'Response', return_value, return_type, protocol_options)
194
+ end
195
+
196
+ class Invocation # :nodoc:
197
+ attr_accessor :protocol
198
+ attr_accessor :protocol_options
199
+ attr_accessor :service_name
200
+ attr_accessor :api
201
+ attr_accessor :api_method
202
+ attr_accessor :method_ordered_params
203
+ attr_accessor :method_named_params
204
+ attr_accessor :service
205
+ end
206
+ end
207
+ end
208
+ end