sk-hoth 0.0.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.
data/README.rdoc ADDED
@@ -0,0 +1,64 @@
1
+ = KingSoa
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
11
+
12
+
13
+
14
+ == Install
15
+
16
+ gem install king_soa
17
+
18
+ == Define services
19
+
20
+ === Low Level
21
+
22
+ You can define services anywhere in your app and add them to the service
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
32
+
33
+ # somewhere in your app just call your service method
34
+ KingSoa.increment_usage(12)
35
+
36
+ A service name is always required. If the service is to be called remotely you
37
+ must add a url and an auth_key.
38
+ The transport is done via http calls, so to make it secure you should either use
39
+ https in public or hide the servers somwhere on your farm.
40
+
41
+ == Integration
42
+
43
+ === Rails
44
+
45
+
46
+ === Sinatra
47
+
48
+ Take a look at spec/server/app where you can see a minimal sinatra implementation
49
+ The base is just:
50
+ require 'king_soa'
51
+ use KingSoa::Rack::Middleware
52
+
53
+
54
+ == Note on Patches/Pull Requests
55
+
56
+ * Fork the project.
57
+ * Make your feature addition or bug fix.
58
+ * Add tests for it. This is important so I don't break it in a future version unintentionally.
59
+ * Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
60
+ * Send me a pull request. Bonus points for topic branches.
61
+
62
+ == Copyright
63
+
64
+ Copyright (c) 2010 Georg Leciejewski. See LICENSE for details.
@@ -0,0 +1,47 @@
1
+ module KingSoa::Rack
2
+ class Middleware
3
+
4
+ def initialize(app)
5
+ @app = app
6
+ end
7
+
8
+ # Takes incoming soa requests and calls the passed in method with given params
9
+ def call(env)
10
+ # Hoth::Logger.debug "env: #{env.inspect}"
11
+ if env["PATH_INFO"] =~ /^\/soa/
12
+ begin
13
+ req = Rack::Request.new(env)
14
+ # find service
15
+ service = KingSoa.find(req.params["name"])
16
+ # authenticate
17
+ authenticated?(service, req.params["auth_key"])
18
+ # perform method with decoded params
19
+ result = service.perform(*service.decode( req.params["params"] ))
20
+ # encode result
21
+ encoded_result = service.encode({"result" => result})
22
+ # and return
23
+ [ 200,
24
+ {'Content-Type' => 'application/json',
25
+ 'Content-Length' => "#{encoded_result.length}"},
26
+ [encoded_result] ]
27
+ rescue Exception => e
28
+ #Hoth::Logger.debug "e: #{e.message}"
29
+ if service
30
+ encoded_error = service.encode({"error" => e})
31
+ [500, {'Content-Type' => 'application/json', 'Content-Length' => "#{encoded_error.length}"}, [encoded_error]]
32
+ else
33
+ encoded_error = {"error" => "An error occurred! (#{e.message})"}.to_json
34
+ [500, {'Content-Type' => "application/json", 'Content-Length' => "#{encoded_error.length}"}, [encoded_error]]
35
+ end
36
+ end
37
+ else
38
+ @app.call(env)
39
+ end
40
+ end
41
+
42
+ def authenticated?(service, key)
43
+ raise "Please provide a valid authentication key" unless service.auth_key == key
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,55 @@
1
+ module KingSoa
2
+ class Registry
3
+ include Singleton
4
+
5
+ ############################################################################
6
+ # Class methods .. only use those since we are in a Singleton
7
+ ############################################################################
8
+
9
+ # Get a method by a given name
10
+ def self.[](service_name)
11
+ instance[service_name]
12
+ end
13
+ # add a service
14
+ def self.<<(service)
15
+ instance << service
16
+ end
17
+ # Return an array of defined services
18
+ def self.services
19
+ instance.services
20
+ end
21
+
22
+ # find a group of services identified by starting with the same string
23
+ #
24
+ def group(name)
25
+ instance.group(name)
26
+ end
27
+ ############################################################################
28
+ # Instance methods - not directly accessible => Singleton
29
+ ############################################################################
30
+
31
+ # returns all available methods
32
+ def services
33
+ @services ||= []
34
+ end
35
+
36
+ # Add a new method onto the stack
37
+ def <<(service)
38
+ (services || []) << service
39
+ end
40
+
41
+ # Get a method by a given name
42
+ def [](service_name)
43
+ name = service_name.to_sym
44
+ services.detect {|s| s.name == name }
45
+ end
46
+
47
+ def group(service_name)
48
+ name = service_name.to_sym
49
+ # srvs = []
50
+ services.collect {|s| s.name[/^#{name}/] }
51
+ end
52
+
53
+
54
+ end
55
+ end
@@ -0,0 +1,88 @@
1
+ module KingSoa
2
+ class Service
3
+ # endpoint url
4
+ attr_accessor :debug, :name, :auth_key, :queue
5
+ attr_reader :request
6
+
7
+ def initialize(opts)
8
+ self.name = opts[:name].to_sym
9
+ self.url = opts[:url] if opts[:url]
10
+ self.queue = opts[:queue] if opts[:queue]
11
+ self.auth_key = opts[:auth_key] if opts[:auth_key]
12
+ end
13
+
14
+ def call_remote(*args)
15
+ set_request_opts(args)
16
+ resp_code = @request.perform
17
+ case resp_code
18
+ when 200
19
+ return self.decode(@request.response_body)["result"]
20
+ else
21
+ return self.decode(@request.response_body)["error"]
22
+ end
23
+ end
24
+
25
+ def perform(*args)
26
+ result = local_class ? local_class.send(:perform, *args) : call_remote(*args)
27
+ return result
28
+ end
29
+
30
+
31
+ def local_class
32
+ @local_class ||= begin
33
+ "#{self.name.to_s.camelize}".constantize
34
+ rescue NameError => e # no local implementation
35
+ false
36
+ end
37
+ end
38
+
39
+ def request
40
+ @request ||= Typhoeus::Easy.new
41
+ end
42
+
43
+ def set_request_opts(args)
44
+ request.url = url
45
+ request.method = :post
46
+ request.timeout = 100 # milliseconds
47
+ request.params = params(args)
48
+ request.user_agent = 'KingSoa'
49
+ request.follow_location = true
50
+ request.verbose = 1 if debug
51
+ end
52
+
53
+ # Url receiving the request
54
+ # TODO. if not present try to grab from endpoint
55
+ def url
56
+ @url
57
+ end
58
+ def url=(url)
59
+ @url = "#{url}/soa"
60
+ end
61
+
62
+ # The params for each soa request consisnt of two values:
63
+ # name => the name of the method to call
64
+ # params => the parameters for the method
65
+ # ==== Parameter
66
+ # params<Hash|Array|String>:: will be json encoded
67
+ # === Returns
68
+ # <Hash{String=>String}>:: params added to the POST body
69
+ def params(payload)
70
+ { 'name' => name.to_s,
71
+ 'params' => encode(payload),
72
+ 'auth_key'=> auth_key }
73
+ end
74
+
75
+ def encode(string)
76
+ string.to_json
77
+ end
78
+
79
+ def decode(string)
80
+ begin
81
+ JSON.parse(string)
82
+ rescue JSON::ParserError => e
83
+ raise e
84
+ end
85
+ end
86
+
87
+ end
88
+ end
data/lib/king_soa.rb ADDED
@@ -0,0 +1,56 @@
1
+ require 'singleton'
2
+ require 'json'
3
+ require 'typhoeus'
4
+ require 'active_support/inflector'
5
+
6
+ require 'king_soa/registry'
7
+ require 'king_soa/service'
8
+ require 'king_soa/rack/middleware'
9
+
10
+ # Define available services.
11
+ #
12
+ # service:
13
+ # name: sign_document
14
+ # url: "https://msg.salesking.eu"
15
+ # auth: 'a-long-random-string'
16
+ # queue: a-queue-name
17
+ #
18
+ #
19
+ # method: save_signed_document
20
+ # url: "https://www.salesking.eu/soa"
21
+ #
22
+ #
23
+ # after defining your services you can call each of them with
24
+ # <tt>Hoth::Services.service_name(params)</tt>
25
+ #
26
+ # KingSoa.sign_document(counter)
27
+ # current_number = Hoth::Services.value_of_counter(counter)
28
+ # created_account = Hoth::Services.create_account(account)
29
+ #
30
+ module KingSoa
31
+
32
+ class << self
33
+
34
+ def init_from_hash(services)
35
+ # create service
36
+
37
+ end
38
+
39
+ # Locate service by a given name
40
+ # ==== Params
41
+ # service<String|Symbol>:: the name to lookup
42
+ def find(service)
43
+ Registry[service]
44
+ end
45
+
46
+ # this is where the services get called
47
+ def method_missing(meth, *args, &blk) # :nodoc:
48
+ if service = Registry[meth]
49
+ service.execute(*args)
50
+ else
51
+ super
52
+ end
53
+ end
54
+ end
55
+
56
+ end
@@ -0,0 +1,36 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper.rb'
2
+
3
+ describe KingSoa::Rack::Middleware do
4
+ include Rack::Test::Methods
5
+
6
+ before(:each) do
7
+ @service = KingSoa::Service.new(:url=>'localhost', :name=>'a_method')
8
+ KingSoa::Registry << @service
9
+ end
10
+
11
+ it "should be able to handle exceptions" do
12
+ app = stub("ApplicationStub").as_null_object
13
+ middleware = KingSoa::Rack::Middleware.new(app)
14
+ env = {"PATH_INFO" => "/soa", "name" => 'a_method'}
15
+
16
+ rack_response = middleware.call env
17
+ rack_response.first.should == 500 #status code
18
+ rack_response.last.should be_a_kind_of(Array)
19
+ rack_response.last.first.should == "{\"error\":\"An error occurred! (Missing rack.input)\"}"
20
+ end
21
+
22
+ xit "says hello" do
23
+ app = stub("ApplicationStub").as_null_object
24
+ middleware = Hoth::Providers::RackProvider.new(app)
25
+
26
+ get '/soa', :name=>'a_method', :params=> "#{{:number=>1}.to_json}"
27
+ last_response.should == 'ads'#be_ok
28
+ last_response.body.should == 'Hello World'
29
+ end
30
+
31
+ # def app
32
+ # dummy_app = lambda { |env| puts "in dummy"; [200, {}, ""] }
33
+ # KingSoa::Rack::Middleware.new(dummy_app)
34
+ # end
35
+
36
+ end
@@ -0,0 +1,28 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ describe KingSoa::Registry do
4
+ before(:each) do
5
+
6
+ end
7
+
8
+ it "should return empty services" do
9
+ reg = KingSoa::Registry.new
10
+ reg.services.should == []
11
+ end
12
+
13
+ it "should add service" do
14
+ reg = KingSoa::Registry.new
15
+ s = KingSoa::Service.new(:url=>'http://localhost')
16
+ reg << s
17
+ reg.services.should == [s]
18
+ end
19
+
20
+ it "should return a service by name" do
21
+ reg = KingSoa::Registry.new
22
+ s = KingSoa::Service.new(:name=>:save_document, :url=>'http://localhost')
23
+ reg << s
24
+ reg[:save_document].should == s
25
+ end
26
+
27
+
28
+ end
@@ -0,0 +1,46 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ describe KingSoa::Service do
4
+ before(:each) do
5
+ end
6
+
7
+ it "should init" do
8
+
9
+ end
10
+ end
11
+
12
+ describe KingSoa::Service, 'local request' do
13
+
14
+ it "should call service" do
15
+ s = KingSoa::Service.new(:name=>:local_soa_class)
16
+ res = s.perform(1,2,3)
17
+ res.should == [1,2,3]
18
+ end
19
+ end
20
+
21
+ # needs the local testserver !!!
22
+ # ruby spec/server/app
23
+ describe KingSoa::Service, 'remote request' do
24
+
25
+ it "should call a service remote" do
26
+ s = KingSoa::Service.new(:name=>:soa_test_service, :url=>'http://localhost:4567', :auth_key=>'12345')
27
+ s.perform(1,2,3).should == [1,2,3]
28
+ end
29
+
30
+ it "should call a service remote and return auth error" do
31
+ s = KingSoa::Service.new(:name=>:soa_test_service, :url=>'http://localhost:4567', :auth_key=>'wrong')
32
+ s.perform(1,2,3).should == "Please provide a valid authentication key"
33
+ end
34
+
35
+ it "should call a service remote and return auth error" do
36
+ s = KingSoa::Service.new(:name=>:wrong_service, :url=>'http://localhost:4567', :auth_key=>'12345')
37
+ s.perform(1,2,3).should == "An error occurred! (undefined method `auth_key' for nil:NilClass)"
38
+ end
39
+ end
40
+
41
+
42
+ class LocalSoaClass
43
+ def self.perform(param1, param2, param3)
44
+ return [param1, param2, param3]
45
+ end
46
+ end
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'sinatra'
4
+ require "#{File.dirname(__FILE__)}/../../lib/king_soa"
5
+
6
+ use KingSoa::Rack::Middleware
7
+
8
+
9
+ ###################################################
10
+ # method to kill this server instance
11
+ #'/die'
12
+ #######################################
13
+ # Somewhere in you app
14
+ #
15
+ # setup test registry
16
+ service = KingSoa::Service.new(:name=>'soa_test_service', :auth_key=>'12345')
17
+ KingSoa::Registry << service
18
+
19
+ # the class beeing called localy
20
+ class SoaTestService
21
+
22
+ def self.perform(param1, param2, param3)
23
+ return [param1, param2, param3]
24
+ end
25
+
26
+ end
@@ -0,0 +1,37 @@
1
+ require 'rubygems'
2
+
3
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
4
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
5
+ require 'king_soa'
6
+ require 'spec'
7
+ require 'spec/autorun'
8
+ # for mocking web requests
9
+ require 'webmock/rspec'
10
+ require 'rack/test'
11
+ include WebMock
12
+
13
+
14
+
15
+ def local_service
16
+ # Rack::Builder.app do
17
+ # use KingSoa::Rack::Middleware
18
+ # run super
19
+ # end
20
+
21
+ # Rack::Builder.new do
22
+ # use KingSoa::Rack::Middleware
23
+ # app = proc do |env|
24
+ # [ 200, {'Content-Type' => 'text/plain'}, "b" ]
25
+ # end
26
+ # run app
27
+ # end.to_app
28
+ # def app
29
+ # Rack::Builder.new {
30
+ # # URLs starting with /account (logged in users) go to Rails
31
+ # map "/soa" do
32
+ # run KingSoa::Rack::Middleware.new
33
+ # end
34
+ # }.to_app
35
+ # end
36
+ # app.run
37
+ end
metadata ADDED
@@ -0,0 +1,139 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sk-hoth
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Georg Leciejewski
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-05-08 00:00:00 +02:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: curb
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: json
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: king_hmac
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
+ - !ruby/object:Gem::Dependency
69
+ name: webmock
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
80
+ description: |
81
+ Creating a SOA requires a centralized location to define all services within the
82
+ SOA. Furthermore you want to know where to deploy those services.
83
+
84
+ email: gl@salesking.eu
85
+ executables: []
86
+
87
+ extensions: []
88
+
89
+ extra_rdoc_files:
90
+ - LICENSE
91
+ - README.rdoc
92
+ files:
93
+ - README.rdoc
94
+ - lib/king_soa.rb
95
+ - lib/king_soa/rack/middleware.rb
96
+ - lib/king_soa/registry.rb
97
+ - lib/king_soa/service.rb
98
+ - spec/king_soa/rack/middleware_spec.rb
99
+ - spec/king_soa/registry_spec.rb
100
+ - spec/king_soa/service_spec.rb
101
+ - spec/server/app.rb
102
+ - spec/spec_helper.rb
103
+ - LICENSE
104
+ has_rdoc: true
105
+ homepage: http://github.com/salesking/king_soa
106
+ licenses: []
107
+
108
+ post_install_message:
109
+ rdoc_options:
110
+ - --charset=UTF-8
111
+ require_paths:
112
+ - lib
113
+ required_ruby_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ segments:
118
+ - 0
119
+ version: "0"
120
+ required_rubygems_version: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ segments:
125
+ - 0
126
+ version: "0"
127
+ requirements: []
128
+
129
+ rubyforge_project:
130
+ rubygems_version: 1.3.6
131
+ signing_key:
132
+ specification_version: 3
133
+ summary: Registry and deployment description abstraction for SOA-Services. Tweaked by SalesKing
134
+ test_files:
135
+ - spec/spec_helper.rb
136
+ - spec/king_soa/registry_spec.rb
137
+ - spec/king_soa/service_spec.rb
138
+ - spec/king_soa/rack/middleware_spec.rb
139
+ - spec/server/app.rb