sanford 0.10.1 → 0.11.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.
Files changed (73) hide show
  1. data/Gemfile +1 -1
  2. data/README.md +41 -56
  3. data/Rakefile +0 -1
  4. data/bench/client.rb +8 -3
  5. data/bench/{services.rb → config.sanford} +11 -6
  6. data/bench/{runner.rb → report.rb} +2 -2
  7. data/bench/report.txt +32 -32
  8. data/lib/sanford/cli.rb +42 -28
  9. data/lib/sanford/config_file.rb +79 -0
  10. data/lib/sanford/{worker.rb → connection_handler.rb} +28 -20
  11. data/lib/sanford/error_handler.rb +7 -7
  12. data/lib/sanford/pid_file.rb +42 -0
  13. data/lib/sanford/process.rb +136 -0
  14. data/lib/sanford/process_signal.rb +20 -0
  15. data/lib/sanford/route.rb +48 -0
  16. data/lib/sanford/router.rb +36 -0
  17. data/lib/sanford/runner.rb +30 -58
  18. data/lib/sanford/sanford_runner.rb +19 -9
  19. data/lib/sanford/server.rb +211 -42
  20. data/lib/sanford/server_data.rb +47 -0
  21. data/lib/sanford/service_handler.rb +8 -46
  22. data/lib/sanford/template_source.rb +19 -2
  23. data/lib/sanford/test_runner.rb +27 -28
  24. data/lib/sanford/version.rb +1 -1
  25. data/lib/sanford.rb +1 -23
  26. data/sanford.gemspec +4 -5
  27. data/test/helper.rb +3 -20
  28. data/test/support/app_server.rb +142 -0
  29. data/test/support/config.sanford +7 -0
  30. data/test/support/config_invalid_run.sanford +3 -0
  31. data/test/support/config_no_run.sanford +0 -0
  32. data/test/support/fake_server_connection.rb +58 -0
  33. data/test/support/pid_file_spy.rb +19 -0
  34. data/test/support/template.erb +1 -0
  35. data/test/system/server_tests.rb +378 -0
  36. data/test/system/service_handler_tests.rb +224 -0
  37. data/test/unit/cli_tests.rb +187 -0
  38. data/test/unit/config_file_tests.rb +59 -0
  39. data/test/unit/connection_handler_tests.rb +254 -0
  40. data/test/unit/error_handler_tests.rb +30 -35
  41. data/test/unit/pid_file_tests.rb +70 -0
  42. data/test/unit/process_signal_tests.rb +61 -0
  43. data/test/unit/process_tests.rb +428 -0
  44. data/test/unit/route_tests.rb +92 -0
  45. data/test/unit/router_tests.rb +65 -0
  46. data/test/unit/runner_tests.rb +61 -15
  47. data/test/unit/sanford_runner_tests.rb +162 -28
  48. data/test/unit/sanford_tests.rb +0 -8
  49. data/test/unit/server_data_tests.rb +87 -0
  50. data/test/unit/server_tests.rb +502 -21
  51. data/test/unit/service_handler_tests.rb +114 -219
  52. data/test/unit/template_engine_tests.rb +1 -1
  53. data/test/unit/template_source_tests.rb +56 -16
  54. data/test/unit/test_runner_tests.rb +206 -0
  55. metadata +67 -67
  56. data/bench/tasks.rb +0 -41
  57. data/lib/sanford/config.rb +0 -28
  58. data/lib/sanford/host.rb +0 -129
  59. data/lib/sanford/host_data.rb +0 -65
  60. data/lib/sanford/hosts.rb +0 -38
  61. data/lib/sanford/manager.rb +0 -275
  62. data/test/support/fake_connection.rb +0 -36
  63. data/test/support/helpers.rb +0 -17
  64. data/test/support/service_handlers.rb +0 -154
  65. data/test/support/services.rb +0 -123
  66. data/test/support/simple_client.rb +0 -62
  67. data/test/system/request_handling_tests.rb +0 -306
  68. data/test/unit/config_tests.rb +0 -56
  69. data/test/unit/host_data_tests.rb +0 -71
  70. data/test/unit/host_tests.rb +0 -141
  71. data/test/unit/hosts_tests.rb +0 -50
  72. data/test/unit/manager_tests.rb +0 -195
  73. data/test/unit/worker_tests.rb +0 -24
