sanford 0.17.0 → 0.18.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,20 +2,17 @@ module Sanford
2
2
 
3
3
  class ServerData
4
4
 
5
- # The server uses this to "compile" its configuration for speed. NsOptions
6
- # is relatively slow everytime an option is read. To avoid this, we read the
7
- # options one time here and memoize their values. This way, we don't pay the
8
- # NsOptions overhead when reading them while handling a request.
9
-
10
- attr_reader :name
11
- attr_reader :pid_file
12
- attr_reader :receives_keep_alive
13
- attr_reader :worker_class, :worker_params, :num_workers
14
- attr_reader :debug, :logger, :dtcp_logger, :verbose_logging
15
- attr_reader :template_source, :shutdown_timeout
16
- attr_reader :init_procs, :error_procs
17
- attr_reader :router, :routes
5
+ # The server uses this to "compile" the common configuration data used
6
+ # by the server instances, error handlers and routes. The goal here is
7
+ # to provide these with a simplified interface with the minimal data needed
8
+ # and to decouple the configuration from each thing that needs its data.
9
+
18
10
  attr_accessor :ip, :port
11
+ attr_reader :name, :pid_file, :shutdown_timeout
12
+ attr_reader :worker_class, :worker_params, :num_workers
13
+ attr_reader :error_procs, :template_source, :logger, :router
14
+ attr_reader :receives_keep_alive, :verbose_logging
15
+ attr_reader :debug, :dtcp_logger, :routes, :process_label
19
16
 
20
17
  def initialize(args = nil)
21
18
  args ||= {}
@@ -24,26 +21,28 @@ module Sanford
24
21
  @port = !(v = ENV['SANFORD_PORT'].to_s).empty? ? v.to_i : args[:port]
25
22
  @pid_file = args[:pid_file]
26
23
 
27
- @receives_keep_alive = !!args[:receives_keep_alive]
28
-
29
- @worker_class = args[:worker_class]
30
- @worker_params = args[:worker_params] || {}
31
- @num_workers = args[:num_workers]
32
-
33
- @debug = !ENV['SANFORD_DEBUG'].to_s.empty?
34
- @logger = args[:logger]
35
- @dtcp_logger = @logger if @debug
36
- @verbose_logging = !!args[:verbose_logging]
24
+ @shutdown_timeout = args[:shutdown_timeout]
37
25
 
26
+ @worker_class = args[:worker_class]
27
+ @worker_params = args[:worker_params] || {}
28
+ @num_workers = args[:num_workers]
29
+ @error_procs = args[:error_procs] || []
38
30
  @template_source = args[:template_source]
31
+ @logger = args[:logger]
32
+ @router = args[:router]
39
33
 
40
- @shutdown_timeout = args[:shutdown_timeout]
34
+ @receives_keep_alive = !!args[:receives_keep_alive]
35
+ @verbose_logging = !!args[:verbose_logging]
41
36
 
42
- @init_procs = args[:init_procs] || []
43
- @error_procs = args[:error_procs] || []
37
+ @debug = !ENV['SANFORD_DEBUG'].to_s.empty?
38
+ @dtcp_logger = @logger if @debug
39
+ @routes = build_routes(args[:routes] || [])
44
40
 
45
- @router = args[:router]
46
- @routes = build_routes(args[:routes] || [])
41
+ @process_label = if (label = ENV['SANFORD_PROCESS_LABEL'].to_s).empty?
42
+ "#{@name}-#{@ip}-#{@port}"
43
+ else
44
+ label
45
+ end
47
46
  end
48
47
 
49
48
  def route_for(name)
@@ -69,8 +69,8 @@ module Sanford
69
69
 
70
70
  class NullTemplateSource < TemplateSource
71
71
 
72
- def initialize
73
- super('')
72
+ def initialize(root = nil)
73
+ super(root || '')
74
74
  end
75
75
 
76
76
  end
@@ -1,3 +1,3 @@
1
1
  module Sanford
2
- VERSION = "0.17.0"
2
+ VERSION = "0.18.0"
3
3
  end
data/sanford.gemspec CHANGED
@@ -18,10 +18,9 @@ Gem::Specification.new do |gem|
18
18
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
19
  gem.require_paths = ["lib"]
20
20
 
21
- gem.add_development_dependency("assert", ["~> 2.15.0"])
21
+ gem.add_development_dependency("assert", ["~> 2.16.1"])
22
22
 
