hoth 0.2.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +4 -4
- data/TODO +2 -0
- data/lib/hoth.rb +2 -4
- data/lib/hoth/encoding/json.rb +28 -0
- data/lib/hoth/encoding/no_op.rb +19 -0
- data/lib/hoth/endpoint.rb +3 -2
- data/lib/hoth/exceptions.rb +1 -0
- data/lib/hoth/extension/core/exception.rb +2 -2
- data/lib/hoth/providers/bertrpc_provider.rb +1 -1
- data/lib/hoth/providers/rack_provider.rb +18 -6
- data/lib/hoth/service.rb +6 -11
- data/lib/hoth/service_definition.rb +11 -0
- data/lib/hoth/service_module.rb +2 -8
- data/lib/hoth/service_registry.rb +11 -4
- data/lib/hoth/services.rb +35 -7
- data/lib/hoth/transport.rb +42 -0
- data/lib/hoth/transport/{hoth_transport.rb → base.rb} +7 -4
- data/lib/hoth/transport/{bert_transport.rb → bert.rb} +1 -1
- data/lib/hoth/transport/http.rb +45 -0
- data/lib/hoth/transport/{workling_transport.rb → workling.rb} +2 -10
- data/spec/unit/encoding/json_spec.rb +25 -0
- data/spec/unit/endpoint_spec.rb +24 -20
- data/spec/unit/providers/rack_provider_spec.rb +42 -10
- data/spec/unit/service_definition_spec.rb +1 -1
- data/spec/unit/service_spec.rb +15 -8
- data/spec/unit/transport/base_spec.rb +43 -0
- data/spec/unit/transport/http_spec.rb +77 -0
- data/spec/unit/transport/{workling_transport_spec.rb → workling_spec.rb} +2 -9
- data/spec/unit/transport_spec.rb +29 -0
- metadata +23 -11
- data/lib/hoth/transport/amqp_transport.rb +0 -7
- data/lib/hoth/transport/http_transport.rb +0 -43
data/README.rdoc
CHANGED
@@ -40,13 +40,13 @@ After defining all you services, you need to specify in which modules they live.
|
|
40
40
|
endpoint :default do
|
41
41
|
host 'localhost'
|
42
42
|
port 3000
|
43
|
-
|
43
|
+
transport :http
|
44
44
|
end
|
45
45
|
|
46
46
|
endpoint :bert do
|
47
47
|
host 'localhost'
|
48
48
|
port 9999
|
49
|
-
|
49
|
+
transport :bert
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
@@ -54,13 +54,13 @@ After defining all you services, you need to specify in which modules they live.
|
|
54
54
|
endpoint :default do
|
55
55
|
host '192.168.1.12'
|
56
56
|
port 3000
|
57
|
-
|
57
|
+
transport :http
|
58
58
|
end
|
59
59
|
|
60
60
|
endpoint :bert do
|
61
61
|
host '192.168.1.15'
|
62
62
|
port 9999
|
63
|
-
|
63
|
+
transport :bert
|
64
64
|
end
|
65
65
|
end
|
66
66
|
|
data/TODO
ADDED
data/lib/hoth.rb
CHANGED
@@ -2,10 +2,8 @@ require 'singleton'
|
|
2
2
|
|
3
3
|
require 'active_support/inflector'
|
4
4
|
|
5
|
-
|
6
|
-
require 'hoth/transport
|
7
|
-
require 'hoth/transport/bert_transport'
|
8
|
-
require 'hoth/transport/workling_transport'
|
5
|
+
# must be loaded after alls transports and all encodings
|
6
|
+
require 'hoth/transport'
|
9
7
|
|
10
8
|
require 'hoth/service_definition'
|
11
9
|
require 'hoth/service_module'
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Hoth
|
4
|
+
module Encoding
|
5
|
+
class Json
|
6
|
+
|
7
|
+
class <<self
|
8
|
+
def encode(string)
|
9
|
+
string.to_json
|
10
|
+
end
|
11
|
+
|
12
|
+
def decode(string)
|
13
|
+
begin
|
14
|
+
Hoth::Logger.debug "Original params before decode: #{string.inspect}"
|
15
|
+
JSON.parse(string)
|
16
|
+
rescue JSON::ParserError => jpe
|
17
|
+
raise EncodingError.wrap(jpe)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def content_type
|
22
|
+
"application/json"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/hoth/endpoint.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Hoth
|
2
2
|
class Endpoint
|
3
|
-
attr_accessor :host, :port, :module_name, :
|
3
|
+
attr_accessor :host, :port, :module_name, :transport
|
4
4
|
|
5
5
|
class ConfigEvaluator
|
6
6
|
attr_reader :endpoint
|
@@ -9,7 +9,7 @@ module Hoth
|
|
9
9
|
instance_eval(&block)
|
10
10
|
end
|
11
11
|
|
12
|
-
[:host, :port, :module_name, :
|
12
|
+
[:host, :port, :module_name, :transport].each do |endpoint_attribute|
|
13
13
|
define_method endpoint_attribute do |value|
|
14
14
|
endpoint.send("#{endpoint_attribute}=", value)
|
15
15
|
end
|
@@ -23,5 +23,6 @@ module Hoth
|
|
23
23
|
def to_url
|
24
24
|
"http://#{@host}:#{@port}/execute"
|
25
25
|
end
|
26
|
+
|
26
27
|
end
|
27
28
|
end
|
data/lib/hoth/exceptions.rb
CHANGED
@@ -14,7 +14,7 @@ module Hoth
|
|
14
14
|
mod(service_name) do
|
15
15
|
fun(:execute) do |*args|
|
16
16
|
return_value = begin
|
17
|
-
Hoth::Transport::
|
17
|
+
Hoth::Transport::Bert::TuplePreparer.prepare(Hoth::Services.send(service_name, *args))
|
18
18
|
rescue Exception => e
|
19
19
|
Ernie.log %Q{An Exception occured: #{e.message} -- #{e.backtrace.join("\n\t")}}
|
20
20
|
false
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'rack/request'
|
2
|
-
require 'json'
|
3
2
|
|
4
3
|
module Hoth
|
5
4
|
module Providers
|
@@ -10,24 +9,37 @@ module Hoth
|
|
10
9
|
end
|
11
10
|
|
12
11
|
def call(env)
|
12
|
+
Hoth::Logger.debug "env: #{env.inspect}"
|
13
13
|
if env["PATH_INFO"] =~ /^\/execute/
|
14
14
|
begin
|
15
15
|
req = Rack::Request.new(env)
|
16
16
|
|
17
17
|
service_name = req.params["name"]
|
18
18
|
service_params = req.params["params"]
|
19
|
-
|
19
|
+
|
20
|
+
responsible_service = ServiceRegistry.locate_service(service_name)
|
21
|
+
|
22
|
+
decoded_params = responsible_service.transport.encoder.decode(service_params)
|
23
|
+
result = Hoth::Services.send(service_name, *decoded_params)
|
24
|
+
|
25
|
+
encoded_result = responsible_service.transport.encoder.encode({"result" => result})
|
20
26
|
|
21
|
-
[200, {'Content-Type' =>
|
27
|
+
[200, {'Content-Type' => responsible_service.transport.encoder.content_type, 'Content-Length' => "#{encoded_result.length}"}, [encoded_result]]
|
22
28
|
rescue Exception => e
|
23
|
-
|
24
|
-
|
29
|
+
Hoth::Logger.debug "e: #{e.message}"
|
30
|
+
if responsible_service
|
31
|
+
encoded_error = responsible_service.transport.encoder.encode({"error" => e})
|
32
|
+
[500, {'Content-Type' => service.transport.encoder.content_type, 'Content-Length' => "#{encoded_error.length}"}, [encoded_error]]
|
33
|
+
else
|
34
|
+
plain_error = "An error occuered! (#{e.message})"
|
35
|
+
[500, {'Content-Type' => "text/plain", 'Content-Length' => "#{plain_error.length}"}, [plain_error]]
|
36
|
+
end
|
25
37
|
end
|
26
38
|
else
|
27
39
|
@app.call(env)
|
28
40
|
end
|
29
41
|
end
|
30
|
-
|
42
|
+
|
31
43
|
end
|
32
44
|
end
|
33
45
|
end
|
data/lib/hoth/service.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Hoth
|
2
2
|
class Service
|
3
|
-
attr_accessor :name, :params_arity, :
|
3
|
+
attr_accessor :name, :params_arity, :module
|
4
4
|
|
5
5
|
def initialize(name, &block)
|
6
6
|
@name = name
|
@@ -13,7 +13,7 @@ module Hoth
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def transport
|
16
|
-
@transport ||=
|
16
|
+
@transport ||= Transport.create(endpoint.transport, self)
|
17
17
|
end
|
18
18
|
|
19
19
|
def impl_class
|
@@ -31,17 +31,12 @@ module Hoth
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def execute(*args)
|
34
|
-
|
35
|
-
|
36
|
-
result = impl_class.send(:execute, *decoded_params)
|
37
|
-
return return_nothing? ? nil : result
|
38
|
-
else
|
39
|
-
transport.call_remote_with(*args)
|
40
|
-
end
|
34
|
+
result = self.is_local? ? impl_class.send(:execute, *args) : transport.call_remote_with(*args)
|
35
|
+
return return_nothing? ? nil : result
|
41
36
|
end
|
42
37
|
|
43
38
|
def return_nothing?
|
44
|
-
|
39
|
+
[:nothing, :nil, nil].include? @return_value
|
45
40
|
end
|
46
41
|
|
47
42
|
def via_endpoint(via = nil)
|
@@ -52,4 +47,4 @@ module Hoth
|
|
52
47
|
@endpoint ||= self.module[Hoth.env][@via_endpoint]
|
53
48
|
end
|
54
49
|
end
|
55
|
-
end
|
50
|
+
end
|
@@ -1,5 +1,16 @@
|
|
1
1
|
module Hoth
|
2
2
|
class ServiceDefinition
|
3
|
+
|
4
|
+
# create a new service with service_name and register it at the
|
5
|
+
# ServiceRegistry. The paramters of the block define the parameters of the
|
6
|
+
# defined service. Within the block you can describe the return value.
|
7
|
+
#
|
8
|
+
# Example:
|
9
|
+
#
|
10
|
+
# service :create_account do |account|
|
11
|
+
# returns :account_id
|
12
|
+
# end
|
13
|
+
#
|
3
14
|
def service(service_name, &block)
|
4
15
|
ServiceRegistry.add_service(Service.new(service_name, &block))
|
5
16
|
end
|
data/lib/hoth/service_module.rb
CHANGED
@@ -31,17 +31,11 @@ module Hoth
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def add_service(service_name, options = {})
|
34
|
-
unless self.environments[Hoth.env]
|
35
|
-
puts("no endpoint-definition for environment '#{Hoth.env}' and service '#{service_name}'")
|
36
|
-
exit
|
37
|
-
end
|
34
|
+
raise HothException.new("no endpoint-definition for environment '#{Hoth.env}' and service '#{service_name}'") unless self.environments[Hoth.env]
|
38
35
|
|
39
36
|
service = ServiceRegistry.locate_service(service_name.to_sym)
|
40
37
|
|
41
|
-
unless service
|
42
|
-
puts("tried to add service '#{service_name}' but was not defined by service-definition")
|
43
|
-
exit
|
44
|
-
end
|
38
|
+
raise HothException.new("tried to add service '#{service_name}' but was not defined by service-definition") unless service
|
45
39
|
|
46
40
|
service.module = self
|
47
41
|
service.via_endpoint(options[:via])
|
@@ -1,26 +1,33 @@
|
|
1
1
|
module Hoth
|
2
|
+
|
3
|
+
# The ServiceRegistry knows all registered services. You can register new
|
4
|
+
# services and locate existing services.
|
5
|
+
|
2
6
|
class ServiceRegistry
|
3
7
|
include Singleton
|
4
8
|
|
9
|
+
# add a service to the registry
|
5
10
|
def self.add_service(service)
|
6
11
|
instance.add_service(service)
|
7
12
|
end
|
13
|
+
# alias_method :register_service, :add_service
|
8
14
|
|
15
|
+
# find a service with a given name
|
9
16
|
def self.locate_service(service_name)
|
10
17
|
instance.locate_service(service_name)
|
11
18
|
end
|
12
19
|
|
13
|
-
def add_service(service)
|
20
|
+
def add_service(service) # :nodoc:
|
14
21
|
@registry[service.name.to_sym] = service
|
15
22
|
end
|
16
23
|
|
17
|
-
def locate_service(service_name)
|
24
|
+
def locate_service(service_name) # :nodoc:
|
18
25
|
@registry[service_name.to_sym]
|
19
26
|
end
|
20
27
|
|
21
28
|
private
|
22
|
-
|
23
|
-
def initialize
|
29
|
+
|
30
|
+
def initialize # :nodoc:
|
24
31
|
@registry = {}
|
25
32
|
end
|
26
33
|
end
|
data/lib/hoth/services.rb
CHANGED
@@ -1,17 +1,45 @@
|
|
1
1
|
module Hoth
|
2
2
|
class Services
|
3
|
+
|
4
|
+
# With Hoth::Services.define followed by a block you define all your
|
5
|
+
# available services.
|
6
|
+
#
|
7
|
+
# Example:
|
8
|
+
#
|
9
|
+
# Hoth::Services.define do
|
10
|
+
#
|
11
|
+
# service :increment_counter do |counter|
|
12
|
+
# returns :nothing
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# service :value_of_counter do |counter|
|
16
|
+
# returns :fixnum
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# service :create_account do |account|
|
20
|
+
# returns :account_id
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# after defining your services you can call each of them with
|
26
|
+
# <tt>Hoth::Services.service_name(params)</tt>
|
27
|
+
#
|
28
|
+
# Hoth::Services.increment_counter(counter)
|
29
|
+
# current_number = Hoth::Services.value_of_counter(counter)
|
30
|
+
# created_account = Hoth::Services.create_account(account)
|
31
|
+
#
|
32
|
+
# see Hoth::ServiceDefinition for further informations of the block
|
33
|
+
# content.
|
34
|
+
|
3
35
|
def self.define(&block)
|
4
36
|
ServiceDefinition.new.instance_eval(&block)
|
5
37
|
end
|
6
38
|
|
7
39
|
class <<self
|
8
|
-
|
9
|
-
|
10
|
-
def
|
11
|
-
@env.to_sym
|
12
|
-
end
|
13
|
-
|
14
|
-
def method_missing(meth, *args, &blk)
|
40
|
+
|
41
|
+
# this is where the services get called
|
42
|
+
def method_missing(meth, *args, &blk) # :nodoc:
|
15
43
|
if _service = ServiceRegistry.locate_service(meth)
|
16
44
|
_service.execute(*args)
|
17
45
|
else
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'hoth/transport/base'
|
2
|
+
require 'hoth/transport/http'
|
3
|
+
require 'hoth/transport/bert'
|
4
|
+
require 'hoth/transport/workling'
|
5
|
+
|
6
|
+
require 'hoth/encoding/json'
|
7
|
+
require 'hoth/encoding/no_op'
|
8
|
+
|
9
|
+
module Hoth
|
10
|
+
module Transport
|
11
|
+
|
12
|
+
POSSIBLE_TRANSPORTS = {
|
13
|
+
:json_via_http => {
|
14
|
+
:transport_class => Transport::Http,
|
15
|
+
:encoder => Encoding::Json
|
16
|
+
},
|
17
|
+
:http => :json_via_http,
|
18
|
+
:workling => {
|
19
|
+
:transport_class => Transport::Workling
|
20
|
+
}
|
21
|
+
}
|
22
|
+
|
23
|
+
class <<self
|
24
|
+
def create(transport_name, service)
|
25
|
+
new_transport_with_encoding(transport_name, service)
|
26
|
+
end
|
27
|
+
|
28
|
+
def new_transport_with_encoding(transport_name, service)
|
29
|
+
if POSSIBLE_TRANSPORTS[transport_name.to_sym]
|
30
|
+
if POSSIBLE_TRANSPORTS[transport_name.to_sym].kind_of?(Hash)
|
31
|
+
POSSIBLE_TRANSPORTS[transport_name.to_sym][:transport_class].new(service, :encoder => POSSIBLE_TRANSPORTS[transport_name.to_sym][:encoder])
|
32
|
+
else
|
33
|
+
new_transport_with_encoding(POSSIBLE_TRANSPORTS[transport_name.to_sym], service)
|
34
|
+
end
|
35
|
+
else
|
36
|
+
raise TransportException.new("specified transport '#{transport_name}' does not exist, use one of these: #{POSSIBLE_TRANSPORTS.keys.join(", ")}")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
@@ -1,14 +1,17 @@
|
|
1
1
|
require 'forwardable'
|
2
|
-
|
2
|
+
|
3
3
|
module Hoth
|
4
4
|
module Transport
|
5
|
-
class
|
5
|
+
class Base
|
6
6
|
extend Forwardable
|
7
7
|
|
8
|
-
|
8
|
+
attr_reader :encoder
|
9
9
|
|
10
|
-
|
10
|
+
def_delegators :@service_delegate, :name, :module, :endpoint, :params, :return_nothing?
|
11
|
+
|
12
|
+
def initialize(service_delegate, options = {})
|
11
13
|
@service_delegate = service_delegate
|
14
|
+
@encoder = options[:encoder] || Encoding::NoOp
|
12
15
|
end
|
13
16
|
|
14
17
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
|
3
|
+
module Hoth
|
4
|
+
module Transport
|
5
|
+
class Http < Base
|
6
|
+
def call_remote_with(*params)
|
7
|
+
unless return_nothing?
|
8
|
+
begin
|
9
|
+
handle_response post_payload(params)
|
10
|
+
rescue Exception => e
|
11
|
+
raise TransportError.wrap(e)
|
12
|
+
end
|
13
|
+
else
|
14
|
+
return nil
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def handle_response(response)
|
19
|
+
case response
|
20
|
+
when Net::HTTPSuccess
|
21
|
+
Hoth::Logger.debug "response.body: #{response.body}"
|
22
|
+
encoder.decode(response.body)["result"]
|
23
|
+
when Net::HTTPServerError
|
24
|
+
begin
|
25
|
+
Hoth::Logger.debug "response.body: #{response.body}"
|
26
|
+
raise encoder.decode(response.body)["error"]
|
27
|
+
rescue JSON::ParserError => jpe
|
28
|
+
raise TransportError.wrap(jpe)
|
29
|
+
end
|
30
|
+
when Net::HTTPRedirection, Net::HTTPClientError, Net::HTTPInformation, Net::HTTPUnknownResponse
|
31
|
+
raise NotImplementedError, "code: #{response.code}, message: #{response.body}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def post_payload(payload)
|
36
|
+
uri = URI.parse(self.endpoint.to_url)
|
37
|
+
return Net::HTTP.post_form(uri,
|
38
|
+
'name' => self.name.to_s,
|
39
|
+
'params' => encoder.encode(payload)
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -4,25 +4,17 @@ rescue LoadError
|
|
4
4
|
STDERR.puts "You need the simple_publisher gem if you want to use Workling/Starling transport."
|
5
5
|
end
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
7
|
module Hoth
|
12
8
|
module Transport
|
13
9
|
|
14
|
-
class
|
10
|
+
class Workling < Base
|
15
11
|
|
16
12
|
def call_remote_with(*args)
|
17
13
|
topic = SimplePublisher::Topic.new(:name => "#{self.module.name.to_s.underscore}_subscribers__#{name.to_s.underscore}")
|
18
14
|
connection = SimplePublisher::StarlingConnection.new(:host => endpoint.host, :port => endpoint.port)
|
19
15
|
|
20
16
|
publisher = SimplePublisher::Publisher.new(:topic => topic, :connection => connection)
|
21
|
-
publisher.publish(args)
|
22
|
-
end
|
23
|
-
|
24
|
-
def decode_params(*params)
|
25
|
-
params
|
17
|
+
publisher.publish(encoder.encode(args))
|
26
18
|
end
|
27
19
|
|
28
20
|
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
|
data/spec/unit/endpoint_spec.rb
CHANGED
@@ -1,30 +1,34 @@
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
|
2
2
|
|
3
|
-
|
3
|
+
module Hoth
|
4
4
|
|
5
|
-
|
6
|
-
endpoint = Hoth::Endpoint.new { port 3000 }
|
7
|
-
endpoint.port.should == 3000
|
8
|
-
end
|
5
|
+
describe Endpoint do
|
9
6
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
7
|
+
it "should have a port" do
|
8
|
+
endpoint = Endpoint.new { port 3000 }
|
9
|
+
endpoint.port.should == 3000
|
10
|
+
end
|
14
11
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
12
|
+
it "should have a host name" do
|
13
|
+
endpoint = Endpoint.new { host "example.com" }
|
14
|
+
endpoint.host.should == "example.com"
|
15
|
+
end
|
19
16
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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
|
24
26
|
|
25
|
-
|
26
|
-
|
27
|
-
|
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
|
+
|
28
32
|
end
|
29
33
|
|
30
34
|
end
|
@@ -2,16 +2,48 @@ require File.expand_path(File.join(File.dirname(__FILE__), '../../', 'spec_help
|
|
2
2
|
|
3
3
|
require File.expand_path(File.join(File.dirname(__FILE__), '../../../', 'lib', 'hoth', 'providers', 'rack_provider'))
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
11
47
|
|
12
|
-
rack_response = middleware.call env
|
13
|
-
rack_response.first.should == 500 #status code
|
14
|
-
rack_response.last.should match('json_class')
|
15
|
-
rack_response.last.should match('RuntimeError')
|
16
48
|
end
|
17
49
|
end
|
data/spec/unit/service_spec.rb
CHANGED
@@ -2,7 +2,6 @@ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
|
|
2
2
|
|
3
3
|
class TestServiceImpl
|
4
4
|
def self.execute(param1, param2)
|
5
|
-
|
6
5
|
end
|
7
6
|
end
|
8
7
|
|
@@ -16,7 +15,7 @@ module Hoth
|
|
16
15
|
service = Service.new("TestService", &service_block)
|
17
16
|
|
18
17
|
service.params_arity.should be(3)
|
19
|
-
service.
|
18
|
+
service.return_nothing?.should be(false)
|
20
19
|
end
|
21
20
|
|
22
21
|
it "should know its service-impl class" do
|
@@ -26,15 +25,22 @@ module Hoth
|
|
26
25
|
|
27
26
|
it "should know that its service-impl class is not available" do
|
28
27
|
service = Service.new("TestServiceWithoutImplClass") {}
|
29
|
-
service.impl_class
|
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)
|
30
37
|
end
|
31
38
|
|
32
39
|
it "should execute the service stub locally if its impl-class was found" do
|
33
40
|
service = Service.new("test_service") { |p1, p2| returns :nothing }
|
34
41
|
|
35
|
-
service.should_receive(:
|
36
|
-
|
37
|
-
service.impl_class.should_receive(:execute).with(decoded_params)
|
42
|
+
service.should_receive(:is_local?).and_return(true)
|
43
|
+
service.impl_class.should_receive(:execute).with(:arg1, :arg2)
|
38
44
|
|
39
45
|
service.execute(:arg1, :arg2)
|
40
46
|
end
|
@@ -42,6 +48,7 @@ module Hoth
|
|
42
48
|
it "should call the remote service if impl-class does not exist" do
|
43
49
|
service = Service.new("test_service_without_impl") { |p1, p2| returns :nothing }
|
44
50
|
|
51
|
+
service.should_receive(:is_local?).and_return(false)
|
45
52
|
service.should_receive(:transport).and_return(transport = mock("TransportMock"))
|
46
53
|
transport.should_receive(:call_remote_with).with(:arg1, :arg2)
|
47
54
|
|
@@ -51,8 +58,8 @@ module Hoth
|
|
51
58
|
it "should create transport instance based on endpoint" do
|
52
59
|
service = Service.new("test_service") { |p1, p2| returns :nothing }
|
53
60
|
service.should_receive(:endpoint).and_return(endpoint = mock("EndpointMock"))
|
54
|
-
endpoint.should_receive(:
|
55
|
-
Hoth::Transport
|
61
|
+
endpoint.should_receive(:transport).and_return(:http)
|
62
|
+
Hoth::Transport.should_receive(:create).with(:http, service)
|
56
63
|
service.transport
|
57
64
|
end
|
58
65
|
|
@@ -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
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '../../', 'spec_helper'))
|
2
|
+
|
3
|
+
module Hoth
|
4
|
+
module Transport
|
5
|
+
describe Http do
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
@service_mock = mock("ServiceMock")
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should call a remote via http" do
|
12
|
+
@service_mock.should_receive(:return_nothing?).and_return(false)
|
13
|
+
|
14
|
+
params = {:first_name => "Seras", :last_name => "Victoria"}
|
15
|
+
|
16
|
+
transport = Http.new(@service_mock)
|
17
|
+
transport.should_receive(:post_payload).with([params])
|
18
|
+
transport.call_remote_with(params)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should post payload encoded with JSON" do
|
22
|
+
@service_mock.should_receive(:endpoint).and_return(endpoint = mock("EndpointMock"))
|
23
|
+
@service_mock.should_receive(:name).and_return("service_name")
|
24
|
+
endpoint.should_receive(:to_url).and_return("http://localhost:3000/execute")
|
25
|
+
|
26
|
+
encoder = mock("JsonEncoderMock")
|
27
|
+
encoder.should_receive(:encode).with("params").and_return("encoded_params")
|
28
|
+
|
29
|
+
URI.should_receive(:parse).with("http://localhost:3000/execute").and_return(uri = mock("URIMock"))
|
30
|
+
Net::HTTP.should_receive(:post_form).with(uri, {"name" => "service_name", "params" => "encoded_params"})
|
31
|
+
|
32
|
+
transport = Http.new(@service_mock, {:encoder => encoder})
|
33
|
+
transport.post_payload("params")
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "Error Handling" do
|
37
|
+
|
38
|
+
before(:each) do
|
39
|
+
service_mock = mock("ServiceMock")
|
40
|
+
service_mock.should_receive(:return_nothing?).any_number_of_times.and_return(false)
|
41
|
+
|
42
|
+
@params = {:first_name => "Seras", :last_name => "Victoria"}
|
43
|
+
encoder = mock("JsonEncoderMock")
|
44
|
+
@transport = Http.new(service_mock, {:encoder => encoder})
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should handle http connection error" do
|
48
|
+
@transport.should_receive(:post_payload).and_raise(Exception)
|
49
|
+
lambda { @transport.call_remote_with(@params) }.should raise_error(TransportError)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should handle successful response" do
|
53
|
+
@transport.should_receive(:post_payload).and_return(response = Net::HTTPSuccess.allocate)
|
54
|
+
response.should_receive(:body).twice.and_return(response_body = '{"result" : "return_value"}')
|
55
|
+
@transport.encoder.should_receive(:decode).with(response_body).and_return({"result" => "return_value"})
|
56
|
+
@transport.call_remote_with(@params)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should handle remote server error" do
|
60
|
+
@transport.should_receive(:post_payload).and_return(response = Net::HTTPServerError.allocate)
|
61
|
+
response.should_receive(:body).any_number_of_times.and_return(response_body = '{"error" : "ServerError"}')
|
62
|
+
@transport.encoder.should_receive(:decode).with(response_body).and_return({"result" => "return_value"})
|
63
|
+
lambda { @transport.call_remote_with(@params) }.should raise_error(TransportError)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should handle any other HTTP errors" do
|
67
|
+
[Net::HTTPRedirection, Net::HTTPClientError, Net::HTTPInformation, Net::HTTPUnknownResponse].each do |http_response|
|
68
|
+
@transport.should_receive(:post_payload).and_return(response = http_response.allocate)
|
69
|
+
response.should_receive(:body).any_number_of_times.and_return('{"error" : "ServerError"}')
|
70
|
+
lambda { @transport.call_remote_with(@params) }.should raise_error(TransportError)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -3,7 +3,7 @@ require File.expand_path(File.join(File.dirname(__FILE__), '../../', 'spec_help
|
|
3
3
|
module Hoth
|
4
4
|
module Transport
|
5
5
|
|
6
|
-
describe "
|
6
|
+
describe "Workling" do
|
7
7
|
|
8
8
|
it "should send publish a message via SimplePublisher" do
|
9
9
|
endpoint = mock("EndpointMock")
|
@@ -32,17 +32,10 @@ module Hoth
|
|
32
32
|
|
33
33
|
publisher.should_receive(:publish).with([uid, email_address])
|
34
34
|
|
35
|
-
transport =
|
35
|
+
transport = Workling.new(service)
|
36
36
|
transport.call_remote_with(uid, email_address)
|
37
37
|
end
|
38
38
|
|
39
|
-
it "should decode params from a message wrapper" do
|
40
|
-
params_as_message = ["uid"]
|
41
|
-
|
42
|
-
transport = WorklingTransport.new(mock("ServiceMock"))
|
43
|
-
transport.decode_params(*params_as_message).should == ["uid"]
|
44
|
-
end
|
45
|
-
|
46
39
|
end
|
47
40
|
|
48
41
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
|
2
|
+
|
3
|
+
module Hoth
|
4
|
+
describe Transport do
|
5
|
+
|
6
|
+
it "should create a new transport by transport name for a given service" do
|
7
|
+
service = mock("ServiceMock")
|
8
|
+
Transport.should_receive(:new_transport_with_encoding).with(:transport_name, service)
|
9
|
+
Transport.create(:transport_name, service)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should create a new transport instance with encoding from a transport name" do
|
13
|
+
transport = Transport.new_transport_with_encoding :json_via_http, mock("ServiceMock")
|
14
|
+
transport.should be_kind_of(Transport::Http)
|
15
|
+
transport.encoder.should == Encoding::Json
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should create a new transport instance with encoding from a transport alias" do
|
19
|
+
transport = Transport.new_transport_with_encoding :http, mock("ServiceMock")
|
20
|
+
transport.should be_kind_of(Transport::Http)
|
21
|
+
transport.encoder.should == Encoding::Json
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should raise an exception if transport name does not exist" do
|
25
|
+
lambda { Transport.new_transport_with_encoding :not_existing_transport, mock("ServiceMock") }.should raise_error(TransportException)
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 0.
|
7
|
+
- 3
|
8
|
+
- 0
|
9
|
+
version: 0.3.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Dirk Breuer
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-03-
|
17
|
+
date: 2010-03-31 00:00:00 +02:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -89,10 +89,13 @@ extensions: []
|
|
89
89
|
extra_rdoc_files:
|
90
90
|
- LICENSE
|
91
91
|
- README.rdoc
|
92
|
+
- TODO
|
92
93
|
files:
|
93
94
|
- README.rdoc
|
94
95
|
- THANKS.md
|
95
96
|
- lib/hoth.rb
|
97
|
+
- lib/hoth/encoding/json.rb
|
98
|
+
- lib/hoth/encoding/no_op.rb
|
96
99
|
- lib/hoth/endpoint.rb
|
97
100
|
- lib/hoth/exceptions.rb
|
98
101
|
- lib/hoth/extension/core/exception.rb
|
@@ -104,13 +107,14 @@ files:
|
|
104
107
|
- lib/hoth/service_module.rb
|
105
108
|
- lib/hoth/service_registry.rb
|
106
109
|
- lib/hoth/services.rb
|
107
|
-
- lib/hoth/transport
|
108
|
-
- lib/hoth/transport/
|
109
|
-
- lib/hoth/transport/
|
110
|
-
- lib/hoth/transport/
|
111
|
-
- lib/hoth/transport/
|
110
|
+
- lib/hoth/transport.rb
|
111
|
+
- lib/hoth/transport/base.rb
|
112
|
+
- lib/hoth/transport/bert.rb
|
113
|
+
- lib/hoth/transport/http.rb
|
114
|
+
- lib/hoth/transport/workling.rb
|
112
115
|
- lib/hoth/util/logger.rb
|
113
116
|
- spec/spec_helper.rb
|
117
|
+
- spec/unit/encoding/json_spec.rb
|
114
118
|
- spec/unit/endpoint_spec.rb
|
115
119
|
- spec/unit/extension/core/exception_spec.rb
|
116
120
|
- spec/unit/hoth_spec.rb
|
@@ -118,8 +122,12 @@ files:
|
|
118
122
|
- spec/unit/service_definition_spec.rb
|
119
123
|
- spec/unit/service_module_spec.rb
|
120
124
|
- spec/unit/service_spec.rb
|
121
|
-
- spec/unit/transport/
|
125
|
+
- spec/unit/transport/base_spec.rb
|
126
|
+
- spec/unit/transport/http_spec.rb
|
127
|
+
- spec/unit/transport/workling_spec.rb
|
128
|
+
- spec/unit/transport_spec.rb
|
122
129
|
- LICENSE
|
130
|
+
- TODO
|
123
131
|
has_rdoc: true
|
124
132
|
homepage: http://github.com/railsbros/hoth
|
125
133
|
licenses: []
|
@@ -152,6 +160,7 @@ specification_version: 3
|
|
152
160
|
summary: Registry and deployment description abstraction for SOA-Services
|
153
161
|
test_files:
|
154
162
|
- spec/spec_helper.rb
|
163
|
+
- spec/unit/encoding/json_spec.rb
|
155
164
|
- spec/unit/endpoint_spec.rb
|
156
165
|
- spec/unit/extension/core/exception_spec.rb
|
157
166
|
- spec/unit/hoth_spec.rb
|
@@ -159,4 +168,7 @@ test_files:
|
|
159
168
|
- spec/unit/service_definition_spec.rb
|
160
169
|
- spec/unit/service_module_spec.rb
|
161
170
|
- spec/unit/service_spec.rb
|
162
|
-
- spec/unit/transport/
|
171
|
+
- spec/unit/transport/base_spec.rb
|
172
|
+
- spec/unit/transport/http_spec.rb
|
173
|
+
- spec/unit/transport/workling_spec.rb
|
174
|
+
- spec/unit/transport_spec.rb
|
@@ -1,43 +0,0 @@
|
|
1
|
-
require 'json'
|
2
|
-
require 'net/http'
|
3
|
-
|
4
|
-
|
5
|
-
module Hoth
|
6
|
-
module Transport
|
7
|
-
class HttpTransport < HothTransport
|
8
|
-
def call_remote_with(*args)
|
9
|
-
response = Net::HTTP.post_form(
|
10
|
-
URI.parse(self.endpoint.to_url),
|
11
|
-
{ 'name' => self.name.to_s, 'params' => "#{args.to_json}" }
|
12
|
-
)
|
13
|
-
|
14
|
-
if return_value
|
15
|
-
case response
|
16
|
-
when Net::HTTPSuccess
|
17
|
-
Hoth::Logger.debug "response.body: #{response.body}"
|
18
|
-
JSON(response.body)["result"]
|
19
|
-
when Net::HTTPServerError
|
20
|
-
begin
|
21
|
-
raise JSON(response.body)["error"]
|
22
|
-
rescue JSON::ParserError => jpe
|
23
|
-
raise TransportError.wrap(jpe)
|
24
|
-
end
|
25
|
-
when Net::HTTPRedirection, Net::HTTPClientError, Net::HTTPInformation, Net::HTTPUnknownResponse
|
26
|
-
#TODO Handle redirects(3xx) and http errors(4xx), http information(1xx), unknown responses(xxx)
|
27
|
-
raise NotImplementedError, "HTTP code: #{response.code}, message: #{response.message}"
|
28
|
-
end
|
29
|
-
else
|
30
|
-
response.is_a?(Net::HTTPSuccess)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def decode_params(params)
|
35
|
-
Hoth::Logger.debug "Original params before decode: #{params.inspect}"
|
36
|
-
JSON.parse(params)
|
37
|
-
rescue JSON::ParserError => jpe
|
38
|
-
raise TransportError.wrap(jpe)
|
39
|
-
end
|
40
|
-
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|