@@ -0,0 +1,47 @@
1
+ module Sanford
2
+
3
+ class ServerData
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 :ip, :port
12
+ attr_reader :pid_file
13
+ attr_reader :logger, :verbose_logging
14
+ attr_reader :receives_keep_alive
15
+ attr_reader :error_procs
16
+ attr_reader :routes
17
+ attr_reader :template_source
18
+
19
+ def initialize(args = nil)
20
+ args ||= {}
21
+ @name = args[:name]
22
+ @ip = args[:ip]
23
+ @port = args[:port]
24
+ @pid_file = args[:pid_file]
25
+ @logger = args[:logger]
26
+ @verbose_logging = !!args[:verbose_logging]
27
+ @receives_keep_alive = !!args[:receives_keep_alive]
28
+ @error_procs = args[:error_procs] || []
29
+ @routes = build_routes(args[:routes] || [])
30
+ @template_source = args[:template_source]
31
+ end
32
+
33
+ def route_for(name)
34
+ @routes[name] || raise(NotFoundError, "no service named '#{name}'")
35
+ end
36
+
37
+ private
38
+
39
+ def build_routes(routes)
40
+ routes.inject({}){ |h, route| h.merge(route.name => route) }
41
+ end
42
+
43
+ end
44
+
45
+ NotFoundError = Class.new(RuntimeError)
46
+
47
+ end
@@ -1,22 +1,7 @@
1
- require 'sanford/sanford_runner'
2
- require 'sanford/template_source'
3
-
4
1
  module Sanford
5
2
 
6
3
  module ServiceHandler
7
4
 
8
- DISALLOWED_TEMPLATE_EXTS = Sanford::TemplateSource::DISALLOWED_ENGINE_EXTS
9
-
10
- def self.constantize(class_name)
11
- names = class_name.to_s.split('::').reject{|name| name.empty? }
12
- klass = names.inject(Object) do |constant, name|
13
- constant.const_get(name)
14
- end
15
- klass == Object ? false : klass
16
- rescue NameError
17
- false
18
- end
19
-
20
5
  def self.included(klass)
21
6
  klass.class_eval do
22
7
  extend ClassMethods
@@ -31,18 +16,18 @@ module Sanford
31
16
  end
32
17
 
33
18
  def init
34
- self.run_callback 'before_init'
19
+ run_callback 'before_init'
35
20
  self.init!
36
- self.run_callback 'after_init'
21
+ run_callback 'after_init'
37
22
  end
38
23
 
39
24
  def init!
40
25
  end
41
26
 
42
27
  def run
43
- self.run_callback 'before_run'
28
+ run_callback 'before_run'
44
29
  data = self.run!
45
- self.run_callback 'after_run'
30
+ run_callback 'after_run'
46
31
  [ 200, data ]
47
32
  end
48
33
 
@@ -55,26 +40,19 @@ module Sanford
55
40
  "#<#{self.class}:#{reference} @request=#{self.request.inspect}>"
56
41
  end
57
42
 
58
- protected
43
+ private
59
44
 
60
45
  # Helpers
61
46
 
62
47
  def render(path, options = nil)
63
48
  options ||= {}
64
- get_engine(path, options['source'] || Sanford.config.template_source).render(
65
- path,
66
- self,
67
- options['locals'] || {}
68
- )
69
- end
70
-
71
- def run_handler(handler_class, params = nil)
72
- handler_class.run(params || {}, self.logger)
49
+ source = options['source'] || @sanford_runner.template_source
50
+ source.render(path, self, options['locals'] || {})
73
51
  end
74
52
 
75
53
  def halt(*args); @sanford_runner.halt(*args); end
