king_soa 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +94 -25
- data/lib/king_soa.rb +5 -27
- data/lib/king_soa/registry.rb +8 -1
- data/lib/king_soa/service.rb +7 -2
- data/spec/king_soa/rack/middleware_spec.rb +1 -1
- data/spec/king_soa/service_spec.rb +0 -2
- metadata +28 -4
data/README.rdoc
CHANGED
@@ -1,55 +1,124 @@
|
|
1
1
|
= KingSoa
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
KingSoa orchestrates a SOA landscape, by knowing where services live and how to
|
4
|
+
call them.
|
5
5
|
|
6
|
-
==
|
6
|
+
== Why?
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
While your app is growing you stuff more and more in it, unrelated to its core
|
9
|
+
business intelligence. You'll end up with an monolythic clumsy piece, getting
|
10
|
+
harder to handle every day. Just think of: scalability, testing, deployment, ..
|
11
11
|
|
12
|
+
To keep it clean a division of responsibilities, and with it a separation into
|
13
|
+
smaller parts, is needed. But now you've got a communication problem, because
|
14
|
+
each part needs to know about the others. This is where KingSoa enters the stage.
|
12
15
|
|
13
16
|
|
14
17
|
== Install
|
15
18
|
|
16
19
|
gem install king_soa
|
17
20
|
|
21
|
+
== Dependencies
|
22
|
+
|
23
|
+
Needed gems:
|
24
|
+
|
25
|
+
* resque => When a service has a queue set, its enqueued via resque
|
26
|
+
* typhoeus => making http requests to remotely located services
|
27
|
+
* json => all arguments of a service call are json encoded
|
28
|
+
|
18
29
|
== Define services
|
19
30
|
|
20
|
-
|
31
|
+
The service registry(KingSoa::Registry) is keeping track of available
|
32
|
+
services and behaves more or less like an array. Be aware that KingSoa::Registry
|
33
|
+
is a singleton, so that the rack middleware and your app are seeing the same.
|
34
|
+
|
35
|
+
A service (KingSoa::Service) has a name and knows how its gonna be called.
|
36
|
+
A service call goes out to a class named like the CamelCased service.name with
|
37
|
+
an self.perform method. Depending on the service settings it can be located
|
38
|
+
local, remote(http) or beeing put onto a queue.
|
21
39
|
|
22
40
|
You can define services anywhere in your app and add them to the service
|
23
|
-
registry.
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
41
|
+
registry. This should most likely be done in an initalizer(Rails).
|
42
|
+
|
43
|
+
# A remote service MUST have an url and an auth key set
|
44
|
+
a = KingSoa::Service.new(:name => :increment_usage,
|
45
|
+
:url => 'http://localhost:4567',
|
46
|
+
:auth => '12345')
|
47
|
+
|
48
|
+
# A local service calling CrunchImage.perform
|
49
|
+
b = KingSoa::Service.new(:name => :crunch_image)
|
50
|
+
|
51
|
+
# A queued service MUST have a queue name, and of course you should have a worker looking for it
|
52
|
+
c = KingSoa::Service.new(:name => :delete_user,
|
53
|
+
:queue => :deletions)
|
54
|
+
# register all of them
|
55
|
+
KingSoa::Registry << a << b << c
|
56
|
+
|
57
|
+
# somewhere in your app just call the services
|
58
|
+
KingSoa.increment_usage(12)
|
59
|
+
KingSoa.crunch_image(image_path)
|
60
|
+
KingSoa.delete_user(user_id)
|
61
|
+
|
62
|
+
=== Remote Services
|
63
|
+
|
64
|
+
The transport is done via http calls. All http calls have an authentication key
|
65
|
+
to provide some minimal access restriction. To make it secure you should either
|
66
|
+
use https in public or hide the endpoints somwhere on your farm.
|
67
|
+
|
68
|
+
Service endpoints receiving such calls need to use the provided rack middleware.
|
69
|
+
The middleware is doing the authentication, executing the service class and
|
70
|
+
returns values or errors.
|
71
|
+
|
72
|
+
=== Local Services
|
73
|
+
|
74
|
+
A local service calls a class with the CamelCased service name. This class MUST
|
75
|
+
have a self.perform method which receives all of the givven arguments.
|
76
|
+
|
77
|
+
=== Queued Services
|
78
|
+
|
79
|
+
The service is put onto a resque queue and somewhere in you cloud you should
|
80
|
+
have a worker looing for it. The service class should also have the resque
|
81
|
+
@queue attribute set so the job can be resceduled if it fails.
|
40
82
|
|
41
83
|
== Integration
|
42
84
|
|
85
|
+
Just think of a buch of small sinatra or specialized rails apps, each having
|
86
|
+
some internal services and consuming services from others.
|
87
|
+
|
43
88
|
=== Rails
|
44
89
|
|
90
|
+
Add the middleware(application.rb/environment.rb) if you want to receive calls.
|
91
|
+
require 'king_soa'
|
92
|
+
config.middleware.use KingSoa::Rack::Middleware
|
93
|
+
|
94
|
+
Setup services for example in an initializer reading a services.yml file. For
|
95
|
+
now there is no convinience loading method and no proposed yml format.
|
96
|
+
#services.yml
|
97
|
+
sign_document:
|
98
|
+
queue: signings
|
99
|
+
send_sms:
|
100
|
+
url: 'http://messaging.localhost:3000'
|
101
|
+
auth: '12345678'
|
102
|
+
|
103
|
+
# king_soa_init
|
104
|
+
service_defs = YAML.load_file(File.join(RAILS_ROOT, 'config', 'services.yml'))
|
105
|
+
service_defs.each do |k, v|
|
106
|
+
opts = { :name => k }
|
107
|
+
[:url, :auth, :queue].each do |opt|
|
108
|
+
opts[opt] = v[opt.to_s] if v[opt.to_s]
|
109
|
+
end
|
110
|
+
KingSoa::Registry << KingSoa::Service.new(opts)
|
111
|
+
end
|
45
112
|
|
46
113
|
=== Sinatra
|
47
114
|
|
48
115
|
Take a look at spec/server/app where you can see a minimal sinatra implementation
|
49
116
|
The base is just:
|
117
|
+
|
50
118
|
require 'king_soa'
|
51
119
|
use KingSoa::Rack::Middleware
|
52
|
-
|
120
|
+
|
121
|
+
The service definition should of course also be done, see rails example.
|
53
122
|
|
54
123
|
== Note on Patches/Pull Requests
|
55
124
|
|
data/lib/king_soa.rb
CHANGED
@@ -8,43 +8,21 @@ require 'king_soa/registry'
|
|
8
8
|
require 'king_soa/service'
|
9
9
|
require 'king_soa/rack/middleware'
|
10
10
|
|
11
|
-
# Define available services.
|
12
|
-
#
|
13
|
-
# service:
|
14
|
-
# name: sign_document
|
15
|
-
# url: "https://msg.salesking.eu"
|
16
|
-
# auth: 'a-long-random-string'
|
17
|
-
# queue: a-queue-name
|
18
|
-
#
|
19
|
-
#
|
20
|
-
# method: save_signed_document
|
21
|
-
# url: "https://www.salesking.eu/soa"
|
22
|
-
#
|
23
|
-
#
|
24
|
-
# after defining your services you can call each of them with
|
25
|
-
#
|
26
|
-
# <tt>KingSoa.service_name(args)</tt>
|
27
|
-
#
|
28
|
-
# KingSoa.sign_document(counter)
|
29
|
-
# current_number = Hoth::Services.value_of_counter(counter)
|
30
|
-
# created_account = Hoth::Services.create_account(account)
|
31
|
-
#
|
32
11
|
module KingSoa
|
33
12
|
|
34
13
|
class << self
|
35
|
-
|
36
|
-
def init_from_hash(services)
|
37
|
-
# create service
|
38
|
-
end
|
39
14
|
|
40
|
-
# Locate service by a given name
|
15
|
+
# Locate service by a given name:
|
16
|
+
# KingSoa.find(:my_service_name)
|
41
17
|
# ==== Parameter
|
42
18
|
# service<String|Symbol>:: the name to lookup
|
43
19
|
def find(service)
|
44
20
|
Registry[service]
|
45
21
|
end
|
46
22
|
|
47
|
-
#
|
23
|
+
# This is where the services get called.
|
24
|
+
# Tries to locate the service in the registry and if found call its perform
|
25
|
+
# method
|
48
26
|
def method_missing(meth, *args, &blk) # :nodoc:
|
49
27
|
if service = Registry[meth]
|
50
28
|
service.perform(*args)
|
data/lib/king_soa/registry.rb
CHANGED
@@ -33,17 +33,24 @@ module KingSoa
|
|
33
33
|
@services ||= []
|
34
34
|
end
|
35
35
|
|
36
|
-
# Add a new
|
36
|
+
# Add a new service onto the stack
|
37
|
+
# === Parameter
|
38
|
+
# service<KingSoa::Service>:: the service to add
|
37
39
|
def <<(service)
|
38
40
|
(services || []) << service
|
39
41
|
end
|
40
42
|
|
41
43
|
# Get a method by a given name
|
44
|
+
# === Parameter
|
45
|
+
# service_name<String|Symbol>:: the service to find
|
46
|
+
# === Returns
|
47
|
+
# <KingSoa::Service> or <nil>
|
42
48
|
def [](service_name)
|
43
49
|
name = service_name.to_sym
|
44
50
|
services.detect {|s| s.name == name }
|
45
51
|
end
|
46
52
|
|
53
|
+
# untested
|
47
54
|
def group(service_name)
|
48
55
|
name = service_name.to_sym
|
49
56
|
# srvs = []
|
data/lib/king_soa/service.rb
CHANGED
@@ -31,7 +31,10 @@ module KingSoa
|
|
31
31
|
Resque::Job.create(queue, local_class_name, *args)
|
32
32
|
end
|
33
33
|
|
34
|
-
# Call a method:
|
34
|
+
# Call a method:
|
35
|
+
# * remote over http
|
36
|
+
# * local by calling perform method on a class
|
37
|
+
# * put a job onto a queue
|
35
38
|
# === Parameter
|
36
39
|
# args:: whatever arguments the service methods recieves. Those are later json
|
37
40
|
# encoded for remote or queued methods
|
@@ -45,6 +48,7 @@ module KingSoa
|
|
45
48
|
end
|
46
49
|
end
|
47
50
|
|
51
|
+
# The local class, if found
|
48
52
|
def local_class
|
49
53
|
@local_class ||= begin
|
50
54
|
local_class_name.constantize
|
@@ -53,7 +57,8 @@ module KingSoa
|
|
53
57
|
end
|
54
58
|
end
|
55
59
|
|
56
|
-
# Return the classname infered from service name
|
60
|
+
# Return the classname infered from the camelized service name.
|
61
|
+
# A service named: save_attachment => class SaveAttachment
|
57
62
|
def local_class_name
|
58
63
|
self.name.to_s.camelize
|
59
64
|
end
|
@@ -16,7 +16,7 @@ describe KingSoa::Rack::Middleware do
|
|
16
16
|
rack_response = middleware.call env
|
17
17
|
rack_response.first.should == 500 #status code
|
18
18
|
rack_response.last.should be_a_kind_of(Array)
|
19
|
-
rack_response.last.first.should == "{\"error\":\"An error occurred
|
19
|
+
rack_response.last.first.should == "{\"error\":\"An error occurred => Missing rack.input\"}"
|
20
20
|
end
|
21
21
|
|
22
22
|
xit "should handle result" do
|
@@ -10,7 +10,6 @@ describe KingSoa::Service do
|
|
10
10
|
end
|
11
11
|
|
12
12
|
describe KingSoa::Service, 'local request' do
|
13
|
-
|
14
13
|
it "should call service" do
|
15
14
|
s = KingSoa::Service.new(:name=>:local_soa_class)
|
16
15
|
s.perform(1,2,3).should == [1,2,3]
|
@@ -26,7 +25,6 @@ describe KingSoa::Service, 'queued' do
|
|
26
25
|
end
|
27
26
|
|
28
27
|
describe KingSoa::Service, 'remote request' do
|
29
|
-
|
30
28
|
before :all do
|
31
29
|
start_test_server
|
32
30
|
end
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: 0.0.
|
8
|
+
- 3
|
9
|
+
version: 0.0.3
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Georg Leciejewski
|
@@ -42,7 +42,7 @@ dependencies:
|
|
42
42
|
type: :runtime
|
43
43
|
version_requirements: *id002
|
44
44
|
- !ruby/object:Gem::Dependency
|
45
|
-
name:
|
45
|
+
name: resque
|
46
46
|
prerelease: false
|
47
47
|
requirement: &id003 !ruby/object:Gem::Requirement
|
48
48
|
requirements:
|
@@ -51,8 +51,32 @@ dependencies:
|
|
51
51
|
segments:
|
52
52
|
- 0
|
53
53
|
version: "0"
|
54
|
-
type: :
|
54
|
+
type: :runtime
|
55
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
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: rack
|
70
|
+
prerelease: false
|
71
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
segments:
|
76
|
+
- 0
|
77
|
+
version: "0"
|
78
|
+
type: :development
|
79
|
+
version_requirements: *id005
|
56
80
|
description: |
|
57
81
|
Creating a SOA requires a centralized location to define all services within the
|
58
82
|
SOA. KingSoa takes care of keeping services in a service registry and knows how to call them.
|