actionservice 0.2.102 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/ChangeLog +1 -1
- data/README +48 -14
- data/Rakefile +6 -15
- data/TODO +2 -0
- data/examples/{soap → googlesearch}/README +26 -10
- data/examples/{soap/lib → googlesearch/delegated}/google_search_service.rb +22 -9
- data/examples/{soap/app/controllers → googlesearch/delegated}/search_controller.rb +0 -0
- data/examples/googlesearch/direct/search_controller.rb +106 -0
- data/examples/metaWeblog/README +28 -0
- data/examples/metaWeblog/blog_controller.rb +117 -0
- data/lib/action_service/base.rb +40 -2
- data/lib/action_service/container.rb +87 -18
- data/lib/action_service/exporting.rb +6 -12
- data/lib/action_service/invocation.rb +51 -10
- data/lib/action_service/protocol/abstract.rb +11 -11
- data/lib/action_service/protocol/registry.rb +7 -7
- data/lib/action_service/protocol/soap.rb +18 -10
- data/lib/action_service/protocol/xmlrpc.rb +65 -32
- data/lib/action_service/router/action_controller.rb +24 -10
- data/lib/action_service/router/wsdl.rb +5 -5
- data/lib/action_service/struct.rb +17 -1
- data/lib/action_service/support/class_inheritable_options.rb +1 -1
- data/test/protocol_xmlrpc_test.rb +23 -3
- metadata +12 -9
data/lib/action_service/base.rb
CHANGED
@@ -1,12 +1,50 @@
|
|
1
1
|
require 'action_service/support/class_inheritable_options'
|
2
2
|
|
3
|
-
module ActionService
|
4
|
-
class ActionServiceError < StandardError
|
3
|
+
module ActionService # :nodoc:
|
4
|
+
class ActionServiceError < StandardError # :nodoc:
|
5
5
|
end
|
6
6
|
|
7
|
+
# Action Service objects implement an exported API. Used in controllers
|
8
|
+
# operating in _Delegated_ dispatching mode.
|
9
|
+
#
|
10
|
+
# An ActionService::Base derivative is the <em>public interface</em>
|
11
|
+
# of an API.
|
12
|
+
#
|
13
|
+
# === Example
|
14
|
+
#
|
15
|
+
# class SearchCriteria < ActionStruct::Base
|
16
|
+
# member :firstname, String
|
17
|
+
# member :lastname, String
|
18
|
+
# member :email, String
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# class PersonApi < ActionService::Base
|
22
|
+
# def find_person(criteria)
|
23
|
+
# Person.find_all [...]
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# def delete_person(id)
|
27
|
+
# Person.find_by_id(id).destroy
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# export :find_person, :expects => [SearchCriteria], :returns => [[Person]]
|
31
|
+
# export :delete_person, :expects => [Integer]
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# See ActionService::Exporting::ClassMethods for more details on the syntax
|
35
|
+
# for declaring exported methods.
|
7
36
|
class Base
|
8
37
|
# Whether to send back a detailed stack trace to the remote caller
|
9
38
|
# when an exception is thrown on the server
|
10
39
|
class_inheritable_option :report_exceptions, true
|
40
|
+
|
41
|
+
# Whether to mangle method names into friendly camelized names
|
42
|
+
# when declaring them to remote callers
|
43
|
+
class_inheritable_option :export_name_mangling, true
|
44
|
+
|
45
|
+
# If present, the name of a method to call when the remote caller
|
46
|
+
# tried to call a nonexistent method. Semantically equivalent to
|
47
|
+
# +method_missing+.
|
48
|
+
class_inheritable_option :default_export
|
11
49
|
end
|
12
50
|
end
|
@@ -1,12 +1,12 @@
|
|
1
|
-
module ActionService
|
2
|
-
module Container
|
1
|
+
module ActionService # :nodoc:
|
2
|
+
module Container # :nodoc:
|
3
3
|
DirectDispatching = :direct
|
4
4
|
DelegatedDispatching = :delegated
|
5
5
|
|
6
|
-
class ContainerError < ActionService::ActionServiceError
|
6
|
+
class ContainerError < ActionService::ActionServiceError # :nodoc:
|
7
7
|
end
|
8
8
|
|
9
|
-
def self.append_features(base)
|
9
|
+
def self.append_features(base) # :nodoc:
|
10
10
|
super
|
11
11
|
base.class_inheritable_option(:service_dispatching_mode, DirectDispatching)
|
12
12
|
base.extend(ClassMethods)
|
@@ -14,7 +14,36 @@ module ActionService
|
|
14
14
|
end
|
15
15
|
|
16
16
|
module ClassMethods
|
17
|
-
|
17
|
+
# Declares a service that will provides access to the exported API
|
18
|
+
# of the given service +object+. +object+ must be an ActionService::Base
|
19
|
+
# derivative.
|
20
|
+
#
|
21
|
+
# === Example
|
22
|
+
#
|
23
|
+
# class ApiController < ApplicationController
|
24
|
+
# service_dispatching_mode :delegated
|
25
|
+
#
|
26
|
+
# service :person, PersonApi.new
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# This will generate a <tt>/api/person</tt> action in the controller.
|
30
|
+
# All exported methods of +object+ will be available for invocation by
|
31
|
+
# clients sending protocol messages to <tt>/api/person</tt> with HTTP POST.
|
32
|
+
#
|
33
|
+
# You can also declare a service that defers the service +object+
|
34
|
+
# creation until request time. To do this, pass a block instead of a
|
35
|
+
# service object instance. The block will be executed in instance
|
36
|
+
# context, so it will have access to controller instance variables. Its
|
37
|
+
# return value should be the service object.
|
38
|
+
#
|
39
|
+
# === Deferred Example
|
40
|
+
#
|
41
|
+
# class ApiController < ApplicationController
|
42
|
+
# service_dispatching_mode :delegated
|
43
|
+
#
|
44
|
+
# service(:person) { PersonApi.new }
|
45
|
+
# end
|
46
|
+
def service(name, object=nil, &block)
|
18
47
|
if (object && block_given?) || (object.nil? && block.nil?)
|
19
48
|
raise(ContainerError, "either service, or a block must be given")
|
20
49
|
end
|
@@ -28,18 +57,20 @@ module ActionService
|
|
28
57
|
call_service_definition_callbacks(self, name, info)
|
29
58
|
end
|
30
59
|
|
31
|
-
|
32
|
-
|
33
|
-
end
|
34
|
-
|
60
|
+
# Returns +true+ if #service was used to declare a service with
|
61
|
+
# the given name.
|
35
62
|
def has_service?(name)
|
36
63
|
services.has_key?(name.to_sym)
|
37
64
|
end
|
38
65
|
|
39
|
-
def services
|
66
|
+
def services # :nodoc:
|
40
67
|
read_inheritable_attribute("action_services") || {}
|
41
68
|
end
|
42
69
|
|
70
|
+
def add_service_definition_callback(&block) # :nodoc:
|
71
|
+
write_inheritable_array("service_definition_callbacks", [block])
|
72
|
+
end
|
73
|
+
|
43
74
|
private
|
44
75
|
def call_service_definition_callbacks(container_class, service_name, service_info)
|
45
76
|
(read_inheritable_attribute("service_definition_callbacks") || []).each do |block|
|
@@ -48,7 +79,7 @@ module ActionService
|
|
48
79
|
end
|
49
80
|
end
|
50
81
|
|
51
|
-
module InstanceMethods
|
82
|
+
module InstanceMethods # :nodoc:
|
52
83
|
def service_object(service_name)
|
53
84
|
info = self.class.services[service_name.to_sym]
|
54
85
|
unless info
|
@@ -73,13 +104,29 @@ module ActionService
|
|
73
104
|
def dispatch_direct_service_request(protocol_request)
|
74
105
|
public_method_name = protocol_request.public_method_name
|
75
106
|
method_name = self.class.internal_export_name(public_method_name)
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
@invocation_params = protocol_request.unmarshal
|
81
|
-
if export_info[:expects]
|
107
|
+
block = nil
|
108
|
+
expects = nil
|
109
|
+
if method_name
|
110
|
+
export_info = self.class.exports[method_name]
|
82
111
|
expects = export_info[:expects]
|
112
|
+
protocol_request.type = Protocol::CheckedMessage
|
113
|
+
protocol_request.signature = expects
|
114
|
+
protocol_request.return_signature = export_info[:returns]
|
115
|
+
else
|
116
|
+
protocol_request.type = Protocol::UncheckedMessage
|
117
|
+
system_exports = self.class.read_inheritable_attribute('default_system_exports') || {}
|
118
|
+
protocol = protocol_request.protocol
|
119
|
+
block = system_exports[protocol.class]
|
120
|
+
unless block
|
121
|
+
method_name = service_class.default_export
|
122
|
+
unless method_name && service.respond_to?(method_name)
|
123
|
+
raise(ContainerError, "no such method /#{service_name}##{public_method_name}")
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
@invocation_params = protocol_request.unmarshal
|
129
|
+
if expects
|
83
130
|
@params ||= {}
|
84
131
|
(1..@invocation_params.size).each do |i|
|
85
132
|
i -= 1
|
@@ -90,7 +137,29 @@ module ActionService
|
|
90
137
|
end
|
91
138
|
end
|
92
139
|
end
|
93
|
-
|
140
|
+
|
141
|
+
perform_invoke = lambda do
|
142
|
+
if block
|
143
|
+
result = block.call(public_method_name, self.class, *@invocation_params)
|
144
|
+
else
|
145
|
+
result = send(method_name)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
try_default = true
|
149
|
+
result = nil
|
150
|
+
catch(:try_default_export) do
|
151
|
+
result = perform_invoke.call
|
152
|
+
try_default = false
|
153
|
+
end
|
154
|
+
if try_default
|
155
|
+
method_name = self.class.default_export
|
156
|
+
if method_name
|
157
|
+
protocol_request.type = Protocol::UncheckedMessage
|
158
|
+
else
|
159
|
+
raise(ContainerError, "no such method ##{public_method_name}")
|
160
|
+
end
|
161
|
+
result = perform_invoke.call
|
162
|
+
end
|
94
163
|
protocol_request.marshal(result)
|
95
164
|
end
|
96
165
|
|
@@ -1,19 +1,13 @@
|
|
1
|
-
module ActionService
|
2
|
-
module Exporting
|
3
|
-
class ExportError < ActionService::ActionServiceError
|
1
|
+
module ActionService # :nodoc:
|
2
|
+
module Exporting # :nodoc:
|
3
|
+
class ExportError < ActionService::ActionServiceError # :nodoc:
|
4
4
|
end
|
5
5
|
|
6
|
-
def self.append_features(base)
|
6
|
+
def self.append_features(base) # :nodoc:
|
7
7
|
super
|
8
|
-
|
9
|
-
# when declaring them to remote callers
|
8
|
+
base.class_inheritable_option :report_exceptions, true
|
10
9
|
base.class_inheritable_option :export_name_mangling, true
|
11
|
-
|
12
|
-
# If present, the name of a method to call when the remote caller
|
13
|
-
# tried to call a nonexistent method. Semantically equivalent to
|
14
|
-
# +method_missing+.
|
15
10
|
base.class_inheritable_option :default_export
|
16
|
-
|
17
11
|
base.extend(ClassMethods)
|
18
12
|
end
|
19
13
|
|
@@ -38,7 +32,7 @@ module ActionService
|
|
38
32
|
# A type annotation is an Array, containing as elements one or more of:
|
39
33
|
#
|
40
34
|
# * A Class object for the desired type.
|
41
|
-
# * An Array containing a Class object.
|
35
|
+
# * An Array containing a Class object. Indicates that the argument at that
|
42
36
|
# position is to be an Array containing values of type Class.
|
43
37
|
# * A Hash containing the argument name as the key, and one of the above as
|
44
38
|
# value.
|
@@ -1,10 +1,10 @@
|
|
1
|
-
module ActionService
|
2
|
-
module Invocation
|
1
|
+
module ActionService # :nodoc:
|
2
|
+
module Invocation # :nodoc:
|
3
3
|
ConcreteInvocation = :concrete
|
4
4
|
VirtualInvocation = :virtual
|
5
5
|
UnexportedConcreteInvocation = :unexported_concrete
|
6
6
|
|
7
|
-
class InvocationError < ActionService::ActionServiceError
|
7
|
+
class InvocationError < ActionService::ActionServiceError # :nodoc:
|
8
8
|
end
|
9
9
|
|
10
10
|
def self.append_features(base) # :nodoc:
|
@@ -13,7 +13,43 @@ module ActionService
|
|
13
13
|
base.send(:include, ActionService::Invocation::InstanceMethods)
|
14
14
|
end
|
15
15
|
|
16
|
+
# Invocation interceptors provide a means to execute custom code before
|
17
|
+
# and after API method invocations on ActionService::Base objects when
|
18
|
+
# running in _Delegated_ dispatching mode.
|
19
|
+
#
|
20
|
+
# When running in _Direct_ mode, ActionController filters should be used
|
21
|
+
# instead.
|
22
|
+
#
|
23
|
+
# The semantics of invocation interceptors are the same as ActionController
|
24
|
+
# filters, and accept the same parameters and options.
|
25
|
+
#
|
26
|
+
# A _before_ interceptor can also cancel execution by returning +false+,
|
27
|
+
# or returning a <tt>[false, "cancel reason"]</tt> array if it wishes to supply
|
28
|
+
# a reason for canceling the request.
|
29
|
+
#
|
30
|
+
# === Example
|
31
|
+
#
|
32
|
+
# class CustomService < ActionService::Base
|
33
|
+
# before_invocation :intercept_add, :only => [:add]
|
34
|
+
#
|
35
|
+
# def add(a, b)
|
36
|
+
# a + b
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# export :add, :expects => [Integer, Integer], :returns => [Integer]
|
40
|
+
#
|
41
|
+
# private
|
42
|
+
# def intercept_add
|
43
|
+
# return [false, "permission denied"] # cancel it
|
44
|
+
# end
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# Options:
|
48
|
+
# [<tt>:except</tt>] A list of exports to which the interceptor will NOT apply
|
49
|
+
# [<tt>:only</tt>] A list of exports to which the interceptor will apply
|
16
50
|
module ClassMethods
|
51
|
+
# Appends the given +interceptors+ to be called
|
52
|
+
# _before_ method invocation.
|
17
53
|
def append_before_invocation(*interceptors, &block)
|
18
54
|
conditions = extract_conditions!(interceptors)
|
19
55
|
interceptors << block if block_given?
|
@@ -21,6 +57,8 @@ module ActionService
|
|
21
57
|
append_interceptors_to_chain("before", interceptors)
|
22
58
|
end
|
23
59
|
|
60
|
+
# Prepends the given +interceptors+ to be called
|
61
|
+
# _before_ method invocation.
|
24
62
|
def prepend_before_invocation(*interceptors, &block)
|
25
63
|
conditions = extract_conditions!(interceptors)
|
26
64
|
interceptors << block if block_given?
|
@@ -30,6 +68,8 @@ module ActionService
|
|
30
68
|
|
31
69
|
alias :before_invocation :append_before_invocation
|
32
70
|
|
71
|
+
# Appends the given +interceptors+ to be called
|
72
|
+
# _after_ method invocation.
|
33
73
|
def append_after_invocation(*interceptors, &block)
|
34
74
|
conditions = extract_conditions!(interceptors)
|
35
75
|
interceptors << block if block_given?
|
@@ -37,6 +77,8 @@ module ActionService
|
|
37
77
|
append_interceptors_to_chain("after", interceptors)
|
38
78
|
end
|
39
79
|
|
80
|
+
# Prepends the given +interceptors+ to be called
|
81
|
+
# _after_ method invocation.
|
40
82
|
def prepend_after_invocation(*interceptors, &block)
|
41
83
|
conditions = extract_conditions!(interceptors)
|
42
84
|
interceptors << block if block_given?
|
@@ -46,24 +88,23 @@ module ActionService
|
|
46
88
|
|
47
89
|
alias :after_invocation :append_after_invocation
|
48
90
|
|
49
|
-
def before_invocation_interceptors
|
91
|
+
def before_invocation_interceptors # :nodoc:
|
50
92
|
read_inheritable_attribute("before_invocation_interceptors")
|
51
93
|
end
|
52
94
|
|
53
|
-
def after_invocation_interceptors
|
95
|
+
def after_invocation_interceptors # :nodoc:
|
54
96
|
read_inheritable_attribute("after_invocation_interceptors")
|
55
97
|
end
|
56
98
|
|
57
|
-
def included_intercepted_methods
|
99
|
+
def included_intercepted_methods # :nodoc:
|
58
100
|
read_inheritable_attribute("included_intercepted_methods") || {}
|
59
101
|
end
|
60
102
|
|
61
|
-
def excluded_intercepted_methods
|
103
|
+
def excluded_intercepted_methods # :nodoc:
|
62
104
|
read_inheritable_attribute("excluded_intercepted_methods") || {}
|
63
105
|
end
|
64
106
|
|
65
107
|
private
|
66
|
-
|
67
108
|
def append_interceptors_to_chain(condition, interceptors)
|
68
109
|
write_inheritable_array("#{condition}_invocation_interceptors", interceptors)
|
69
110
|
end
|
@@ -90,7 +131,7 @@ module ActionService
|
|
90
131
|
end
|
91
132
|
end
|
92
133
|
|
93
|
-
module InstanceMethods
|
134
|
+
module InstanceMethods # :nodoc:
|
94
135
|
def self.append_features(base)
|
95
136
|
super
|
96
137
|
base.class_eval do
|
@@ -183,7 +224,7 @@ module ActionService
|
|
183
224
|
end
|
184
225
|
end
|
185
226
|
|
186
|
-
class InvocationRequest
|
227
|
+
class InvocationRequest # :nodoc:
|
187
228
|
attr_accessor :type
|
188
229
|
attr :public_method_name
|
189
230
|
attr_accessor :method_name
|
@@ -1,12 +1,12 @@
|
|
1
|
-
module ActionService
|
2
|
-
module Protocol
|
1
|
+
module ActionService # :nodoc:
|
2
|
+
module Protocol # :nodoc:
|
3
3
|
CheckedMessage = :checked
|
4
4
|
UncheckedMessage = :unchecked
|
5
5
|
|
6
|
-
class ProtocolError < ActionService::ActionServiceError
|
6
|
+
class ProtocolError < ActionService::ActionServiceError # :nodoc:
|
7
7
|
end
|
8
8
|
|
9
|
-
class AbstractProtocol
|
9
|
+
class AbstractProtocol # :nodoc:
|
10
10
|
attr :container_class
|
11
11
|
|
12
12
|
def initialize(container_class)
|
@@ -30,14 +30,16 @@ module ActionService
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
class AbstractProtocolMessage
|
33
|
+
class AbstractProtocolMessage # :nodoc:
|
34
34
|
attr_accessor :signature
|
35
35
|
attr_accessor :return_signature
|
36
36
|
attr_accessor :type
|
37
|
+
attr :options
|
37
38
|
|
38
39
|
def initialize(options={})
|
39
40
|
@signature = @return_signature = nil
|
40
|
-
@
|
41
|
+
@options = options
|
42
|
+
@type = @options[:type] || CheckedMessage
|
41
43
|
end
|
42
44
|
|
43
45
|
def signature=(value)
|
@@ -79,14 +81,13 @@ module ActionService
|
|
79
81
|
end
|
80
82
|
end
|
81
83
|
|
82
|
-
class ProtocolRequest < AbstractProtocolMessage
|
84
|
+
class ProtocolRequest < AbstractProtocolMessage # :nodoc:
|
83
85
|
attr :protocol
|
84
86
|
attr :raw_body
|
85
87
|
|
86
88
|
attr_accessor :service_name
|
87
89
|
attr_accessor :public_method_name
|
88
90
|
attr_accessor :content_type
|
89
|
-
attr_accessor :values
|
90
91
|
|
91
92
|
def initialize(protocol, raw_body, service_name, public_method_name, content_type, options={})
|
92
93
|
super(options)
|
@@ -95,11 +96,10 @@ module ActionService
|
|
95
96
|
@service_name = service_name
|
96
97
|
@public_method_name = public_method_name
|
97
98
|
@content_type = content_type
|
98
|
-
@values = nil
|
99
99
|
end
|
100
100
|
|
101
101
|
def unmarshal
|
102
|
-
@
|
102
|
+
@protocol.unmarshal_request(self)
|
103
103
|
end
|
104
104
|
|
105
105
|
def marshal(return_value)
|
@@ -107,7 +107,7 @@ module ActionService
|
|
107
107
|
end
|
108
108
|
end
|
109
109
|
|
110
|
-
class ProtocolResponse < AbstractProtocolMessage
|
110
|
+
class ProtocolResponse < AbstractProtocolMessage # :nodoc:
|
111
111
|
attr :protocol
|
112
112
|
attr :raw_body
|
113
113
|
|
@@ -1,17 +1,17 @@
|
|
1
|
-
module ActionService
|
2
|
-
module Protocol
|
1
|
+
module ActionService # :nodoc:
|
2
|
+
module Protocol # :nodoc:
|
3
3
|
HeaderAndBody = :header_and_body
|
4
4
|
BodyOnly = :body_only
|
5
5
|
|
6
|
-
module Registry
|
7
|
-
def self.append_features(base)
|
6
|
+
module Registry # :nodoc:
|
7
|
+
def self.append_features(base) # :nodoc:
|
8
8
|
super
|
9
9
|
base.extend(ClassMethods)
|
10
10
|
base.send(:include, ActionService::Protocol::Registry::InstanceMethods)
|
11
11
|
end
|
12
12
|
|
13
|
-
module ClassMethods
|
14
|
-
def register_protocol(type, klass)
|
13
|
+
module ClassMethods # :nodoc:
|
14
|
+
def register_protocol(type, klass) # :nodoc:
|
15
15
|
case type
|
16
16
|
when HeaderAndBody
|
17
17
|
write_inheritable_array("header_and_body_protocols", [klass])
|
@@ -23,7 +23,7 @@ module ActionService
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
module InstanceMethods
|
26
|
+
module InstanceMethods # :nodoc:
|
27
27
|
private
|
28
28
|
def probe_request_protocol(action_pack_request)
|
29
29
|
(header_and_body_protocols + body_only_protocols).each do |protocol|
|