sanford 0.1.0
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/.gitignore +19 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +164 -0
- data/Rakefile +8 -0
- data/bench/client.rb +30 -0
- data/bench/report.txt +10 -0
- data/bench/runner.rb +102 -0
- data/bench/services.rb +23 -0
- data/bench/tasks.rb +18 -0
- data/lib/sanford/config.rb +33 -0
- data/lib/sanford/connection.rb +70 -0
- data/lib/sanford/exception_handler.rb +43 -0
- data/lib/sanford/exceptions.rb +37 -0
- data/lib/sanford/host.rb +162 -0
- data/lib/sanford/manager.rb +58 -0
- data/lib/sanford/rake.rb +45 -0
- data/lib/sanford/server.rb +39 -0
- data/lib/sanford/service_handler.rb +93 -0
- data/lib/sanford/version.rb +3 -0
- data/lib/sanford.rb +32 -0
- data/log/.gitkeep +0 -0
- data/sanford.gemspec +27 -0
- data/test/helper.rb +20 -0
- data/test/support/helpers.rb +72 -0
- data/test/support/service_handlers.rb +115 -0
- data/test/support/services.rb +72 -0
- data/test/support/simple_client.rb +55 -0
- data/test/system/managing_test.rb +105 -0
- data/test/system/request_handling_test.rb +202 -0
- data/test/unit/config_test.rb +54 -0
- data/test/unit/connection_test.rb +23 -0
- data/test/unit/exception_handler_test.rb +69 -0
- data/test/unit/host/version_group_test.rb +39 -0
- data/test/unit/host_test.rb +142 -0
- data/test/unit/manager_test.rb +21 -0
- data/test/unit/server_test.rb +22 -0
- data/test/unit/service_handler_test.rb +170 -0
- metadata +209 -0
data/lib/sanford/host.rb
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
# Sanford's Host mixin is used to define service hosts. When mixed into a class
|
2
|
+
# it provides the interface for configuring the service host and for adding
|
3
|
+
# versioned services. It also contains the logic for routing a request to a
|
4
|
+
# a service handler.
|
5
|
+
#
|
6
|
+
# Options:
|
7
|
+
# * `name` - A string for naming this host. This can be used when specifying
|
8
|
+
# a host with the rake tasks and will be used to name the PID
|
9
|
+
# file. Defaults to the class's name.
|
10
|
+
# * `ip` - The string for the ip that the TCP Server should bind to. This
|
11
|
+
# defaults to '0.0.0.0'.
|
12
|
+
# * `port` - The integer for the port that the TCP Server should bind to.
|
13
|
+
# This isn't defaulted and must be provided.
|
14
|
+
# * `pid_dir` - The directory to write the PID file to. This is defaulted to
|
15
|
+
# Dir.pwd.
|
16
|
+
# * `logger` - The logger to use if the Sanford server logs messages. This is
|
17
|
+
# defaulted to an instance of Ruby's Logger.
|
18
|
+
#
|
19
|
+
require 'logger'
|
20
|
+
require 'ns-options'
|
21
|
+
require 'pathname'
|
22
|
+
|
23
|
+
require 'sanford/config'
|
24
|
+
require 'sanford/exception_handler'
|
25
|
+
require 'sanford/exceptions'
|
26
|
+
require 'sanford/service_handler'
|
27
|
+
|
28
|
+
module Sanford
|
29
|
+
module Host
|
30
|
+
|
31
|
+
# Notes:
|
32
|
+
# * When Host is included on a class, it needs to mixin NsOptions and define
|
33
|
+
# the options directly on the class (instead of on the Host module).
|
34
|
+
# Otherwise, NsOptions will not work correctly for the class.
|
35
|
+
def self.included(host_class)
|
36
|
+
host_class.class_eval do
|
37
|
+
include NsOptions
|
38
|
+
extend Sanford::Host::Interface
|
39
|
+
|
40
|
+
options :config do
|
41
|
+
option :name, String, :default => host_class.to_s
|
42
|
+
option :ip, String, :default => '0.0.0.0'
|
43
|
+
option :port, Integer
|
44
|
+
option :pid_dir, Pathname, :default => Dir.pwd
|
45
|
+
option :logger, :default => proc{ Sanford::NullLogger.new }
|
46
|
+
|
47
|
+
option :exception_handler, :default => Sanford::ExceptionHandler
|
48
|
+
option :versioned_services, Hash, :default => {}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
Sanford.config.hosts.add(host_class)
|
52
|
+
end
|
53
|
+
|
54
|
+
INTERFACE_OPTIONS = [ :name, :ip, :port, :pid_dir, :logger, :exception_handler ]
|
55
|
+
|
56
|
+
# Notes:
|
57
|
+
# * The `initialize` takes the values configured on the class and merges
|
58
|
+
# the passed in options. This is used to set the individual instance's
|
59
|
+
# configuration (which allows overwriting options like the port).
|
60
|
+
def initialize(options = nil)
|
61
|
+
options = self.remove_nil_values(options)
|
62
|
+
config_options = self.class.config.to_hash.merge(options)
|
63
|
+
self.config.apply(config_options)
|
64
|
+
raise(Sanford::InvalidHostError.new(self.class)) if !self.port
|
65
|
+
end
|
66
|
+
|
67
|
+
INTERFACE_OPTIONS.each do |name|
|
68
|
+
|
69
|
+
define_method(name) do
|
70
|
+
self.config.send(name)
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
def run(request)
|
76
|
+
request_handler(request).run
|
77
|
+
end
|
78
|
+
|
79
|
+
def inspect
|
80
|
+
reference = '0x0%x' % (self.object_id << 1)
|
81
|
+
"#<#{self.class}:#{reference} ip=#{self.config.ip.inspect} " \
|
82
|
+
"port=#{self.config.port.inspect}>"
|
83
|
+
end
|
84
|
+
|
85
|
+
protected
|
86
|
+
|
87
|
+
def request_handler(request)
|
88
|
+
handler_class(get_handler_class_name(request)).new(self.logger, request)
|
89
|
+
end
|
90
|
+
|
91
|
+
def handler_class(class_name_str)
|
92
|
+
self.logger.info(" Handler: #{class_name_str.inspect}")
|
93
|
+
Sanford::ServiceHandler.constantize(class_name_str).tap do |handler_class|
|
94
|
+
raise Sanford::NoHandlerClassError.new(self, class_name_str) if !handler_class
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def get_handler_class_name(request)
|
99
|
+
services = self.config.versioned_services[request.version] || {}
|
100
|
+
services[request.name].tap do |name|
|
101
|
+
raise Sanford::NotFoundError if !name
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def remove_nil_values(options)
|
106
|
+
(options || {}).inject({}) do |hash, (k, v)|
|
107
|
+
hash.merge!({ k => v }) if !v.nil?
|
108
|
+
hash
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
module Interface
|
113
|
+
|
114
|
+
INTERFACE_OPTIONS.each do |name|
|
115
|
+
|
116
|
+
define_method(name) do |*args|
|
117
|
+
self.config.send("#{name}=", *args) if !args.empty?
|
118
|
+
self.config.send(name)
|
119
|
+
end
|
120
|
+
|
121
|
+
define_method("#{name}=") do |new_value|
|
122
|
+
self.config.send("#{name}=", new_value)
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
def version(name, &block)
|
128
|
+
version_group = Sanford::Host::VersionGroup.new(name, &block)
|
129
|
+
self.config.versioned_services.merge!(version_group.to_hash)
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
|
134
|
+
class VersionGroup
|
135
|
+
attr_reader :name, :services
|
136
|
+
|
137
|
+
def initialize(name, &definition_block)
|
138
|
+
@name = name
|
139
|
+
@services = {}
|
140
|
+
self.instance_eval(&definition_block)
|
141
|
+
end
|
142
|
+
|
143
|
+
def service_handler_ns(value = nil)
|
144
|
+
@service_handler_ns = value if value
|
145
|
+
@service_handler_ns
|
146
|
+
end
|
147
|
+
|
148
|
+
def service(service_name, handler_class_name)
|
149
|
+
if self.service_handler_ns && !(handler_class_name =~ /^::/)
|
150
|
+
handler_class_name = "#{self.service_handler_ns}::#{handler_class_name}"
|
151
|
+
end
|
152
|
+
@services[service_name] = handler_class_name
|
153
|
+
end
|
154
|
+
|
155
|
+
def to_hash
|
156
|
+
{ self.name => self.services }
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# The Manager class is responsible for managing sanford's server process. Given
|
2
|
+
# a host, it can start and stop the host's server process. This is done using
|
3
|
+
# the Daemons gem and `run_proc`. The class provides a convenience method on the
|
4
|
+
# class called `call`, which will find a host, build a new manager and call the
|
5
|
+
# relevant action (this is what the rake tasks use).
|
6
|
+
#
|
7
|
+
require 'daemons'
|
8
|
+
|
9
|
+
require 'sanford/config'
|
10
|
+
require 'sanford/exceptions'
|
11
|
+
require 'sanford/server'
|
12
|
+
|
13
|
+
module Sanford
|
14
|
+
|
15
|
+
class Manager
|
16
|
+
attr_reader :host, :process_name
|
17
|
+
|
18
|
+
def self.call(action, options = nil)
|
19
|
+
options ||= {}
|
20
|
+
options[:host] ||= ENV['SANFORD_HOST']
|
21
|
+
options[:ip] ||= ENV['SANFORD_IP']
|
22
|
+
options[:port] ||= ENV['SANFORD_PORT']
|
23
|
+
|
24
|
+
host_class = if (host_class_or_name = options.delete(:host))
|
25
|
+
Sanford.config.find_host(host_class_or_name)
|
26
|
+
else
|
27
|
+
Sanford.config.hosts.first
|
28
|
+
end
|
29
|
+
raise(Sanford::NoHostError.new(host_class_or_name)) if !host_class
|
30
|
+
self.new(host_class, options).call(action)
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(host_class, options = {})
|
34
|
+
@host = host_class.new(options)
|
35
|
+
@process_name = [ self.host.ip, self.host.port, self.host.name ].join('_')
|
36
|
+
end
|
37
|
+
|
38
|
+
def call(action)
|
39
|
+
options = self.default_options.merge({ :ARGV => [ action.to_s ] })
|
40
|
+
FileUtils.mkdir_p(options[:dir])
|
41
|
+
::Daemons.run_proc(self.process_name, options) do
|
42
|
+
server = Sanford::Server.new(self.host)
|
43
|
+
server.start
|
44
|
+
server.join_thread
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
protected
|
49
|
+
|
50
|
+
def default_options
|
51
|
+
{ :dir_mode => :normal,
|
52
|
+
:dir => self.host.pid_dir
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
data/lib/sanford/rake.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
module Sanford::Rake
|
2
|
+
|
3
|
+
class Tasks
|
4
|
+
extend ::Rake::DSL
|
5
|
+
|
6
|
+
def self.load
|
7
|
+
namespace :sanford do
|
8
|
+
|
9
|
+
# Overwrite this to load your application's environment so that it can
|
10
|
+
# be used with Sanford
|
11
|
+
task :setup
|
12
|
+
|
13
|
+
task :load_manager => :setup do
|
14
|
+
require 'sanford'
|
15
|
+
Sanford.init
|
16
|
+
end
|
17
|
+
|
18
|
+
desc "Start a Sanford server and daemonize the process"
|
19
|
+
task :start => :load_manager do
|
20
|
+
Sanford::Manager.call :start
|
21
|
+
end
|
22
|
+
|
23
|
+
desc "Stop a daemonized Sanford server process"
|
24
|
+
task :stop => :load_manager do
|
25
|
+
Sanford::Manager.call :stop
|
26
|
+
end
|
27
|
+
|
28
|
+
desc "Restart a daemonized Sanford server process"
|
29
|
+
task :restart => :load_manager do
|
30
|
+
Sanford::Manager.call :restart
|
31
|
+
end
|
32
|
+
|
33
|
+
desc "Run a Sanford server (not daemonized)"
|
34
|
+
task :run => :load_manager do
|
35
|
+
Sanford::Manager.call :run
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
Sanford::Rake::Tasks.load
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# Sanford's server uses DatTCP for a TCP Server. When a client connects, the
|
2
|
+
# `serve` method is called. Sanford creates a new instance of a connection
|
3
|
+
# handler and hands it the service host and client socket. This is because the
|
4
|
+
# `serve` method can be accessed by multiple threads, so we essentially create a
|
5
|
+
# new connection handler per thread.
|
6
|
+
#
|
7
|
+
require 'dat-tcp'
|
8
|
+
|
9
|
+
require 'sanford/connection'
|
10
|
+
|
11
|
+
module Sanford
|
12
|
+
|
13
|
+
class Server
|
14
|
+
include DatTCP::Server
|
15
|
+
|
16
|
+
attr_reader :service_host
|
17
|
+
|
18
|
+
def initialize(service_host, options = {})
|
19
|
+
@service_host = service_host
|
20
|
+
super(self.service_host.ip, self.service_host.port, options)
|
21
|
+
end
|
22
|
+
|
23
|
+
def name
|
24
|
+
self.service_host.name
|
25
|
+
end
|
26
|
+
|
27
|
+
def serve(socket)
|
28
|
+
connection = Sanford::Connection.new(self.service_host, socket)
|
29
|
+
connection.process
|
30
|
+
end
|
31
|
+
|
32
|
+
def inspect
|
33
|
+
reference = '0x0%x' % (self.object_id << 1)
|
34
|
+
"#<#{self.class}:#{reference} @service_host=#{self.service_host.inspect}>"
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'sanford-protocol'
|
3
|
+
|
4
|
+
module Sanford
|
5
|
+
|
6
|
+
module ServiceHandler
|
7
|
+
|
8
|
+
def self.constantize(class_name)
|
9
|
+
names = class_name.to_s.split('::').reject{|name| name.empty? }
|
10
|
+
klass = names.inject(Object) do |constant, name|
|
11
|
+
constant.const_get(name)
|
12
|
+
end
|
13
|
+
klass == Object ? false : klass
|
14
|
+
rescue NameError
|
15
|
+
false
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_reader :logger, :request
|
19
|
+
|
20
|
+
def initialize(logger, request)
|
21
|
+
@logger = logger
|
22
|
+
@request = request
|
23
|
+
end
|
24
|
+
|
25
|
+
def init
|
26
|
+
self.init!
|
27
|
+
end
|
28
|
+
|
29
|
+
def init!
|
30
|
+
end
|
31
|
+
|
32
|
+
# This method has very specific handling when before/after callbacks halt.
|
33
|
+
# It should always return a response tuple: `[ status, data ]`
|
34
|
+
# * If `before_run` halts, then the handler is not 'run' (it's `init` and
|
35
|
+
# `run` methods are not called) and it's response tuple is returned.
|
36
|
+
# * If `after_run` halts, then it's response tuple is returned, even if
|
37
|
+
# calling `before_run` or 'running' the handler generated a response
|
38
|
+
# tuple.
|
39
|
+
# * If `before_run` and `after_run` do not halt, then the response tuple
|
40
|
+
# from 'running' is used.
|
41
|
+
def run
|
42
|
+
response_tuple = self.run_callback 'before_run'
|
43
|
+
response_tuple ||= catch(:halt) do
|
44
|
+
self.init
|
45
|
+
data = self.run!
|
46
|
+
[ 200, data ]
|
47
|
+
end
|
48
|
+
after_response_tuple = self.run_callback 'after_run'
|
49
|
+
(response_tuple = after_response_tuple) if after_response_tuple
|
50
|
+
response_tuple
|
51
|
+
end
|
52
|
+
|
53
|
+
def run!
|
54
|
+
raise NotImplementedError
|
55
|
+
end
|
56
|
+
|
57
|
+
def before_run
|
58
|
+
end
|
59
|
+
|
60
|
+
def after_run
|
61
|
+
end
|
62
|
+
|
63
|
+
def params
|
64
|
+
self.request.params
|
65
|
+
end
|
66
|
+
|
67
|
+
def inspect
|
68
|
+
reference = '0x0%x' % (self.object_id << 1)
|
69
|
+
"#<#{self.class}:#{reference} @request=#{self.request.inspect}>"
|
70
|
+
end
|
71
|
+
|
72
|
+
protected
|
73
|
+
|
74
|
+
def halt(status, options = nil)
|
75
|
+
options = OpenStruct.new(options || {})
|
76
|
+
response_status = [ status, options.message ]
|
77
|
+
throw(:halt, [ response_status, options.data ])
|
78
|
+
end
|
79
|
+
|
80
|
+
# Notes:
|
81
|
+
# * Callbacks need to catch :halt incase the halt method is called. They
|
82
|
+
# also need to be sure to return nil if nothing is thrown, so that it
|
83
|
+
# is not considered as a response.
|
84
|
+
def run_callback(name)
|
85
|
+
catch(:halt) do
|
86
|
+
self.send(name.to_s)
|
87
|
+
nil
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
data/lib/sanford.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
module Sanford; end
|
2
|
+
|
3
|
+
require 'sanford/config'
|
4
|
+
require 'sanford/manager'
|
5
|
+
require 'sanford/host'
|
6
|
+
require 'sanford/version'
|
7
|
+
|
8
|
+
module Sanford
|
9
|
+
|
10
|
+
def self.config
|
11
|
+
Sanford::Config
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.configure(&block)
|
15
|
+
self.config.define(&block)
|
16
|
+
self.config
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.init
|
20
|
+
require self.config.services_config
|
21
|
+
end
|
22
|
+
|
23
|
+
class NullLogger
|
24
|
+
require 'logger'
|
25
|
+
|
26
|
+
Logger::Severity.constants.each do |name|
|
27
|
+
define_method(name.downcase){|*args| } # no-op
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
data/log/.gitkeep
ADDED
File without changes
|
data/sanford.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'sanford/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "sanford"
|
8
|
+
gem.version = Sanford::VERSION
|
9
|
+
gem.authors = ["Collin Redding", "Kelly Redding"]
|
10
|
+
gem.email = ["collin.redding@me.com", "kelly@kellyredding.com"]
|
11
|
+
gem.description = "Simple hosts for Sanford services."
|
12
|
+
gem.summary = "Simple hosts for Sanford services."
|
13
|
+
gem.homepage = "https://github.com/redding/sanford"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.add_dependency("daemons", ["~>1.1"])
|
21
|
+
gem.add_dependency("dat-tcp", ["~>0.1"])
|
22
|
+
gem.add_dependency("ns-options", ["~>1.0.0"])
|
23
|
+
gem.add_dependency("sanford-protocol", ["~>0.5"])
|
24
|
+
|
25
|
+
gem.add_development_dependency("assert", ["~>1.0"])
|
26
|
+
gem.add_development_dependency("assert-mocha", ["~>1.0"])
|
27
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
ENV['SANFORD_PROTOCOL_DEBUG'] = 'yes'
|
2
|
+
|
3
|
+
require 'ostruct'
|
4
|
+
|
5
|
+
ROOT = File.expand_path('../..', __FILE__)
|
6
|
+
|
7
|
+
require 'sanford'
|
8
|
+
|
9
|
+
Sanford.configure do |config|
|
10
|
+
config.services_config = File.join(ROOT, 'test/support/services')
|
11
|
+
end
|
12
|
+
Sanford.init
|
13
|
+
|
14
|
+
require 'test/support/service_handlers'
|
15
|
+
require 'test/support/simple_client'
|
16
|
+
require 'test/support/helpers'
|
17
|
+
|
18
|
+
if defined?(Assert)
|
19
|
+
require 'assert-mocha'
|
20
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Test
|
2
|
+
|
3
|
+
module Environment
|
4
|
+
|
5
|
+
def self.store_and_clear_hosts
|
6
|
+
@previous_hosts = Sanford.config.hosts.dup
|
7
|
+
Sanford.config.hosts.clear
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.restore_hosts
|
11
|
+
Sanford.config.hosts = @previous_hosts
|
12
|
+
@previous_hosts = nil
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
module ForkServerHelper
|
20
|
+
|
21
|
+
def start_server(server, &block)
|
22
|
+
begin
|
23
|
+
pid = fork do
|
24
|
+
trap("TERM"){ server.stop }
|
25
|
+
server.start
|
26
|
+
server.join_thread
|
27
|
+
end
|
28
|
+
sleep 0.3 # Give time for the socket to start listening.
|
29
|
+
yield
|
30
|
+
ensure
|
31
|
+
if pid
|
32
|
+
Process.kill("TERM", pid)
|
33
|
+
Process.wait(pid)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
|
42
|
+
module ForkManagerHelper
|
43
|
+
|
44
|
+
# start a Sanford server using Sanford's manager in a forked process
|
45
|
+
def call_sanford_manager(*args, &block)
|
46
|
+
pid = fork do
|
47
|
+
STDOUT.reopen('/dev/null')
|
48
|
+
trap("TERM"){ exit }
|
49
|
+
Sanford::Manager.call(*args)
|
50
|
+
end
|
51
|
+
sleep 1 # give time for the command to run
|
52
|
+
yield
|
53
|
+
ensure
|
54
|
+
if pid
|
55
|
+
Process.kill("TERM", pid)
|
56
|
+
Process.wait(pid)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def open_socket(host, port)
|
61
|
+
socket = TCPSocket.new(host, port)
|
62
|
+
ensure
|
63
|
+
socket.close rescue false
|
64
|
+
end
|
65
|
+
|
66
|
+
def expected_pid_file(host, ip, port)
|
67
|
+
host.config.pid_dir.join("#{ip}_#{port}_#{host}.pid")
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# A bunch of service handler examples. These are defined to implement certain
|
2
|
+
# edge cases and are for specific tests within the test suite.
|
3
|
+
#
|
4
|
+
|
5
|
+
class StaticServiceHandler
|
6
|
+
include Sanford::ServiceHandler
|
7
|
+
|
8
|
+
# builds with the same request and logger always, just for convenience
|
9
|
+
|
10
|
+
def initialize(logger = nil, request = nil)
|
11
|
+
request ||= Sanford::Protocol::Request.new('v1', 'name', {})
|
12
|
+
super(logger || Sanford::NullLogger.new, request)
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
class ManualThrowServiceHandler < StaticServiceHandler
|
18
|
+
|
19
|
+
def run!
|
20
|
+
throw(:halt, 'halted!')
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
class HaltWithServiceHandler < StaticServiceHandler
|
26
|
+
|
27
|
+
def initialize(halt_with)
|
28
|
+
request = Sanford::Protocol::Request.new('v1', 'name', {
|
29
|
+
'halt_with' => halt_with.dup
|
30
|
+
})
|
31
|
+
super(Sanford::NullLogger.new, request)
|
32
|
+
end
|
33
|
+
|
34
|
+
def run!
|
35
|
+
params['halt_with'].tap do |halt_with|
|
36
|
+
halt(halt_with.delete('code'), halt_with)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
class NoopServiceHandler < StaticServiceHandler
|
43
|
+
|
44
|
+
# simply overwrites the default `run!` so it doesn't error
|
45
|
+
|
46
|
+
def run!
|
47
|
+
# nothing!
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
class FlaggedServiceHandler < NoopServiceHandler
|
53
|
+
|
54
|
+
# flags a bunch of methods as they are run by setting instance variables
|
55
|
+
|
56
|
+
FLAGGED_METHODS = {
|
57
|
+
'init' => :init_called,
|
58
|
+
'init!' => :init_bang_called,
|
59
|
+
'run!' => :run_bang_called,
|
60
|
+
'before_run' => :before_run_called,
|
61
|
+
'after_run' => :after_run_called
|
62
|
+
}
|
63
|
+
FLAGS = FLAGGED_METHODS.values
|
64
|
+
|
65
|
+
attr_reader *FLAGS
|
66
|
+
|
67
|
+
def initialize(*passed)
|
68
|
+
super
|
69
|
+
FLAGS.each{|name| self.instance_variable_set("@#{name}", false) }
|
70
|
+
end
|
71
|
+
|
72
|
+
FLAGGED_METHODS.each do |method_name, instance_variable_name|
|
73
|
+
|
74
|
+
# def before_run
|
75
|
+
# super
|
76
|
+
# @before_run_called = true
|
77
|
+
# end
|
78
|
+
define_method(method_name) do
|
79
|
+
super
|
80
|
+
self.instance_variable_set("@#{instance_variable_name}", true)
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
class ConfigurableServiceHandler < FlaggedServiceHandler
|
88
|
+
|
89
|
+
def initialize(options = {})
|
90
|
+
@options = options
|
91
|
+
super
|
92
|
+
end
|
93
|
+
|
94
|
+
def before_run
|
95
|
+
super
|
96
|
+
if @options[:before_run]
|
97
|
+
self.instance_eval(&@options[:before_run])
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def run!
|
102
|
+
super
|
103
|
+
if @options[:run!]
|
104
|
+
self.instance_eval(&@options[:run!])
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def after_run
|
109
|
+
super
|
110
|
+
if @options[:after_run]
|
111
|
+
self.instance_eval(&@options[:after_run])
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|