sanford 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -5,5 +5,4 @@ gemspec
5
5
  gem 'rake'
6
6
  gem 'pry'
7
7
 
8
- gem 'bson'
9
- gem 'bson_ext', '~>1.7'
8
+ gem 'bson_ext'
@@ -0,0 +1,28 @@
1
+ require 'ns-options'
2
+ require 'pathname'
3
+ require 'sanford/logger'
4
+ require 'sanford/runner'
5
+ require 'sanford/template_source'
6
+
7
+ module Sanford
8
+
9
+ class Config
10
+ include NsOptions::Proxy
11
+
12
+ option :services_file, Pathname, :default => proc{ ENV['SANFORD_SERVICES_FILE'] }
13
+ option :logger, :default => proc{ Sanford::NullLogger.new }
14
+
15
+ attr_reader :template_source
16
+
17
+ def initialize
18
+ super
19
+ @template_source = NullTemplateSource.new
20
+ end
21
+
22
+ def set_template_source(path, &block)
23
+ @template_source = TemplateSource.new(path).tap{ |s| block.call(s) if block }
24
+ end
25
+
26
+ end
27
+
28
+ end
data/lib/sanford/host.rb CHANGED
@@ -25,7 +25,6 @@ module Sanford
25
25
  option :logger, :default => proc{ Sanford.config.logger }
26
26
  option :verbose_logging, :default => true
27
27
  option :receives_keep_alive, :default => false
28
- option :runner, :default => proc{ Sanford.config.runner }
29
28
  option :error_procs, Array, :default => []
30
29
  option :init_procs, Array, :default => []
31
30
 
@@ -79,10 +78,6 @@ module Sanford
79
78
  self.configuration.receives_keep_alive *args
80
79
  end
81
80
 
82
- def runner(*args)
83
- self.configuration.runner *args
84
- end
85
-
86
81
  def error(&block)
87
82
  self.configuration.error_procs << block
88
83
  end
@@ -111,7 +106,7 @@ module Sanford
111
106
 
112
107
  module ClassMethods
113
108
 
114
- # the class level of a `Host` should just proxy it's methods down to it's
109
+ # the class level of a `Host` should just proxy its methods down to its
115
110
  # instance (it's a `Singleton`)
116
111
 
117
112
  # `name` is defined by all objects, so we can't rely on `method_missing`
@@ -1,4 +1,5 @@
1
1
  require 'sanford/service_handler'
2
+ require 'sanford/sanford_runner'
2
3
 
3
4
  module Sanford
4
5
 
@@ -12,7 +13,7 @@ module Sanford
12
13
  # NOTE: The `name` attribute shouldn't be removed, it is used to identify
13
14
  # a `HostData`, particularly in error handlers
14
15
 
15
- attr_reader :name, :logger, :verbose, :keep_alive, :runner, :error_procs
16
+ attr_reader :name, :logger, :verbose, :keep_alive, :error_procs
16
17
 
17
18
  def initialize(service_host, options = nil)
18
19
  service_host.configuration.init_procs.each(&:call)
@@ -24,7 +25,6 @@ module Sanford
24
25
  @logger = configuration[:logger]
25
26
  @verbose = configuration[:verbose_logging]
26
27
  @keep_alive = configuration[:receives_keep_alive]
27
- @runner = configuration[:runner]
28
28
  @error_procs = configuration[:error_procs]
29
29
 
30
30
  @handlers = service_host.services.inject({}) do |h, (name, handler_class_name)|
@@ -37,7 +37,7 @@ module Sanford
37
37
  end
38
38
 
39
39
  def run(handler_class, request)
40
- self.runner.new(handler_class, request, self.logger).run
40
+ SanfordRunner.new(handler_class, request, self.logger).run
41
41
  end
42
42
 
43
43
  protected
@@ -0,0 +1,38 @@
1
+ require 'set'
2
+ require 'sanford/host'
3
+
4
+ module Sanford
5
+
6
+ class Hosts
7
+
8
+ def initialize(values = [])
9
+ @set = Set.new(values)
10
+ end
11
+
12
+ def method_missing(method, *args, &block)
13
+ @set.send(method, *args, &block)
14
+ end
15
+
16
+ def respond_to?(method)
17
+ super || @set.respond_to?(method)
18
+ end
19
+
20
+ # We want class names to take precedence over a configured name, so that if
21
+ # a user specifies a specific class, they always get it
22
+ def find(name)
23
+ find_by_class_name(name) || find_by_name(name)
24
+ end
25
+
26
+ private
27
+
28
+ def find_by_class_name(class_name)
29
+ @set.detect{|host_class| host_class.to_s == class_name.to_s }
30
+ end
31
+
32
+ def find_by_name(name)
33
+ @set.detect{|host_class| host_class.name == name.to_s }
34
+ end
35
+
36
+ end
37
+
38
+ end
@@ -16,7 +16,7 @@ module Sanford
16
16
 
