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.
@@ -6,28 +6,59 @@ module Deas
6
6
  self.new(*args).run
7
7
  end
8
8
 
9
- def initialize(exception, sinatra_call, error_procs)
10
- @exception = exception
11
- @sinatra_call = sinatra_call
9
+ attr_reader :exception, :context, :error_procs
12
10
 
13
- @error_procs = [*error_procs].compact
11
+ def initialize(exception, context_hash)
12
+ @exception = exception
13
+ @context = Context.new(context_hash)
14
+ @error_procs = context_hash[:server_data].error_procs.reverse
14
15
  end
15
16
 
17
+ # The exception that we are generating a response for can change in the case
18
+ # that the configured error proc raises an exception. If this occurs, a
19
+ # response will be generated for that exception, instead of the original
20
+ # one. This is designed to avoid "hidden" errors happening, this way the
21
+ # server will respond and log based on the last exception that occurred.
22
+
16
23
  def run
17
- response = nil
18
- @error_procs.each do |error_proc|
19
- begin
20
- result = @sinatra_call.instance_exec(@exception, &error_proc)
21
- response = result if result
22
- rescue Exception => proc_exception
24
+ @error_procs.inject(nil) do |response, error_proc|
25
+ result = begin
26
+ error_proc.call(@exception, @context)
27
+ rescue StandardError => proc_exception
23
28
  @exception = proc_exception
24
- # reset the response if an exception occurs while evaulating the
25
- # error procs -- a new exception will now be handled by the
26
- # remaining procs
27
- response = nil
29
+ response = nil # reset response
30
+ end
31
+ response || result
32
+ end
33
+ end
34
+
35
+ class Context
36
+
37
+ attr_reader :server_data
38
+ attr_reader :request, :response, :handler_class, :handler, :params
39
+
40
+ def initialize(args)
41
+ @server_data = args[:server_data]
42
+ @request = args[:request]
43
+ @response = args[:response]
44
+ @handler_class = args[:handler_class]
45
+ @handler = args[:handler]
46
+ @params = args[:params]
47
+ end
48
+
49
+ def ==(other)
50
+ if other.kind_of?(self.class)
51
+ self.server_data == other.server_data &&
52
+ self.handler_class == other.handler_class &&
53
+ self.request == other.request &&
54
+ self.response == other.response &&
55
+ self.handler == other.handler &&
56
+ self.params == other.params
57
+ else
58
+ super
28
59
  end
29
60
  end
30
- response
61
+
31
62
  end
32
63
 
33
64
  end
@@ -15,24 +15,32 @@ module Deas
15
15
  raise NotImplementedError
16
16
  end
17
17
 
18
- def run(sinatra_call)
18
+ def run(server_data, sinatra_call)
19
19
  runner = SinatraRunner.new(self.handler_class, {
20
- :sinatra_call => sinatra_call,
21
- :request => sinatra_call.request,
22
- :response => sinatra_call.response,
23
- :session => sinatra_call.session,
24
- :params => sinatra_call.params,
25
- :logger => sinatra_call.settings.logger,
26
- :router => sinatra_call.settings.router,
27
- :template_source => sinatra_call.settings.template_source
20
+ :sinatra_call => sinatra_call,
21
+ :request => sinatra_call.request,
22
+ :response => sinatra_call.response,
23
+ :session => sinatra_call.session,
24
+ :params => sinatra_call.params,
25
+ :logger => server_data.logger,
26
+ :router => server_data.router,
27
+ :template_source => server_data.template_source
28
28
  })
29
29
 
30
- sinatra_call.request.env.tap do |env|
31
- env['deas.params'] = runner.params
32
- env['deas.handler_class_name'] = self.handler_class.name
33
- env['deas.logging'].call " Handler: #{env['deas.handler_class_name']}"
34
- env['deas.logging'].call " Params: #{env['deas.params'].inspect}"
30
+ runner.request.env.tap do |env|
31
+ # make runner data available to Rack (ie middlewares)
32
+ # this is specifically needed by the Logging middleware
33
+ # this is also needed by the Sinatra error handlers so they can provide
34
+ # error context. This may change when we eventually remove Sinatra.
35
+ env['deas.handler_class'] = self.handler_class
36
+ env['deas.handler'] = runner.handler
37
+ env['deas.params'] = runner.params
38
+
39
+ # this handles the verbose logging (it is a no-op if summary logging)
40
+ env['deas.logging'].call " Handler: #{self.handler_class.name}"
41
+ env['deas.logging'].call " Params: #{runner.params.inspect}"
35
42
  end
