actionservice 0.2.99
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 +3 -0
- data/HACKING +39 -0
- data/MIT-LICENSE +21 -0
- data/README +36 -0
- data/Rakefile +121 -0
- data/TODO +25 -0
- data/examples/soap/README +121 -0
- data/examples/soap/app/controllers/search_controller.rb +8 -0
- data/examples/soap/lib/google_search_service.rb +90 -0
- data/lib/action_service/base.rb +74 -0
- data/lib/action_service/container.rb +69 -0
- data/lib/action_service/invocation.rb +172 -0
- data/lib/action_service/protocol/abstract.rb +61 -0
- data/lib/action_service/protocol/registry.rb +49 -0
- data/lib/action_service/protocol/soap.rb +386 -0
- data/lib/action_service/protocol.rb +3 -0
- data/lib/action_service/router/action_controller.rb +71 -0
- data/lib/action_service/router/wsdl.rb +180 -0
- data/lib/action_service/router.rb +2 -0
- data/lib/action_service/struct.rb +15 -0
- data/lib/action_service/support/class_inheritable_options.rb +26 -0
- data/lib/action_service.rb +50 -0
- data/setup.rb +1360 -0
- data/test/abstract_unit.rb +9 -0
- data/test/base_test.rb +47 -0
- data/test/container_test.rb +29 -0
- data/test/invocation_test.rb +144 -0
- data/test/protocol_registry_test.rb +54 -0
- data/test/protocol_soap_test.rb +94 -0
- data/test/router_action_controller_test.rb +98 -0
- data/test/router_wsdl_test.rb +43 -0
- data/test/struct_test.rb +16 -0
- metadata +87 -0
@@ -0,0 +1,9 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
require 'action_service'
|
5
|
+
require 'action_controller'
|
6
|
+
require 'action_controller/test_process'
|
7
|
+
|
8
|
+
ActionController::Base.logger = nil
|
9
|
+
ActionController::Base.ignore_missing_templates = true
|
data/test/base_test.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/abstract_unit'
|
2
|
+
|
3
|
+
class BaseTestService < ActionService::Base
|
4
|
+
def add(a, b)
|
5
|
+
end
|
6
|
+
|
7
|
+
def void
|
8
|
+
end
|
9
|
+
|
10
|
+
export :add, :expects => [Integer, Integer], :returns => [Integer]
|
11
|
+
export :void
|
12
|
+
end
|
13
|
+
|
14
|
+
class UnmangledOrReportedService < ActionService::Base
|
15
|
+
export_name_mangling false
|
16
|
+
report_exceptions false
|
17
|
+
|
18
|
+
def custom_name_casing
|
19
|
+
end
|
20
|
+
|
21
|
+
export :custom_name_casing
|
22
|
+
end
|
23
|
+
|
24
|
+
class BaseTest < Test::Unit::TestCase
|
25
|
+
def test_exports
|
26
|
+
assert(BaseTestService.has_export?(:add))
|
27
|
+
assert(BaseTestService.exports.keys.map{|x|x.to_s}.sort == ['add', 'void'])
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_signatures
|
31
|
+
expects = BaseTestService.exports[:add][:expects]
|
32
|
+
assert(expects == [Integer, Integer])
|
33
|
+
returns = BaseTestService.exports[:add][:returns]
|
34
|
+
assert(returns == [Integer])
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_options
|
38
|
+
assert(UnmangledOrReportedService.export_name_mangling == false)
|
39
|
+
assert(UnmangledOrReportedService.report_exceptions == false)
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_mangling
|
43
|
+
assert(BaseTestService.has_public_export?('Add'))
|
44
|
+
assert(BaseTestService.internal_export_name('Add') == :add)
|
45
|
+
assert(UnmangledOrReportedService.public_export_name(:custom_name_casing) == 'custom_name_casing')
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/abstract_unit'
|
2
|
+
|
3
|
+
class AbstractTestContainer
|
4
|
+
end
|
5
|
+
|
6
|
+
AbstractTestContainer.class_eval do
|
7
|
+
include ActionService::Container
|
8
|
+
end
|
9
|
+
|
10
|
+
$immediate_service = Object.new
|
11
|
+
$deferred_service = Object.new
|
12
|
+
|
13
|
+
class TestContainer < AbstractTestContainer
|
14
|
+
service :immediate_service, $immediate_service
|
15
|
+
service(:deferred_service) { $deferred_service }
|
16
|
+
end
|
17
|
+
|
18
|
+
class ContainerTest < Test::Unit::TestCase
|
19
|
+
def test_registration
|
20
|
+
assert(TestContainer.has_service?(:immediate_service))
|
21
|
+
assert(TestContainer.has_service?(:deferred_service))
|
22
|
+
assert(!TestContainer.has_service?(:fake_service))
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_service_object
|
26
|
+
assert(TestContainer.service_object(:immediate_service) == $immediate_service)
|
27
|
+
assert(TestContainer.service_object(:deferred_service) == $deferred_service)
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/abstract_unit'
|
2
|
+
|
3
|
+
class InvocationTestService < ActionService::Base
|
4
|
+
attr_accessor :before_invoked
|
5
|
+
attr_accessor :after_invoked
|
6
|
+
attr_accessor :only_invoked
|
7
|
+
attr_accessor :invocation_result
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@before_invoked = nil
|
11
|
+
@after_invoked = nil
|
12
|
+
@only_invoked = nil
|
13
|
+
@invocation_result = nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def add(a, b)
|
17
|
+
a + b
|
18
|
+
end
|
19
|
+
|
20
|
+
def transmogrify(str)
|
21
|
+
str.upcase
|
22
|
+
end
|
23
|
+
|
24
|
+
def fail_with_reason
|
25
|
+
end
|
26
|
+
|
27
|
+
def fail_generic
|
28
|
+
end
|
29
|
+
|
30
|
+
def no_before
|
31
|
+
5
|
32
|
+
end
|
33
|
+
|
34
|
+
def no_after
|
35
|
+
end
|
36
|
+
|
37
|
+
def only_one
|
38
|
+
end
|
39
|
+
|
40
|
+
def only_two
|
41
|
+
end
|
42
|
+
|
43
|
+
def not_exported
|
44
|
+
end
|
45
|
+
|
46
|
+
export :add, :expects => [Integer, Integer], :returns => [Integer]
|
47
|
+
export :transmogrify, :expects_and_returns => [String]
|
48
|
+
export :fail_with_reason
|
49
|
+
export :fail_generic
|
50
|
+
export :no_before
|
51
|
+
export :no_after
|
52
|
+
export :only_one
|
53
|
+
export :only_two
|
54
|
+
|
55
|
+
before_invocation :intercept_before, :except => [:no_before]
|
56
|
+
after_invocation :intercept_after, :except => [:no_after]
|
57
|
+
|
58
|
+
before_invocation :intercept_only, :only => [:only_one, :only_two]
|
59
|
+
|
60
|
+
protected
|
61
|
+
def intercept_before(name, args)
|
62
|
+
@before_invoked = name
|
63
|
+
return [false, "permission denied"] if name == :fail_with_reason
|
64
|
+
return false if name == :fail_generic
|
65
|
+
end
|
66
|
+
|
67
|
+
def intercept_after(name, args, result)
|
68
|
+
@after_invoked = name
|
69
|
+
@invocation_result = result
|
70
|
+
end
|
71
|
+
|
72
|
+
def intercept_only(name, args)
|
73
|
+
raise "Interception error" unless name == :only_one || name == :only_two
|
74
|
+
@only_invoked = name
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class InvocationTest < Test::Unit::TestCase
|
79
|
+
|
80
|
+
def setup
|
81
|
+
@service = InvocationTestService.new
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_invocation
|
85
|
+
assert(@service.perform_invocation(:add, [5, 10]) == 15)
|
86
|
+
assert(@service.perform_invocation(:transmogrify, "hello") == "HELLO")
|
87
|
+
assert_raises(ActionService::Invocation::InvocationError) do
|
88
|
+
@service.perform_invocation(:not_exported)
|
89
|
+
end
|
90
|
+
assert_raises(ActionService::Invocation::InvocationError) do
|
91
|
+
@service.perform_invocation(:nonexistent_method_xyzzy)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_interceptor_registration
|
96
|
+
assert(InvocationTestService.before_invocation_interceptors.length == 2)
|
97
|
+
assert(InvocationTestService.after_invocation_interceptors.length == 1)
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_interception
|
101
|
+
assert(@service.before_invoked.nil? && @service.after_invoked.nil? && @service.only_invoked.nil? && @service.invocation_result.nil?)
|
102
|
+
@service.perform_invocation(:add, [20, 50])
|
103
|
+
assert(@service.before_invoked == :add)
|
104
|
+
assert(@service.after_invoked == :add)
|
105
|
+
assert(@service.invocation_result == 70)
|
106
|
+
end
|
107
|
+
|
108
|
+
def test_interception_canceling
|
109
|
+
reason = nil
|
110
|
+
@service.perform_invocation(:fail_with_reason){|r| reason = r}
|
111
|
+
assert(@service.before_invoked == :fail_with_reason)
|
112
|
+
assert(@service.after_invoked.nil?)
|
113
|
+
assert(@service.invocation_result.nil?)
|
114
|
+
assert(reason == "permission denied")
|
115
|
+
reason = true
|
116
|
+
@service.before_invoked = @service.after_invoked = @service.invocation_result = nil
|
117
|
+
@service.perform_invocation(:fail_generic){|r| reason = r}
|
118
|
+
assert(@service.before_invoked == :fail_generic)
|
119
|
+
assert(@service.after_invoked.nil?)
|
120
|
+
assert(@service.invocation_result.nil?)
|
121
|
+
assert(reason == true)
|
122
|
+
end
|
123
|
+
|
124
|
+
def test_interception_except_conditions
|
125
|
+
@service.perform_invocation(:no_before)
|
126
|
+
assert(@service.before_invoked.nil?)
|
127
|
+
assert(@service.after_invoked == :no_before)
|
128
|
+
assert(@service.invocation_result == 5)
|
129
|
+
@service.before_invoked = @service.after_invoked = @service.invocation_result = nil
|
130
|
+
@service.perform_invocation(:no_after)
|
131
|
+
assert(@service.before_invoked == :no_after)
|
132
|
+
assert(@service.after_invoked.nil?)
|
133
|
+
assert(@service.invocation_result.nil?)
|
134
|
+
end
|
135
|
+
|
136
|
+
def test_interception_only_conditions
|
137
|
+
assert(@service.only_invoked.nil?)
|
138
|
+
@service.perform_invocation(:only_one)
|
139
|
+
assert(@service.only_invoked == :only_one)
|
140
|
+
@service.only_invoked = nil
|
141
|
+
@service.perform_invocation(:only_two)
|
142
|
+
assert(@service.only_invoked == :only_two)
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/abstract_unit'
|
2
|
+
|
3
|
+
|
4
|
+
module Foo
|
5
|
+
include ActionService::Protocol
|
6
|
+
|
7
|
+
def self.append_features(base)
|
8
|
+
super
|
9
|
+
base.register_protocol(BodyOnly, FooMinimalProtocol)
|
10
|
+
base.register_protocol(HeaderAndBody, FooMinimalProtocolTwo)
|
11
|
+
base.register_protocol(HeaderAndBody, FooMinimalProtocolTwo)
|
12
|
+
base.register_protocol(HeaderAndBody, FooFullProtocol)
|
13
|
+
end
|
14
|
+
|
15
|
+
class FooFullProtocol < AbstractProtocol
|
16
|
+
def request_supported?(request)
|
17
|
+
true
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class FooMinimalProtocol < AbstractProtocol
|
22
|
+
def request_supported?(request)
|
23
|
+
true
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class FooMinimalProtocolTwo < AbstractProtocol
|
28
|
+
def request_supported?(request)
|
29
|
+
false
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class ProtocolRegistry
|
35
|
+
include ActionService::Protocol::Registry
|
36
|
+
include Foo
|
37
|
+
|
38
|
+
def all_protocols
|
39
|
+
header_and_body_protocols + body_only_protocols
|
40
|
+
end
|
41
|
+
|
42
|
+
def request_protocol
|
43
|
+
probe_request_protocol(nil)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
class ProtocolRegistryTest < Test::Unit::TestCase
|
49
|
+
def test_registration
|
50
|
+
registry = ProtocolRegistry.new
|
51
|
+
assert(registry.all_protocols.length == 4)
|
52
|
+
assert(registry.request_protocol.is_a?(Foo::FooFullProtocol))
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/abstract_unit'
|
2
|
+
require 'soap/rpc/element'
|
3
|
+
|
4
|
+
class SoapService < ActionService::Base
|
5
|
+
attr :int
|
6
|
+
attr :string
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@int = 20
|
10
|
+
@string = "wrong string value"
|
11
|
+
end
|
12
|
+
|
13
|
+
def some_method(int, string)
|
14
|
+
@int = int
|
15
|
+
@string = string
|
16
|
+
true
|
17
|
+
end
|
18
|
+
|
19
|
+
export :some_method, :expects => [Integer, String], :returns => [TrueClass]
|
20
|
+
end
|
21
|
+
|
22
|
+
class SoapContainer
|
23
|
+
include ActionService::Container
|
24
|
+
include ActionService::Protocol::Registry
|
25
|
+
include ActionService::Protocol::Soap
|
26
|
+
|
27
|
+
def request_protocol(request)
|
28
|
+
probe_request_protocol(request)
|
29
|
+
end
|
30
|
+
|
31
|
+
def dispatch_request(protocol, info)
|
32
|
+
dispatch_service_invocation_request(protocol, info)
|
33
|
+
end
|
34
|
+
|
35
|
+
wsdl_service_name 'Test'
|
36
|
+
service :soap_service, SoapService.new
|
37
|
+
end
|
38
|
+
|
39
|
+
class ProtocolSoapTest < Test::Unit::TestCase
|
40
|
+
def setup
|
41
|
+
@container = SoapContainer.new
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_soap_request_dispatching
|
45
|
+
service_name = 'soap_service'
|
46
|
+
public_method_name = 'SomeMethod'
|
47
|
+
int_value = 5
|
48
|
+
string_value = "test string"
|
49
|
+
qname = XSD::QName.new('urn:ActionService', public_method_name)
|
50
|
+
param_def = [
|
51
|
+
['in', 'param1', [SOAP::SOAPInt]],
|
52
|
+
['in', 'param2', [SOAP::SOAPString]],
|
53
|
+
]
|
54
|
+
request = SOAP::RPC::SOAPMethodRequest.new(qname, param_def)
|
55
|
+
request.set_param([
|
56
|
+
['param1', SOAP::Mapping.obj2soap(int_value)],
|
57
|
+
['param2', SOAP::Mapping.obj2soap(string_value)]
|
58
|
+
])
|
59
|
+
header = SOAP::SOAPHeader.new
|
60
|
+
body = SOAP::SOAPBody.new(request)
|
61
|
+
envelope = SOAP::SOAPEnvelope.new(header, body)
|
62
|
+
raw_request = SOAP::Processor.marshal(envelope)
|
63
|
+
test_request = ActionController::TestRequest.new
|
64
|
+
test_request.request_parameters['action'] = service_name
|
65
|
+
test_request.env['REQUEST_METHOD'] = "POST"
|
66
|
+
test_request.env['HTTP_CONTENTTYPE'] = 'text/xml'
|
67
|
+
test_request.env['HTTP_SOAPACTION'] = "/soap/#{service_name}/#{public_method_name}"
|
68
|
+
test_request.env['RAW_POST_DATA'] = raw_request
|
69
|
+
protocol = @container.request_protocol(test_request)
|
70
|
+
assert(protocol.is_a?(ActionService::Protocol::Soap::SoapProtocol))
|
71
|
+
request_info = protocol.request_info(test_request)
|
72
|
+
assert(request_info.service_name == service_name)
|
73
|
+
assert(request_info.public_method_name == public_method_name)
|
74
|
+
method_name = SoapService.internal_export_name(public_method_name)
|
75
|
+
export_info = SoapService.exports[method_name]
|
76
|
+
params = protocol.unmarshal_request(request_info, export_info)
|
77
|
+
assert(params.length == 2)
|
78
|
+
assert(params == [int_value, string_value])
|
79
|
+
response = @container.dispatch_request(protocol, request_info)
|
80
|
+
service = SoapContainer.service_object(:soap_service)
|
81
|
+
assert(service.int == int_value)
|
82
|
+
assert(service.string == string_value)
|
83
|
+
raw_response = response.raw_body
|
84
|
+
envelope = SOAP::Processor.unmarshal(raw_response)
|
85
|
+
assert(envelope.is_a?(SOAP::SOAPEnvelope))
|
86
|
+
resp = envelope.body.response
|
87
|
+
assert(resp.is_a?(SOAP::SOAPBoolean))
|
88
|
+
assert(resp.data == true)
|
89
|
+
end
|
90
|
+
|
91
|
+
def test_service_name
|
92
|
+
assert(SoapContainer.soap_mapper.custom_namespace == 'urn:Test')
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/abstract_unit'
|
2
|
+
require 'wsdl/parser'
|
3
|
+
|
4
|
+
class ActionTestPerson < ActionService::Struct
|
5
|
+
member :id, Integer
|
6
|
+
member :names, [String]
|
7
|
+
member :lastname, String
|
8
|
+
member :deleted, TrueClass
|
9
|
+
end
|
10
|
+
|
11
|
+
class ActionTestService < ActionService::Base
|
12
|
+
attr :added
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@added = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def add(a, b)
|
19
|
+
@added = a + b
|
20
|
+
end
|
21
|
+
|
22
|
+
def find_people
|
23
|
+
raise "Hi, I'm a SOAP error"
|
24
|
+
end
|
25
|
+
|
26
|
+
export :add, :expects => [Integer, Integer], :returns => [Integer]
|
27
|
+
export :find_people, :returns => [[ActionTestPerson]]
|
28
|
+
end
|
29
|
+
|
30
|
+
class ActionTestController < ActionController::Base
|
31
|
+
wsdl_service_name 'ActionTest'
|
32
|
+
service(:test_service) { ActionTestService.new }
|
33
|
+
end
|
34
|
+
|
35
|
+
class RouterActionControllerTest < Test::Unit::TestCase
|
36
|
+
def setup
|
37
|
+
@controller = ActionTestController.new
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_standard_invocation
|
41
|
+
service_name = 'test_service'
|
42
|
+
public_method_name = 'Add'
|
43
|
+
a = 20
|
44
|
+
b = 50
|
45
|
+
qname = XSD::QName.new('urn:ActionTest', public_method_name)
|
46
|
+
param_def = [
|
47
|
+
['in', 'param1', [SOAP::SOAPInt]],
|
48
|
+
['in', 'param2', [SOAP::SOAPString]],
|
49
|
+
]
|
50
|
+
request = SOAP::RPC::SOAPMethodRequest.new(qname, param_def)
|
51
|
+
request.set_param([
|
52
|
+
['param1', SOAP::Mapping.obj2soap(a)],
|
53
|
+
['param2', SOAP::Mapping.obj2soap(b)]
|
54
|
+
])
|
55
|
+
header = SOAP::SOAPHeader.new
|
56
|
+
body = SOAP::SOAPBody.new(request)
|
57
|
+
envelope = SOAP::SOAPEnvelope.new(header, body)
|
58
|
+
raw_request = SOAP::Processor.marshal(envelope)
|
59
|
+
test_request = ActionController::TestRequest.new
|
60
|
+
test_request.request_parameters['action'] = service_name
|
61
|
+
test_request.env['REQUEST_METHOD'] = "POST"
|
62
|
+
test_request.env['HTTP_CONTENT_TYPE'] = 'text/xml'
|
63
|
+
test_request.env['HTTP_SOAPACTION'] = "/soap/#{service_name}/#{public_method_name}"
|
64
|
+
test_request.env['RAW_POST_DATA'] = raw_request
|
65
|
+
test_response = ActionController::TestResponse.new
|
66
|
+
@controller.process(test_request, test_response)
|
67
|
+
raw_response = test_response.body
|
68
|
+
envelope = SOAP::Processor.unmarshal(raw_response)
|
69
|
+
assert(envelope.is_a?(SOAP::SOAPEnvelope))
|
70
|
+
resp = envelope.body.response
|
71
|
+
assert(resp.is_a?(SOAP::SOAPInt))
|
72
|
+
assert(resp.data == 70)
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_error_invocation
|
76
|
+
service_name = 'test_service'
|
77
|
+
public_method_name = 'FindPeople'
|
78
|
+
qname = XSD::QName.new('urn:ActionTest', public_method_name)
|
79
|
+
request = SOAP::RPC::SOAPMethodRequest.new(qname)
|
80
|
+
header = SOAP::SOAPHeader.new
|
81
|
+
body = SOAP::SOAPBody.new(request)
|
82
|
+
envelope = SOAP::SOAPEnvelope.new(header, body)
|
83
|
+
raw_request = SOAP::Processor.marshal(envelope)
|
84
|
+
test_request = ActionController::TestRequest.new
|
85
|
+
test_request.request_parameters['action'] = service_name
|
86
|
+
test_request.env['REQUEST_METHOD'] = "POST"
|
87
|
+
test_request.env['HTTP_CONTENT_TYPE'] = 'text/xml'
|
88
|
+
test_request.env['HTTP_SOAPACTION'] = "/soap/#{service_name}/#{public_method_name}"
|
89
|
+
test_request.env['RAW_POST_DATA'] = raw_request
|
90
|
+
test_response = ActionController::TestResponse.new
|
91
|
+
@controller.process(test_request, test_response)
|
92
|
+
raw_response = test_response.body
|
93
|
+
envelope = SOAP::Processor.unmarshal(raw_response)
|
94
|
+
assert(envelope.is_a?(SOAP::SOAPEnvelope))
|
95
|
+
resp = envelope.body.response
|
96
|
+
assert(resp.is_a?(SOAP::SOAPFault))
|
97
|
+
end
|
98
|
+
end
|