sanford 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/bench/report.txt +34 -4
- data/bench/runner.rb +122 -1
- data/bench/services.rb +5 -2
- data/lib/sanford/error_handler.rb +60 -0
- data/lib/sanford/exceptions.rb +11 -10
- data/lib/sanford/host.rb +79 -101
- data/lib/sanford/host_data.rb +55 -0
- data/lib/sanford/logger.rb +23 -0
- data/lib/sanford/manager.rb +13 -22
- data/lib/sanford/rake.rb +1 -0
- data/lib/sanford/runner.rb +50 -0
- data/lib/sanford/server.rb +31 -15
- data/lib/sanford/service_handler.rb +34 -43
- data/lib/sanford/test_runner.rb +47 -0
- data/lib/sanford/version.rb +1 -1
- data/lib/sanford/worker.rb +124 -0
- data/lib/sanford.rb +49 -6
- data/sanford.gemspec +1 -1
- data/test/helper.rb +1 -0
- data/test/support/fake_connection.rb +18 -0
- data/test/support/helpers.rb +6 -10
- data/test/support/service_handlers.rb +56 -68
- data/test/support/services.rb +55 -10
- data/test/system/managing_test.rb +18 -18
- data/test/system/request_handling_test.rb +10 -100
- data/test/unit/config_test.rb +1 -43
- data/test/unit/error_handler_test.rb +133 -0
- data/test/unit/host_configuration_test.rb +41 -0
- data/test/unit/host_data_test.rb +65 -0
- data/test/unit/host_test.rb +20 -112
- data/test/unit/{host/version_group_test.rb → host_version_group_test.rb} +0 -0
- data/test/unit/hosts_test.rb +56 -0
- data/test/unit/manager_test.rb +3 -3
- data/test/unit/runner_test.rb +26 -0
- data/test/unit/server_test.rb +10 -2
- data/test/unit/service_handler_test.rb +126 -115
- data/test/unit/worker_test.rb +195 -0
- metadata +28 -16
- data/lib/sanford/config.rb +0 -33
- data/lib/sanford/connection.rb +0 -70
- data/lib/sanford/exception_handler.rb +0 -43
- data/test/unit/connection_test.rb +0 -23
- data/test/unit/exception_handler_test.rb +0 -69
data/bench/report.txt
CHANGED
@@ -2,9 +2,39 @@ Running benchmark report...
|
|
2
2
|
|
3
3
|
Hitting "simple" service with {}, 10000 times
|
4
4
|
....................................................................................................
|
5
|
-
Total Time:
|
6
|
-
Average Time: 1.
|
7
|
-
Min Time: 0.
|
8
|
-
Max Time:
|
5
|
+
Total Time: 10195.4432ms
|
6
|
+
Average Time: 1.0195ms
|
7
|
+
Min Time: 0.7040ms
|
8
|
+
Max Time: 117.8309ms
|
9
|
+
|
10
|
+
Distribution (number of requests):
|
11
|
+
0ms: 8897
|
12
|
+
0.7ms: 6897
|
13
|
+
0.8ms: 1451
|
14
|
+
0.9ms: 549
|
15
|
+
1ms: 1049
|
16
|
+
1.0ms: 308
|
17
|
+
1.1ms: 236
|
18
|
+
1.2ms: 198
|
19
|
+
1.3ms: 134
|
20
|
+
1.4ms: 66
|
21
|
+
1.5ms: 44
|
22
|
+
1.6ms: 26
|
23
|
+
1.7ms: 18
|
24
|
+
1.8ms: 15
|
25
|
+
1.9ms: 4
|
26
|
+
117ms: 1
|
27
|
+
2ms: 24
|
28
|
+
3ms: 7
|
29
|
+
4ms: 2
|
30
|
+
82ms: 1
|
31
|
+
83ms: 1
|
32
|
+
84ms: 3
|
33
|
+
85ms: 2
|
34
|
+
86ms: 2
|
35
|
+
88ms: 3
|
36
|
+
89ms: 4
|
37
|
+
90ms: 2
|
38
|
+
91ms: 2
|
9
39
|
|
10
40
|
Done running benchmark report
|
data/bench/runner.rb
CHANGED
@@ -43,10 +43,11 @@ module Bench
|
|
43
43
|
output("\n", false)
|
44
44
|
|
45
45
|
total_time = benchmarks.inject(0){|s, n| s + n }
|
46
|
+
average = total_time / benchmarks.size
|
46
47
|
data = {
|
47
48
|
:number_of_requests => times,
|
48
49
|
:total_time_taken => self.round_and_display(total_time),
|
49
|
-
:average_time_taken => self.round_and_display(
|
50
|
+
:average_time_taken => self.round_and_display(average),
|
50
51
|
:min_time_taken => self.round_and_display(benchmarks.min),
|
51
52
|
:max_time_taken => self.round_and_display(benchmarks.max)
|
52
53
|
}
|
@@ -55,6 +56,19 @@ module Bench
|
|
55
56
|
output "Average Time: #{data[:average_time_taken].rjust(size)}ms"
|
56
57
|
output "Min Time: #{data[:min_time_taken].rjust(size)}ms"
|
57
58
|
output "Max Time: #{data[:max_time_taken].rjust(size)}ms"
|
59
|
+
|
60
|
+
output "\n"
|
61
|
+
|
62
|
+
distribution = Distribution.new(benchmarks)
|
63
|
+
|
64
|
+
output "Distribution (number of requests):"
|
65
|
+
distribution.each do |grouping|
|
66
|
+
output " #{grouping.time}ms: #{grouping.number_of_requests}"
|
67
|
+
grouping.precise_groupings.each do |precise_grouping|
|
68
|
+
output " #{precise_grouping.time}ms: #{precise_grouping.number_of_requests}"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
58
72
|
output "\n"
|
59
73
|
end
|
60
74
|
|
@@ -97,6 +111,113 @@ module Bench
|
|
97
111
|
[ integer, fractional.ljust(4, '0') ].join('.')
|
98
112
|
end
|
99
113
|
|
114
|
+
class Distribution
|
115
|
+
|
116
|
+
def initialize(benchmarks)
|
117
|
+
hash = benchmarks.inject({}) do |hash, benchmark|
|
118
|
+
index = Index.new(benchmark).number
|
119
|
+
hash[index] ||= Grouping.new(index)
|
120
|
+
hash[index].add(benchmark)
|
121
|
+
hash
|
122
|
+
end
|
123
|
+
@groupings = hash.values.sort
|
124
|
+
end
|
125
|
+
|
126
|
+
def each(&block)
|
127
|
+
@groupings.each(&block)
|
128
|
+
end
|
129
|
+
|
130
|
+
class BaseGrouping
|
131
|
+
|
132
|
+
def self.max_time(time = nil)
|
133
|
+
size = time.to_s.size
|
134
|
+
@max_time = size if size > @max_time.to_i
|
135
|
+
@max_time
|
136
|
+
end
|
137
|
+
|
138
|
+
def self.max_number(number = nil)
|
139
|
+
size = number.to_s.size
|
140
|
+
@max_size = size if size > @max_size.to_i
|
141
|
+
@max_size
|
142
|
+
end
|
143
|
+
|
144
|
+
attr_reader :name
|
145
|
+
|
146
|
+
def initialize(index)
|
147
|
+
@name = index.to_s
|
148
|
+
@set = []
|
149
|
+
self.class.max_time(index.to_s.size)
|
150
|
+
end
|
151
|
+
|
152
|
+
def add(benchmark)
|
153
|
+
result = @set.push(benchmark)
|
154
|
+
self.class.max_number(@set.size.to_s.size)
|
155
|
+
result
|
156
|
+
end
|
157
|
+
|
158
|
+
def time
|
159
|
+
@name.rjust(self.class.max_time)
|
160
|
+
end
|
161
|
+
|
162
|
+
def number_of_requests
|
163
|
+
@set.size.to_s.rjust(self.class.max_number)
|
164
|
+
end
|
165
|
+
|
166
|
+
def <=>(other)
|
167
|
+
self.name.to_f <=> other.name.to_f
|
168
|
+
end
|
169
|
+
|
170
|
+
end
|
171
|
+
|
172
|
+
class Grouping < BaseGrouping
|
173
|
+
|
174
|
+
def initialize(index)
|
175
|
+
super
|
176
|
+
@precise_groupings = {}
|
177
|
+
end
|
178
|
+
|
179
|
+
def add(benchmark)
|
180
|
+
add_precise_grouping(benchmark) if self.collect_precise?
|
181
|
+
super(benchmark)
|
182
|
+
end
|
183
|
+
|
184
|
+
def collect_precise?
|
185
|
+
@name.to_i <= 1
|
186
|
+
end
|
187
|
+
|
188
|
+
def precise_groupings
|
189
|
+
@precise_groupings.values.sort
|
190
|
+
end
|
191
|
+
|
192
|
+
protected
|
193
|
+
|
194
|
+
def add_precise_grouping(benchmark)
|
195
|
+
index = PreciseIndex.new(benchmark).number
|
196
|
+
@precise_groupings[index] ||= BaseGrouping.new(index)
|
197
|
+
@precise_groupings[index].add(benchmark)
|
198
|
+
end
|
199
|
+
|
200
|
+
end
|
201
|
+
|
202
|
+
class Index < Struct.new(:number)
|
203
|
+
|
204
|
+
def initialize(benchmark)
|
205
|
+
super benchmark.to_i
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
class PreciseIndex < Struct.new(:number)
|
210
|
+
|
211
|
+
MODIFIER = 10.to_f
|
212
|
+
|
213
|
+
def initialize(benchmark)
|
214
|
+
super((benchmark * MODIFIER).to_i / MODIFIER)
|
215
|
+
end
|
216
|
+
|
217
|
+
end
|
218
|
+
|
219
|
+
end
|
220
|
+
|
100
221
|
end
|
101
222
|
|
102
223
|
end
|
data/bench/services.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
class BenchHost
|
2
2
|
include Sanford::Host
|
3
3
|
|
4
|
-
|
5
|
-
|
4
|
+
port 12000
|
5
|
+
pid_dir File.expand_path("../../tmp", __FILE__)
|
6
|
+
|
7
|
+
logger Logger.new(STDOUT)
|
8
|
+
verbose_logging false
|
6
9
|
|
7
10
|
version 'v1' do
|
8
11
|
service 'simple', 'BenchHost::Simple'
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'sanford-protocol'
|
3
|
+
|
4
|
+
module Sanford
|
5
|
+
|
6
|
+
class ErrorHandler
|
7
|
+
|
8
|
+
attr_reader :exception, :host_data, :request
|
9
|
+
|
10
|
+
def initialize(exception, host_data, request = nil)
|
11
|
+
@exception, @host_data, @request = exception, host_data, request
|
12
|
+
end
|
13
|
+
|
14
|
+
# The exception that we are generating a response for can change in the case
|
15
|
+
# that the configured error proc raises an exception. If this occurs, a
|
16
|
+
# response will be generated for that exception, instead of the original
|
17
|
+
# one. This is designed to avoid "hidden" errors happening, this way the
|
18
|
+
# server will respond and log based on the last exception that occurred.
|
19
|
+
|
20
|
+
def run
|
21
|
+
begin
|
22
|
+
result = @host_data.error_proc.call(@exception, @host_data, @request)
|
23
|
+
rescue Exception => proc_exception
|
24
|
+
@exception = proc_exception
|
25
|
+
end
|
26
|
+
self.response_from_proc(result) || self.response_from_exception(@exception)
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
|
31
|
+
def response_from_proc(result)
|
32
|
+
case result
|
33
|
+
when Sanford::Protocol::Response
|
34
|
+
result
|
35
|
+
when Integer, Symbol
|
36
|
+
build_response result
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def response_from_exception(exception)
|
41
|
+
case(exception)
|
42
|
+
when Sanford::Protocol::BadMessageError, Sanford::Protocol::BadRequestError
|
43
|
+
build_response :bad_request, :message => exception.message
|
44
|
+
when Sanford::NotFoundError
|
45
|
+
build_response :not_found
|
46
|
+
when Sanford::Protocol::TimeoutError
|
47
|
+
build_response :timeout
|
48
|
+
when Exception
|
49
|
+
build_response :error, :message => "An unexpected error occurred."
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def build_response(status, options = nil)
|
54
|
+
options = OpenStruct.new(options || {})
|
55
|
+
Sanford::Protocol::Response.new([ status, options.message ], options.data)
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
data/lib/sanford/exceptions.rb
CHANGED
@@ -1,37 +1,38 @@
|
|
1
1
|
module Sanford
|
2
2
|
|
3
|
-
|
3
|
+
BaseError = Class.new(RuntimeError)
|
4
4
|
|
5
|
-
|
5
|
+
NotFoundError = Class.new(RuntimeError)
|
6
6
|
|
7
7
|
class NoHostError < BaseError
|
8
8
|
attr_reader :message
|
9
9
|
|
10
10
|
def initialize(host_name)
|
11
|
-
|
12
|
-
"No hosts have been defined. "
|
13
|
-
"Please define a host before trying to run Sanford."
|
11
|
+
message = if Sanford.hosts.empty?
|
12
|
+
"No hosts have been defined. Please define a host before trying to run Sanford."
|
14
13
|
else
|
15
14
|
"A host couldn't be found with the name #{host_name.inspect}. "
|
16
15
|
end
|
16
|
+
super message
|
17
17
|
end
|
18
|
+
|
18
19
|
end
|
19
20
|
|
20
21
|
class InvalidHostError < BaseError
|
21
|
-
attr_reader :message
|
22
22
|
|
23
23
|
def initialize(host)
|
24
|
-
|
24
|
+
super "A port must be configured or provided to run a server for '#{host}'"
|
25
25
|
end
|
26
|
+
|
26
27
|
end
|
27
28
|
|
28
29
|
class NoHandlerClassError < BaseError
|
29
|
-
attr_reader :message
|
30
30
|
|
31
|
-
def initialize(
|
32
|
-
|
31
|
+
def initialize(handler_class_name)
|
32
|
+
super "Sanford couldn't find the service handler '#{handler_class_name}'. " \
|
33
33
|
"It doesn't exist or hasn't been required in yet."
|
34
34
|
end
|
35
|
+
|
35
36
|
end
|
36
37
|
|
37
38
|
end
|
data/lib/sanford/host.rb
CHANGED
@@ -1,136 +1,93 @@
|
|
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
1
|
require 'ns-options'
|
21
2
|
require 'pathname'
|
22
3
|
|
23
|
-
require 'sanford/config'
|
24
|
-
require 'sanford/exception_handler'
|
25
4
|
require 'sanford/exceptions'
|
26
|
-
require 'sanford/
|
5
|
+
require 'sanford/logger'
|
27
6
|
|
28
7
|
module Sanford
|
8
|
+
|
29
9
|
module Host
|
30
10
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
11
|
+
class Configuration
|
12
|
+
include NsOptions::Proxy
|
13
|
+
|
14
|
+
# A Host's configuration is a seperate ns-options proxy class because
|
15
|
+
# `Host` is a module, so it itself cannot be a ns-options proxy (and
|
16
|
+
# still function as a mixin). Also, since it is making the `Host`
|
17
|
+
# a `Singleton`, mixing that with `NsOptions::Proxy` could have strange
|
18
|
+
# effects (messing up someone's `initialize`). Thus, the `Configuration`
|
19
|
+
# is a separate class and not on the `Host` directly.
|
20
|
+
|
21
|
+
option :name, String
|
22
|
+
option :ip, String, :default => '0.0.0.0'
|
23
|
+
option :port, Integer
|
24
|
+
option :pid_dir, Pathname, :default => Dir.pwd
|
25
|
+
option :logger, :default => proc{ Sanford::NullLogger.new }
|
26
|
+
option :verbose_logging, :default => true
|
27
|
+
option :error_proc, Proc, :default => proc{ }
|
28
|
+
|
29
|
+
def initialize(host)
|
30
|
+
self.name = host.class.to_s
|
50
31
|
end
|
51
|
-
Sanford.config.hosts.add(host_class)
|
52
|
-
end
|
53
32
|
|
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
33
|
end
|
66
34
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
35
|
+
def self.included(host_class)
|
36
|
+
host_class.class_eval do
|
37
|
+
include Singleton
|
38
|
+
extend Sanford::Host::ClassMethods
|
71
39
|
end
|
72
|
-
|
40
|
+
Sanford.register(host_class)
|
73
41
|
end
|
74
42
|
|
75
|
-
|
76
|
-
request_handler(request).run
|
77
|
-
end
|
43
|
+
attr_reader :configuration, :versioned_services
|
78
44
|
|
79
|
-
def
|
80
|
-
|
81
|
-
|
82
|
-
"port=#{self.config.port.inspect}>"
|
45
|
+
def initialize
|
46
|
+
@configuration = Configuration.new(self)
|
47
|
+
@versioned_services = {}
|
83
48
|
end
|
84
49
|
|
85
|
-
|
86
|
-
|
87
|
-
def request_handler(request)
|
88
|
-
handler_class(get_handler_class_name(request)).new(self.logger, request)
|
50
|
+
def name(*args)
|
51
|
+
self.configuration.name *args
|
89
52
|
end
|
90
53
|
|
91
|
-
def
|
92
|
-
self.
|
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
|
54
|
+
def ip(*args)
|
55
|
+
self.configuration.ip *args
|
96
56
|
end
|
97
57
|
|
98
|
-
def
|
99
|
-
|
100
|
-
services[request.name].tap do |name|
|
101
|
-
raise Sanford::NotFoundError if !name
|
102
|
-
end
|
58
|
+
def port(*args)
|
59
|
+
self.configuration.port *args
|
103
60
|
end
|
104
61
|
|
105
|
-
def
|
106
|
-
|
107
|
-
hash.merge!({ k => v }) if !v.nil?
|
108
|
-
hash
|
109
|
-
end
|
62
|
+
def pid_dir(*args)
|
63
|
+
self.configuration.pid_dir *args
|
110
64
|
end
|
111
65
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
define_method(name) do |*args|
|
117
|
-
self.config.send("#{name}=", *args) if !args.empty?
|
118
|
-
self.config.send(name)
|
119
|
-
end
|
66
|
+
def logger(*args)
|
67
|
+
self.configuration.logger *args
|
68
|
+
end
|
120
69
|
|
121
|
-
|
122
|
-
|
123
|
-
|
70
|
+
def verbose_logging(*args)
|
71
|
+
self.configuration.verbose_logging *args
|
72
|
+
end
|
124
73
|
|
125
|
-
|
74
|
+
def error(&block)
|
75
|
+
self.configuration.error_proc = block
|
76
|
+
end
|
126
77
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
78
|
+
def version(name, &block)
|
79
|
+
version_group = Sanford::Host::VersionGroup.new(name, &block)
|
80
|
+
@versioned_services.merge!(version_group.to_hash)
|
81
|
+
end
|
131
82
|
|
83
|
+
def inspect
|
84
|
+
reference = '0x0%x' % (self.object_id << 1)
|
85
|
+
"#<#{self.class}:#{reference} ip=#{self.configuration.ip.inspect} " \
|
86
|
+
"port=#{self.configuration.port.inspect}>"
|
132
87
|
end
|
133
88
|
|
89
|
+
protected
|
90
|
+
|
134
91
|
class VersionGroup
|
135
92
|
attr_reader :name, :services
|
136
93
|
|
@@ -158,5 +115,26 @@ module Sanford
|
|
158
115
|
|
159
116
|
end
|
160
117
|
|
118
|
+
module ClassMethods
|
119
|
+
|
120
|
+
# the class level of a `Host` should just proxy it's methods down to it's
|
121
|
+
# instance (it's a `Singleton`)
|
122
|
+
|
123
|
+
# `name` is defined by all objects, so we can't rely on `method_missing`
|
124
|
+
def name(*args)
|
125
|
+
self.instance.name(*args)
|
126
|
+
end
|
127
|
+
|
128
|
+
def method_missing(method, *args, &block)
|
129
|
+
self.instance.send(method, *args, &block)
|
130
|
+
end
|
131
|
+
|
132
|
+
def respond_to?(method)
|
133
|
+
super || self.instance.respond_to?(method)
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
|
161
138
|
end
|
139
|
+
|
162
140
|
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'sanford/exceptions'
|
2
|
+
require 'sanford/service_handler'
|
3
|
+
|
4
|
+
module Sanford
|
5
|
+
|
6
|
+
class HostData
|
7
|
+
|
8
|
+
# When trying to run a server for a host, we need to build up the host's
|
9
|
+
# data to increase the performance of the server. This is done by
|
10
|
+
# constantizing a host's handlers and merging a host's configuration with
|
11
|
+
# optional overrides.
|
12
|
+
|
13
|
+
attr_reader :name, :ip, :port, :pid_dir, :logger, :verbose, :error_proc
|
14
|
+
|
15
|
+
def initialize(service_host, options = nil)
|
16
|
+
configuration = service_host.configuration.to_hash.merge(remove_nil_values(options || {}))
|
17
|
+
|
18
|
+
@name = configuration[:name]
|
19
|
+
@ip, @port = configuration[:ip], configuration[:port]
|
20
|
+
@pid_dir = configuration[:pid_dir]
|
21
|
+
@logger, @verbose = configuration[:logger], configuration[:verbose_logging]
|
22
|
+
@error_proc = configuration[:error_proc]
|
23
|
+
|
24
|
+
@handlers = service_host.versioned_services.inject({}) do |hash, (version, services)|
|
25
|
+
hash.merge({ version => self.constantize_services(services) })
|
26
|
+
end
|
27
|
+
|
28
|
+
raise Sanford::InvalidHostError.new(service_host) if !self.port
|
29
|
+
end
|
30
|
+
|
31
|
+
def handler_class_for(version, service)
|
32
|
+
version_group = @handlers[version] || {}
|
33
|
+
version_group[service] || raise(Sanford::NotFoundError)
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
|
38
|
+
def constantize_services(services)
|
39
|
+
services.inject({}) do |hash, (name, handler_class_name)|
|
40
|
+
hash.merge({ name => self.constantize(handler_class_name) })
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def constantize(handler_class_name)
|
45
|
+
Sanford::ServiceHandler.constantize(handler_class_name) ||
|
46
|
+
raise(Sanford::NoHandlerClassError.new(handler_class_name))
|
47
|
+
end
|
48
|
+
|
49
|
+
def remove_nil_values(hash)
|
50
|
+
hash.inject({}){|h, (k, v)| !v.nil? ? h.merge({ k => v }) : h }
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Sanford
|
2
|
+
|
3
|
+
class Logger
|
4
|
+
attr_reader :summary, :verbose
|
5
|
+
|
6
|
+
def initialize(logger, verbose = true)
|
7
|
+
loggers = [ logger, Sanford::NullLogger.new ]
|
8
|
+
loggers.reverse! if !verbose
|
9
|
+
@verbose, @summary = loggers
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
class NullLogger
|
15
|
+
require 'logger'
|
16
|
+
|
17
|
+
::Logger::Severity.constants.each do |name|
|
18
|
+
define_method(name.downcase){|*args| } # no-op
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
data/lib/sanford/manager.rb
CHANGED
@@ -1,44 +1,35 @@
|
|
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
1
|
require 'daemons'
|
8
2
|
|
9
|
-
require 'sanford/
|
10
|
-
require 'sanford/exceptions'
|
3
|
+
require 'sanford/host_data'
|
11
4
|
require 'sanford/server'
|
12
5
|
|
13
6
|
module Sanford
|
14
7
|
|
15
8
|
class Manager
|
16
|
-
attr_reader :host, :process_name
|
17
9
|
|
18
10
|
def self.call(action, options = nil)
|
19
11
|
options ||= {}
|
20
|
-
options[:host] ||= ENV['SANFORD_HOST']
|
21
12
|
options[:ip] ||= ENV['SANFORD_IP']
|
22
13
|
options[:port] ||= ENV['SANFORD_PORT']
|
23
14
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
raise(Sanford::NoHostError.new(host_class_or_name)) if !host_class
|
30
|
-
self.new(host_class, options).call(action)
|
15
|
+
name = options.delete(:host) || ENV['SANFORD_HOST']
|
16
|
+
service_host = name ? Sanford.hosts.find(name) : Sanford.hosts.first
|
17
|
+
raise(Sanford::NoHostError.new(name)) if !service_host
|
18
|
+
|
19
|
+
self.new(service_host, options).call(action)
|
31
20
|
end
|
32
21
|
|
33
|
-
|
34
|
-
|
22
|
+
attr_reader :host, :process_name
|
23
|
+
|
24
|
+
def initialize(service_host, options = {})
|
25
|
+
@host = Sanford::HostData.new(service_host, options)
|
35
26
|
@process_name = [ self.host.ip, self.host.port, self.host.name ].join('_')
|
36
27
|
end
|
37
28
|
|
38
29
|
def call(action)
|
39
|
-
|
40
|
-
FileUtils.mkdir_p(
|
41
|
-
::Daemons.run_proc(self.process_name,
|
30
|
+
daemons_options = self.default_options.merge({ :ARGV => [ action.to_s ] })
|
31
|
+
FileUtils.mkdir_p(daemons_options[:dir])
|
32
|
+
::Daemons.run_proc(self.process_name, daemons_options) do
|
42
33
|
server = Sanford::Server.new(self.host)
|
43
34
|
server.start
|
44
35
|
server.join_thread
|