17
17
  module InstanceMethods
18
18
 
19
- attr_reader :handler_class, :request, :logger
19
+ attr_reader :handler_class, :request, :logger, :handler
20
20
 
21
21
  def initialize(handler_class, request, logger = nil)
22
22
  @handler_class, @request = handler_class, request
@@ -33,8 +33,7 @@ module Sanford
33
33
  end
34
34
 
35
35
  def run
36
- response_args = catch_halt{ self.run!(@handler) }
37
- Sanford::Protocol::Response.new(response_args.status, response_args.data)
36
+ build_response catch_halt{ self.run! }
38
37
  end
39
38
 
40
39
  def run!
@@ -56,6 +55,12 @@ module Sanford
56
55
  catch(:halt){ ResponseArgs.new(*block.call) }
57
56
  end
58
57
 
58
+ protected
59
+
60
+ def build_response(args)
61
+ Sanford::Protocol::Response.new(args.status, args.data) if args
62
+ end
63
+
59
64
  end
60
65
 
61
66
  module ClassMethods
@@ -69,14 +74,4 @@ module Sanford
69
74
 
70
75
  end
71
76
 
72
- class DefaultRunner
73
- include Sanford::Runner
74
-
75
- def run!(handler)
76
- handler.init
77
- handler.run
78
- end
79
-
80
- end
81
-
82
77
  end
@@ -0,0 +1,27 @@
1
+ require 'sanford/runner'
2
+
3
+ module Sanford
4
+
5
+ class SanfordRunner
6
+ include Sanford::Runner
7
+
8
+ # call the handler init and the handler run - if the init halts, run won't
9
+ # be called.
10
+
11
+ def run!
12
+ run_callbacks self.handler_class.before_callbacks
13
+ self.handler.init
14
+ response_args = self.handler.run
15
+ run_callbacks self.handler_class.after_callbacks
16
+ response_args
17
+ end
18
+
19
+ private
20
+
21
+ def run_callbacks(callbacks)
22
+ callbacks.each{|proc| self.handler.instance_eval(&proc) }
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -89,7 +89,7 @@ module Sanford
89
89
  # packetizes our stream. This improves both latency and throughput.
90
90
  # TCP_CORK disables Nagle's algorithm, which is ideal for sporadic
91
91
  # traffic (like Telnet) but is less optimal for HTTP. Sanford is similar
92
- # to HTTP, it doesn't receive sporadic packets, it has all it's data
92
+ # to HTTP, it doesn't receive sporadic packets, it has all its data
93
93
  # come in at once.
94
94
  # For more information: http://baus.net/on-tcp_cork
95
95
 
@@ -54,6 +54,15 @@ module Sanford
54
54
 
55
55
  # Helpers
56
56
 
57
+ def render(path, options = nil)
58
+ options ||= {}
59
+ get_engine(path, options['source'] || Sanford.config.template_source).render(
60
+ path,
61
+ self,
62
+ options['locals'] || {}
63
+ )
64
+ end
65
+
57
66
  def run_handler(handler_class, params = nil)
58
67
  handler_class.run(params || {}, self.logger)
59
68
  end
@@ -69,23 +78,40 @@ module Sanford
69
78
  end
70
79
  end
71
80
 
81
+ private
82
+
83
+ def get_engine(path, source)
84
+ source.engines[File.extname(get_template(path, source))[1..-1] || '']
85
+ end
86
+
87
+ def get_template(path, source)
88
+ Dir.glob("#{Pathname.new(source.path).join(path.to_s)}.*").first.to_s
89
+ end
90
+
72
91
  end
73
92
 
74
93
  module ClassMethods
75
94
 
76
95
  def run(params = nil, logger = nil)
77
- Sanford.config.runner.run(self, params || {}, logger)
96
+ SanfordRunner.run(self, params || {}, logger)
78
97
  end
79
98
 
99
+ def before_callbacks; @before_callbacks ||= []; end
100
+ def after_callbacks; @after_callbacks ||= []; end
80
101
  def before_init_callbacks; @before_init_callbacks ||= []; end