76
54
  def request; @sanford_runner.request; end
77
- def params; self.request.params; end
55
+ def params; @sanford_runner.params; end
78
56
  def logger; @sanford_runner.logger; end
79
57
 
80
58
  def run_callback(callback)
@@ -83,26 +61,10 @@ module Sanford
83
61
  end
84
62
  end
85
63
 
86
- private
87
-
88
- def get_engine(path, source)
89
- source.engines[File.extname(get_template(path, source))[1..-1] || '']
90
- end
91
-
92
- def get_template(path, source)
93
- files = Dir.glob("#{Pathname.new(source.path).join(path.to_s)}.*")
94
- files = files.reject{ |p| DISALLOWED_TEMPLATE_EXTS.include?(File.extname(p)) }
95
- files.first.to_s
96
- end
97
-
98
64
  end
99
65
 
100
66
  module ClassMethods
101
67
 
102
- def run(params = nil, logger = nil)
103
- SanfordRunner.run(self, params || {}, logger)
104
- end
105
-
106
68
  def before_callbacks; @before_callbacks ||= []; end
107
69
  def after_callbacks; @after_callbacks ||= []; end
108
70
  def before_init_callbacks; @before_init_callbacks ||= []; end
@@ -4,7 +4,7 @@ module Sanford
4
4
 
5
5
  class TemplateSource
6
6
 
7
- DISALLOWED_ENGINE_EXTS = [ '.rb' ]
7
+ DISALLOWED_ENGINE_EXTS = [ 'rb' ]
8
8
 
9
9
  DisallowedEngineExtError = Class.new(ArgumentError)
10
10
 
@@ -17,7 +17,7 @@ module Sanford
17
17
  end
18
18
 
19
19
  def engine(input_ext, engine_class, registered_opts = nil)
20
- if DISALLOWED_ENGINE_EXTS.include?(".#{input_ext}")
20
+ if DISALLOWED_ENGINE_EXTS.include?(input_ext)
21
21
  raise DisallowedEngineExtError, "`#{input_ext}` is disallowed as an"\
22
22
  " engine extension."
23
23
  end
@@ -25,6 +25,23 @@ module Sanford
25
25
  @engines[input_ext.to_s] = engine_class.new(engine_opts)
26
26
  end
27
27
 
28
+ def render(template_path, service_handler, locals)
29
+ engine = @engines[get_template_ext(template_path)]
30
+ engine.render(template_path, service_handler, locals)
31
+ end
32
+
33
+ private
34
+
35
+ def get_template_ext(template_path)
36
+ files = Dir.glob("#{File.join(@path, template_path.to_s)}.*")
37
+ files = files.reject{ |p| !@engines.keys.include?(parse_ext(p)) }
38
+ parse_ext(files.first.to_s || '')
39
+ end
40
+
41
+ def parse_ext(template_path)
42
+ File.extname(template_path)[1..-1]
43
+ end
44
+
28
45
  end
29
46
 
30
47
  class NullTemplateSource < TemplateSource
@@ -1,53 +1,52 @@
1
1
  require 'sanford-protocol'
2
+ require 'sanford/logger'
2
3
  require 'sanford/runner'
3
4
  require 'sanford/service_handler'
5
+ require 'sanford/template_source'
4
6
 
5
7
  module Sanford
6
8
 
7
- InvalidServiceHandlerError = Class.new(RuntimeError)
9
+ InvalidServiceHandlerError = Class.new(StandardError)
8
10
 
9
- class TestRunner
10
- include Sanford::Runner
11
+ class TestRunner < Sanford::Runner
11
12
 
12
13
  attr_reader :response
13
14
 
14
- def initialize(handler_class, request_or_params, *args)
15
+ def initialize(handler_class, args = nil)
15
16
  if !handler_class.include?(Sanford::ServiceHandler)
16
17
  raise InvalidServiceHandlerError, "#{handler_class.inspect} is not a"\
17
18
  " Sanford::ServiceHandler"
18
19
  end