23
- gem.add_dependency("dat-tcp", ["~> 0.8.0"])
24
- gem.add_dependency("much-plugin", ["~> 0.1.0"])
25
- gem.add_dependency("ns-options", ["~> 1.1.6"])
26
- gem.add_dependency("sanford-protocol", ["~> 0.11.0"])
23
+ gem.add_dependency("dat-tcp", ["~> 0.8.1"])
24
+ gem.add_dependency("much-plugin", ["~> 0.2.0"])
25
+ gem.add_dependency("sanford-protocol", ["~> 0.12.0"])
27
26
  end
data/test/helper.rb CHANGED
@@ -12,3 +12,14 @@ require 'pathname'
12
12
  ROOT_PATH = Pathname.new(File.expand_path('../..', __FILE__))
13
13
 
14
14
  require 'test/support/factory'
15
+
16
+ JOIN_SECONDS = 0.1
17
+
18
+ # 1.8.7 backfills
19
+
20
+ # Array#sample
21
+ if !(a = Array.new).respond_to?(:sample) && a.respond_to?(:choice)
22
+ class Array
23
+ alias_method :sample, :choice
24
+ end
25
+ end
@@ -10,13 +10,16 @@ LOGGER = Logger.new(ROOT_PATH.join('log/app_server.log').to_s)
10
10
  LOGGER.datetime_format = "" # turn off the datetime in the logs
11
11
 
12
12
  class AppERBEngine < Sanford::TemplateEngine
13
- RenderScope = Struct.new(:view)
13
+ RenderScope = Class.new(Struct.new(:view)) do
14
+ def get_binding; binding; end
15
+ end
14
16
 
15
17
  def render(path, service_handler, locals)
16
18
  require 'erb'
17
19
  full_path = ROOT_PATH.join("test/support/#{path}.erb")
18
- binding = RenderScope.new(service_handler).send(:binding)
19
- ERB.new(File.read(full_path)).result(binding)
20
+
21
+ b = RenderScope.new(service_handler).get_binding
22
+ ERB.new(File.read(full_path)).result(b)
20
23
  end
21
24
  end
22
25
 
@@ -12,4 +12,11 @@ module Factory
12
12
  exception
13
13
  end
14
14
 
15
+ def self.protocol_response
16
+ Sanford::Protocol::Response.new(
17
+ [Factory.integer(999), Factory.text],
18
+ { Factory.string => Factory.string }
19
+ )
20
+ end
21
+
15
22
  end
@@ -308,9 +308,9 @@ module Sanford::Server
308
308
  should "return a response generated by the error handler" do
309
309
  assert_equal 200, subject.code
310
310
  assert_nil subject.status.message
311
- expected = "The server on #{AppServer.ip}:#{AppServer.port} " \
312
- "threw a StandardError."
313
- assert_equal expected, subject.data
311
+ exp = "The server on #{AppServer.ip}:#{AppServer.port} " \
312
+ "threw a StandardError."
313
+ assert_equal exp, subject.data
314
314
  end
315
315
 
316
316
  end
@@ -346,6 +346,24 @@ module Sanford::Server
346
346
 
347
347
  end
348
348
 
349
+ class WithEnvProcessLabelTests < SystemTests
350
+ desc "with a process label env var"
351
+ setup do
352
+ ENV['SANFORD_PROCESS_LABEL'] = Factory.string
353
+
354
+ @server = AppServer.new
355
+ end
356
+ teardown do
357
+ ENV.delete('SANFORD_PROCESS_LABEL')
358
+ end
359
+ subject{ @server }
360
+
361
+ should "set the daemons process label to the env var" do
362
+ assert_equal ENV['SANFORD_PROCESS_LABEL'], subject.process_label
363
+ end
364
+
365
+ end
366
+
349
367
  class TestClient
350
368
  attr_accessor :delay, :bytes
351
369
 
@@ -62,8 +62,8 @@ module Sanford::ServiceHandler
62
62
  setup do
63
63
  @params = { 'message' => Factory.text }
64
64
  @runner = test_runner(AppHandlers::Template, {
65
- :params => @params,
66
- :template_source => AppServer.configuration.template_source
65
+ :params => @params,
66
+ :template_source => AppServer.config.template_source
67
67
  })
68
68
  @handler = @runner.handler
69
69
  end
@@ -78,8 +78,8 @@ module Sanford::ServiceHandler
78
78
  should "return a 200 response and render the template when run" do
79
79
  response = @runner.run
80
80
  assert_equal 200, response.code