81
102
  def after_init_callbacks; @after_init_callbacks ||= []; end
82
103
  def before_run_callbacks; @before_run_callbacks ||= []; end
83
104
  def after_run_callbacks; @after_run_callbacks ||= []; end
84
105
 
106
+ def before(&block); self.before_callbacks << block; end
107
+ def after(&block); self.after_callbacks << block; end
85
108
  def before_init(&block); self.before_init_callbacks << block; end
86
109
  def after_init(&block); self.after_init_callbacks << block; end
87
110
  def before_run(&block); self.before_run_callbacks << block; end
88
111
  def after_run(&block); self.after_run_callbacks << block; end
112
+
113
+ def prepend_before(&block); self.before_callbacks.unshift(block); end
114
+ def prepend_after(&block); self.after_callbacks.unshift(block); end
89
115
  def prepend_before_init(&block); self.before_init_callbacks.unshift(block); end
90
116
  def prepend_after_init(&block); self.after_init_callbacks.unshift(block); end
91
117
  def prepend_before_run(&block); self.before_run_callbacks.unshift(block); end
@@ -0,0 +1,32 @@
1
+ require 'pathname'
2
+
3
+ module Sanford
4
+
5
+ class TemplateEngine
6
+
7
+ attr_reader :source_path, :opts
8
+
9
+ def initialize(opts = nil)
10
+ @opts = opts || {}
11
+ @source_path = Pathname.new(@opts['source_path'].to_s)
12
+ end
13
+
14
+ def render(path, service_handler, locals)
15
+ raise NotImplementedError
16
+ end
17
+
18
+ end
19
+
20
+ class NullTemplateEngine < TemplateEngine
21
+
22
+ def render(path, service_handler, locals)
23
+ template_file = self.source_path.join(path).to_s
24
+ unless File.exists?(template_file)
25
+ raise ArgumentError, "template file `#{template_file}` does not exist"
26
+ end
27
+ File.read(template_file)
28
+ end
29
+
30
+ end
31
+
32
+ end
@@ -0,0 +1,30 @@
1
+ require 'sanford/template_engine'
2
+
3
+ module Sanford
4
+
5
+ class TemplateSource
6
+
7
+ attr_reader :path, :engines
8
+
9
+ def initialize(path)
10
+ @path = path.to_s
11
+ @default_opts = { 'source_path' => @path }
12
+ @engines = Hash.new{ |h,k| Sanford::NullTemplateEngine.new(@default_opts) }
13
+ end
14
+
15
+ def engine(input_ext, engine_class, registered_opts = nil)
16
+ engine_opts = @default_opts.merge(registered_opts || {})
17
+ @engines[input_ext.to_s] = engine_class.new(engine_opts)
18
+ end
19
+
20
+ end
21
+
22
+ class NullTemplateSource < TemplateSource
23
+
24
+ def initialize
25
+ super('')
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -9,20 +9,18 @@ module Sanford
9
9
  class TestRunner
10
10
  include Sanford::Runner
11
11
 
12
- attr_reader :handler, :response
12
+ attr_reader :response
13
13
 
14
- def initialize(handler_class, *args)
14
+ def initialize(handler_class, request_or_params, *args)
15
15
  if !handler_class.include?(Sanford::ServiceHandler)
16
16
  raise InvalidServiceHandlerError, "#{handler_class.inspect} is not a"\
17
17
  " Sanford::ServiceHandler"
18
18
  end
19
- super
19
+
20
+ super handler_class, build_request(request_or_params), *args
20
21
  end
21
22
 
22
23
  def init!
23
- if !@request.kind_of?(Sanford::Protocol::Request)
24
- @request = test_request(@request)
25
- end
26
24
  @response = build_response catch(:halt){ @handler.init; nil }
27
25
  end
28
26
 
@@ -31,21 +29,25 @@ module Sanford
31
29
  # want to `run` at all.
32
30
 
33
31
  def run
34
- @response ||= build_response(catch_halt{ @handler.run }).tap do |response|
32
+ @response ||= super.tap do |response|
35
33
  # attempt to serialize (and then throw away) the response data
36
34
  # this will error on the developer if BSON can't serialize their response
37
35
  Sanford::Protocol::BsonBody.new.encode(response.to_hash)
38
36
  end
39
37
  end
40
38
 
41
- protected
39
+ def run!
40
+ self.handler.run
41
+ end
42
42
 
