deas 0.36.0 → 0.37.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,22 @@
1
+ module Deas
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 :error_procs, :logger, :router, :template_source
11
+
12
+ def initialize(args = nil)
13
+ args ||= {}
14
+ @error_procs = args[:error_procs] || []
15
+ @logger = args[:logger]
16
+ @router = args[:router]
17
+ @template_source = args[:template_source]
18
+ end
19
+
20
+ end
21
+
22
+ end
@@ -1,21 +1,23 @@
1
1
  require 'sinatra/base'
2
2
  require 'deas/error_handler'
3
+ require 'deas/server_data'
3
4
 
4
5
  module Deas
6
+
5
7
  module SinatraApp
6
8
 
7
9
  def self.new(server_config)
10
+ # This is generic server initialization stuff. Eventually do this in the
11
+ # server's initialization logic more like Sanford does.
8
12
  server_config.validate!
13
+ server_data = ServerData.new(server_config.to_hash)
9
14
 
10
15
  Sinatra.new do
11
-
12
16
  # built-in settings
13
- set :environment, server_config.env
14
- set :root, server_config.root
15
-
16
- set :public_folder, server_config.public_root
17
- set :views, server_config.views_root
18
-
17
+ set :environment, server_config.env
18
+ set :root, server_config.root
19
+ set :public_folder, server_config.public_root
20
+ set :views, server_config.views_root
19
21
  set :dump_errors, server_config.dump_errors
20
22
  set :method_override, server_config.method_override
21
23
  set :sessions, server_config.sessions
@@ -24,18 +26,18 @@ module Deas
24
26
  set :default_encoding, server_config.default_encoding
25
27
  set :logging, false
26
28
 
27
- # raise_errors and show_exceptions prevent Deas error handlers from
28
- # being called and Deas' logging doesn't finish. They should always be
29
- # false.
29
+ # TODO: sucks to have to do this but b/c or Rack there is no better way
30
+ # to make the server data available to middleware. We should remove this
31
+ # once we remove Sinatra. Whatever rack app implemenation will needs to
32
+ # provide the server data or maybe the server data *will be* the rack app.
33
+ # Not sure right now, just jotting down notes.
34
+ set :deas_server_data, server_data
35
+
36
+ # raise_errors and show_exceptions prevent Deas error handlers from being
37
+ # called and Deas' logging doesn't finish. They should always be false.
30
38
  set :raise_errors, false
31
39
  set :show_exceptions, false
32
40
 
33
- # custom settings
34
- set :deas_error_procs, server_config.error_procs
35
- set :logger, server_config.logger
36
- set :router, server_config.router
37
- set :template_source, server_config.template_source
38
-
39
41
  # TODO: rework with `server_config.default_encoding` once we move off of using Sinatra
40
42
  # TODO: could maybe move into a deas-json mixin once off of Sinatra
41
43
  # Add charset to json content type responses - by default only added to these:
@@ -47,20 +49,39 @@ module Deas
47
49
 
48
50
  # routes
49
51
  server_config.routes.each do |route|
50
- send(route.method, route.path){ route.run(self) }
52
+ # TODO: `self` is the sinatra_call; eventually stop sending it
53
+ # (part of phasing out Sinatra)
54
+ send(route.method, route.path){ route.run(server_data, self) }
51
55
  end
52
56
 
53
57
  # error handling
54
58
  not_found do
59
+ # `self` is the sinatra call in this context
55
60
  env['sinatra.error'] ||= Sinatra::NotFound.new
56
- ErrorHandler.run(env['sinatra.error'], self, settings.deas_error_procs)
61
+ ErrorHandler.run(env['sinatra.error'], {
62
+ :server_data => server_data,
63
+ :request => self.request,
64
+ :response => self.response,
65
+ :handler_class => self.request.env['deas.handler_class'],
66
+ :handler => self.request.env['deas.handler'],
67
+ :params => self.request.env['deas.params'],
68
+ })
57
69
  end
58
70
  error do