81
- expected = "ERB Template Message: #{@params['message']}\n"
82
- assert_equal expected, response.data
81
+ exp = "ERB Template Message: #{@params['message']}\n"
82
+ assert_equal exp, response.data
83
83
  end
84
84
 
85
85
  end
@@ -129,8 +129,8 @@ class Sanford::CLI
129
129
  end
130
130
 
131
131
  should "output the error with the help" do
132
- expected = "#{@command.inspect} is not a valid command"
133
- assert_includes expected, @kernel_spy.output
132
+ exp = "#{@command.inspect} is not a valid command"
133
+ assert_includes exp, @kernel_spy.output
134
134
  assert_includes "Usage: sanford", @kernel_spy.output
135
135
  end
136
136
 
@@ -84,59 +84,99 @@ class Sanford::ConnectionHandler
84
84
 
85
85
  end
86
86
 
87
- class RunWithExceptionTests < InitTests
88
- desc "and run with a route that throws an exception"
87
+ class RunWithExceptionSetupTests < InitTests
89
88
  setup do
90
- Assert.stub(@route, :run){ raise @exception }
89
+ @route_exception = Factory.exception
90
+ Assert.stub(@route, :run){ raise @route_exception }
91
+ Assert.stub(Sanford::ErrorHandler, :new) do |*args|
92
+ @error_handler_spy = ErrorHandlerSpy.new(*args)
93
+ end
94
+ end
91
95
 
92
- error_handler = Sanford::ErrorHandler.new(@exception, {
93
- :server_data => @server_data,
94
- :request => @request
95
- })
96
- @expected_response = error_handler.run
97
- @expected_exception = error_handler.exception
96
+ end
98
97
 
98
+ class RunWithExceptionTests < RunWithExceptionSetupTests
99
+ desc "and run with an exception"
100
+ setup do
99
101
  @processed_service = @connection_handler.run
100
102
  end
101
- subject{ @processed_service }
102
103
 
103
- should "return a processed service with an exception" do
104
- assert_instance_of ProcessedService, subject
105
- assert_equal @expected_response, subject.response
106
- assert_equal @expected_exception, subject.exception
104
+ should "run an error handler" do
105
+ assert_equal @route_exception, @error_handler_spy.passed_exception
106
+ exp = {
107
+ :server_data => @server_data,
108
+ :request => @processed_service.request,
109
+ :handler_class => @processed_service.handler_class,
110
+ :response => nil
111
+ }
112
+ assert_equal exp, @error_handler_spy.context_hash
113
+ assert_true @error_handler_spy.run_called
107
114
  end
108
115
 
109
- should "have written the error response to the connection" do
110
- assert_equal @expected_response, @connection.response
116
+ should "store the error handler response and exception on the processed service" do
117
+ assert_equal @error_handler_spy.response, @processed_service.response
118
+ assert_equal @error_handler_spy.exception, @processed_service.exception
119
+ end
120
+
121
+ should "write the error response to the connection" do
122
+ assert_equal @error_handler_spy.response, @connection.response
111
123
  assert_true @connection.write_closed
112
124
  end
113
125
 
114
126
  end
115
127
 
116
- class RunWithExceptionWhileWritingTests < InitTests
128
+ class RunWithShutdownErrorTests < RunWithExceptionSetupTests
129
+ desc "and run with a dat worker pool shutdown error"
130
+ setup do
131
+ @shutdown_error = DatWorkerPool::ShutdownError.new(Factory.text)
132
+ Assert.stub(@route, :run){ raise @shutdown_error }
133
+ end
134
+
135
+ should "run an error handler" do
136
+ assert_raises{ @connection_handler.run }
137
+
138
+ passed_exception = @error_handler_spy.passed_exception
139
+ assert_instance_of Sanford::ShutdownError, passed_exception
140
+ assert_equal @shutdown_error.message, passed_exception.message
141
+ assert_equal @shutdown_error.backtrace, passed_exception.backtrace
142
+ assert_true @error_handler_spy.run_called
143
+ end
144
+
145
+ should "raise the shutdown error" do
146
+ assert_raises(@shutdown_error.class){ @connection_handler.run }
147
+ end
148
+
149
+ end
150
+
151
+ class RunWithExceptionWhileWritingTests < RunWithExceptionSetupTests
117
152
  desc "and run with an exception thrown while writing the response"
118
153
  setup do
154
+ Assert.stub(@route, :run){ @response }
119
155
  @connection.raise_on_write = true
120
156
 
