sk-hoth 0.0.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +69 -39
- data/THANKS.md +9 -0
- data/TODO +2 -0
- data/lib/hoth/encoding/json.rb +28 -0
- data/lib/hoth/encoding/no_op.rb +19 -0
- data/lib/hoth/endpoint.rb +28 -0
- data/lib/hoth/exceptions.rb +14 -0
- data/lib/hoth/extension/core/exception.rb +15 -0
- data/lib/hoth/modules.rb +27 -0
- data/lib/hoth/providers/bertrpc_provider.rb +35 -0
- data/lib/hoth/providers/rack_provider.rb +45 -0
- data/lib/hoth/service.rb +50 -0
- data/lib/hoth/service_definition.rb +18 -0
- data/lib/hoth/service_module.rb +49 -0
- data/lib/hoth/service_registry.rb +34 -0
- data/lib/hoth/services.rb +51 -0
- data/lib/hoth/transport/base.rb +19 -0
- data/lib/hoth/transport/bert.rb +87 -0
- data/lib/hoth/transport/http.rb +41 -0
- data/lib/hoth/transport/http_hmac.rb +37 -0
- data/lib/hoth/transport/workling.rb +23 -0
- data/lib/hoth/transport.rb +48 -0
- data/lib/hoth/util/logger.rb +46 -0
- data/lib/hoth.rb +56 -0
- data/spec/spec_helper.rb +7 -26
- data/spec/unit/encoding/json_spec.rb +25 -0
- data/spec/unit/endpoint_spec.rb +34 -0
- data/spec/unit/extension/core/exception_spec.rb +34 -0
- data/spec/unit/hoth_spec.rb +30 -0
- data/spec/unit/providers/rack_provider_spec.rb +49 -0
- data/spec/unit/service_definition_spec.rb +21 -0
- data/spec/unit/service_module_spec.rb +59 -0
- data/spec/unit/service_spec.rb +77 -0
- data/spec/unit/transport/base_spec.rb +43 -0
- data/spec/unit/transport/http_hmac_spec.rb +44 -0
- data/spec/unit/transport/http_spec.rb +73 -0
- data/spec/unit/transport/workling_spec.rb +42 -0
- data/spec/unit/transport_spec.rb +29 -0
- metadata +86 -23
- data/lib/king_soa/rack/middleware.rb +0 -47
- data/lib/king_soa/registry.rb +0 -55
- data/lib/king_soa/service.rb +0 -88
- data/lib/king_soa.rb +0 -56
- data/spec/king_soa/rack/middleware_spec.rb +0 -36
- data/spec/king_soa/registry_spec.rb +0 -28
- data/spec/king_soa/service_spec.rb +0 -46
- data/spec/server/app.rb +0 -26
data/README.rdoc
CHANGED
@@ -1,56 +1,86 @@
|
|
1
|
-
=
|
2
|
-
|
3
|
-
Creating a SOA requires a centralized location to define all services within the
|
4
|
-
SOA. Furthermore you want to know where those services live.
|
5
|
-
|
6
|
-
== Tech details
|
7
|
-
|
8
|
-
the soa registry is keeping a bunch of service objects which know where a method
|
9
|
-
lives. Methods are classes with an self.perform method and can be located local,
|
10
|
-
remote(http) or beeing put onto a queue
|
1
|
+
= Hoth
|
11
2
|
|
3
|
+
Creating a SOA requires a centralized location to define all services within the SOA. Furthermore you want to know where those services live.
|
12
4
|
|
5
|
+
= How to use
|
13
6
|
|
14
7
|
== Install
|
15
8
|
|
16
|
-
gem install
|
9
|
+
gem install hoth
|
17
10
|
|
18
|
-
== Define services
|
11
|
+
== Define services and modules
|
19
12
|
|
20
|
-
===
|
13
|
+
=== Service-Definition
|
21
14
|
|
22
|
-
|
23
|
-
registry.
|
24
|
-
Be aware that the registry is a singleton, so that the rack middleware and your
|
25
|
-
app are seeing the same :
|
26
|
-
# create the service
|
27
|
-
service = KingSoa::Service.new(:name=>:increment_usage,
|
28
|
-
:url=>'http://localhost:4567',
|
29
|
-
:auth_key=>'12345')
|
30
|
-
# register
|
31
|
-
KingSoa::Registry << service
|
15
|
+
This is how you define services:
|
32
16
|
|
33
|
-
|
34
|
-
|
17
|
+
Hoth::Services.define do
|
18
|
+
|
19
|
+
service :service_name do |first_param, second_param|
|
20
|
+
returns :descriptive_name
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
35
24
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
25
|
+
This definition describes a service with a name, some parameters and its return value. The naming of the parameters is just for your understanding and will never be used again, so be descriptive. Same goes for the return value. The only exception is, if you want to assure that a service returns nil you can write
|
26
|
+
|
27
|
+
returns :nothing
|
28
|
+
|
29
|
+
A service whith this return value will always return nil. You can also specify `:nil`, with the same result.
|
30
|
+
|
31
|
+
=== Module-Definition
|
32
|
+
|
33
|
+
After defining all you services, you need to specify in which modules they live. Each module can be seen as a set of implemented services. Each module can have one or more endpoints. Here is how you define these modules with its endpoints and services:
|
34
|
+
|
35
|
+
|
36
|
+
Hoth::Modules.define do
|
37
|
+
|
38
|
+
service_module :module_name do
|
39
|
+
env :development, :test do
|
40
|
+
endpoint :default do
|
41
|
+
host 'localhost'
|
42
|
+
port 3000
|
43
|
+
transport :http
|
44
|
+
end
|
45
|
+
|
46
|
+
endpoint :bert do
|
47
|
+
host 'localhost'
|
48
|
+
port 9999
|
49
|
+
transport :bert
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
env :production do
|
54
|
+
endpoint :default do
|
55
|
+
host '192.168.1.12'
|
56
|
+
port 3000
|
57
|
+
transport :http
|
58
|
+
end
|
59
|
+
|
60
|
+
endpoint :bert do
|
61
|
+
host '192.168.1.15'
|
62
|
+
port 9999
|
63
|
+
transport :bert
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
add_service :first_service
|
68
|
+
add_service :second_service, :via => :bert
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
42
72
|
|
43
|
-
=== Rails
|
44
73
|
|
74
|
+
As you can see, it is possible to define different endpoints for different environments. Each endpoint has a host, a port and a transport-type. After defining your endpoints you can add your previously defined services to the module and define which endpoint they should use. If you do not specify an endpoint the :default endpoint will be used.
|
45
75
|
|
46
|
-
|
76
|
+
== Integrate in your project
|
47
77
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
78
|
+
Just execute current code (in rails you can add this line to an initializer):
|
79
|
+
|
80
|
+
Hoth.init!
|
81
|
+
|
82
|
+
By default, Hoth looks for the files service_definition and module_definition in the config-Directory (`./config`). If you need to load these files from another place, just set `Hoth.config_path` to your needs.
|
52
83
|
|
53
|
-
|
54
84
|
== Note on Patches/Pull Requests
|
55
85
|
|
56
86
|
* Fork the project.
|
@@ -61,4 +91,4 @@ The base is just:
|
|
61
91
|
|
62
92
|
== Copyright
|
63
93
|
|
64
|
-
Copyright (c) 2010
|
94
|
+
Copyright (c) 2009-2010 Dirk Breuer. See LICENSE for details.
|
data/THANKS.md
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
# Hoth THANKS
|
2
|
+
|
3
|
+
A number of people have contributed to Hoth by reporting problems,
|
4
|
+
suggesting improvements or submitting changes. Some of these people are:
|
5
|
+
|
6
|
+
* Andreas Bade (<andi.bade@gmail.com>)
|
7
|
+
* Sebastian Cohnen (<sebastian.cohnen@gmail.com>)
|
8
|
+
* Björn Vollmer (<bjoern.vollmer@googlemail.com>)
|
9
|
+
* Andreas Riemer
|
data/TODO
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Hoth
|
4
|
+
module Encoding
|
5
|
+
class Json
|
6
|
+
|
7
|
+
class <<self
|
8
|
+
def encode(string)
|
9
|
+
string.to_json
|
10
|
+
end
|
11
|
+
|
12
|
+
def decode(string)
|
13
|
+
begin
|
14
|
+
Hoth::Logger.debug "Original params before decode: #{string.inspect}"
|
15
|
+
JSON.parse(string)
|
16
|
+
rescue JSON::ParserError => jpe
|
17
|
+
raise EncodingError.wrap(jpe)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def content_type
|
22
|
+
"application/json"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Hoth
|
2
|
+
class Endpoint
|
3
|
+
attr_accessor :host, :port, :module_name, :transport
|
4
|
+
|
5
|
+
class ConfigEvaluator
|
6
|
+
attr_reader :endpoint
|
7
|
+
def initialize(endpoint, &block)
|
8
|
+
@endpoint = endpoint
|
9
|
+
instance_eval(&block)
|
10
|
+
end
|
11
|
+
|
12
|
+
[:host, :port, :module_name, :transport].each do |endpoint_attribute|
|
13
|
+
define_method endpoint_attribute do |value|
|
14
|
+
endpoint.send("#{endpoint_attribute}=", value)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(&block)
|
20
|
+
ConfigEvaluator.new(self, &block)
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_url
|
24
|
+
"http://#{@host}:#{@port}/execute"
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,14 @@
|
|
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
|
+
class TransportException < HothException; end
|
14
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class Exception
|
2
|
+
def to_json(*params)
|
3
|
+
{
|
4
|
+
'json_class' => self.class.name,
|
5
|
+
'message' => self.message,
|
6
|
+
'backtrace' => self.backtrace
|
7
|
+
}.to_json(*params)
|
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
|
data/lib/hoth/modules.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
module Hoth
|
2
|
+
class Modules
|
3
|
+
include Singleton
|
4
|
+
|
5
|
+
attr_reader :service_modules
|
6
|
+
|
7
|
+
def self.define(&block)
|
8
|
+
instance.instance_eval(&block)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.module(module_name)
|
12
|
+
instance.service_modules[module_name]
|
13
|
+
end
|
14
|
+
|
15
|
+
def service_module(module_name, &block)
|
16
|
+
service_module = ServiceModule.new(:name => module_name)
|
17
|
+
service_module.instance_eval(&block)
|
18
|
+
@service_modules[module_name] = service_module
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def initialize
|
24
|
+
@service_modules = {}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
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::Bert::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,45 @@
|
|
1
|
+
require 'rack/request'
|
2
|
+
|
3
|
+
module Hoth
|
4
|
+
module Providers
|
5
|
+
class RackProvider
|
6
|
+
|
7
|
+
def initialize(app)
|
8
|
+
@app = app
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(env)
|
12
|
+
Hoth::Logger.debug "env: #{env.inspect}"
|
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
|
+
|
20
|
+
responsible_service = ServiceRegistry.locate_service(service_name)
|
21
|
+
|
22
|
+
decoded_params = responsible_service.transport.encoder.decode(service_params)
|
23
|
+
result = Hoth::Services.send(service_name, *decoded_params)
|
24
|
+
|
25
|
+
encoded_result = responsible_service.transport.encoder.encode({"result" => result})
|
26
|
+
|
27
|
+
[200, {'Content-Type' => responsible_service.transport.encoder.content_type, 'Content-Length' => "#{encoded_result.length}"}, [encoded_result]]
|
28
|
+
rescue Exception => e
|
29
|
+
Hoth::Logger.debug "e: #{e.message}"
|
30
|
+
if responsible_service
|
31
|
+
encoded_error = responsible_service.transport.encoder.encode({"error" => e})
|
32
|
+
[500, {'Content-Type' => service.transport.encoder.content_type, 'Content-Length' => "#{encoded_error.length}"}, [encoded_error]]
|
33
|
+
else
|
34
|
+
plain_error = "An error occuered! (#{e.message})"
|
35
|
+
[500, {'Content-Type' => "text/plain", 'Content-Length' => "#{plain_error.length}"}, [plain_error]]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
else
|
39
|
+
@app.call(env)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/hoth/service.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
module Hoth
|
2
|
+
class Service
|
3
|
+
attr_accessor :name, :params_arity, :module
|
4
|
+
|
5
|
+
def initialize(name, &block)
|
6
|
+
@name = name
|
7
|
+
@params_arity = block.arity
|
8
|
+
instance_eval(&block)
|
9
|
+
end
|
10
|
+
|
11
|
+
def returns(return_value)
|
12
|
+
@return_value = return_value
|
13
|
+
end
|
14
|
+
|
15
|
+
def transport
|
16
|
+
@transport ||= Transport.create(endpoint.transport, self)
|
17
|
+
end
|
18
|
+
|
19
|
+
def impl_class
|
20
|
+
@impl_class_name ||= "#{self.name.to_s.camelize}Impl"
|
21
|
+
begin
|
22
|
+
@impl_class_name.constantize
|
23
|
+
rescue NameError => e
|
24
|
+
# no local implementation
|
25
|
+
false
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def is_local?
|
30
|
+
!!impl_class
|
31
|
+
end
|
32
|
+
|
33
|
+
def execute(*args)
|
34
|
+
result = self.is_local? ? impl_class.send(:execute, *args) : transport.call_remote_with(*args)
|
35
|
+
return return_nothing? ? nil : result
|
36
|
+
end
|
37
|
+
|
38
|
+
def return_nothing?
|
39
|
+
[:nothing, :nil, nil].include? @return_value
|
40
|
+
end
|
41
|
+
|
42
|
+
def via_endpoint(via = nil)
|
43
|
+
@via_endpoint = via || :default
|
44
|
+
end
|
45
|
+
|
46
|
+
def endpoint
|
47
|
+
@endpoint ||= self.module[Hoth.env][@via_endpoint]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Hoth
|
2
|
+
class ServiceDefinition
|
3
|
+
|
4
|
+
# create a new service with service_name and register it at the
|
5
|
+
# ServiceRegistry. The paramters of the block define the parameters of the
|
6
|
+
# defined service. Within the block you can describe the return value.
|
7
|
+
#
|
8
|
+
# Example:
|
9
|
+
#
|
10
|
+
# service :create_account do |account|
|
11
|
+
# returns :account_id
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
def service(service_name, &block)
|
15
|
+
ServiceRegistry.add_service(Service.new(service_name, &block))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Hoth
|
2
|
+
class ServiceModule
|
3
|
+
attr_accessor :name, :environments
|
4
|
+
|
5
|
+
class Environment
|
6
|
+
attr_accessor :endpoints
|
7
|
+
|
8
|
+
def initialize(&block)
|
9
|
+
@endpoints = {}
|
10
|
+
instance_eval(&block)
|
11
|
+
end
|
12
|
+
|
13
|
+
def endpoint(endpoint_name, &block)
|
14
|
+
@endpoints[endpoint_name.to_sym] = Endpoint.new(&block)
|
15
|
+
end
|
16
|
+
|
17
|
+
def [](endpoint_name)
|
18
|
+
@endpoints[endpoint_name.to_sym]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(attributes = {})
|
23
|
+
@environments = {}
|
24
|
+
@name = attributes[:name]
|
25
|
+
end
|
26
|
+
|
27
|
+
def env(*env_names, &block)
|
28
|
+
env_names.each do |env_name|
|
29
|
+
@environments[env_name.to_sym] = Environment.new(&block)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def add_service(service_name, options = {})
|
34
|
+
raise HothException.new("no endpoint-definition for environment '#{Hoth.env}' and service '#{service_name}'") unless self.environments[Hoth.env]
|
35
|
+
|
36
|
+
service = ServiceRegistry.locate_service(service_name.to_sym)
|
37
|
+
|
38
|
+
raise HothException.new("tried to add service '#{service_name}' but was not defined by service-definition") unless service
|
39
|
+
|
40
|
+
service.module = self
|
41
|
+
service.via_endpoint(options[:via])
|
42
|
+
end
|
43
|
+
|
44
|
+
def [](env_name)
|
45
|
+
@environments[env_name.to_sym]
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Hoth
|
2
|
+
|
3
|
+
# The ServiceRegistry knows all registered services. You can register new
|
4
|
+
# services and locate existing services.
|
5
|
+
|
6
|
+
class ServiceRegistry
|
7
|
+
include Singleton
|
8
|
+
|
9
|
+
# add a service to the registry
|
10
|
+
def self.add_service(service)
|
11
|
+
instance.add_service(service)
|
12
|
+
end
|
13
|
+
# alias_method :register_service, :add_service
|
14
|
+
|
15
|
+
# find a service with a given name
|
16
|
+
def self.locate_service(service_name)
|
17
|
+
instance.locate_service(service_name)
|
18
|
+
end
|
19
|
+
|
20
|
+
def add_service(service) # :nodoc:
|
21
|
+
@registry[service.name.to_sym] = service
|
22
|
+
end
|
23
|
+
|
24
|
+
def locate_service(service_name) # :nodoc:
|
25
|
+
@registry[service_name.to_sym]
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def initialize # :nodoc:
|
31
|
+
@registry = {}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Hoth
|
2
|
+
class Services
|
3
|
+
|
4
|
+
# With Hoth::Services.define followed by a block you define all your
|
5
|
+
# available services.
|
6
|
+
#
|
7
|
+
# Example:
|
8
|
+
#
|
9
|
+
# Hoth::Services.define do
|
10
|
+
#
|
11
|
+
# service :increment_counter do |counter|
|
12
|
+
# returns :nothing
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# service :value_of_counter do |counter|
|
16
|
+
# returns :fixnum
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# service :create_account do |account|
|
20
|
+
# returns :account_id
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# after defining your services you can call each of them with
|
26
|
+
# <tt>Hoth::Services.service_name(params)</tt>
|
27
|
+
#
|
28
|
+
# Hoth::Services.increment_counter(counter)
|
29
|
+
# current_number = Hoth::Services.value_of_counter(counter)
|
30
|
+
# created_account = Hoth::Services.create_account(account)
|
31
|
+
#
|
32
|
+
# see Hoth::ServiceDefinition for further informations of the block
|
33
|
+
# content.
|
34
|
+
|
35
|
+
def self.define(&block)
|
36
|
+
ServiceDefinition.new.instance_eval(&block)
|
37
|
+
end
|
38
|
+
|
39
|
+
class <<self
|
40
|
+
|
41
|
+
# this is where the services get called
|
42
|
+
def method_missing(meth, *args, &blk) # :nodoc:
|
43
|
+
if _service = ServiceRegistry.locate_service(meth)
|
44
|
+
_service.execute(*args)
|
45
|
+
else
|
46
|
+
super
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module Hoth
|
4
|
+
module Transport
|
5
|
+
class Base
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
attr_reader :encoder
|
9
|
+
|
10
|
+
def_delegators :@service_delegate, :name, :module, :endpoint, :params, :return_nothing?
|
11
|
+
|
12
|
+
def initialize(service_delegate, options = {})
|
13
|
+
@service_delegate = service_delegate
|
14
|
+
@encoder = options[:encoder] || Encoding::NoOp
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'bert'
|
2
|
+
require 'bertrpc'
|
3
|
+
|
4
|
+
module Hoth
|
5
|
+
module Transport
|
6
|
+
class Bert < Base
|
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
|