59
- ErrorHandler.run(env['sinatra.error'], self, settings.deas_error_procs)
71
+ # `self` is the sinatra call in this context
72
+ ErrorHandler.run(env['sinatra.error'], {
73
+ :server_data => server_data,
74
+ :request => self.request,
75
+ :response => self.response,
76
+ :handler_class => self.request.env['deas.handler_class'],
77
+ :handler => self.request.env['deas.handler'],
78
+ :params => self.request.env['deas.params'],
79
+ })
60
80
  end
61
81
 
62
82
  end
63
83
  end
64
84
 
65
85
  end
86
+
66
87
  end
@@ -1,3 +1,3 @@
1
1
  module Deas
2
- VERSION = "0.36.0"
2
+ VERSION = "0.37.0"
3
3
  end
@@ -1,6 +1,31 @@
1
1
  require 'assert/factory'
2
+ require 'deas/logger'
3
+ require 'deas/router'
4
+ require 'deas/server_data'
5
+ require 'deas/template_source'
6
+ require 'test/support/fake_sinatra_call'
2
7
 
3
8
  module Factory
4
9
  extend Assert::Factory
5
10
 
11
+ def self.exception(klass = nil, message = nil)
12
+ klass ||= StandardError
13
+ message ||= Factory.text
14
+ exception = nil
15
+ begin; raise(klass, message); rescue klass => exception; end
16
+ exception
17
+ end
18
+
19
+ def self.server_data(opts = nil)
20
+ Deas::ServerData.new({
21
+ :logger => Deas::NullLogger.new,
22
+ :router => Deas::Router.new,
23
+ :template_source => Deas::NullTemplateSource.new
24
+ }.merge(opts || {}))
25
+ end
26
+
27
+ def self.sinatra_call(settings = nil)
28
+ FakeSinatraCall.new(settings)
29
+ end
30
+
6
31
  end
@@ -1,7 +1,4 @@
1
1
  require 'ostruct'
2
- require 'deas/logger'
3
- require 'deas/router'
4
- require 'deas/template_source'
5
2
 
6
3
  class FakeSinatraCall
7
4
 
@@ -11,7 +8,7 @@ class FakeSinatraCall
11
8
  attr_accessor :request, :response, :params, :logger, :router, :session
12
9
  attr_accessor :settings
13
10
 
14
- def initialize(settings = {})
11
+ def initialize(settings = nil)
15
12
  @request = FakeRequest.new('GET','/something', {}, OpenStruct.new)
16
13
  @response = FakeResponse.new
17
14
  @session = @request.session
@@ -25,10 +22,8 @@ class FakeSinatraCall
25
22
  @headers = {}
26
23
 
27
24
  @settings = OpenStruct.new({
28
- :logger => @logger,
29
- :router => @router,
30
- :template_source => @template_source
31
- }.merge(settings))
25
+ :deas_server_data => Factory.server_data
26
+ }.merge(settings || {}))
32
27
  end
33
28
 
34
29
  def halt(*args)
@@ -10,12 +10,12 @@ class DeasTestServer
10
10
 
11
11
  set :a_setting, 'something'
12
12
 
13
- error do |exception|
13
+ error do |exception, context|
14
14
  case exception
15
15
  when Sinatra::NotFound
16
- halt 404, "Couldn't be found"
16
+ [404, "Couldn't be found"]
17
17
  when Exception
18
- halt 500, "Oops, something went wrong"
18
+ [500, "Oops, something went wrong"]
19
19
  end
20
20
  end
21
21
 
@@ -1,106 +1,155 @@
1
1
  require 'assert'
2
2
  require 'deas/error_handler'
3
3
 
4
- require 'test/support/fake_sinatra_call'
5
-
6
4
  class Deas::ErrorHandler
7
5
 
8
6
  class UnitTests < Assert::Context
9
7
  desc "Deas::ErrorHandler"
10
8
  setup do