121
- error_handler = Sanford::ErrorHandler.new(@connection.write_exception, {
122
- :server_data => @server_data,
123
- :request => @request
124
- })
125
- @expected_response = error_handler.run
126
- @expected_exception = error_handler.exception
127
-
128
157
  @processed_service = @connection_handler.run
129
158
  end
130
159
  subject{ @processed_service }
131
160
 
132
- should "return a processed service with an exception" do
133
- assert_instance_of ProcessedService, subject
134
- assert_equal @expected_response, subject.response
135
- assert_equal @expected_exception, subject.exception
161
+ should "run an error handler" do
162
+ assert_equal @connection.write_exception, @error_handler_spy.passed_exception
163
+ exp = {
164
+ :server_data => @server_data,
165
+ :request => @processed_service.request,
166
+ :handler_class => @processed_service.handler_class,
167
+ :response => @response
168
+ }
169
+ assert_equal exp, @error_handler_spy.context_hash
170
+ assert_true @error_handler_spy.run_called
171
+ end
172
+
173
+ should "store the error handler response and exception on the processed service" do
174
+ assert_equal @error_handler_spy.response, @processed_service.response
175
+ assert_equal @error_handler_spy.exception, @processed_service.exception
136
176
  end
137
177
 
138
- should "have written the error response to the connection" do
139
- assert_equal @expected_response, @connection.response
178
+ should "write the error response to the connection" do
179
+ assert_equal @error_handler_spy.response, @connection.response
140
180
  assert_true @connection.write_closed
141
181
  end
142
182
 
@@ -161,19 +201,21 @@ class Sanford::ConnectionHandler
161
201
  should "have logged the service" do
162
202
  time_taken = @processed_service.time_taken
163
203
  status = @processed_service.response.status.to_s
164
- expected = "[Sanford] ===== Received request =====" \
165
- "[Sanford] Service: #{@request.name.inspect}" \
166
- "[Sanford] Params: #{@request.params.inspect}" \
167
- "[Sanford] Handler: #{@route.handler_class}" \
168
- "[Sanford] ===== Completed in #{time_taken}ms #{status} ====="
169
- assert_equal expected, subject.info_logged.join
204
+ exp = "[Sanford] ===== Received request =====" \
205
+ "[Sanford] Service: #{@request.name.inspect}" \
206
+ "[Sanford] Params: #{@request.params.inspect}" \
207
+ "[Sanford] Handler: #{@route.handler_class}" \
208
+ "[Sanford] ===== Completed in #{time_taken}ms #{status} ====="
209
+ assert_equal exp, subject.info_logged.join
170
210
  end
171
211
 
172
212
  should "log an exception when one is thrown" do
173
213
  err = @processed_service.exception
174
- backtrace = err.backtrace.join("\n")
175
- expected = "[Sanford] #{err.class}: #{err.message}\n#{backtrace}"
176
- assert_equal expected, subject.error_logged.join
214
+ exp = "[Sanford] #{err.class}: #{err.message}"
215
+ assert_equal exp, subject.error_logged.first
216
+ err.backtrace.each do |l|
217
+ assert_includes "[Sanford] #{l}", subject.error_logged
218
+ end
177
219
  end
178
220
 
179
221
  end
@@ -198,14 +240,14 @@ class Sanford::ConnectionHandler
198
240
  time_taken = @processed_service.time_taken
199
241
  status = @processed_service.response.status.to_i
200
242
  exception_msg = "#{@exception.class}: #{@exception.message}"
201
- expected = "[Sanford] " \
202
- "time=#{time_taken} " \
203
- "status=#{status} " \
204
- "handler=#{@route.handler_class} " \
205
- "service=#{@request.name.inspect} " \
206
- "params=#{@request.params.inspect} " \
207
- "error=#{exception_msg.inspect}"
208
- assert_equal expected, subject.info_logged.join
243
+ exp = "[Sanford] " \
244
+ "time=#{time_taken} " \
245
+ "status=#{status} " \
246
+ "handler=#{@route.handler_class} " \
247
+ "service=#{@request.name.inspect} " \
248
+ "params=#{@request.params.inspect} " \
249
+ "error=#{exception_msg.inspect}"
250
+ assert_equal exp, subject.info_logged.join
209
251
  end
210
252
 
211
253
  should "not have logged the exception" do
@@ -216,6 +258,24 @@ class Sanford::ConnectionHandler
216
258
 
217
259
  TestHandler = Class.new
218
260
 
