deas 0.36.0 → 0.37.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.
@@ -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