11
- @exception = RuntimeError.new
12
- @fake_sinatra_call = FakeSinatraCall.new
13
- @error_handler = Deas::ErrorHandler.new(@exception, @fake_sinatra_call, [])
9
+ # always make sure there are multiple error procs or tests can be false
10
+ # positives
11
+ @error_proc_spies = (Factory.integer(3) + 1).times.map{ ErrorProcSpy.new }
12
+ @server_data = Factory.server_data(:error_procs => @error_proc_spies)
13
+ @request = Factory.string
14
+ @response = Factory.string
15
+ @handler_class = Factory.string
16
+ @handler = Factory.string
17
+ @params = Factory.string
18
+
19
+ @context_hash = {
20
+ :server_data => @server_data,
21
+ :request => @request,
22
+ :response => @response,
23
+ :handler_class => @handler_class,
24
+ :handler => @handler,
25
+ :params => @params,
26
+ }
27
+
28
+ @handler_class = Deas::ErrorHandler
14
29
  end
15
- subject{ @error_handler }
30
+ subject{ @handler_class }
16
31
 
17
- should have_instance_methods :run
18
- should have_class_methods :run
32
+ should have_imeths :run
19
33
 
20
34
  end
21
35
 
22
- class RunTests < UnitTests
23
- desc "run"
36
+ class InitTests < UnitTests
37
+ desc "when init"
24
38
  setup do
25
- @error_procs = [ proc do |exception|
26
- settings.exception_that_occurred = exception
27
- "my return value"
28
- end ]
39
+ @exception = Factory.exception
40
+ @error_handler = @handler_class.new(@exception, @context_hash)
41
+ end
42
+ subject{ @error_handler }
43
+
44
+ should have_readers :exception, :context, :error_procs
45
+ should have_imeths :run
46
+
47
+ should "know its attrs" do
48
+ assert_equal @exception, subject.exception
49
+
50
+ exp = Context.new(@context_hash)
51
+ assert_equal exp, subject.context
29
52
 
30
- @error_handler = Deas::ErrorHandler.new(@exception, @fake_sinatra_call, @error_procs)
53
+ exp = @server_data.error_procs.reverse
54
+ assert_equal exp, subject.error_procs
55
+ end
56
+
57
+ end
58
+
59
+ class RunTests < InitTests
60
+ desc "and run"
61
+ setup do
31
62
  @response = @error_handler.run
32
63
  end
33
64
 
34
- should "run the proc in the context of the app" do
35
- assert_equal @exception, @fake_sinatra_call.settings.exception_that_occurred
65
+ should "call each of its procs" do
66
+ subject.error_procs.each do |proc_spy|
67
+ assert_true proc_spy.called
68
+ assert_equal subject.exception, proc_spy.exception
69
+ assert_equal subject.context, proc_spy.context
70
+ end
36
71
  end
37
72
 
38
- should "return whatever the proc returns" do
39
- assert_equal "my return value", @response
73
+ should "return the last non-nil response" do
74
+ assert_nil @response
75
+
76
+ exp = Factory.string
77
+ subject.error_procs.first.response = exp
78
+ assert_equal exp, subject.run
40
79
  end
41
80
 
42
81
  end
43
82
 
44
- class RunWithMultipleProcsTests < UnitTests
45
- desc "run with multiple procs"
83
+ class RunWithProcsThatRaiseTests < InitTests
84
+ desc "and run with procs that raise exceptions"
46
85
  setup do
47
- @error_procs = [
48
- proc do |exception|
49
- settings.first_proc_run = true
50
- 'first'
51
- end,
52
- proc do |exception|
53
- settings.second_proc_run = true
54
- 'second'
55
- end,
56
- proc do |exception|
57
- settings.third_proc_run = true
58
- nil
59
- end
60
- ]
61
-
62
- @error_handler = Deas::ErrorHandler.new(@exception, @fake_sinatra_call, @error_procs)
63
- @response = @error_handler.run
86
+ @first_exception, @last_exception = Factory.exception, Factory.exception
87
+ @error_handler.error_procs.first.raise_exception = @first_exception
88
+ @error_handler.error_procs.last.raise_exception = @last_exception
89
+
90
+ @error_handler.run
64
91
  end
