king_soa 0.0.2 → 0.0.3
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/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.
|