43
- def test_request(params)
44
- Sanford::Protocol::Request.new('name', params || {})
43
+ private
44
+
45
+ def build_request(req)
46
+ !req.kind_of?(Sanford::Protocol::Request) ? test_request(req) : req
45
47
  end
46
48
 
47
- def build_response(response_args)
48
- Sanford::Protocol::Response.new(response_args.status, response_args.data) if response_args
49
+ def test_request(params)
50
+ Sanford::Protocol::Request.new('name', params || {})
49
51
  end
50
52
 
51
53
  end
@@ -1,3 +1,3 @@
1
1
  module Sanford
2
- VERSION = "0.9.0"
2
+ VERSION = "0.10.0"
3
3
  end
data/lib/sanford.rb CHANGED
@@ -1,29 +1,20 @@
1
- require 'ns-options'
2
- require 'pathname'
3
- require 'set'
4
-
5
1
  require 'sanford/version'
2
+ require 'sanford/config'
3
+ require 'sanford/hosts'
6
4
  require 'sanford/host'
7
- require 'sanford/logger'
8
- require 'sanford/runner'
9
- require 'sanford/server'
10
5
  require 'sanford/service_handler'
11
6
 
12
7
  ENV['SANFORD_SERVICES_FILE'] ||= 'config/services'
13
8
 
14
9
  module Sanford
15
10
 
16
- def self.config
17
- Sanford::Config
18
- end
19
-
11
+ def self.config; @config ||= Config.new; end
20
12
  def self.configure(&block)
21
- self.config.define(&block)
22
- self.config
13
+ block.call(self.config)
23
14
  end
24
15
 
25
16
  def self.init
26
- @hosts ||= Hosts.new
17
+ @hosts ||= Sanford::Hosts.new
27
18
  require self.config.services_file
28
19
  end
29
20
 
@@ -35,43 +26,4 @@ module Sanford
35
26
  @hosts
36
27
  end
37
28
 
38
- module Config
39
- include NsOptions::Proxy
40
- option :services_file, Pathname, :default => ENV['SANFORD_SERVICES_FILE']
41
- option :logger, :default => Sanford::NullLogger.new
42
- option :runner, :default => Sanford::DefaultRunner
43
- end
44
-
45
- class Hosts
46
-
47
- def initialize(values = [])
48
- @set = Set.new(values)
49
- end
50
-
51
- def method_missing(method, *args, &block)
52
- @set.send(method, *args, &block)
53
- end
54
-
55
- def respond_to?(method)
56
- super || @set.respond_to?(method)
57
- end
58
-
59
- # We want class names to take precedence over a configured name, so that if
60
- # a user specifies a specific class, they always get it
61
- def find(name)
62
- find_by_class_name(name) || find_by_name(name)
63
- end
64
-
65
- private
66
-
67
- def find_by_class_name(class_name)
68
- @set.detect{|host_class| host_class.to_s == class_name.to_s }
69
- end
70
-
71
- def find_by_name(name)
72
- @set.detect{|host_class| host_class.name == name.to_s }
73
- end
74
-
75
- end
76
-
77
29
  end
data/sanford.gemspec CHANGED
@@ -20,7 +20,7 @@ Gem::Specification.new do |gem|
20
20
 
21
21
  gem.add_dependency("dat-tcp", ["~> 0.4"])
22
22
  gem.add_dependency("ns-options", ["~> 1.1"])
23
- gem.add_dependency("sanford-protocol", ["~> 0.7"])
23
+ gem.add_dependency("sanford-protocol", ["~> 0.8"])
24
24
 
25
25
  gem.add_development_dependency("assert", ["~> 2.10"])
26
26
  gem.add_development_dependency("assert-mocha", ["~> 1.1"])
data/test/helper.rb CHANGED
@@ -11,8 +11,17 @@ ENV['SANFORD_PROTOCOL_DEBUG'] = 'yes'
11
11
 
12
12
  require 'sanford'
13
13
  ROOT = File.expand_path('../..', __FILE__)
14
+
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
14
20
  Sanford.configure do |config|
15
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
16
25
  end
17
26
  Sanford.init
18
27
 
@@ -0,0 +1,6 @@
1
+ require 'assert/factory'
2
+
3
+ module Factory
4
+ extend Assert::Factory
5
+
6
+ end
@@ -24,10 +24,18 @@ module CallbackServiceHandler
24
24
 
25
25
  def self.included(receiver)
26
26
  receiver.class_eval do
