hoth 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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.
@@ -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.
@@ -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,7 @@
1
+ module Hoth
2
+ class Definition
3
+ def service(service_name, options = {})
4
+ service = ServiceRegistry.add_service(Service.new(service_name, options))
5
+ end
6
+ end
7
+ end
@@ -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
@@ -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,7 @@
1
+ module Hoth
2
+ module Transport
3
+ class AmqpTransport
4
+
5
+ end
6
+ end
7
+ 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
@@ -0,0 +1 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
@@ -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