43
+
36
44
  runner.run
37
45
  end
38
46
 
@@ -13,7 +13,7 @@ module Deas
13
13
 
14
14
  def initialize(app)
15
15
  @app = app
16
- @logger = @app.settings.logger
16
+ @logger = @app.settings.deas_server_data.logger
17
17
  end
18
18
 
19
19
  # The Rack call interface. The receiver acts as a prototype and runs
@@ -101,7 +101,7 @@ module Deas
101
101
  line_attrs = {
102
102
  'method' => request.request_method,
103
103
  'path' => request.path,
104
- 'handler' => env['deas.handler_class_name'],
104
+ 'handler' => env['deas.handler_class'].name,
105
105
  'params' => env['deas.params'],
106
106
  'time' => env['deas.time_taken'],
107
107
  'status' => status
@@ -16,14 +16,15 @@ module Deas
16
16
  end
17
17
  end
18
18
 
19
- def run(sinatra_call)
20
- type = sinatra_call.settings.router.request_type_name(sinatra_call.request)
19
+ def run(server_data, sinatra_call)
20
+ type = server_data.router.request_type_name(sinatra_call.request)
21
21
  proxy = begin
22
22
  @handler_proxies[type]
23
23
  rescue HandlerProxyNotFound
24
24
  sinatra_call.halt(404)
25
25
  end
26
- proxy.run(sinatra_call)
26
+ # TODO: eventually stop sending sinatra call (part of phasing out Sinatra)
27
+ proxy.run(server_data, sinatra_call)
27
28
  end
28
29
 
29
30
  end
@@ -9,220 +9,236 @@ require 'deas/show_exceptions'
9
9
  require 'deas/sinatra_app'
10
10
  require 'deas/template_source'
11
11
 
12
- module Deas; end
13
- module Deas::Server
14
-
15
- class Configuration
16
- include NsOptions::Proxy
17
-
18
- # Sinatra-based options
19
-
20
- option :env, String, :default => 'development'
21
-
22
- option :root, Pathname, :required => true
23
- option :public_root, Pathname
24
- option :views_root, Pathname
25
-
26
- option :dump_errors, NsOptions::Boolean, :default => false
27
- option :method_override, NsOptions::Boolean, :default => true
28
- option :sessions, NsOptions::Boolean, :default => false
29
- option :show_exceptions, NsOptions::Boolean, :default => false
30
- option :static_files, NsOptions::Boolean, :default => true
31
- option :reload_templates, NsOptions::Boolean, :default => false
32
- option :default_encoding, String, :default => 'utf-8'
33
-
34
- # server handling options
35
-
36
- option :verbose_logging, NsOptions::Boolean, :default => true
37
- option :logger, :default => proc{ Deas::NullLogger.new }
38
- option :template_source, :default => proc{ Deas::NullTemplateSource.new }
39
-
40
- attr_accessor :settings, :error_procs, :init_procs, :template_helpers
41
- attr_accessor :middlewares, :router
42
-
43
- def initialize(values = nil)
44
- # these are defaulted here because we want to use the Configuration
45
- # instance `root`. If we define a proc above, we will be using the
46
- # Configuration class `root`, which will not update these options as
47
- # expected.
48
- super((values || {}).merge({
49
- :public_root => proc{ self.root.join('public') },
50
- :views_root => proc{ self.root.join('views') }
51
- }))
52
- @settings = {}
53
- @error_procs, @init_procs, @template_helpers, @middlewares = [], [], [], []
54
- @router = Deas::Router.new
55
- @valid = nil
12
+ module Deas
13
+
14
+ module Server
15
+
16
+ def self.included(receiver)
17
+ receiver.class_eval do
18
+ extend ClassMethods
19
+ include InstanceMethods
20
+ end
56
21
  end
57
22
 
58
- def valid?
59
- !!@valid
23
+ module InstanceMethods
24
+ # TODO: once Deas is no longer powered by Sinatra, this should define an
25
+ # `initialize` method that builds a server instance. Right now there is
26
+ # a `new` class method that builds a SinatraApp which does this init
27
+ # behavior
60
28
  end
61
29
 
62
- # for the config to be considered "valid", a few things need to happen. The
63
- # key here is that this only needs to be done _once_ for each config.
30
+ module ClassMethods
64
31
 
65
- def validate!
66
- return @valid if !@valid.nil? # only need to run this once per config
32
+ # TODO: needed while Deas is powered by Sinatra
33
+ # eventually do an initialize method more like Sanford does
34
+ def new
35
+ Deas::SinatraApp.new(self.configuration)
36
+ end
67
37
 
68
- # ensure all user and plugin configs/settings are applied
69
- self.init_procs.each{ |p| p.call }
70
- raise Deas::ServerRootError if self.root.nil?
38
+ def configuration
39
+ @configuration ||= Configuration.new
40
+ end
71
41
 
72
- # validate the routes
73
- self.routes.each(&:validate!)
42
+ # sinatra settings DSL
74
43
 
75
- # set the :erb :outvar setting if it hasn't been set. this is used
76
- # by template helpers and plugins and needs to be queryable. the actual
77
- # value doesn't matter - it just needs to be set
78
- self.settings[:erb] ||= {}
79
- self.settings[:erb][:outvar] ||= '@_out_buf'
44
+ def env(*args)
45
+ self.configuration.env *args
46
+ end
80
47
 
81
- # append the show exceptions and loggine middlewares last. This ensures
82
- # that the logging and exception showing happens just before the app gets
83
- # the request and just after the app sends a response.
84
- self.middlewares << [Deas::ShowExceptions] if self.show_exceptions
85
- [*Deas::Logging.middleware(self.verbose_logging)].tap do |mw_args|
86
- self.middlewares << mw_args
48
+ def root(*args)
49
+ self.configuration.root *args
87
50
  end
88
51
 
89
- @valid = true # if it made it this far, its valid!
90
- end
52
+ def public_root(*args)
53
+ self.configuration.public_root *args
54
+ end
91
55
 
92
- def urls
93
- self.router.urls
94
- end
56
+ def views_root(*args)
57
+ self.configuration.views_root *args
58
+ end
95
59
 
96
- def routes
97
- self.router.routes
98
- end
60
+ def dump_errors(*args)
61
+ self.configuration.dump_errors *args
62
+ end
99
63
 
100
- end
64
+ def method_override(*args)
65
+ self.configuration.method_override *args
66
+ end
101
67
 
102
- def self.included(receiver)
103
- receiver.class_eval{ extend ClassMethods }
104
- end
68
+ def sessions(*args)
69
+ self.configuration.sessions *args
70
+ end
105
71
 
106
- module ClassMethods
72
+ def show_exceptions(*args)
73
+ self.configuration.show_exceptions *args
74
+ end
107
75
 
108
- def new
109
- Deas::SinatraApp.new(self.configuration)
110
- end
76
+ def static_files(*args)
77
+ self.configuration.static_files *args
78
+ end
111
79
 
112
- def configuration
113
- @configuration ||= Configuration.new
114
- end
80
+ def reload_templates(*args)
81
+ self.configuration.reload_templates *args
82
+ end
115
83
 
116
- # sinatra settings DSL
84
+ # Server handling DSL
117
85
 
118
- def env(*args)
119
- self.configuration.env *args
120
- end
86
+ def init(&block)
87
+ self.configuration.init_procs << block
88
+ end
121
89
 
122
- def root(*args)
123
- self.configuration.root *args
124
- end
90
+ def error(&block)
91
+ self.configuration.error_procs << block
92
+ end
125
93
 
126
- def public_root(*args)
127
- self.configuration.public_root *args
128
- end
94
+ def template_helpers(*helper_modules)
95
+ helper_modules.each{ |m| self.configuration.template_helpers << m }
96
+ self.configuration.template_helpers
97
+ end
129
98
 
130
- def views_root(*args)
131
- self.configuration.views_root *args
132
- end
99
+ def template_helper?(helper_module)
100
+ self.configuration.template_helpers.include?(helper_module)
101
+ end
133
102
 
134
- def dump_errors(*args)
135
- self.configuration.dump_errors *args
136
- end
103
+ def use(*args)
104
+ self.configuration.middlewares << args
105
+ end
137
106
 
138
- def method_override(*args)
139
- self.configuration.method_override *args
140
- end
107
+ def set(name, value)
108
+ self.configuration.settings[name.to_sym] = value
109
+ end
141
110
 
142
- def sessions(*args)
143
- self.configuration.sessions *args
144
- end
111
+ def verbose_logging(*args)
112
+ self.configuration.verbose_logging *args
113
+ end
145
114
 
146
- def show_exceptions(*args)
147
- self.configuration.show_exceptions *args
148
- end
115
+ def logger(*args)
116
+ self.configuration.logger *args
117
+ end
149
118
 
150
- def static_files(*args)
151
- self.configuration.static_files *args
152
- end
119
+ def default_encoding(*args)
120
+ self.configuration.default_encoding *args
121
+ end
153
122
 
154
- def reload_templates(*args)
155
- self.configuration.reload_templates *args
156
- end
123
+ def template_source(*args)
124
+ self.configuration.template_source *args
125
+ end
157
126
 
158
- # Server handling DSL
127
+ # router handling
159
128
 
160
- def init(&block)
161
- self.configuration.init_procs << block
162
- end
129
+ def router(value = nil)
130
+ self.configuration.router = value if !value.nil?
131
+ self.configuration.router
132
+ end
163
133
 
164
- def error(&block)
165
- self.configuration.error_procs << block
166
- end
134
+ def view_handler_ns(*args); self.router.view_handler_ns(*args); end
135
+ def base_url(*args); self.router.base_url(*args); end
167
136
 
168
- def template_helpers(*helper_modules)
169
- helper_modules.each{ |m| self.configuration.template_helpers << m }
170
- self.configuration.template_helpers
171
- end
137
+ def url(*args, &block); self.router.url(*args, &block); end
138
+ def url_for(*args, &block); self.router.url_for(*args, &block); end
172
139
 
173
- def template_helper?(helper_module)
174
- self.configuration.template_helpers.include?(helper_module)
175
- end
140
+ def default_request_type_name(*args); self.router.default_request_type_name(*args); end
141
+ def add_request_type(*args, &block); self.router.add_request_type(*args, &block); end
142
+ def request_type_name(*args); self.router.request_type_name(*args); end
176
143
 
177
- def use(*args)
178
- self.configuration.middlewares << args
179
- end
144
+ def get(*args, &block); self.router.get(*args, &block); end
145
+ def post(*args, &block); self.router.post(*args, &block); end
146
+ def put(*args, &block); self.router.put(*args, &block); end
147
+ def patch(*args, &block); self.router.patch(*args, &block); end
148
+ def delete(*args, &block); self.router.delete(*args, &block); end
180
149
 
181
- def set(name, value)
182
- self.configuration.settings[name.to_sym] = value
183
- end
150
+ def route(*args, &block); self.router.route(*args, &block); end
151
+ def redirect(*args, &block); self.router.redirect(*args, &block); end
184
152
 
185
- def verbose_logging(*args)
186
- self.configuration.verbose_logging *args
187
153
  end
188
154
 
189
- def logger(*args)
190
- self.configuration.logger *args
191
- end
155
+ class Configuration
156
+ include NsOptions::Proxy
192
157
 
193
- def default_encoding(*args)
194
- self.configuration.default_encoding *args
195
- end
158
+ # Sinatra-based options
196
159
 
197
- def template_source(*args)
198
- self.configuration.template_source *args
199
- end
160
+ option :env, String, :default => 'development'
200
161
 
201
- # router handling
162
+ option :root, Pathname, :required => true
163
+ option :public_root, Pathname
164
+ option :views_root, Pathname
202
165
 
203
- def router(value = nil)
204
- self.configuration.router = value if !value.nil?
205
- self.configuration.router
206
- end
166
+ option :dump_errors, NsOptions::Boolean, :default => false
167
+ option :method_override, NsOptions::Boolean, :default => true
168
+ option :sessions, NsOptions::Boolean, :default => false
169
+ option :show_exceptions, NsOptions::Boolean, :default => false
170
+ option :static_files, NsOptions::Boolean, :default => true
171
+ option :reload_templates, NsOptions::Boolean, :default => false
172
+ option :default_encoding, String, :default => 'utf-8'
207
173
 
208
- def view_handler_ns(*args); self.router.view_handler_ns(*args); end
209
- def base_url(*args); self.router.base_url(*args); end
174
+ # server handling options
210
175
 
211
- def url(*args, &block); self.router.url(*args, &block); end
212
- def url_for(*args, &block); self.router.url_for(*args, &block); end
176
+ option :verbose_logging, NsOptions::Boolean, :default => true
177
+ option :logger, :default => proc{ Deas::NullLogger.new }
178
+ option :template_source, :default => proc{ Deas::NullTemplateSource.new }
213
179
 
214
- def default_request_type_name(*args); self.router.default_request_type_name(*args); end
215
- def add_request_type(*args, &block); self.router.add_request_type(*args, &block); end
216
- def request_type_name(*args); self.router.request_type_name(*args); end
180
+ attr_accessor :settings, :init_procs, :error_procs, :template_helpers
181
+ attr_accessor :middlewares, :router
217
182
 
218
- def get(*args, &block); self.router.get(*args, &block); end
219
- def post(*args, &block); self.router.post(*args, &block); end
220
- def put(*args, &block); self.router.put(*args, &block); end
221
- def patch(*args, &block); self.router.patch(*args, &block); end
222
- def delete(*args, &block); self.router.delete(*args, &block); end
183
+ def initialize(values = nil)
184
+ # these are defaulted here because we want to use the Configuration
185
+ # instance `root`. If we define a proc above, we will be using the
186
+ # Configuration class `root`, which will not update these options as
187
+ # expected.
188
+ super((values || {}).merge({
189
+ :public_root => proc{ self.root.join('public') },
190
+ :views_root => proc{ self.root.join('views') }
191
+ }))
192
+ @settings = {}
193
+ @init_procs, @error_procs, @template_helpers, @middlewares = [], [], [], []
194
+ @router = Deas::Router.new
195
+ @valid = nil
196
+ end
197
+
198
+ def urls
199
+ self.router.urls
200
+ end
201
+
202
+ def routes
203
+ self.router.routes
204
+ end
205
+
206
+ def to_hash
207
+ super.merge({
208
+ :error_procs => self.error_procs,
209
+ :router => self.router
210
+ })
211
+ end
212
+
213
+ def valid?
214
+ !!@valid
215
+ end
223
216
 
224
- def route(*args, &block); self.router.route(*args, &block); end
225
- def redirect(*args, &block); self.router.redirect(*args, &block); end
217
+ # for the config to be considered "valid", a few things need to happen. The
218
+ # key here is that this only needs to be done _once_ for each config.
219
+
220
+ def validate!
221
+ return @valid if !@valid.nil? # only need to run this once per config
222
+
223
+ # ensure all user and plugin configs/settings are applied
224
+ self.init_procs.each{ |p| p.call }
225
+ raise Deas::ServerRootError if self.root.nil?
226
+
227
+ # validate the routes
228
+ self.routes.each(&:validate!)
229
+
230
+ # append the show exceptions and logging middlewares last. This ensures
231
+ # that the logging and exception showing happens just before the app gets
232
+ # the request and just after the app sends a response.
233
+ self.middlewares << [Deas::ShowExceptions] if self.show_exceptions
234
+ [*Deas::Logging.middleware(self.verbose_logging)].tap do |mw_args|
235
+ self.middlewares << mw_args
236
+ end
237
+
238
+ @valid = true # if it made it this far, its valid!
239
+ end
240
+
241
+ end
226
242
 
227
243
  end
228
244