sk-hoth 0.0.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +69 -39
- data/THANKS.md +9 -0
- data/TODO +2 -0
- data/lib/hoth/encoding/json.rb +28 -0
- data/lib/hoth/encoding/no_op.rb +19 -0
- data/lib/hoth/endpoint.rb +28 -0
- data/lib/hoth/exceptions.rb +14 -0
- data/lib/hoth/extension/core/exception.rb +15 -0
- data/lib/hoth/modules.rb +27 -0
- data/lib/hoth/providers/bertrpc_provider.rb +35 -0
- data/lib/hoth/providers/rack_provider.rb +45 -0
- data/lib/hoth/service.rb +50 -0
- data/lib/hoth/service_definition.rb +18 -0
- data/lib/hoth/service_module.rb +49 -0
- data/lib/hoth/service_registry.rb +34 -0
- data/lib/hoth/services.rb +51 -0
- data/lib/hoth/transport/base.rb +19 -0
- data/lib/hoth/transport/bert.rb +87 -0
- data/lib/hoth/transport/http.rb +41 -0
- data/lib/hoth/transport/http_hmac.rb +37 -0
- data/lib/hoth/transport/workling.rb +23 -0
- data/lib/hoth/transport.rb +48 -0
- data/lib/hoth/util/logger.rb +46 -0
- data/lib/hoth.rb +56 -0
- data/spec/spec_helper.rb +7 -26
- data/spec/unit/encoding/json_spec.rb +25 -0
- data/spec/unit/endpoint_spec.rb +34 -0
- data/spec/unit/extension/core/exception_spec.rb +34 -0
- data/spec/unit/hoth_spec.rb +30 -0
- data/spec/unit/providers/rack_provider_spec.rb +49 -0
- data/spec/unit/service_definition_spec.rb +21 -0
- data/spec/unit/service_module_spec.rb +59 -0
- data/spec/unit/service_spec.rb +77 -0
- data/spec/unit/transport/base_spec.rb +43 -0
- data/spec/unit/transport/http_hmac_spec.rb +44 -0
- data/spec/unit/transport/http_spec.rb +73 -0
- data/spec/unit/transport/workling_spec.rb +42 -0
- data/spec/unit/transport_spec.rb +29 -0
- metadata +86 -23
- data/lib/king_soa/rack/middleware.rb +0 -47
- data/lib/king_soa/registry.rb +0 -55
- data/lib/king_soa/service.rb +0 -88
- data/lib/king_soa.rb +0 -56
- data/spec/king_soa/rack/middleware_spec.rb +0 -36
- data/spec/king_soa/registry_spec.rb +0 -28
- data/spec/king_soa/service_spec.rb +0 -46
- data/spec/server/app.rb +0 -26
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
|
3
|
+
module Hoth
|
4
|
+
module Transport
|
5
|
+
class Http < Base
|
6
|
+
def call_remote_with(*params)
|
7
|
+
begin
|
8
|
+
handle_response post_payload(params)
|
9
|
+
rescue Exception => e
|
10
|
+
raise TransportError.wrap(e)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def handle_response(response)
|
15
|
+
case response
|
16
|
+
when Net::HTTPSuccess
|
17
|
+
Hoth::Logger.debug "response.body: #{response.body}"
|
18
|
+
encoder.decode(response.body)["result"]
|
19
|
+
when Net::HTTPServerError
|
20
|
+
begin
|
21
|
+
Hoth::Logger.debug "response.body: #{response.body}"
|
22
|
+
raise encoder.decode(response.body)["error"]
|
23
|
+
rescue JSON::ParserError => jpe
|
24
|
+
raise TransportError.wrap(jpe)
|
25
|
+
end
|
26
|
+
when Net::HTTPRedirection, Net::HTTPClientError, Net::HTTPInformation, Net::HTTPUnknownResponse
|
27
|
+
raise NotImplementedError, "code: #{response.code}, message: #{response.body}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def post_payload(payload)
|
32
|
+
uri = URI.parse(self.endpoint.to_url)
|
33
|
+
return Net::HTTP.post_form(uri,
|
34
|
+
'name' => self.name.to_s,
|
35
|
+
'params' => encoder.encode(payload)
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'king_hmac'
|
2
|
+
require 'net/http'
|
3
|
+
module Hoth
|
4
|
+
module Transport
|
5
|
+
class HttpHmac < Http
|
6
|
+
attr_accessor :hmac_access_id, :hmac_secret
|
7
|
+
# just for testing request
|
8
|
+
attr_accessor :req
|
9
|
+
# Manually construct the HTTP request because it needs to be signed with
|
10
|
+
# hmac before.
|
11
|
+
# This function is more or less a copy of HTTP.post_form
|
12
|
+
# == Prerequisites
|
13
|
+
# you must set both instance variables: hmac_access_id, hmac_secret before
|
14
|
+
# the call can be made
|
15
|
+
# === Returns
|
16
|
+
# <Object>:. Net::HTTPResponse
|
17
|
+
def post_payload(payload)
|
18
|
+
raise TransportError, 'no hmac credentials set for hmac transport' unless hmac_access_id && hmac_secret
|
19
|
+
uri = URI.parse(self.endpoint.to_url)
|
20
|
+
# construct request object
|
21
|
+
self.req = Net::HTTP::Post.new(uri.path)
|
22
|
+
# add its form data
|
23
|
+
self.req.form_data = {'name' => self.name.to_s,
|
24
|
+
'params' => encoder.encode(payload) }
|
25
|
+
# ensure a date header is set, needed for hmac
|
26
|
+
self.req['Date'] = Time.now.httpdate if self.req['Date'].nil?
|
27
|
+
# sign request object => set Authorisation header
|
28
|
+
KingHmac::Auth.sign!(self.req, hmac_access_id, hmac_secret)
|
29
|
+
# go for it
|
30
|
+
return Net::HTTP.new(uri.host, uri.port).start {|http|
|
31
|
+
http.request(self.req)
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
begin
|
2
|
+
require 'simple_publisher'
|
3
|
+
rescue LoadError
|
4
|
+
STDERR.puts "You need the simple_publisher gem if you want to use Workling/Starling transport."
|
5
|
+
end
|
6
|
+
|
7
|
+
module Hoth
|
8
|
+
module Transport
|
9
|
+
|
10
|
+
class Workling < Base
|
11
|
+
|
12
|
+
def call_remote_with(*args)
|
13
|
+
topic = SimplePublisher::Topic.new(:name => "#{self.module.name.to_s.underscore}_subscribers__#{name.to_s.underscore}")
|
14
|
+
connection = SimplePublisher::StarlingConnection.new(:host => endpoint.host, :port => endpoint.port)
|
15
|
+
|
16
|
+
publisher = SimplePublisher::Publisher.new(:topic => topic, :connection => connection)
|
17
|
+
publisher.publish(encoder.encode(args))
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'hoth/transport/base'
|
2
|
+
require 'hoth/transport/http'
|
3
|
+
require 'hoth/transport/http_hmac'
|
4
|
+
require 'hoth/transport/bert'
|
5
|
+
require 'hoth/transport/workling'
|
6
|
+
|
7
|
+
require 'hoth/encoding/json'
|
8
|
+
require 'hoth/encoding/no_op'
|
9
|
+
|
10
|
+
module Hoth
|
11
|
+
module Transport
|
12
|
+
|
13
|
+
POSSIBLE_TRANSPORTS = {
|
14
|
+
:json_via_http => {
|
15
|
+
:transport_class => Transport::Http,
|
16
|
+
:encoder => Encoding::Json
|
17
|
+
},
|
18
|
+
:http => :json_via_http,
|
19
|
+
:json_via_http_hmac => {
|
20
|
+
:transport_class => Transport::HttpHmac,
|
21
|
+
:encoder => Encoding::Json
|
22
|
+
},
|
23
|
+
:http_hmac => :json_via_http_hmac,
|
24
|
+
:workling => {
|
25
|
+
:transport_class => Transport::Workling
|
26
|
+
}
|
27
|
+
}
|
28
|
+
|
29
|
+
class <<self
|
30
|
+
def create(transport_name, service)
|
31
|
+
new_transport_with_encoding(transport_name, service)
|
32
|
+
end
|
33
|
+
|
34
|
+
def new_transport_with_encoding(transport_name, service)
|
35
|
+
if POSSIBLE_TRANSPORTS[transport_name.to_sym]
|
36
|
+
if POSSIBLE_TRANSPORTS[transport_name.to_sym].kind_of?(Hash)
|
37
|
+
POSSIBLE_TRANSPORTS[transport_name.to_sym][:transport_class].new(service, :encoder => POSSIBLE_TRANSPORTS[transport_name.to_sym][:encoder])
|
38
|
+
else
|
39
|
+
new_transport_with_encoding(POSSIBLE_TRANSPORTS[transport_name.to_sym], service)
|
40
|
+
end
|
41
|
+
else
|
42
|
+
raise TransportException.new("specified transport '#{transport_name}' does not exist, use one of these: #{POSSIBLE_TRANSPORTS.keys.join(", ")}")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Hoth
|
2
|
+
class Logger
|
3
|
+
class <<self
|
4
|
+
|
5
|
+
def log_provider=(log_provider)
|
6
|
+
@log_provider = log_provider
|
7
|
+
end
|
8
|
+
|
9
|
+
def init_logging!
|
10
|
+
Hoth::Logger.log_provider = if Object.const_defined?("Rails")
|
11
|
+
Rails.logger
|
12
|
+
else
|
13
|
+
require 'logger'
|
14
|
+
::Logger.new("/tmp/hoth.log")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def debug(msg)
|
19
|
+
log_provider.debug msg
|
20
|
+
end
|
21
|
+
|
22
|
+
def info(msg)
|
23
|
+
log_provider.info msg
|
24
|
+
end
|
25
|
+
|
26
|
+
def warn(msg)
|
27
|
+
log_provider.warn msg
|
28
|
+
end
|
29
|
+
|
30
|
+
def error(msg)
|
31
|
+
log_provider.error msg
|
32
|
+
end
|
33
|
+
|
34
|
+
def fatal(msg)
|
35
|
+
log_provider.fatal msg
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def log_provider
|
41
|
+
@log_provider || init_logging!
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
data/lib/hoth.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
require 'active_support/inflector'
|
4
|
+
|
5
|
+
# must be loaded after alls transports and all encodings
|
6
|
+
require 'hoth/transport'
|
7
|
+
|
8
|
+
require 'hoth/service_definition'
|
9
|
+
require 'hoth/service_module'
|
10
|
+
require 'hoth/endpoint'
|
11
|
+
require 'hoth/service'
|
12
|
+
require 'hoth/modules'
|
13
|
+
require 'hoth/service_registry'
|
14
|
+
require 'hoth/services'
|
15
|
+
|
16
|
+
require 'hoth/util/logger'
|
17
|
+
|
18
|
+
require 'hoth/extension/core/exception'
|
19
|
+
require 'hoth/exceptions'
|
20
|
+
|
21
|
+
module Hoth
|
22
|
+
|
23
|
+
class <<self
|
24
|
+
def init!
|
25
|
+
load_service_definition
|
26
|
+
load_module_definition
|
27
|
+
Logger.init_logging!
|
28
|
+
end
|
29
|
+
|
30
|
+
def config_path
|
31
|
+
@config_path || "config/"
|
32
|
+
end
|
33
|
+
|
34
|
+
def config_path=(config_path)
|
35
|
+
@config_path = config_path
|
36
|
+
end
|
37
|
+
|
38
|
+
def load_service_definition
|
39
|
+
require File.join(config_path, "service_definition")
|
40
|
+
end
|
41
|
+
|
42
|
+
def load_module_definition
|
43
|
+
require File.join(config_path, "module_definition")
|
44
|
+
end
|
45
|
+
|
46
|
+
def env
|
47
|
+
@env || ENV["HOTH_ENV"] || (Object.const_defined?("Rails") ? Rails.env.to_sym : :development)
|
48
|
+
end
|
49
|
+
|
50
|
+
def env=(env)
|
51
|
+
@env = env.to_sym
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -2,36 +2,17 @@ require 'rubygems'
|
|
2
2
|
|
3
3
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
4
4
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
5
|
-
require '
|
5
|
+
require 'hoth'
|
6
6
|
require 'spec'
|
7
7
|
require 'spec/autorun'
|
8
8
|
# for mocking web requests
|
9
9
|
require 'webmock/rspec'
|
10
|
-
require 'rack/test'
|
11
10
|
include WebMock
|
12
11
|
|
12
|
+
Hoth.env = "test"
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
# end
|
20
|
-
|
21
|
-
# Rack::Builder.new do
|
22
|
-
# use KingSoa::Rack::Middleware
|
23
|
-
# app = proc do |env|
|
24
|
-
# [ 200, {'Content-Type' => 'text/plain'}, "b" ]
|
25
|
-
# end
|
26
|
-
# run app
|
27
|
-
# end.to_app
|
28
|
-
# def app
|
29
|
-
# Rack::Builder.new {
|
30
|
-
# # URLs starting with /account (logged in users) go to Rails
|
31
|
-
# map "/soa" do
|
32
|
-
# run KingSoa::Rack::Middleware.new
|
33
|
-
# end
|
34
|
-
# }.to_app
|
35
|
-
# end
|
36
|
-
# app.run
|
37
|
-
end
|
14
|
+
Spec::Matchers.define :string_matching do |regex|
|
15
|
+
match do |string|
|
16
|
+
string =~ regex
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '../../', 'spec_helper'))
|
2
|
+
|
3
|
+
module Hoth
|
4
|
+
module Encoding
|
5
|
+
|
6
|
+
describe Json do
|
7
|
+
|
8
|
+
it "should decode a JSON string" do
|
9
|
+
decoded_json = Json.decode '{"test":23}'
|
10
|
+
decoded_json.should ==({"test" => 23})
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should encode a JSON string" do
|
14
|
+
encoded_json = Json.encode({"test" => 23})
|
15
|
+
'{"test":23}'.should == encoded_json
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should know its ContentType" do
|
19
|
+
Json.content_type.should == "application/json"
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
|
2
|
+
|
3
|
+
module Hoth
|
4
|
+
|
5
|
+
describe Endpoint do
|
6
|
+
|
7
|
+
it "should have a port" do
|
8
|
+
endpoint = Endpoint.new { port 3000 }
|
9
|
+
endpoint.port.should == 3000
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should have a host name" do
|
13
|
+
endpoint = Endpoint.new { host "example.com" }
|
14
|
+
endpoint.host.should == "example.com"
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should have a transport name" do
|
18
|
+
endpoint = Endpoint.new { transport :json_via_http }
|
19
|
+
endpoint.transport.should == :json_via_http
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should should cast itself to URL string" do
|
23
|
+
endpoint = Endpoint.new { port 3000; host "example.com" }
|
24
|
+
endpoint.to_url.should == "http://example.com:3000/execute"
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should should know the deployment module this endpoint is associated to" do
|
28
|
+
endpoint = Endpoint.new { module_name "TestModule" }
|
29
|
+
endpoint.module_name.should == "TestModule"
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '../../../', 'spec_helper'))
|
2
|
+
|
3
|
+
describe Exception do
|
4
|
+
|
5
|
+
it "should be able to create json with empty backtrace" do
|
6
|
+
e = Exception.new "message"
|
7
|
+
e.to_json.should == "{\"json_class\":\"Exception\",\"message\":\"message\",\"backtrace\":null}"
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should be able to create json with backtrace" do
|
11
|
+
e = Exception.new "message"
|
12
|
+
e.set_backtrace ["back", "trace"]
|
13
|
+
e.to_json.should == "{\"json_class\":\"Exception\",\"message\":\"message\",\"backtrace\":[\"back\",\"trace\"]}"
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should be able to deserialize exception from json" do
|
17
|
+
e = Exception.new "message"
|
18
|
+
e.set_backtrace ["back", "trace"]
|
19
|
+
deserialized = JSON(e.to_json)
|
20
|
+
deserialized.message.should == "message"
|
21
|
+
deserialized.backtrace.should == ["back", "trace"]
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should be able to serialize and deserialize descendants of the Exception class" do
|
25
|
+
class ExceptionSpec < Exception; end
|
26
|
+
e = ExceptionSpec.new "message"
|
27
|
+
e.set_backtrace ["back", "trace"]
|
28
|
+
deserialized = JSON(e.to_json)
|
29
|
+
deserialized.message.should == "message"
|
30
|
+
deserialized.backtrace.should == ["back", "trace"]
|
31
|
+
deserialized.should be_a ExceptionSpec
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hoth do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
@old_hoth_env = Hoth.env
|
7
|
+
Hoth.instance_variable_set "@env", nil
|
8
|
+
end
|
9
|
+
|
10
|
+
after(:each) do
|
11
|
+
Hoth.env = @old_hoth_env
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should set the environment explicitly" do
|
15
|
+
Hoth.env = :test
|
16
|
+
Hoth.env.should == :test
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should default to :development if no environment is set" do
|
20
|
+
Hoth.env.should equal(:development)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should return the Rails env if Rails is available" do
|
24
|
+
module Rails; end
|
25
|
+
Rails.should_receive(:env).and_return(:production)
|
26
|
+
Hoth.env.should equal(:production)
|
27
|
+
Object.send :remove_const, :Rails
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '../../', 'spec_helper'))
|
2
|
+
|
3
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '../../../', 'lib', 'hoth', 'providers', 'rack_provider'))
|
4
|
+
|
5
|
+
require 'rack/mock'
|
6
|
+
|
7
|
+
module Hoth
|
8
|
+
module Providers
|
9
|
+
|
10
|
+
describe RackProvider do
|
11
|
+
|
12
|
+
it "should get transport and encoder based on called service" do
|
13
|
+
app = stub("ApplicationStub").as_null_object
|
14
|
+
middleware = Hoth::Providers::RackProvider.new(app)
|
15
|
+
|
16
|
+
encoder = mock("EncoderMock")
|
17
|
+
encoder.should_receive(:decode).with("some_parameter").and_return(decoded_params = "some_parameter_decoded")
|
18
|
+
encoder.should_receive(:content_type).and_return("application/json")
|
19
|
+
|
20
|
+
transport = mock("TransportMock")
|
21
|
+
transport.should_receive(:encoder).exactly(3).times.and_return(encoder)
|
22
|
+
|
23
|
+
service = mock("ServiceMock")
|
24
|
+
service.should_receive(:transport).exactly(3).times.and_return(transport)
|
25
|
+
ServiceRegistry.should_receive(:locate_service).with("service_name").and_return(service)
|
26
|
+
|
27
|
+
Hoth::Services.should_receive(:send).with("service_name", *decoded_params).and_return(service_result = "result")
|
28
|
+
encoder.should_receive(:encode).with({"result" => service_result}).and_return("result_encoded")
|
29
|
+
|
30
|
+
mock_request = Rack::MockRequest.new(middleware)
|
31
|
+
mock_request.post("http://localhost/execute?name=service_name¶ms=some_parameter")
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should be able to handle exceptions" do
|
35
|
+
app = stub("ApplicationStub").as_null_object
|
36
|
+
middleware = Hoth::Providers::RackProvider.new app
|
37
|
+
env = {"PATH_INFO" => "/execute/some_method", "other_params" => nil}
|
38
|
+
Rack::Request.should_receive(:new).and_raise(RuntimeError)
|
39
|
+
|
40
|
+
rack_response = middleware.call env
|
41
|
+
rack_response.first.should == 500 #status code
|
42
|
+
rack_response.last.should be_a_kind_of(Array)
|
43
|
+
rack_response.last.first.should == "An error occuered! (RuntimeError)"
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
|
2
|
+
|
3
|
+
module Hoth
|
4
|
+
describe ServiceDefinition do
|
5
|
+
|
6
|
+
it "should create a Service and add it to the registry instance" do
|
7
|
+
service_name = :my_service
|
8
|
+
|
9
|
+
definition = ServiceDefinition.new
|
10
|
+
definition.service service_name do |some_params|
|
11
|
+
returns :nothing
|
12
|
+
end
|
13
|
+
|
14
|
+
service = ServiceRegistry.locate_service(service_name)
|
15
|
+
service.should_not be(nil)
|
16
|
+
service.params_arity.should be(1)
|
17
|
+
service.return_nothing?.should be(true)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
|
2
|
+
|
3
|
+
module Hoth
|
4
|
+
|
5
|
+
describe ServiceModule do
|
6
|
+
|
7
|
+
it "should have a name" do
|
8
|
+
service_module = ServiceModule.new :name => :my_service_module
|
9
|
+
service_module.name.should equal(:my_service_module)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should have an environment" do
|
13
|
+
service_module = ServiceModule.new(:name => "service_module_name")
|
14
|
+
block = Proc.new {}
|
15
|
+
service_module.env :test, &block
|
16
|
+
service_module[:test].should be_a(ServiceModule::Environment)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should have multiple environments" do
|
20
|
+
service_module = ServiceModule.new(:name => "service_module_name")
|
21
|
+
block = Proc.new {}
|
22
|
+
service_module.env :test, :development, &block
|
23
|
+
service_module[:test].should be_a(ServiceModule::Environment)
|
24
|
+
service_module[:development].should be_a(ServiceModule::Environment)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should be able to add services" do
|
28
|
+
service_module = ServiceModule.new(:name => "service_module_name")
|
29
|
+
block = Proc.new {}
|
30
|
+
|
31
|
+
service_module.env :test, &block
|
32
|
+
|
33
|
+
ServiceRegistry.should_receive(:locate_service).with(:service_name).and_return(service = mock("ServiceMock"))
|
34
|
+
service.should_receive(:module=).with(service_module)
|
35
|
+
service.should_receive(:via_endpoint).with(:special_endpoint)
|
36
|
+
|
37
|
+
service_module.add_service("service_name", :via => :special_endpoint)
|
38
|
+
end
|
39
|
+
|
40
|
+
describe ServiceModule::Environment do
|
41
|
+
|
42
|
+
it "should have an endpoint" do
|
43
|
+
endpoint_mock = mock("Endpoint", :null_object => true)
|
44
|
+
|
45
|
+
endpoint_block = Proc.new do
|
46
|
+
endpoint :development do
|
47
|
+
host 'localhost'
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
env = ServiceModule::Environment.new(&endpoint_block)
|
52
|
+
env[:development].should_not be(nil)
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
|
2
|
+
|
3
|
+
class TestServiceImpl
|
4
|
+
def self.execute(param1, param2)
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
module Hoth
|
9
|
+
|
10
|
+
describe Service do
|
11
|
+
|
12
|
+
it "should define parameters and return values" do
|
13
|
+
service_block = Proc.new { |param_1, param_2, param_3| returns :some_data }
|
14
|
+
|
15
|
+
service = Service.new("TestService", &service_block)
|
16
|
+
|
17
|
+
service.params_arity.should be(3)
|
18
|
+
service.return_nothing?.should be(false)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should know its service-impl class" do
|
22
|
+
service = Service.new("TestService") {}
|
23
|
+
service.impl_class
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should know that its service-impl class is not available" do
|
27
|
+
service = Service.new("TestServiceWithoutImplClass") {}
|
28
|
+
service.impl_class.should be(false)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should know if it is local or not based on Impl-Class availability" do
|
32
|
+
service = Service.new("TestServiceWithoutImplClass") {}
|
33
|
+
service.is_local?.should be(false)
|
34
|
+
|
35
|
+
service = Service.new("test_service") {}
|
36
|
+
service.is_local?.should be(true)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should execute the service stub locally if its impl-class was found" do
|
40
|
+
service = Service.new("test_service") { |p1, p2| returns :nothing }
|
41
|
+
|
42
|
+
service.should_receive(:is_local?).and_return(true)
|
43
|
+
service.impl_class.should_receive(:execute).with(:arg1, :arg2)
|
44
|
+
|
45
|
+
service.execute(:arg1, :arg2).should be(nil)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should execute the service stub locally if its impl-class was found and return a value" do
|
49
|
+
service = Service.new("test_service") { |p1, p2| returns :value }
|
50
|
+
|
51
|
+
service.should_receive(:is_local?).and_return(true)
|
52
|
+
service.impl_class.should_receive(:execute).with(:arg1, :arg2).and_return(result = mock("ResultMock"))
|
53
|
+
|
54
|
+
service.execute(:arg1, :arg2).should be(result)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should call the remote service if impl-class does not exist" do
|
58
|
+
service = Service.new("test_service_without_impl") { |p1, p2| returns :nothing }
|
59
|
+
|
60
|
+
service.should_receive(:is_local?).and_return(false)
|
61
|
+
service.should_receive(:transport).and_return(transport = mock("TransportMock"))
|
62
|
+
transport.should_receive(:call_remote_with).with(:arg1, :arg2)
|
63
|
+
|
64
|
+
service.execute(:arg1, :arg2)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should create transport instance based on endpoint" do
|
68
|
+
service = Service.new("test_service") { |p1, p2| returns :nothing }
|
69
|
+
service.should_receive(:endpoint).and_return(endpoint = mock("EndpointMock"))
|
70
|
+
endpoint.should_receive(:transport).and_return(:http)
|
71
|
+
Hoth::Transport.should_receive(:create).with(:http, service)
|
72
|
+
service.transport
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '../../', 'spec_helper'))
|
2
|
+
|
3
|
+
module Hoth
|
4
|
+
module Transport
|
5
|
+
|
6
|
+
describe "Base" do
|
7
|
+
|
8
|
+
it "should initialize with service-delegate" do
|
9
|
+
service = mock("ServiceMock")
|
10
|
+
transport = Base.new(service)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should use an NoOp encoder if no encoder class was given at all" do
|
14
|
+
service = mock("ServiceMock")
|
15
|
+
transport = Base.new(service)
|
16
|
+
transport.encoder.should == Encoding::NoOp
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should delegate calls to service-delegate" do
|
20
|
+
service = mock("ServiceMock")
|
21
|
+
service.should_receive(:name)
|
22
|
+
service.should_receive(:module)
|
23
|
+
service.should_receive(:endpoint)
|
24
|
+
service.should_receive(:params)
|
25
|
+
service.should_receive(:return_nothing?)
|
26
|
+
transport = Base.new(service)
|
27
|
+
|
28
|
+
[:name, :module, :endpoint, :params, :return_nothing?].each do |method|
|
29
|
+
transport.send(method)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should have an encoder" do
|
34
|
+
service = mock("ServiceMock")
|
35
|
+
encoder = mock("EncoderMock")
|
36
|
+
transport = Base.new(service, :encoder => encoder)
|
37
|
+
transport.encoder.should be(encoder)
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|