actionservice 0.2.102 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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|
|