hoth 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/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
|