19
-
20
- super handler_class, build_request(request_or_params), *args
21
- end
22
-
23
- def init!
24
- @response = build_response catch(:halt){ @handler.init; nil }
20
+ args = (args || {}).dup
21
+ @request = args.delete(:request)
22
+ @params = args.delete(:params) || {}
23
+ @logger = args.delete(:logger) || Sanford::NullLogger.new
24
+ @template_source = args.delete(:template_source) ||
25
+ Sanford::NullTemplateSource.new
26
+
27
+ super(handler_class)
28
+ args.each{ |key, value| @handler.send("#{key}=", value) }
29
+
30
+ return_value = catch(:halt){ @handler.init; nil }
31
+ @response = build_and_serialize_response{ return_value } if return_value
25
32
  end
26
33
 
27
- # we override the `run` method because the TestRunner wants to control
28
- # storing any generated response. If `init` generated a response, we don't
29
- # want to `run` at all.
34
+ # If `init` generated a response, we don't want to `run` at all. This makes
35
+ # the `TestRunner` behave similar to the `SanfordRunner`, i.e. `halt` in
36
+ # `init` stops processing where `halt` is called.
30
37
 
31
38
  def run
32
- @response ||= super.tap do |response|
33
- # attempt to serialize (and then throw away) the response data
34
- # this will error on the developer if BSON can't serialize their response
35
- Sanford::Protocol::BsonBody.new.encode(response.to_hash)
36
- end
37
- end
38
-
39
- def run!
40
- self.handler.run
39
+ @response ||= build_and_serialize_response{ self.handler.run }
41
40
  end
42
41
 
43
42
  private
44
43
 
45
- def build_request(req)
46
- !req.kind_of?(Sanford::Protocol::Request) ? test_request(req) : req
47
- end
48
-
49
- def test_request(params)
50
- Sanford::Protocol::Request.new('name', params || {})
44
+ def build_and_serialize_response(&block)
45
+ build_response(&block).tap do |response|
46
+ # attempt to serialize (and then throw away) the response data
47
+ # this will error on the developer if BSON can't serialize their response
48
+ Sanford::Protocol::BsonBody.new.encode(response.to_hash) if response
49
+ end
51
50
  end
52
51
 
53
52
  end
@@ -1,3 +1,3 @@
1
1
  module Sanford
2
- VERSION = "0.10.1"
2
+ VERSION = "0.11.0"
3
3
  end
data/lib/sanford.rb CHANGED
@@ -1,29 +1,7 @@
1
1
  require 'sanford/version'
2
- require 'sanford/config'
3
- require 'sanford/hosts'
4
- require 'sanford/host'
2
+ require 'sanford/server'
5
3
  require 'sanford/service_handler'
6
4
 
7
- ENV['SANFORD_SERVICES_FILE'] ||= 'config/services'
8
-
9
5
  module Sanford
10
6
 
11
- def self.config; @config ||= Config.new; end
12
- def self.configure(&block)
13
- block.call(self.config)
14
- end
15
-
16
- def self.init
17
- @hosts ||= Sanford::Hosts.new
18
- require self.config.services_file
19
- end
20
-
21
- def self.register(host)
22
- @hosts.add(host)
23
- end
24
-
25
- def self.hosts
26
- @hosts
27
- end
28
-
29
7
  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_dependency("dat-tcp", ["~> 0.4"])
22
- gem.add_dependency("ns-options", ["~> 1.1"])
23
- gem.add_dependency("sanford-protocol", ["~> 0.8"])
21
+ gem.add_dependency("dat-tcp", ["~> 0.5"])
22
+ gem.add_dependency("ns-options", ["~> 1.1"])
23
+ gem.add_dependency("sanford-protocol", ["~> 0.9"])
24
24
 
25
- gem.add_development_dependency("assert", ["~> 2.10"])
26
- gem.add_development_dependency("assert-mocha", ["~> 1.1"])
25
+ gem.add_development_dependency("assert", ["~> 2.11"])
27
26
  end
