sanford 0.10.1 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
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 %>