axtro-actionwebservice 2.3.5.1.20101118142125
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 +184 -0
- data/TODO +32 -0
- data/examples/googlesearch/README +143 -0
- data/examples/googlesearch/autoloading/google_search_api.rb +51 -0
- data/examples/googlesearch/autoloading/google_search_controller.rb +58 -0
- data/examples/googlesearch/delegated/google_search_service.rb +109 -0
- data/examples/googlesearch/delegated/search_controller.rb +8 -0
- data/examples/googlesearch/direct/google_search_api.rb +51 -0
- data/examples/googlesearch/direct/search_controller.rb +59 -0
- data/examples/metaWeblog/README +17 -0
- data/examples/metaWeblog/apis/blogger_api.rb +61 -0
- data/examples/metaWeblog/apis/blogger_service.rb +35 -0
- data/examples/metaWeblog/apis/meta_weblog_api.rb +68 -0
- data/examples/metaWeblog/apis/meta_weblog_service.rb +49 -0
- data/examples/metaWeblog/controllers/xmlrpc_controller.rb +17 -0
- data/generators/web_service/USAGE +28 -0
- data/generators/web_service/templates/api_definition.rb +6 -0
- data/generators/web_service/templates/controller.rb +9 -0
- data/generators/web_service/templates/functional_test.rb +20 -0
- data/generators/web_service/web_service_generator.rb +30 -0
- data/lib/action_web_service.rb +61 -0
- data/lib/action_web_service/acts_as_web_service.rb +26 -0
- data/lib/action_web_service/api.rb +298 -0
- data/lib/action_web_service/base.rb +39 -0
- data/lib/action_web_service/casting.rb +160 -0
- data/lib/action_web_service/client.rb +4 -0
- data/lib/action_web_service/client/base.rb +29 -0
- data/lib/action_web_service/client/soap_client.rb +114 -0
- data/lib/action_web_service/client/xmlrpc_client.rb +59 -0
- data/lib/action_web_service/container.rb +4 -0
- data/lib/action_web_service/container/action_controller_container.rb +94 -0
- data/lib/action_web_service/container/delegated_container.rb +87 -0
- data/lib/action_web_service/container/direct_container.rb +70 -0
- data/lib/action_web_service/dispatcher.rb +3 -0
- data/lib/action_web_service/dispatcher/abstract.rb +209 -0
- data/lib/action_web_service/dispatcher/action_controller_dispatcher.rb +397 -0
- data/lib/action_web_service/invocation.rb +203 -0
- data/lib/action_web_service/protocol.rb +5 -0
- data/lib/action_web_service/protocol/abstract.rb +113 -0
- data/lib/action_web_service/protocol/discovery.rb +38 -0
- data/lib/action_web_service/protocol/soap_protocol.rb +177 -0
- data/lib/action_web_service/protocol/soap_protocol/marshaler.rb +243 -0
- data/lib/action_web_service/protocol/soap_protocol/marshaler.rb~ +243 -0
- data/lib/action_web_service/protocol/xmlrpc_protocol.rb +124 -0
- data/lib/action_web_service/scaffolding.rb +282 -0
- data/lib/action_web_service/simple.rb +54 -0
- data/lib/action_web_service/string_to_datetime_for_soap.rb +17 -0
- data/lib/action_web_service/struct.rb +69 -0
- data/lib/action_web_service/support/class_inheritable_options.rb +27 -0
- data/lib/action_web_service/support/signature_types.rb +262 -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 +111 -0
- data/lib/action_web_service/version.rb +11 -0
- data/lib/action_web_service/version.rb~ +10 -0
- data/lib/actionwebservice.rb +2 -0
- data/setup.rb +1380 -0
- data/test/abstract_client.rb +185 -0
- data/test/abstract_dispatcher.rb +550 -0
- data/test/abstract_unit.rb +44 -0
- data/test/api_test.rb +103 -0
- data/test/apis/auto_load_api.rb +4 -0
- data/test/apis/broken_auto_load_api.rb +3 -0
- data/test/base_test.rb +43 -0
- data/test/casting_test.rb +96 -0
- data/test/client_soap_test.rb +157 -0
- data/test/client_xmlrpc_test.rb +155 -0
- data/test/container_test.rb +76 -0
- data/test/dispatcher_action_controller_soap_test.rb +147 -0
- data/test/dispatcher_action_controller_xmlrpc_test.rb +60 -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 +187 -0
- data/test/run +6 -0
- data/test/scaffolded_controller_test.rb +148 -0
- data/test/struct_test.rb +85 -0
- data/test/test_invoke_test.rb +114 -0
- metadata +167 -0
@@ -0,0 +1,39 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
module ActionWebService # :nodoc:
|
3
|
+
class ActionWebServiceError < StandardError # :nodoc:
|
4
|
+
end
|
5
|
+
|
6
|
+
# An Action Web Service object implements a specified API.
|
7
|
+
#
|
8
|
+
# Used by controllers operating in _Delegated_ dispatching mode.
|
9
|
+
#
|
10
|
+
# ==== Example
|
11
|
+
#
|
12
|
+
# class PersonService < ActionWebService::Base
|
13
|
+
# web_service_api PersonAPI
|
14
|
+
#
|
15
|
+
# def find_person(criteria)
|
16
|
+
# Person.find(:all) [...]
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# def delete_person(id)
|
20
|
+
# Person.find_by_id(id).destroy
|
21
|
+
# end
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# class PersonAPI < ActionWebService::API::Base
|
25
|
+
# api_method :find_person, :expects => [SearchCriteria], :returns => [[Person]]
|
26
|
+
# api_method :delete_person, :expects => [:int]
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# class SearchCriteria < ActionWebService::Struct
|
30
|
+
# member :firstname, :string
|
31
|
+
# member :lastname, :string
|
32
|
+
# member :email, :string
|
33
|
+
# end
|
34
|
+
class Base
|
35
|
+
# Whether to report exceptions back to the caller in the protocol's exception
|
36
|
+
# format
|
37
|
+
class_inheritable_option :web_service_exception_reporting, true
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'time'
|
3
|
+
require 'date'
|
4
|
+
require 'xmlrpc/datetime'
|
5
|
+
|
6
|
+
module ActionWebService # :nodoc:
|
7
|
+
module Casting # :nodoc:
|
8
|
+
class CastingError < ActionWebServiceError # :nodoc:
|
9
|
+
end
|
10
|
+
|
11
|
+
# Performs casting of arbitrary values into the correct types for the signature
|
12
|
+
class BaseCaster # :nodoc:
|
13
|
+
def initialize(api_method)
|
14
|
+
@api_method = api_method
|
15
|
+
end
|
16
|
+
|
17
|
+
# Coerces the parameters in +params+ (an Enumerable) into the types
|
18
|
+
# this method expects
|
19
|
+
def cast_expects(params)
|
20
|
+
self.class.cast_expects(@api_method, params)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Coerces the given +return_value+ into the type returned by this
|
24
|
+
# method
|
25
|
+
def cast_returns(return_value)
|
26
|
+
self.class.cast_returns(@api_method, return_value)
|
27
|
+
end
|
28
|
+
|
29
|
+
class << self
|
30
|
+
include ActionWebService::SignatureTypes
|
31
|
+
|
32
|
+
def cast_expects(api_method, params) # :nodoc:
|
33
|
+
return [] if api_method.expects.nil?
|
34
|
+
api_method.expects.zip(params).map{ |type, param| cast(param, type) }
|
35
|
+
end
|
36
|
+
|
37
|
+
def cast_returns(api_method, return_value) # :nodoc:
|
38
|
+
return nil if api_method.returns.nil?
|
39
|
+
cast(return_value, api_method.returns[0])
|
40
|
+
end
|
41
|
+
|
42
|
+
def cast(value, signature_type) # :nodoc:
|
43
|
+
return value if signature_type.nil? # signature.length != params.length
|
44
|
+
return nil if value.nil?
|
45
|
+
# XMLRPC protocol doesn't support nil values. It uses false instead.
|
46
|
+
# It should never happen for SOAP.
|
47
|
+
if signature_type.structured? && value.equal?(false)
|
48
|
+
return nil
|
49
|
+
end
|
50
|
+
unless signature_type.array? || signature_type.structured?
|
51
|
+
return value if canonical_type(value.class) == signature_type.type
|
52
|
+
end
|
53
|
+
if signature_type.array?
|
54
|
+
unless value.respond_to?(:entries) && !value.is_a?(String)
|
55
|
+
raise CastingError, "Don't know how to cast #{value.class} into #{signature_type.type.inspect}"
|
56
|
+
end
|
57
|
+
value.entries.map do |entry|
|
58
|
+
cast(entry, signature_type.element_type)
|
59
|
+
end
|
60
|
+
elsif signature_type.simple?
|
61
|
+
return value
|
62
|
+
elsif signature_type.structured?
|
63
|
+
cast_to_structured_type(value, signature_type)
|
64
|
+
elsif !signature_type.custom?
|
65
|
+
cast_base_type(value, signature_type)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def cast_base_type(value, signature_type) # :nodoc:
|
70
|
+
# This is a work-around for the fact that XML-RPC special-cases DateTime values into its own DateTime type
|
71
|
+
# in order to support iso8601 dates. This doesn't work too well for us, so we'll convert it into a Time,
|
72
|
+
# with the caveat that we won't be able to handle pre-1970 dates that are sent to us.
|
73
|
+
#
|
74
|
+
# See http://dev.rubyonrails.com/ticket/2516
|
75
|
+
value = value.to_time if value.is_a?(XMLRPC::DateTime)
|
76
|
+
|
77
|
+
case signature_type.type
|
78
|
+
when :int
|
79
|
+
Integer(value)
|
80
|
+
when :string
|
81
|
+
value.to_s
|
82
|
+
when :base64
|
83
|
+
if value.is_a?(ActionWebService::Base64)
|
84
|
+
value
|
85
|
+
else
|
86
|
+
ActionWebService::Base64.new(value.to_s)
|
87
|
+
end
|
88
|
+
when :bool
|
89
|
+
return false if value.nil?
|
90
|
+
return value if value == true || value == false
|
91
|
+
case value.to_s.downcase
|
92
|
+
when '1', 'true', 'y', 'yes'
|
93
|
+
true
|
94
|
+
when '0', 'false', 'n', 'no'
|
95
|
+
false
|
96
|
+
else
|
97
|
+
raise CastingError, "Don't know how to cast #{value.class} into Boolean"
|
98
|
+
end
|
99
|
+
when :float
|
100
|
+
Float(value)
|
101
|
+
when :decimal
|
102
|
+
BigDecimal(value.to_s)
|
103
|
+
when :time
|
104
|
+
if value.kind_of?(Hash)
|
105
|
+
value = "%s/%s/%s %s:%s:%s" % value.values_at(*%w[2 3 1 4 5 6])
|
106
|
+
Time.respond_to?(:strptime) ? Time.strptime(value.to_s, "%m/%d/%Y %H:%M:%S") : Time.parse(value.to_s)
|
107
|
+
elsif value.kind_of?(Time)
|
108
|
+
value
|
109
|
+
elsif value.kind_of?(DateTime)
|
110
|
+
value.to_time
|
111
|
+
else
|
112
|
+
Time.parse(value.to_s)
|
113
|
+
end
|
114
|
+
when :date
|
115
|
+
if value.kind_of?(Hash)
|
116
|
+
value = "%s/%s/%s" % value.values_at(*%w[2 3 1])
|
117
|
+
return Date.strptime(value.to_s,"%m/%d/%Y")
|
118
|
+
end
|
119
|
+
value.kind_of?(Date) ? value : Date.parse(value.to_s)
|
120
|
+
when :datetime
|
121
|
+
if value.kind_of?(Hash)
|
122
|
+
value = "%s/%s/%s %s:%s:%s" % value.values_at(*%w[2 3 1 4 5 6])
|
123
|
+
return DateTime.strptime(value.to_s,"%m/%d/%Y %H:%M:%S")
|
124
|
+
end
|
125
|
+
value.kind_of?(DateTime) ? value : DateTime.parse(value.to_s)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def cast_to_structured_type(value, signature_type) # :nodoc:
|
130
|
+
obj = nil
|
131
|
+
# if the canonical classes are the same or if the given value is of
|
132
|
+
# a type that is derived from the signature_type do not attempt to
|
133
|
+
# "cast" the value into the signature_type as it's already good to go
|
134
|
+
obj = (
|
135
|
+
canonical_type(value.class) == canonical_type(signature_type.type) or
|
136
|
+
derived_from?(signature_type.type, value.class)
|
137
|
+
) ? value : signature_type.type_class.new
|
138
|
+
if value.respond_to?(:each_pair)
|
139
|
+
klass = signature_type.type_class
|
140
|
+
value.each_pair do |name, val|
|
141
|
+
type = klass.respond_to?(:member_type) ? klass.member_type(name) : nil
|
142
|
+
val = cast(val, type) if type
|
143
|
+
# See http://dev.rubyonrails.com/ticket/3567
|
144
|
+
val = val.to_time if val.is_a?(XMLRPC::DateTime)
|
145
|
+
obj.__send__("#{name}=", val) if obj.respond_to?(name)
|
146
|
+
end
|
147
|
+
elsif value.respond_to?(:attributes)
|
148
|
+
signature_type.each_member do |name, type|
|
149
|
+
val = value.__send__(name)
|
150
|
+
obj.__send__("#{name}=", cast(val, type)) if obj.respond_to?(name)
|
151
|
+
end
|
152
|
+
else
|
153
|
+
raise CastingError, "Don't know how to cast #{value.class} to #{signature_type.type_class}"
|
154
|
+
end
|
155
|
+
obj
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
module ActionWebService # :nodoc:
|
3
|
+
module Client # :nodoc:
|
4
|
+
class ClientError < StandardError # :nodoc:
|
5
|
+
end
|
6
|
+
|
7
|
+
class Base # :nodoc:
|
8
|
+
def initialize(api, endpoint_uri)
|
9
|
+
@api = api
|
10
|
+
@endpoint_uri = endpoint_uri
|
11
|
+
end
|
12
|
+
|
13
|
+
def method_missing(name, *args) # :nodoc:
|
14
|
+
call_name = method_name(name)
|
15
|
+
return super(name, *args) if call_name.nil?
|
16
|
+
self.perform_invocation(call_name, args)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
def method_name(name)
|
21
|
+
if @api.has_api_method?(name.to_sym)
|
22
|
+
name.to_s
|
23
|
+
elsif @api.has_public_api_method?(name.to_s)
|
24
|
+
@api.api_method_name(name.to_s).to_s
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'soap/rpc/driver'
|
3
|
+
require 'uri'
|
4
|
+
|
5
|
+
module ActionWebService # :nodoc:
|
6
|
+
module Client # :nodoc:
|
7
|
+
|
8
|
+
# Implements SOAP client support (using RPC encoding for the messages).
|
9
|
+
#
|
10
|
+
# ==== Example Usage
|
11
|
+
#
|
12
|
+
# class PersonAPI < ActionWebService::API::Base
|
13
|
+
# api_method :find_all, :returns => [[Person]]
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# soap_client = ActionWebService::Client::Soap.new(PersonAPI, "http://...")
|
17
|
+
# persons = soap_client.find_all
|
18
|
+
#
|
19
|
+
class Soap < Base
|
20
|
+
# provides access to the underlying soap driver
|
21
|
+
attr_reader :driver
|
22
|
+
|
23
|
+
# Creates a new web service client using the SOAP RPC protocol.
|
24
|
+
#
|
25
|
+
# +api+ must be an ActionWebService::API::Base derivative, and
|
26
|
+
# +endpoint_uri+ must point at the relevant URL to which protocol requests
|
27
|
+
# will be sent with HTTP POST.
|
28
|
+
#
|
29
|
+
# Valid options:
|
30
|
+
# [<tt>:namespace</tt>] If the remote server has used a custom namespace to
|
31
|
+
# declare its custom types, you can specify it here. This would
|
32
|
+
# be the namespace declared with a [WebService(Namespace = "http://namespace")] attribute
|
33
|
+
# in .NET, for example.
|
34
|
+
# [<tt>:driver_options</tt>] If you want to supply any custom SOAP RPC driver
|
35
|
+
# options, you can provide them as a Hash here
|
36
|
+
#
|
37
|
+
# The <tt>:driver_options</tt> option can be used to configure the backend SOAP
|
38
|
+
# RPC driver. An example of configuring the SOAP backend to do
|
39
|
+
# client-certificate authenticated SSL connections to the server:
|
40
|
+
#
|
41
|
+
# opts = {}
|
42
|
+
# opts['protocol.http.ssl_config.verify_mode'] = 'OpenSSL::SSL::VERIFY_PEER'
|
43
|
+
# opts['protocol.http.ssl_config.client_cert'] = client_cert_file_path
|
44
|
+
# opts['protocol.http.ssl_config.client_key'] = client_key_file_path
|
45
|
+
# opts['protocol.http.ssl_config.ca_file'] = ca_cert_file_path
|
46
|
+
# client = ActionWebService::Client::Soap.new(api, 'https://some/service', :driver_options => opts)
|
47
|
+
def initialize(api, endpoint_uri, options={})
|
48
|
+
super(api, endpoint_uri)
|
49
|
+
@namespace = options[:namespace] || 'urn:ActionWebService'
|
50
|
+
@driver_options = options[:driver_options] || {}
|
51
|
+
@protocol = ActionWebService::Protocol::Soap::SoapProtocol.new @namespace
|
52
|
+
@soap_action_base = options[:soap_action_base]
|
53
|
+
@soap_action_base ||= URI.parse(endpoint_uri).path
|
54
|
+
@driver = create_soap_rpc_driver(api, endpoint_uri)
|
55
|
+
@driver_options.each do |name, value|
|
56
|
+
@driver.options[name.to_s] = value
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
protected
|
61
|
+
def perform_invocation(method_name, args)
|
62
|
+
method = @api.api_methods[method_name.to_sym]
|
63
|
+
args = method.cast_expects(args.dup) rescue args
|
64
|
+
return_value = @driver.send(method_name, *args)
|
65
|
+
method.cast_returns(return_value.dup) rescue return_value
|
66
|
+
end
|
67
|
+
|
68
|
+
def soap_action(method_name)
|
69
|
+
"#{@soap_action_base}/#{method_name}"
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
def create_soap_rpc_driver(api, endpoint_uri)
|
74
|
+
@protocol.register_api(api)
|
75
|
+
driver = SoapDriver.new(endpoint_uri, nil)
|
76
|
+
driver.mapping_registry = @protocol.marshaler.registry
|
77
|
+
api.api_methods.each do |name, method|
|
78
|
+
qname = XSD::QName.new(@namespace, method.public_name)
|
79
|
+
action = soap_action(method.public_name)
|
80
|
+
expects = method.expects
|
81
|
+
returns = method.returns
|
82
|
+
param_def = []
|
83
|
+
if expects
|
84
|
+
expects.each do |type|
|
85
|
+
type_binding = @protocol.marshaler.lookup_type(type)
|
86
|
+
if SOAP::Version >= "1.5.5"
|
87
|
+
param_def << ['in', type.name.to_s, [type_binding.type.type_class.to_s]]
|
88
|
+
else
|
89
|
+
param_def << ['in', type.name, type_binding.mapping]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
if returns
|
94
|
+
type_binding = @protocol.marshaler.lookup_type(returns[0])
|
95
|
+
if SOAP::Version >= "1.5.5"
|
96
|
+
param_def << ['retval', 'return', [type_binding.type.type_class.to_s]]
|
97
|
+
else
|
98
|
+
param_def << ['retval', 'return', type_binding.mapping]
|
99
|
+
end
|
100
|
+
end
|
101
|
+
driver.add_method(qname, action, method.name.to_s, param_def)
|
102
|
+
end
|
103
|
+
driver
|
104
|
+
end
|
105
|
+
|
106
|
+
class SoapDriver < SOAP::RPC::Driver # :nodoc:
|
107
|
+
def add_method(qname, soapaction, name, param_def)
|
108
|
+
@proxy.add_rpc_method(qname, soapaction, name, param_def)
|
109
|
+
add_rpc_method_interface(name, param_def)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'uri'
|
3
|
+
require 'xmlrpc/client'
|
4
|
+
|
5
|
+
module ActionWebService # :nodoc:
|
6
|
+
module Client # :nodoc:
|
7
|
+
|
8
|
+
# Implements XML-RPC client support
|
9
|
+
#
|
10
|
+
# ==== Example Usage
|
11
|
+
#
|
12
|
+
# class BloggerAPI < ActionWebService::API::Base
|
13
|
+
# inflect_names false
|
14
|
+
# api_method :getRecentPosts, :returns => [[Blog::Post]]
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# blog = ActionWebService::Client::XmlRpc.new(BloggerAPI, "http://.../RPC", :handler_name => "blogger")
|
18
|
+
# posts = blog.getRecentPosts
|
19
|
+
class XmlRpc < Base
|
20
|
+
|
21
|
+
# Creates a new web service client using the XML-RPC protocol.
|
22
|
+
#
|
23
|
+
# +api+ must be an ActionWebService::API::Base derivative, and
|
24
|
+
# +endpoint_uri+ must point at the relevant URL to which protocol requests
|
25
|
+
# will be sent with HTTP POST.
|
26
|
+
#
|
27
|
+
# Valid options:
|
28
|
+
# [<tt>:handler_name</tt>] If the remote server defines its services inside special
|
29
|
+
# handler (the Blogger API uses a <tt>"blogger"</tt> handler name for example),
|
30
|
+
# provide it here, or your method calls will fail
|
31
|
+
def initialize(api, endpoint_uri, options={})
|
32
|
+
@api = api
|
33
|
+
@handler_name = options[:handler_name]
|
34
|
+
@protocol = ActionWebService::Protocol::XmlRpc::XmlRpcProtocol.new
|
35
|
+
@client = XMLRPC::Client.new2(endpoint_uri, options[:proxy], options[:timeout])
|
36
|
+
end
|
37
|
+
|
38
|
+
protected
|
39
|
+
def perform_invocation(method_name, args)
|
40
|
+
method = @api.api_methods[method_name.to_sym]
|
41
|
+
if method.expects && method.expects.length != args.length
|
42
|
+
raise(ArgumentError, "#{method.public_name}: wrong number of arguments (#{args.length} for #{method.expects.length})")
|
43
|
+
end
|
44
|
+
args = method.cast_expects(args.dup) rescue args
|
45
|
+
if method.expects
|
46
|
+
method.expects.each_with_index{ |type, i| args[i] = @protocol.value_to_xmlrpc_wire_format(args[i], type) }
|
47
|
+
end
|
48
|
+
ok, return_value = @client.call2(public_name(method_name), *args)
|
49
|
+
return (method.cast_returns(return_value.dup) rescue return_value) if ok
|
50
|
+
raise(ClientError, "#{return_value.faultCode}: #{return_value.faultString}")
|
51
|
+
end
|
52
|
+
|
53
|
+
def public_name(method_name)
|
54
|
+
public_name = @api.public_api_method_name(method_name)
|
55
|
+
@handler_name ? "#{@handler_name}.#{public_name}" : public_name
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
module ActionWebService # :nodoc:
|
3
|
+
module Container # :nodoc:
|
4
|
+
module ActionController # :nodoc:
|
5
|
+
def self.included(base) # :nodoc:
|
6
|
+
class << base
|
7
|
+
include ClassMethods
|
8
|
+
alias_method_chain :inherited, :api
|
9
|
+
alias_method_chain :web_service_api, :require
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
# Creates a client for accessing remote web services, using the
|
15
|
+
# given +protocol+ to communicate with the +endpoint_uri+.
|
16
|
+
#
|
17
|
+
# ==== Example
|
18
|
+
#
|
19
|
+
# class MyController < ActionController::Base
|
20
|
+
# web_client_api :blogger, :xmlrpc, "http://blogger.com/myblog/api/RPC2", :handler_name => 'blogger'
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# In this example, a protected method named <tt>blogger</tt> will
|
24
|
+
# now exist on the controller, and calling it will return the
|
25
|
+
# XML-RPC client object for working with that remote service.
|
26
|
+
#
|
27
|
+
# +options+ is the set of protocol client specific options (see
|
28
|
+
# a protocol client class for details).
|
29
|
+
#
|
30
|
+
# If your API definition does not exist on the load path with the
|
31
|
+
# correct rules for it to be found using +name+, you can pass in
|
32
|
+
# the API definition class via +options+, using a key of <tt>:api</tt>
|
33
|
+
def web_client_api(name, protocol, endpoint_uri, options={})
|
34
|
+
unless method_defined?(name)
|
35
|
+
api_klass = options.delete(:api) || require_web_service_api(name)
|
36
|
+
class_eval do
|
37
|
+
define_method(name) do
|
38
|
+
create_web_service_client(api_klass, protocol, endpoint_uri, options)
|
39
|
+
end
|
40
|
+
protected name
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def web_service_api_with_require(definition=nil) # :nodoc:
|
46
|
+
return web_service_api_without_require if definition.nil?
|
47
|
+
case definition
|
48
|
+
when String, Symbol
|
49
|
+
klass = require_web_service_api(definition)
|
50
|
+
else
|
51
|
+
klass = definition
|
52
|
+
end
|
53
|
+
web_service_api_without_require(klass)
|
54
|
+
end
|
55
|
+
|
56
|
+
def require_web_service_api(name) # :nodoc:
|
57
|
+
case name
|
58
|
+
when String, Symbol
|
59
|
+
file_name = name.to_s.underscore + "_api"
|
60
|
+
class_name = file_name.camelize
|
61
|
+
class_names = [class_name, class_name.sub(/Api$/, 'API')]
|
62
|
+
begin
|
63
|
+
require_dependency(file_name)
|
64
|
+
rescue LoadError => load_error
|
65
|
+
requiree = / -- (.*?)(\.rb)?$/.match(load_error.message).to_a[1]
|
66
|
+
msg = requiree == file_name ? "Missing API definition file in apis/#{file_name}.rb" : "Can't load file: #{requiree}"
|
67
|
+
raise LoadError.new(msg).copy_blame!(load_error)
|
68
|
+
end
|
69
|
+
klass = nil
|
70
|
+
class_names.each do |name|
|
71
|
+
klass = name.constantize rescue nil
|
72
|
+
break unless klass.nil?
|
73
|
+
end
|
74
|
+
unless klass
|
75
|
+
raise(NameError, "neither #{class_names[0]} or #{class_names[1]} found")
|
76
|
+
end
|
77
|
+
klass
|
78
|
+
else
|
79
|
+
raise(ArgumentError, "expected String or Symbol argument")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
def inherited_with_api(child)
|
85
|
+
inherited_without_api(child)
|
86
|
+
begin child.web_service_api(child.controller_path)
|
87
|
+
rescue MissingSourceFile => e
|
88
|
+
raise unless e.is_missing?("apis/#{child.controller_path}_api")
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|