hoth 0.1.1

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/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