data/test/helper.rb CHANGED
@@ -5,27 +5,10 @@
5
5
  $LOAD_PATH.unshift(File.expand_path("../..", __FILE__))
6
6
 
7
7
  require 'pry' # require pry for debugging (`binding.pry`)
8
- require 'assert-mocha' if defined?(Assert)
9
8
 
10
9
  ENV['SANFORD_PROTOCOL_DEBUG'] = 'yes'
11
10
 
12
- require 'sanford'
13
- ROOT = File.expand_path('../..', __FILE__)
11
+ require 'pathname'
12
+ ROOT_PATH = Pathname.new(File.expand_path('../..', __FILE__))
14
13
 
15
- MyTestEngine = Class.new(Sanford::TemplateEngine) do
16
- def render(path, service_handler, locals)
17
- [path.to_s, service_handler.class.to_s, locals]
18
- end
19
- end
20
- Sanford.configure do |config|
21
- config.services_file = File.join(ROOT, 'test/support/services')
22
- config.set_template_source File.join(ROOT, 'test/support') do |s|
23
- s.engine 'test', MyTestEngine
24
- end
25
- end
26
- Sanford.init
27
-
28
- require 'test/support/fake_connection'
29
- require 'test/support/service_handlers'
30
- require 'test/support/simple_client'
31
- require 'test/support/helpers'
14
+ require 'test/support/factory'
@@ -0,0 +1,142 @@
1
+ require 'pathname'
2
+ require 'sanford'
3
+ require 'sanford-protocol'
4
+
5
+ if !defined?(ROOT_PATH)
6
+ ROOT_PATH = Pathname.new(File.expand_path('../../..', __FILE__))
7
+ end
8
+
9
+ class AppERBEngine < Sanford::TemplateEngine
10
+ RenderScope = Struct.new(:view)
11
+
12
+ def render(path, service_handler, locals)
13
+ require 'erb'
14
+ full_path = ROOT_PATH.join("test/support/#{path}.erb")
15
+ binding = RenderScope.new(service_handler).send(:binding)
16
+ ERB.new(File.read(full_path)).result(binding)
17
+ end
18
+ end
19
+
20
+ class AppServer
21
+ include Sanford::Server
22
+
23
+ name 'app'
24
+ ip 'localhost'
25
+ port 12000
26
+
27
+ receives_keep_alive true
28
+
29
+ logger Logger.new(ROOT_PATH.join('log/app_server.log').to_s)
30
+ verbose_logging true
31
+
32
+ router do
33
+ service_handler_ns 'AppHandlers'
34
+
35
+ service 'echo', 'Echo'
36
+ service 'raise', 'Raise'
37
+ service 'bad_response', 'BadResponse'
38
+ service 'template', 'Template'
39
+ service 'halt', 'Halt'
40
+ service 'custom_error', 'CustomError'
41
+ end
42
+
43
+ build_template_source ROOT_PATH.join('test/support').to_s do |s|
44
+ s.engine 'erb', AppERBEngine
45
+ end
46
+
47
+ error do |exception, server_data, request|
48
+ if request && request.name == 'custom_error'
49
+ data = "The server on #{server_data.ip}:#{server_data.port} " \
50
+ "threw a #{exception.class}."
51
+ Sanford::Protocol::Response.new(200, data)
52
+ end
53
+ end
54
+
55
+ end
56
+
57
+ module AppHandlers
58
+
59
+ class Echo
60
+ include Sanford::ServiceHandler
61
+
62
+ def run!
63
+ params['message']
64
+ end
65
+ end
66
+
67
+ class Raise
68
+ include Sanford::ServiceHandler
69
+
70
+ def run!
71
+ raise "hahaha"
72
+ end
73
+ end
74
+
75
+ class BadResponse
76
+ include Sanford::ServiceHandler
77
+
78
+ def run!
79
+ Class.new
80
+ end
81
+ end
82
+
83
+ class Template
84
+ include Sanford::ServiceHandler
85
+
86
+ attr_reader :message
87
+
88
+ def init!
89
+ @message = params['message']
90
+ end
91
+
92
+ def run!
93
+ render "template"
94
+ end
95
+ end
96
+
97
+ class Halt
98
+ include Sanford::ServiceHandler
99
+
100
+ before do
101
+ halt(200, :message => "in before") if params['when'] == 'before'
102
+ end
103
+
104
+ before_init do
105
+ halt(200, :message => "in before init") if params['when'] == 'before_init'
106
+ end
107
+
108
+ def init!
109
+ halt(200, :message => "in init") if params['when'] == 'init'
110
+ end
111
+
112
+ after_init do
113
+ halt(200, :message => "in after init") if params['when'] == 'after_init'
114
+ end
115
+
116
+ before_run do
117
+ halt(200, :message => "in before run") if params['when'] == 'before_run'
118
+ end
119
+
120
+ def run!
121
+ halt(200, :message => "in run") if params['when'] == 'run'
122
+ false
123
+ end
124
+
125
+ after_run do
126
+ halt(200, :message => "in after run") if params['when'] == 'after_run'
127
+ end
128
+
129
+ after do
130
+ halt(200, :message => "in after") if params['when'] == 'after'
131
+ end
132
+ end
133
+
134
+ class CustomError
135
+ include Sanford::ServiceHandler
136
+
137
+ def run!
138
+ raise StandardError
139
+ end
140
+ end
141
+
142
+ end
@@ -0,0 +1,7 @@
1
+ require 'test/support/app_server'
2
+
3
+ if !defined?(TestConstant)
4
+ TestConstant = Class.new
5
+ end
6
+
7
+ run AppServer.new
@@ -0,0 +1,3 @@
1
+ MyServer = Class.new
2
+
3
+ run MyServer
File without changes
@@ -0,0 +1,58 @@
1
+ require 'sanford-protocol'
2
+
3
+ class FakeServerConnection
4
+
5
+ attr_reader :request, :response
6
+ attr_reader :write_closed
7
+ attr_accessor :raise_on_write, :write_exception
8
+ attr_writer :read_data
9
+
10
+ def self.with_request(name, params = nil)
11
+ self.new.tap{ |c| c.add_request(name, params) }
12
+ end
13
+
14
+ def initialize(read_data = nil)
15
+ @read_data = read_data
16
+ @raise_on_write = false
17
+ @write_closed = false
18
+
19
+ @write_exception = RuntimeError.new('oops')
20
+
21
+ @request = nil
22
+ @response = nil
23
+ end
24
+
25
+ def add_request(name, params = nil)
26
+ @request = Sanford::Protocol::Request.new(name, params || {})
27
+ @read_data = @request.to_hash
28
+ end
29
+
30
+ def read_data
31
+ @read_data || {}
32
+ end
33
+
34
+ def write_data(data = nil)
35
+ write_data!(data) if data
36
+ @write_data
37
+ end
38
+
39
+ def peek_data
40
+ @read_data ? @read_data[0] : ""
41
+ end
42
+
43
+ def close_write
44
+ @write_closed = true
45
+ end
46
+
47
+ private
48
+
49
+ def write_data!(data)
50
+ if @raise_on_write
51
+ @raise_on_write = false
52
+ raise @write_exception
53
+ end
54
+ @response = Sanford::Protocol::Response.parse(data) rescue nil
55
+ @write_data = data
56
+ end
57
+
58
+ end
@@ -0,0 +1,19 @@
1
+ class PIDFileSpy
2
+
3
+ attr_reader :pid, :write_called, :remove_called
4
+
5
+ def initialize(pid)
6
+ @pid = pid
7
+ @write_called = false
8
+ @remove_called = false
9
+ end
10
+
11
+ def write
12
+ @write_called = true
13
+ end
14
+
15
+ def remove
16
+ @remove_called = true
17
+ end
18
+
19
+ end
@@ -0,0 +1 @@
1
+ ERB Template Message: <%= view.message %>