65
92
 
66
- should "run all the error procs" do
67
- assert_equal true, @fake_sinatra_call.settings.first_proc_run
68
- assert_equal true, @fake_sinatra_call.settings.second_proc_run
69
- assert_equal true, @fake_sinatra_call.settings.third_proc_run
93
+ should "call each of its procs" do
94
+ subject.error_procs.each{ |proc_spy| assert_true proc_spy.called }
70
95
  end
71
96
 
72
- should "return the last non-nil response" do
73
- assert_equal 'second', @response
97
+ should "call each proc with the most recently raised exception" do
98
+ assert_equal @exception, @error_handler.error_procs.first.exception
99
+ assert_equal @first_exception, @error_handler.error_procs.last.exception
100
+ end
101
+
102
+ should "alter the handler's exception to be the last raised exception" do
103
+ assert_equal @last_exception, subject.exception
74
104
  end
75
105
 
76
106
  end
77
107
 
78
- class RunWithProcsThatHaltTests < UnitTests
79
- desc "run with a proc that halts"
108
+ class ContextTests < UnitTests
109
+ desc "Context"
80
110
  setup do
81
- @error_procs = [
82
- proc do |exception|
83
- settings.first_proc_run = true
84
- halt 401
85
- end,
86
- proc do |exception|
87
- settings.second_proc_run = true
88
- end
89
- ]
90
-
91
- @error_handler = Deas::ErrorHandler.new(@exception, @fake_sinatra_call, @error_procs)
92
- @response = catch(:halt){ @error_handler.run }
111
+ @context = Context.new(@context_hash)
112
+ end
113
+ subject{ @context }
114
+
115
+ should have_readers :server_data
116
+ should have_readers :request, :response, :handler_class, :handler, :params
117
+
118
+ should "know its attributes" do
119
+ assert_equal @context_hash[:server_data], subject.server_data
120
+ assert_equal @context_hash[:request], subject.request
121
+ assert_equal @context_hash[:response], subject.response
122
+ assert_equal @context_hash[:handler_class], subject.handler_class
123
+ assert_equal @context_hash[:handler], subject.handler
124
+ assert_equal @context_hash[:params], subject.params
93
125
  end
94
126
 
95
- should "run error procs until one halts" do
96
- assert_equal true, @fake_sinatra_call.settings.first_proc_run
97
- assert_nil @fake_sinatra_call.settings.second_proc_run
127
+ should "know if it equals another context" do
128
+ exp = Context.new(@context_hash)
129
+ assert_equal exp, subject
130
+
131
+ exp = Context.new({})
132
+ assert_not_equal exp, subject
98
133
  end
99
134
 
100
- should "return whatever was halted" do
101
- assert_equal [ 401 ], @response
135
+ end
136
+
137
+ class ErrorProcSpy
138
+ attr_reader :called, :exception, :context
139
+ attr_accessor :response, :raise_exception
140
+
141
+ def initialize
142
+ @called = false
102
143
  end
103
144
 
145
+ def call(exception, context)
146
+ @called = true
147
+ @exception = exception
148
+ @context = context
149
+
150
+ raise self.raise_exception if self.raise_exception
151
+ @response
152
+ end
104
153
  end
105
154
 
106
155
  end
@@ -3,7 +3,6 @@ require 'deas/handler_proxy'
3
3
 
4
4
  require 'deas/exceptions'
5
5
  require 'deas/sinatra_runner'
6
- require 'test/support/fake_sinatra_call'
7
6
  require 'test/support/view_handlers'
8
7
 
9
8
  class Deas::HandlerProxy
@@ -39,41 +38,43 @@ class Deas::HandlerProxy
39
38
 
40
39
  Assert.stub(@proxy, :handler_class){ EmptyViewHandler }
41
40
 