27
+ attr_reader :before_called, :after_called
27
28
  attr_reader :before_init_called, :init_bang_called, :after_init_called
28
29
  attr_reader :before_run_called, :run_bang_called, :after_run_called
29
30
  attr_reader :second_before_init_called, :second_after_run_called
30
31
 
32
+ before do
33
+ @before_called = true
34
+ end
35
+ after do
36
+ @after_called = true
37
+ end
38
+
31
39
  before_init do
32
40
  @before_init_called = true
33
41
  end
@@ -117,6 +125,14 @@ class HaltingBehaviorServiceHandler
117
125
 
118
126
  end
119
127
 
128
+ class RenderHandler
129
+ include Sanford::ServiceHandler
130
+
131
+ def run!
132
+ render 'test_template'
133
+ end
134
+ end
135
+
120
136
  class RunOtherHandler
121
137
  include Sanford::ServiceHandler
122
138
 
@@ -0,0 +1 @@
1
+ This is a json template for use in template engine tests.
File without changes
@@ -251,7 +251,7 @@ class RequestHandlingTests < Assert::Context
251
251
  end
252
252
  end
253
253
 
254
- # Sending the server a protocol version that doesn't match it's version
254
+ # Sending the server a protocol version that doesn't match its version
255
255
  class WrongProtocolVersionTests < ForkedServerTests
256
256
  desc "when sent a request with a wrong protocol version"
257
257
 
@@ -0,0 +1,56 @@
1
+ require 'assert'
2
+ require 'sanford/config'
3
+
4
+ require 'ns-options/assert_macros'
5
+ require 'ns-options/proxy'
6
+ require 'sanford/logger'
7
+ require 'sanford/runner'
8
+ require 'sanford/template_source'
9
+ require 'test/support/factory'
10
+
11
+ class Sanford::Config
12
+
13
+ class UnitTests < Assert::Context
14
+ include NsOptions::AssertMacros
15
+
16
+ desc "Sanford::Config"
17
+ setup do
18
+ @config = Sanford::Config.new
19
+ end
20
+ subject{ @config }
21
+
22
+ should have_options :services_file, :logger
23
+ should have_readers :template_source
24
+ should have_imeths :set_template_source
25
+
26
+ should "be an NsOptions::Proxy" do
27
+ assert_includes NsOptions::Proxy, subject.class
28
+ end
29
+
30
+ should "default its services file" do
31
+ exp = Pathname.new(ENV['SANFORD_SERVICES_FILE'])
32
+ assert_equal exp, subject.services_file
33
+ end
34
+
35
+ should "default its logger to a NullLogger" do
36
+ assert_kind_of Sanford::NullLogger, subject.logger
37
+ end
38
+
39
+ should "have a null template source by default" do
40
+ assert_kind_of Sanford::NullTemplateSource, subject.template_source
41
+ end
42
+
43
+ should "set a new template source" do
44
+ path = '/path/to/app/assets'
45
+ block_called = false
46
+ subject.set_template_source(path) { |s| block_called = true}
47
+
48
+ assert_not_nil subject.template_source
49
+ assert_kind_of Sanford::TemplateSource, subject.template_source
50
+ assert_equal path, subject.template_source.path
51
+ assert_true block_called
52
+ end
53
+
54
+ end
55
+
56
+ end
@@ -1,8 +1,6 @@
1
1
  require 'assert'
2
2
  require 'sanford/host_data'
3
3
 
4
- require 'test/support/services'
5
-
6
4
  class Sanford::HostData
7
5
 
8
6
  class UnitTests < Assert::Context
@@ -16,7 +14,7 @@ class Sanford::HostData
16
14
  end
17
15
  subject{ @host_data }
18
16
 
19
- should have_readers :name, :logger, :verbose, :keep_alive, :runner, :error_procs
17
+ should have_readers :name, :logger, :verbose, :keep_alive, :error_procs
20
18
  should have_imeths :handler_class_for, :run
21
19
 
22
20
  should "call the init procs" do
@@ -28,7 +26,6 @@ class Sanford::HostData
28
26
  assert_equal TestHost.configuration.logger.class, subject.logger.class
29
27
  assert_equal TestHost.configuration.verbose_logging, subject.verbose
30
28
  assert_equal TestHost.configuration.receives_keep_alive, subject.keep_alive
31
- assert_equal TestHost.configuration.runner.class, subject.runner.class
32
29
  assert_equal TestHost.configuration.error_procs, subject.error_procs
33
30
  end
34
31