261
+ class ErrorHandlerSpy
262
+ attr_reader :passed_exception, :context_hash, :exception, :response
263
+ attr_reader :run_called
264
+
265
+ def initialize(exception, context_hash)
266
+ @passed_exception = exception
267
+ @context_hash = context_hash
268
+ @exception = Factory.exception
269
+ @response = Factory.protocol_response
270
+ @run_called = false
271
+ end
272
+
273
+ def run
274
+ @run_called = true
275
+ @response
276
+ end
277
+ end
278
+
219
279
  class SpyLogger
220
280
  attr_reader :info_logged, :error_logged
221
281
 
@@ -100,7 +100,7 @@ class Sanford::ErrorHandler
100
100
  subject{ @response }
101
101
 
102
102
  should "return a bad request response" do
103
- exp = Sanford::Protocol::Response.new([:bad_request, @exception.message])
103
+ exp = Sanford::Protocol::Response.new([400, @exception.message])
104
104
  assert_equal exp, subject
105
105
  end
106
106
 
@@ -117,7 +117,7 @@ class Sanford::ErrorHandler
117
117
  subject{ @response }
118
118
 
119
119
  should "return a bad request response" do
120
- exp = Sanford::Protocol::Response.new([:bad_request, @exception.message])
120
+ exp = Sanford::Protocol::Response.new([400, @exception.message])
121
121
  assert_equal exp, subject
122
122
  end
123
123
 
@@ -134,7 +134,7 @@ class Sanford::ErrorHandler
134
134
  subject{ @response }
135
135
 
136
136
  should "return a not found response" do
137
- exp = Sanford::Protocol::Response.new(:not_found)
137
+ exp = Sanford::Protocol::Response.new(404)
138
138
  assert_equal exp, subject
139
139
  end
140
140
 
@@ -151,7 +151,7 @@ class Sanford::ErrorHandler
151
151
  subject{ @response }
152
152
 
153
153
  should "return a timeout response" do
154
- exp = Sanford::Protocol::Response.new(:timeout)
154
+ exp = Sanford::Protocol::Response.new(408)
155
155
  assert_equal exp, subject
156
156
  end
157
157
 
@@ -168,7 +168,7 @@ class Sanford::ErrorHandler
168
168
  subject{ @response }
169
169
 
170
170
  should "return an error response" do
171
- exp = Sanford::Protocol::Response.new([:error, "An unexpected error occurred."])
171
+ exp = Sanford::Protocol::Response.new([500, "An unexpected error occurred."])
172
172
  assert_equal exp, subject
173
173
  end
174
174
 
@@ -198,7 +198,7 @@ class Sanford::ErrorHandler
198
198
  end
199
199
 
200
200
  should "return an error response" do
201
- exp = Sanford::Protocol::Response.new([:error, "An unexpected error occurred."])
201
+ exp = Sanford::Protocol::Response.new([500, "An unexpected error occurred."])
202
202
  assert_equal exp, @response
203
203
  end
204
204
 
@@ -208,7 +208,7 @@ class Sanford::ErrorHandler
208
208
  desc "with a protocol response returned from an error proc"
209
209
  setup do
210
210
  @proc_response = Sanford::Protocol::Response.new(Factory.integer)
211
- @error_proc_spies.choice.response = @proc_response
211
+ @error_proc_spies.sample.response = @proc_response
212
212
 
213
213
  @response = @handler.run
214
214
  end
@@ -224,7 +224,7 @@ class Sanford::ErrorHandler
224
224
  desc "with a response code returned from an error proc"
225
225
  setup do
226
226
  @response_code = Factory.integer
227
- @error_proc_spies.choice.response = @response_code
227
+ @error_proc_spies.sample.response = @response_code
228
228
 
229
229
  @response = @handler.run
230
230
  end
@@ -240,15 +240,15 @@ class Sanford::ErrorHandler
240
240
  class RunWithSymbolFromErrorProcTests < RunSetupTests
241
241
  desc "with a response symbol returned from an error proc"
242
242
  setup do
243
- @response_symbol = [:not_found, :bad_request, :error].choice
244
- @error_proc_spies.choice.response = @response_symbol
243
+ @response_code = [400, 404, 500].sample
244
+ @error_proc_spies.sample.response = @response_code
245
245
 
246
246
  @response = @handler.run
247
247
  end
248
248
  subject{ @response }
249
249
 
250
250
  should "use the response symbol to build a response and return it" do
251
- exp = Sanford::Protocol::Response.new(@response_symbol)
251
+ exp = Sanford::Protocol::Response.new(@response_code)
252
252
  assert_equal exp, subject
253
253
  end
254
254