hoth 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.rdoc +18 -0
- data/lib/hoth.rb +23 -0
- data/lib/hoth/definition.rb +7 -0
- data/lib/hoth/deployment_module.rb +41 -0
- data/lib/hoth/endpoint.rb +25 -0
- data/lib/hoth/exceptions.rb +13 -0
- data/lib/hoth/extension/core/exception.rb +15 -0
- data/lib/hoth/providers/bertrpc_provider.rb +35 -0
- data/lib/hoth/providers/rack_provider.rb +33 -0
- data/lib/hoth/service.rb +36 -0
- data/lib/hoth/service_deployment.rb +27 -0
- data/lib/hoth/service_registry.rb +27 -0
- data/lib/hoth/services.rb +23 -0
- data/lib/hoth/transport/amqp_transport.rb +7 -0
- data/lib/hoth/transport/bert_transport.rb +87 -0
- data/lib/hoth/transport/hoth_transport.rb +16 -0
- data/lib/hoth/transport/json_transport.rb +42 -0
- data/lib/hoth/transport/workling_transport.rb +27 -0
- data/lib/hoth/util/logger.rb +46 -0
- data/spec/hoth_spec.rb +1 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/unit/definition_spec.rb +21 -0
- data/spec/unit/deployment_module_spec.rb +58 -0
- data/spec/unit/endpoint_spec.rb +52 -0
- data/spec/unit/extension/core/exception_spec.rb +34 -0
- data/spec/unit/providers/rack_provider_spec.rb +17 -0
- data/spec/unit/service_spec.rb +65 -0
- data/spec/unit/transport/workling_transport_spec.rb +46 -0
- metadata +149 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Dirk Breuer
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
= hoth
|
2
|
+
|
3
|
+
Description goes here.
|
4
|
+
|
5
|
+
== Note on Patches/Pull Requests
|
6
|
+
|
7
|
+
* Fork the project.
|
8
|
+
* Make your feature addition or bug fix.
|
9
|
+
* Add tests for it. This is important so I don't break it in a
|
10
|
+
future version unintentionally.
|
11
|
+
* Commit, do not mess with rakefile, version, or history.
|
12
|
+
(if you want to have your own version, that is fine but
|
13
|
+
bump version in a commit by itself I can ignore when I pull)
|
14
|
+
* Send me a pull request. Bonus points for topic branches.
|
15
|
+
|
16
|
+
== Copyright
|
17
|
+
|
18
|
+
Copyright (c) 2009 Dirk Breuer. See LICENSE for details.
|
data/lib/hoth.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
require 'active_support/inflector'
|
4
|
+
|
5
|
+
require 'hoth/transport/hoth_transport'
|
6
|
+
require 'hoth/transport/json_transport'
|
7
|
+
require 'hoth/transport/bert_transport'
|
8
|
+
require 'hoth/transport/workling_transport'
|
9
|
+
|
10
|
+
require 'hoth/definition'
|
11
|
+
require 'hoth/deployment_module'
|
12
|
+
require 'hoth/endpoint'
|
13
|
+
require 'hoth/service'
|
14
|
+
require 'hoth/service_deployment'
|
15
|
+
require 'hoth/service_registry'
|
16
|
+
require 'hoth/services'
|
17
|
+
|
18
|
+
require 'hoth/util/logger'
|
19
|
+
|
20
|
+
require 'hoth/extension/core/exception'
|
21
|
+
require 'hoth/exceptions'
|
22
|
+
|
23
|
+
Hoth::Logger.init_logging!
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Hoth
|
2
|
+
class DeploymentModule
|
3
|
+
attr_accessor :name, :environments
|
4
|
+
|
5
|
+
class Environment
|
6
|
+
attr_accessor :endpoint, :deployment_options
|
7
|
+
|
8
|
+
def initialize(attributes = {})
|
9
|
+
@endpoint = attributes[:endpoint]
|
10
|
+
@deployment_options = attributes[:deployment_options]
|
11
|
+
end
|
12
|
+
|
13
|
+
def propagate_module_name_to_endpoint(module_name)
|
14
|
+
@endpoint.module_name = module_name
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(attributes = {})
|
19
|
+
@environments = {}
|
20
|
+
@name = attributes[:name]
|
21
|
+
end
|
22
|
+
|
23
|
+
def env(*options)
|
24
|
+
attributes = Hash === options.last ? options.pop : {}
|
25
|
+
options.each do |env_name|
|
26
|
+
@environments[env_name.to_sym] = Environment.new(attributes)
|
27
|
+
@environments[env_name.to_sym].propagate_module_name_to_endpoint(@name)
|
28
|
+
end
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
def [](env_name)
|
33
|
+
@environments[env_name.to_sym]
|
34
|
+
end
|
35
|
+
|
36
|
+
def path(path = nil)
|
37
|
+
path.nil? ? @path : @path = path
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Hoth
|
2
|
+
class Endpoint
|
3
|
+
attr_accessor :host, :port, :module_name, :transport_type
|
4
|
+
|
5
|
+
def initialize(attributes)
|
6
|
+
@host = attributes[:host]
|
7
|
+
@port = attributes[:port]
|
8
|
+
@module_name = attributes[:module_name]
|
9
|
+
@transport_type = attributes[:transport_type]
|
10
|
+
end
|
11
|
+
|
12
|
+
def ==(endpoint)
|
13
|
+
self.host == endpoint.host &&
|
14
|
+
self.port == endpoint.port
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_url
|
18
|
+
"http://#{@host}:#{@port}/execute"
|
19
|
+
end
|
20
|
+
|
21
|
+
def is_local?
|
22
|
+
ENV["LOCAL"] == "true" ? true : false # make dynamic
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Hoth
|
2
|
+
class HothException < StandardError
|
3
|
+
attr_reader :original
|
4
|
+
def self.wrap(original)
|
5
|
+
wrapped = new("#{original.class} says: #{original.message}")
|
6
|
+
wrapped.set_backtrace original.backtrace
|
7
|
+
wrapped.instance_variable_set :@original, original
|
8
|
+
wrapped
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class TransportError < HothException; end
|
13
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class Exception
|
2
|
+
def to_json(*a)
|
3
|
+
{
|
4
|
+
'json_class' => self.class.name,
|
5
|
+
'message' => self.message,
|
6
|
+
'backtrace' => self.backtrace
|
7
|
+
}.to_json(*a)
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.json_create(hash)
|
11
|
+
exception = new(hash["message"])
|
12
|
+
exception.set_backtrace hash['backtrace']
|
13
|
+
exception
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'ernie'
|
2
|
+
|
3
|
+
module Hoth
|
4
|
+
module Providers
|
5
|
+
class BertRPCProvider
|
6
|
+
|
7
|
+
def self.create_ernie_definition
|
8
|
+
Ernie.log "Possible Service implementations: #{Object.constants.grep(/.*Impl$/).inspect}"
|
9
|
+
Object.constants.grep(/.*Impl$/).each do |impl_class_name|
|
10
|
+
if impl_class = Object.const_get(impl_class_name) #&& impl_class.respond_to?(:execute)
|
11
|
+
Ernie.log "Service implementation was loaded! (#{impl_class.inspect})"
|
12
|
+
if impl_class.respond_to?(:execute)
|
13
|
+
service_name = impl_class_name.gsub("Impl", "").underscore.to_sym
|
14
|
+
mod(service_name) do
|
15
|
+
fun(:execute) do |*args|
|
16
|
+
return_value = begin
|
17
|
+
Hoth::Transport::BertTransport::TuplePreparer.prepare(Hoth::Services.send(service_name, *args))
|
18
|
+
rescue Exception => e
|
19
|
+
Ernie.log %Q{An Exception occured: #{e.message} -- #{e.backtrace.join("\n\t")}}
|
20
|
+
false
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
else
|
25
|
+
Ernie.log "Implementation wasn't applicatable. :execute method is missing!"
|
26
|
+
end
|
27
|
+
else
|
28
|
+
Ernie.log "Service implementation was not loaded! (#{impl_class_name.inspect})"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'rack/request'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Hoth
|
5
|
+
module Providers
|
6
|
+
class RackProvider
|
7
|
+
|
8
|
+
def initialize(app)
|
9
|
+
@app = app
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
if env["PATH_INFO"] =~ /^\/execute/
|
14
|
+
begin
|
15
|
+
req = Rack::Request.new(env)
|
16
|
+
|
17
|
+
service_name = req.params["name"]
|
18
|
+
service_params = req.params["params"]
|
19
|
+
json_payload = JSON({"result" => Hoth::Services.send(service_name, service_params)})
|
20
|
+
|
21
|
+
[200, {'Content-Type' => 'application/json', 'Content-Length' => "#{json_payload.length}"}, json_payload]
|
22
|
+
rescue Exception => e
|
23
|
+
json_payload = JSON({'error' => e})
|
24
|
+
[500, {'Content-Type' => 'application/json', 'Content-Length' => "#{json_payload.length}"}, json_payload]
|
25
|
+
end
|
26
|
+
else
|
27
|
+
@app.call(env)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/hoth/service.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
module Hoth
|
2
|
+
class Service
|
3
|
+
attr_accessor :name, :endpoint, :params, :return_value
|
4
|
+
|
5
|
+
def initialize(name, args = {})
|
6
|
+
@name = name
|
7
|
+
@endpoint = ServiceDeployment.module(args[:endpoint])[Services.env].endpoint
|
8
|
+
@params = args[:params]
|
9
|
+
@return_value = args[:returns]
|
10
|
+
end
|
11
|
+
|
12
|
+
def transport
|
13
|
+
@transport ||= "hoth/transport/#{endpoint.transport_type}_transport".camelize.constantize.new(self)
|
14
|
+
end
|
15
|
+
|
16
|
+
def service_impl_class
|
17
|
+
@service_impl_class_name ||= "#{self.name.to_s.camelize}Impl"
|
18
|
+
# in Rails development environment we cannot cache the class constant, because it gets unloaded, so you get
|
19
|
+
# an "A copy of xxxImpl has been removed from the module tree but is still active!" error from ActiveSupport dependency mechanism
|
20
|
+
# TODO: Try to solve this problem
|
21
|
+
# TODO: get rid of these Rails dependencies
|
22
|
+
@service_impl_class_name.constantize
|
23
|
+
end
|
24
|
+
|
25
|
+
def execute(*args)
|
26
|
+
if self.endpoint.is_local?
|
27
|
+
decoded_params = transport.decode_params(*args)
|
28
|
+
Hoth::Logger.debug "decoded_params: #{decoded_params.inspect}"
|
29
|
+
result = service_impl_class.send(:execute, *decoded_params)
|
30
|
+
return return_value ? result : nil
|
31
|
+
else
|
32
|
+
transport.call_remote_with(*args)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Hoth
|
2
|
+
class ServiceDeployment
|
3
|
+
include Singleton
|
4
|
+
|
5
|
+
attr_reader :deployment_modules
|
6
|
+
|
7
|
+
def self.define(&block)
|
8
|
+
instance.instance_eval(&block)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.module(module_name)
|
12
|
+
instance.deployment_modules[module_name]
|
13
|
+
end
|
14
|
+
|
15
|
+
def service_module(module_name, &block)
|
16
|
+
deployment_module = DeploymentModule.new(:name => module_name)
|
17
|
+
deployment_module.instance_eval(&block)
|
18
|
+
@deployment_modules[module_name] = deployment_module
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def initialize
|
24
|
+
@deployment_modules = {}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Hoth
|
2
|
+
class ServiceRegistry
|
3
|
+
include Singleton
|
4
|
+
|
5
|
+
def self.add_service(service)
|
6
|
+
instance.add_service(service)
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.locate_service(service_name)
|
10
|
+
instance.locate_service(service_name)
|
11
|
+
end
|
12
|
+
|
13
|
+
def add_service(service)
|
14
|
+
@registry[service.name] = service
|
15
|
+
end
|
16
|
+
|
17
|
+
def locate_service(service_name)
|
18
|
+
@registry[service_name]
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def initialize
|
24
|
+
@registry = {}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Hoth
|
2
|
+
class Services
|
3
|
+
def self.define(&block)
|
4
|
+
(@definition || Definition.new).instance_eval(&block)
|
5
|
+
end
|
6
|
+
|
7
|
+
class <<self
|
8
|
+
attr_writer :env
|
9
|
+
|
10
|
+
def env
|
11
|
+
@env.to_sym
|
12
|
+
end
|
13
|
+
|
14
|
+
def method_missing(meth, *args, &blk)
|
15
|
+
if _service = ServiceRegistry.locate_service(meth)
|
16
|
+
_service.execute(*args)
|
17
|
+
else
|
18
|
+
super
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'bert'
|
2
|
+
require 'bertrpc'
|
3
|
+
|
4
|
+
module Hoth
|
5
|
+
module Transport
|
6
|
+
class BertTransport < HothTransport
|
7
|
+
|
8
|
+
class TuplePreparer
|
9
|
+
def self.prepare(obj)
|
10
|
+
case obj
|
11
|
+
when Array
|
12
|
+
obj.collect { |o| prepare o }
|
13
|
+
when Hash
|
14
|
+
obj.each { |k,v| obj[k] = prepare(v) }
|
15
|
+
else
|
16
|
+
ruby2tuple obj
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.ruby2tuple(ruby)
|
21
|
+
if ruby.respond_to? :to_serialize
|
22
|
+
tuple = t[ruby.class.name.underscore, {}]
|
23
|
+
ruby.to_serialize.each do |field|
|
24
|
+
tuple.last[field] = prepare(ruby.send(field))
|
25
|
+
end
|
26
|
+
tuple
|
27
|
+
else
|
28
|
+
ruby
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class Deserializer
|
34
|
+
def self.deserialize(data)
|
35
|
+
case data
|
36
|
+
when BERT::Tuple
|
37
|
+
tuple2ruby data
|
38
|
+
when Array
|
39
|
+
data.collect { |o| deserialize o }
|
40
|
+
when Hash
|
41
|
+
data.each { |k,v| data[k] = deserialize(v) }
|
42
|
+
else
|
43
|
+
data
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.tuple2ruby(tuple)
|
48
|
+
case tuple
|
49
|
+
when BERT::Tuple
|
50
|
+
begin
|
51
|
+
ruby_class = tuple.first.camelize.constantize
|
52
|
+
ruby_obj = ruby_class.new({})
|
53
|
+
ruby_obj.to_serialize.each do |field|
|
54
|
+
ruby_obj.send("#{field}=", deserialize(tuple.last[field]))
|
55
|
+
end
|
56
|
+
|
57
|
+
ruby_obj
|
58
|
+
rescue NameError => e
|
59
|
+
puts %Q{An Exception occured: #{e.message} -- #{e.backtrace.join("\n\t")}}
|
60
|
+
tuple
|
61
|
+
end
|
62
|
+
else
|
63
|
+
puts "Was not anything we could decode!"
|
64
|
+
tuple
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def call_remote_with(*args)
|
70
|
+
bert_service = BERTRPC::Service.new(self.endpoint.host, self.endpoint.port)
|
71
|
+
|
72
|
+
response = bert_service.call.send(self.name).execute(*TuplePreparer.prepare(args))
|
73
|
+
|
74
|
+
if self.return_value
|
75
|
+
return Deserializer.deserialize(response)
|
76
|
+
else
|
77
|
+
return true
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def decode_params(params)
|
82
|
+
Deserializer.deserialize(params)
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module Hoth
|
4
|
+
module Transport
|
5
|
+
class HothTransport
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
def_delegators :@service_delegate, :name, :endpoint, :params, :return_value
|
9
|
+
|
10
|
+
def initialize(service_delegate)
|
11
|
+
@service_delegate = service_delegate
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'net/http'
|
3
|
+
|
4
|
+
module Hoth
|
5
|
+
module Transport
|
6
|
+
class JsonTransport < HothTransport
|
7
|
+
def call_remote_with(*args)
|
8
|
+
response = Net::HTTP.post_form(
|
9
|
+
URI.parse(self.endpoint.to_url),
|
10
|
+
{ 'name' => self.name.to_s, 'params' => "#{args.to_json}" }
|
11
|
+
)
|
12
|
+
|
13
|
+
if return_value
|
14
|
+
case response
|
15
|
+
when Net::HTTPSuccess
|
16
|
+
Hoth::Logger.debug "response.body: #{response.body}"
|
17
|
+
JSON(response.body)["result"]
|
18
|
+
when Net::HTTPServerError
|
19
|
+
begin
|
20
|
+
raise JSON(response.body)["error"]
|
21
|
+
rescue JSON::ParserError => jpe
|
22
|
+
raise TransportError.wrap(jpe)
|
23
|
+
end
|
24
|
+
when Net::HTTPRedirection, Net::HTTPClientError, Net::HTTPInformation, Net::HTTPUnknownResponse
|
25
|
+
#TODO Handle redirects(3xx) and http errors(4xx), http information(1xx), unknown responses(xxx)
|
26
|
+
raise NotImplementedError, "HTTP code: #{response.code}, message: #{response.message}"
|
27
|
+
end
|
28
|
+
else
|
29
|
+
response.is_a?(Net::HTTPSuccess)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def decode_params(params)
|
34
|
+
Hoth::Logger.debug "Original params before decode: #{params.inspect}"
|
35
|
+
JSON.parse(params)
|
36
|
+
rescue JSON::ParserError => jpe
|
37
|
+
raise TransportError.wrap(jpe)
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,27 @@
|
|
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 WorklingTransport < HothTransport
|
11
|
+
|
12
|
+
def call_remote_with(*args)
|
13
|
+
topic = SimplePublisher::Topic.new(:name => "#{endpoint.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(args)
|
18
|
+
end
|
19
|
+
|
20
|
+
def decode_params(*params)
|
21
|
+
params
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
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/spec/hoth_spec.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
4
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
5
|
+
require 'hoth'
|
6
|
+
require 'spec'
|
7
|
+
require 'spec/autorun'
|
8
|
+
|
9
|
+
Hoth::Services.env = "test"
|
10
|
+
|
11
|
+
Spec::Matchers.define :string_matching do |regex|
|
12
|
+
match do |string|
|
13
|
+
string =~ regex
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
|
2
|
+
|
3
|
+
describe Hoth::Definition do
|
4
|
+
|
5
|
+
it "should create a Service and add it to the registry instance" do
|
6
|
+
service_name = :my_service
|
7
|
+
service_options = {
|
8
|
+
:params => [:some_params],
|
9
|
+
:returns => nil,
|
10
|
+
:endpoint => :my_service_module
|
11
|
+
}
|
12
|
+
|
13
|
+
service = mock("ServiceMock")
|
14
|
+
Hoth::Service.should_receive(:new).with(service_name, service_options).and_return(service)
|
15
|
+
Hoth::ServiceRegistry.should_receive(:add_service).with(service)
|
16
|
+
|
17
|
+
definition = Hoth::Definition.new
|
18
|
+
definition.service service_name, service_options
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
|
2
|
+
|
3
|
+
describe Hoth::DeploymentModule do
|
4
|
+
|
5
|
+
it "should have a name" do
|
6
|
+
deployment_module = Hoth::DeploymentModule.new :name => :my_service_module
|
7
|
+
deployment_module.name.should equal(:my_service_module)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should have an environment" do
|
11
|
+
deployment_module = Hoth::DeploymentModule.new(:name => "deployment_module_name")
|
12
|
+
|
13
|
+
env_specific_options = {:endpoint => endpoint = mock("EndpointMock"), :deployment_options => "deployment_options"}
|
14
|
+
endpoint.should_receive(:module_name=).with("deployment_module_name").exactly(3).times
|
15
|
+
deployment_module.env :test, env_specific_options
|
16
|
+
deployment_module[:test].should be_a(Hoth::DeploymentModule::Environment)
|
17
|
+
|
18
|
+
deployment_module.env :staging, :production, env_specific_options
|
19
|
+
deployment_module[:staging].should be_a(Hoth::DeploymentModule::Environment)
|
20
|
+
deployment_module[:production].should be_a(Hoth::DeploymentModule::Environment)
|
21
|
+
|
22
|
+
[:test, :staging, :production].each do |environment|
|
23
|
+
deployment_module[environment].endpoint.should == endpoint
|
24
|
+
deployment_module[environment].deployment_options.should == "deployment_options"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should have a path pointing to the service root" do
|
29
|
+
deployment_module = Hoth::DeploymentModule.new
|
30
|
+
deployment_module.path "services_dir/my_service"
|
31
|
+
deployment_module.path.should == "services_dir/my_service"
|
32
|
+
end
|
33
|
+
|
34
|
+
describe Hoth::DeploymentModule::Environment do
|
35
|
+
it "should have an endpoint" do
|
36
|
+
endpoint_mock = mock("Hoth::Endpoint", :null_object => true)
|
37
|
+
|
38
|
+
env = Hoth::DeploymentModule::Environment.new(:endpoint => endpoint_mock)
|
39
|
+
env.endpoint.should equal(endpoint_mock)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should have deployment options" do
|
43
|
+
some_options = {:server_instances => 5}
|
44
|
+
|
45
|
+
env = Hoth::DeploymentModule::Environment.new(:deployment_options => some_options)
|
46
|
+
env.deployment_options.should equal(some_options)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should set propagate the module_name of the enclosing DeploymentModule to its endpoint" do
|
50
|
+
endpoint = mock("Hoth::Endpoint", :null_object => true)
|
51
|
+
endpoint.should_receive(:module_name=).with("deployment_module_name")
|
52
|
+
|
53
|
+
env = Hoth::DeploymentModule::Environment.new(:endpoint => endpoint)
|
54
|
+
env.propagate_module_name_to_endpoint("deployment_module_name")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
|
2
|
+
|
3
|
+
describe Hoth::Endpoint do
|
4
|
+
|
5
|
+
it "should have a port" do
|
6
|
+
endpoint = Hoth::Endpoint.new(:port => 3000)
|
7
|
+
endpoint.port.should == 3000
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should have a host name" do
|
11
|
+
endpoint = Hoth::Endpoint.new(:host => "example.com")
|
12
|
+
endpoint.host.should == "example.com"
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should have a transport type" do
|
16
|
+
endpoint = Hoth::Endpoint.new(:transport_type => :json)
|
17
|
+
endpoint.transport_type.should == :json
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should should cast itself to URL string" do
|
21
|
+
endpoint = Hoth::Endpoint.new(:port => 3000, :host => "example.com")
|
22
|
+
endpoint.to_url.should == "http://example.com:3000/execute"
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should compare to another endpoint" do
|
26
|
+
json_endpoint = Hoth::Endpoint.new(
|
27
|
+
:port => 3000,
|
28
|
+
:host => "example.com",
|
29
|
+
:transport_type => :json
|
30
|
+
)
|
31
|
+
|
32
|
+
bert_endpoint = Hoth::Endpoint.new(
|
33
|
+
:port => 3000,
|
34
|
+
:host => "example.com",
|
35
|
+
:transport_type => :bert
|
36
|
+
)
|
37
|
+
|
38
|
+
json_endpoint.should equal(json_endpoint)
|
39
|
+
json_endpoint.should_not equal(bert_endpoint)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should know if it is local or not" do
|
43
|
+
endpoint = Hoth::Endpoint.new({})
|
44
|
+
endpoint.is_local?.should_not be(nil)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should should know the deployment module this endpoint is associated to" do
|
48
|
+
endpoint = Hoth::Endpoint.new(:module_name => "TestModule")
|
49
|
+
endpoint.module_name.should == "TestModule"
|
50
|
+
end
|
51
|
+
|
52
|
+
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,17 @@
|
|
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
|
+
describe Hoth::Providers::RackProvider do
|
6
|
+
it "should be able to handle exceptions" do
|
7
|
+
app = stub("ApplicationStub").as_null_object
|
8
|
+
middleware = Hoth::Providers::RackProvider.new app
|
9
|
+
env = {"PATH_INFO" => "/execute/some_method", "other_params" => nil}
|
10
|
+
Rack::Request.should_receive(:new).and_raise(RuntimeError)
|
11
|
+
|
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
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
|
2
|
+
|
3
|
+
class TestServiceImpl
|
4
|
+
def self.execute(param1, param2)
|
5
|
+
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
describe Hoth::Service do
|
10
|
+
|
11
|
+
before(:each) do
|
12
|
+
Hoth::ServiceDeployment.should_receive(:module).
|
13
|
+
and_return({:test => stub(:endpoint => stub("Endpoint"))})
|
14
|
+
|
15
|
+
@service = Hoth::Service.new("TestService",
|
16
|
+
:endpoint => "test_module",
|
17
|
+
:params => ["parameter", "another_parameter"],
|
18
|
+
:returns => [:some_data]
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should have a name and an endpoint based ob the deployment definition" do
|
23
|
+
Hoth::ServiceDeployment.should_receive(:module).
|
24
|
+
with("test_module").
|
25
|
+
and_return({:test => mock("DeploymentModule", :endpoint => stub("Endpoint"))})
|
26
|
+
|
27
|
+
service = Hoth::Service.new("TestService", :endpoint => "test_module")
|
28
|
+
service.name.should == "TestService"
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should define parameters and return values" do
|
32
|
+
Hoth::ServiceDeployment.should_receive(:module).
|
33
|
+
and_return({:test => stub(:endpoint => stub("Endpoint"))})
|
34
|
+
|
35
|
+
service = Hoth::Service.new("TestService",
|
36
|
+
:endpoint => "test_module",
|
37
|
+
:params => ["parameter", "another_parameter"],
|
38
|
+
:returns => [:some_data]
|
39
|
+
)
|
40
|
+
|
41
|
+
service.params.should == ["parameter", "another_parameter"]
|
42
|
+
service.return_value.should == [:some_data]
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should execute the service stub locally based on the endpoint" do
|
46
|
+
@service.should_receive(:endpoint).and_return(mock("Endpoint", :is_local? => true))
|
47
|
+
transport_mock = mock("HothTranport")
|
48
|
+
transport_mock.should_receive(:decode_params).
|
49
|
+
with(:arg1, :arg2).
|
50
|
+
and_return([:decoded_arg1, :decoded_arg2])
|
51
|
+
|
52
|
+
@service.should_receive(:transport).and_return(transport_mock)
|
53
|
+
|
54
|
+
@service.execute(:arg1, :arg2)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should execute the service stub via transport based on the endpoint" do
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should create transport instance based on endpoint" do
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '../../', 'spec_helper'))
|
2
|
+
|
3
|
+
module Hoth
|
4
|
+
module Transport
|
5
|
+
|
6
|
+
describe "WorklingTransport" do
|
7
|
+
|
8
|
+
it "should send publish a message via SimplePublisher" do
|
9
|
+
endpoint = mock("EndpointMock")
|
10
|
+
endpoint.should_receive(:module_name).and_return("TestServiceModule")
|
11
|
+
endpoint.should_receive(:host).and_return("localhost")
|
12
|
+
endpoint.should_receive(:port).and_return("22122")
|
13
|
+
|
14
|
+
service = mock("ServiceMock")
|
15
|
+
service.should_receive(:name).and_return("TestService")
|
16
|
+
service.should_receive(:endpoint).any_number_of_times.and_return(endpoint)
|
17
|
+
|
18
|
+
SimplePublisher::Topic.should_receive(:new).with(:name => "test_service_module_subscribers__test_service").and_return(topic = mock("Topic"))
|
19
|
+
|
20
|
+
SimplePublisher::StarlingConnection.should_receive(:new).with(:host => "localhost", :port => "22122").and_return(connection = mock("Connection"))
|
21
|
+
|
22
|
+
SimplePublisher::Publisher.should_receive(:new).with(
|
23
|
+
:topic => topic,
|
24
|
+
:connection => connection
|
25
|
+
).and_return(publisher = mock("PublisherMock"))
|
26
|
+
|
27
|
+
uid = "GC-123546"
|
28
|
+
email_address = "test@example.com"
|
29
|
+
|
30
|
+
publisher.should_receive(:publish).with([uid, email_address])
|
31
|
+
|
32
|
+
transport = WorklingTransport.new(service)
|
33
|
+
transport.call_remote_with(uid, email_address)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should decode params from a message wrapper" do
|
37
|
+
params_as_message = ["uid"]
|
38
|
+
|
39
|
+
transport = WorklingTransport.new(mock("ServiceMock"))
|
40
|
+
transport.decode_params(*params_as_message).should == ["uid"]
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
metadata
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hoth
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 1
|
9
|
+
version: 0.1.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- pkw.de Development
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-03-26 00:00:00 +01:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: activesupport
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
version: "0"
|
30
|
+
type: :runtime
|
31
|
+
version_requirements: *id001
|
32
|
+
- !ruby/object:Gem::Dependency
|
33
|
+
name: bertrpc
|
34
|
+
prerelease: false
|
35
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
segments:
|
40
|
+
- 0
|
41
|
+
version: "0"
|
42
|
+
type: :runtime
|
43
|
+
version_requirements: *id002
|
44
|
+
- !ruby/object:Gem::Dependency
|
45
|
+
name: json
|
46
|
+
prerelease: false
|
47
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
segments:
|
52
|
+
- 0
|
53
|
+
version: "0"
|
54
|
+
type: :runtime
|
55
|
+
version_requirements: *id003
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: rspec
|
58
|
+
prerelease: false
|
59
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
segments:
|
64
|
+
- 0
|
65
|
+
version: "0"
|
66
|
+
type: :development
|
67
|
+
version_requirements: *id004
|
68
|
+
description: |
|
69
|
+
Creating a SOA requires a centralized location to define all services within the
|
70
|
+
SOA. Furthermore you want to know where to deploy those services.
|
71
|
+
|
72
|
+
email: dev@pkw.de
|
73
|
+
executables: []
|
74
|
+
|
75
|
+
extensions: []
|
76
|
+
|
77
|
+
extra_rdoc_files:
|
78
|
+
- LICENSE
|
79
|
+
- README.rdoc
|
80
|
+
files:
|
81
|
+
- README.rdoc
|
82
|
+
- lib/hoth.rb
|
83
|
+
- lib/hoth/definition.rb
|
84
|
+
- lib/hoth/deployment_module.rb
|
85
|
+
- lib/hoth/endpoint.rb
|
86
|
+
- lib/hoth/exceptions.rb
|
87
|
+
- lib/hoth/extension/core/exception.rb
|
88
|
+
- lib/hoth/providers/bertrpc_provider.rb
|
89
|
+
- lib/hoth/providers/rack_provider.rb
|
90
|
+
- lib/hoth/service.rb
|
91
|
+
- lib/hoth/service_deployment.rb
|
92
|
+
- lib/hoth/service_registry.rb
|
93
|
+
- lib/hoth/services.rb
|
94
|
+
- lib/hoth/transport/amqp_transport.rb
|
95
|
+
- lib/hoth/transport/bert_transport.rb
|
96
|
+
- lib/hoth/transport/hoth_transport.rb
|
97
|
+
- lib/hoth/transport/json_transport.rb
|
98
|
+
- lib/hoth/transport/workling_transport.rb
|
99
|
+
- lib/hoth/util/logger.rb
|
100
|
+
- spec/hoth_spec.rb
|
101
|
+
- spec/spec_helper.rb
|
102
|
+
- spec/unit/definition_spec.rb
|
103
|
+
- spec/unit/deployment_module_spec.rb
|
104
|
+
- spec/unit/endpoint_spec.rb
|
105
|
+
- spec/unit/extension/core/exception_spec.rb
|
106
|
+
- spec/unit/providers/rack_provider_spec.rb
|
107
|
+
- spec/unit/service_spec.rb
|
108
|
+
- spec/unit/transport/workling_transport_spec.rb
|
109
|
+
- LICENSE
|
110
|
+
has_rdoc: true
|
111
|
+
homepage: http://github.com/pkwde/hoth
|
112
|
+
licenses: []
|
113
|
+
|
114
|
+
post_install_message:
|
115
|
+
rdoc_options:
|
116
|
+
- --charset=UTF-8
|
117
|
+
require_paths:
|
118
|
+
- lib
|
119
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - ">="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
segments:
|
124
|
+
- 0
|
125
|
+
version: "0"
|
126
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - ">="
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
segments:
|
131
|
+
- 0
|
132
|
+
version: "0"
|
133
|
+
requirements: []
|
134
|
+
|
135
|
+
rubyforge_project:
|
136
|
+
rubygems_version: 1.3.6
|
137
|
+
signing_key:
|
138
|
+
specification_version: 3
|
139
|
+
summary: Registry and deployment description abstraction for SOA-Services
|
140
|
+
test_files:
|
141
|
+
- spec/hoth_spec.rb
|
142
|
+
- spec/spec_helper.rb
|
143
|
+
- spec/unit/definition_spec.rb
|
144
|
+
- spec/unit/deployment_module_spec.rb
|
145
|
+
- spec/unit/endpoint_spec.rb
|
146
|
+
- spec/unit/extension/core/exception_spec.rb
|
147
|
+
- spec/unit/providers/rack_provider_spec.rb
|
148
|
+
- spec/unit/service_spec.rb
|
149
|
+
- spec/unit/transport/workling_transport_spec.rb
|