sanford 0.17.0 → 0.18.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.
@@ -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