42
- @fake_sinatra_call = FakeSinatraCall.new
43
- @proxy.run(@fake_sinatra_call)
41
+ @server_data = Factory.server_data
42
+ @fake_sinatra_call = Factory.sinatra_call
43
+ @proxy.run(@server_data, @fake_sinatra_call)
44
44
  end
45
45
 
46
46
  should "build and run a sinatra runner" do
47
47
  assert_equal subject.handler_class, @runner_spy.handler_class
48
48
 
49
49
  exp_args = {
50
- :sinatra_call => @fake_sinatra_call,
51
- :request => @fake_sinatra_call.request,
52
- :response => @fake_sinatra_call.response,
53
- :session => @fake_sinatra_call.session,
54
- :params => @fake_sinatra_call.params,
55
- :logger => @fake_sinatra_call.settings.logger,
56
- :router => @fake_sinatra_call.settings.router,
57
- :template_source => @fake_sinatra_call.settings.template_source
50
+ :sinatra_call => @fake_sinatra_call,
51
+ :request => @fake_sinatra_call.request,
52
+ :response => @fake_sinatra_call.response,
53
+ :session => @fake_sinatra_call.session,
54
+ :params => @fake_sinatra_call.params,
55
+ :logger => @server_data.logger,
56
+ :router => @server_data.router,
57
+ :template_source => @server_data.template_source
58
58
  }
59
59
  assert_equal exp_args, @runner_spy.args
60
60
 
61
61
  assert_true @runner_spy.run_called
62
62
  end
63
63
 
64
- should "add the runner params to the request env" do
64
+ should "add data to the request env to make it available to Rack" do
65
+ exp = subject.handler_class
66
+ assert_equal exp, @fake_sinatra_call.request.env['deas.handler_class']
67
+
68
+ exp = @runner_spy.handler
69
+ assert_equal exp, @fake_sinatra_call.request.env['deas.handler']
70
+
65
71
  exp = @runner_spy.params
66
72
  assert_equal exp, @fake_sinatra_call.request.env['deas.params']
67
73
  end
68
74
 
69
- should "add the handler class name to the request env" do
70
- exp = subject.handler_class.name
71
- assert_equal exp, @fake_sinatra_call.request.env['deas.handler_class_name']
72
- end
73
-
74
- should "log the handler and params" do
75
+ should "log the handler class name and the params" do
75
76
  exp_msgs = [
76
- " Handler: #{subject.handler_class}",
77
+ " Handler: #{subject.handler_class.name}",
77
78
  " Params: #{@runner_spy.params.inspect}"
78
79
  ]
79
80
  assert_equal exp_msgs, @fake_sinatra_call.request.logging_msgs
@@ -84,7 +85,7 @@ class Deas::HandlerProxy
84
85
  class SinatraRunnerSpy
85
86
 
86
87
  attr_reader :run_called
87
- attr_reader :handler_class, :args
88
+ attr_reader :handler_class, :handler, :args
88
89
  attr_reader :sinatra_call
89
90
  attr_reader :request, :response, :session, :params
90
91
  attr_reader :logger, :router, :template_source
@@ -94,15 +95,17 @@ class Deas::HandlerProxy
94
95
  end
95
96
 
96
97
  def build(handler_class, args)
97
- @handler_class, @args = handler_class, args
98
-
99
- @sinatra_call = args[:sinatra_call]
100
- @request = args[:request]
101
- @response = args[:response]
102
- @session = args[:session]
103
- @params = args[:params]
104
- @logger = args[:logger]
105
- @router = args[:router]
98
+ @handler_class = handler_class
99
+ @handler = handler_class.new(self)
100
+ @args = args
101
+
102
+ @sinatra_call = args[:sinatra_call]
103
+ @request = args[:request]
104
+ @response = args[:response]
105
+ @session = args[:session]
106
+ @params = args[:params]
107
+ @logger = args[:logger]
108
+ @router = args[:router]
106
109
  @template_source = args[:template_source]
107
110
  end
108
111