deas 0.43.3 → 0.43.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA512:
3
- data.tar.gz: b170992c80b97715972d5a7bd749a617cdfe821c0cb8fe36e106a2a626ea38adfbe544c719dc211910b3711151b58e9cab7db5e0a6709519f6ad297634030497
4
- metadata.gz: fdffbb2ae1dad2bae870411a2eac9b8ef8d57c3a52b3751667aa8d1fd3cabe704b516388fcc2913ddb48962aa22ad88d196a6a051567ed42bef041b444a2ea01
3
+ metadata.gz: 0408c659dda15d5dabe1982e6a062e9d9021d38cdb5068aeb96cd46298341a740b4c73875c59f7d39a538b93152f92b57fbf87dd2d2476d5091905286301976a
4
+ data.tar.gz: e6af545cc1ed6365fd6825e6ceb5d2490311ca67a86bdb261e42590d72593e20891fde53ebe7b718ccc4bf1ba5b7997aa60a61dbe92f1c4d810838d7390587f9
5
5
  SHA1:
6
- data.tar.gz: c8540720fc644e4b104147aae617d88805a5d772
7
- metadata.gz: aeaae586a530fc712c24f179c44729382c6ffe24
6
+ metadata.gz: cccc4cbf0e9a8ee8e5cdfbd597bbb3d348d2bbac
7
+ data.tar.gz: d29bf90d2085ccaca87d0059e78b2a8a658ad287
data/Gemfile CHANGED
@@ -2,4 +2,5 @@ source "https://rubygems.org"
2
2
 
3
3
  gemspec
4
4
 
5
- gem 'pry', "~> 0.9.0"
5
+ gem 'pry', "~> 0.9.0"
6
+ gem 'rack', " = 1.6.8"
@@ -18,8 +18,8 @@ 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_development_dependency("assert", ["~> 2.16.1"])
22
- gem.add_development_dependency("assert-rack-test", ["~> 1.0.4"])
21
+ gem.add_development_dependency("assert", ["~> 2.16.3"])
22
+ gem.add_development_dependency("assert-rack-test", ["~> 1.0.5"])
23
23
 
24
24
  gem.add_dependency("much-plugin", ["~> 0.2.0"])
25
25
  gem.add_dependency("rack", ["~> 1.1"])
@@ -26,7 +26,7 @@ module Deas
26
26
  error_proc.call(@exception, @context)
27
27
  rescue StandardError => proc_exception
28
28
  @exception = proc_exception
29
- response = nil # reset response
29
+ response = nil # reset response
30
30
  end
31
31
  response || result
32
32
  end
@@ -35,18 +35,18 @@ module Deas
35
35
  class Context
36
36
 
37
37
  attr_reader :server_data
38
- attr_reader :request, :response, :handler_class, :handler
39
- attr_reader :params, :splat, :route_path
38
+ attr_reader :request, :response
39
+ attr_reader :route_path, :handler_class, :handler, :params, :splat
40
40
 
41
41
  def initialize(args)
42
42
  @server_data = args.fetch(:server_data)
43
43
  @request = args.fetch(:request)
44
44
  @response = args.fetch(:response)
45
+ @route_path = args.fetch(:route_path)
45
46
  @handler_class = args.fetch(:handler_class)
46
47
  @handler = args.fetch(:handler)
47
48
  @params = args.fetch(:params)
48
49
  @splat = args.fetch(:splat)
49
- @route_path = args.fetch(:route_path)
50
50
  end
51
51
 
52
52
  def ==(other)
@@ -55,10 +55,10 @@ module Deas
55
55
  self.handler_class == other.handler_class &&
56
56
  self.request == other.request &&
57
57
  self.response == other.response &&
58
+ self.route_path == other.route_path &&
58
59
  self.handler == other.handler &&
59
60
  self.params == other.params &&
60
- self.splat == other.splat &&
61
- self.route_path == other.route_path
61
+ self.splat == other.splat
62
62
  else
63
63
  super
64
64
  end
@@ -33,8 +33,8 @@ module Deas
33
33
  :router => server_data.router,
34
34
  :template_source => server_data.template_source,
35
35
  :request => request_data.request,
36
- :params => request_data.params,
37
- :route_path => request_data.route_path
36
+ :route_path => request_data.route_path,
37
+ :params => request_data.params
38
38
  })
39
39
 
40
40
  runner.request.env.tap do |env|
@@ -42,17 +42,17 @@ module Deas
42
42
  # this is specifically needed by the Logging middleware
43
43
  # this is also needed by the Sinatra error handlers so they can provide
44
44
  # error context. This may change when we eventually remove Sinatra.
45
+ env['deas.route_path'] = runner.route_path
45
46
  env['deas.handler_class'] = self.handler_class
46
47
  env['deas.handler'] = runner.handler
47
48
  env['deas.params'] = runner.params
48
49
  env['deas.splat'] = runner.splat
49
- env['deas.route_path'] = runner.route_path
50
50
 
51
51
  # this handles the verbose logging (it is a no-op if summary logging)
52
+ env['deas.logging'].call " Route: #{runner.route_path.inspect}"
52
53
  env['deas.logging'].call " Handler: #{self.handler_class.name}"
53
54
  env['deas.logging'].call " Params: #{runner.params.inspect}"
54
55
  env['deas.logging'].call " Splat: #{runner.splat.inspect}" if !runner.splat.nil?
55
- env['deas.logging'].call " Route: #{runner.route_path.inspect}"
56
56
  end
57
57
 
58
58
  runner.run
@@ -4,16 +4,16 @@ require 'sinatra/base'
4
4
  module Deas
5
5
 
6
6
  module Logging
7
- def self.middleware_args(verbose)
8
- verbose ? [VerboseLogging] : [SummaryLogging]
7
+ def self.middleware_type(verbose)
8
+ verbose ? VerboseLogging : SummaryLogging
9
9
  end
10
10
  end
11
11
 
12
12
  class BaseLogging
13
13
 
14
- def initialize(app)
14
+ def initialize(app, logger)
15
15
  @app = app
16
- @logger = @app.settings.deas_server_data.logger
16
+ @logger = logger
17
17
  end
18
18
 
19
19
  # The Rack call interface. The receiver acts as a prototype and runs
@@ -8,21 +8,21 @@ module Deas
8
8
  # decouple the rack app from the handlers (we can use any rack app as long
9
9
  # as they provide this data).
10
10
 
11
- attr_reader :request, :response, :params, :route_path
11
+ attr_reader :request, :response, :route_path, :params
12
12
 
13
13
  def initialize(args)
14
14
  @request = args[:request]
15
15
  @response = args[:response]
16
- @params = args[:params]
17
16
  @route_path = args[:route_path]
17
+ @params = args[:params]
18
18
  end
19
19
 
20
20
  def ==(other_request_data)
21
21
  if other_request_data.kind_of?(RequestData)
22
22
  self.request == other_request_data.request &&
23
23
  self.response == other_request_data.response &&
24
- self.params == other_request_data.params &&
25
- self.route_path == other_request_data.route_path
24
+ self.route_path == other_request_data.route_path &&
25
+ self.params == other_request_data.params
26
26
  else
27
27
  super
28
28
  end
@@ -7,9 +7,17 @@ module Deas
7
7
  class Router
8
8
 
9
9
  DEFAULT_REQUEST_TYPE_NAME = 'default'.freeze
10
+ ALLOW_TRAILING_SLASHES = 'allow'.freeze
11
+ REMOVE_TRAILING_SLASHES = 'remove'.freeze
12
+ SLASH = '/'.freeze
13
+
14
+ VALID_TRAILING_SLASHES_VALUES = [
15
+ ALLOW_TRAILING_SLASHES,
16
+ REMOVE_TRAILING_SLASHES
17
+ ].freeze
10
18
 
11
19
  attr_reader :request_types, :urls, :routes, :definitions
12
- attr_reader :escape_query_value_proc
20
+ attr_reader :trailing_slashes, :escape_query_value_proc
13
21
 
14
22
  def initialize(&block)
15
23
  @request_types = []
@@ -24,6 +32,26 @@ module Deas
24
32
  @view_handler_ns
25
33
  end
26
34
 
35
+ def allow_trailing_slashes
36
+ @trailing_slashes = ALLOW_TRAILING_SLASHES
37
+ end
38
+
39
+ def allow_trailing_slashes_set?
40
+ @trailing_slashes == ALLOW_TRAILING_SLASHES
41
+ end
42
+
43
+ def remove_trailing_slashes
44
+ @trailing_slashes = REMOVE_TRAILING_SLASHES
45
+ end
46
+
47
+ def remove_trailing_slashes_set?
48
+ @trailing_slashes == REMOVE_TRAILING_SLASHES
49
+ end
50
+
51
+ def trailing_slashes_set?
52
+ VALID_TRAILING_SLASHES_VALUES.include?(@trailing_slashes)
53
+ end
54
+
27
55
  def escape_query_value(&block)
28
56
  raise(ArgumentError, "no block given") unless block
29
57
  @escape_query_value_proc = block
@@ -109,9 +137,26 @@ module Deas
109
137
  true
110
138
  end
111
139
 
140
+ def validate_trailing_slashes!
141
+ if self.trailing_slashes == REMOVE_TRAILING_SLASHES
142
+ paths = []
143
+ all_missing = self.routes.inject(true) do |result, route|
144
+ paths << route.path if route.path[-1..-1] == SLASH
145
+ result && route.path[-1..-1] != SLASH
146
+ end
147
+ if !all_missing
148
+ msg = "all route paths must *not* end with a \"/\", but these do:\n"\
149
+ "#{paths.join("\n")}"
150
+ raise TrailingSlashesError, msg
151
+ end
152
+ end
153
+ true
154
+ end
155
+
112
156
  def validate!
113
157
  self.apply_definitions!
114
158
  self.routes.each(&:validate!)
159
+ self.validate_trailing_slashes!
115
160
  true
116
161
  end
117
162
 
@@ -192,7 +237,8 @@ module Deas
192
237
  Deas::Route.new(http_method, path, proxies).tap{ |r| self.routes.push(r) }
193
238
  end
194
239
 
195
- InvalidSplatError = Class.new(ArgumentError)
240
+ InvalidSplatError = Class.new(ArgumentError)
241
+ TrailingSlashesError = Class.new(RuntimeError)
196
242
 
197
243
  class HandlerProxies
198
244
 
@@ -14,22 +14,22 @@ module Deas
14
14
  DEFAULT_BODY = [].freeze
15
15
 
16
16
  attr_reader :handler_class, :handler
17
+ attr_reader :request, :route_path, :params
17
18
  attr_reader :logger, :router, :template_source
18
- attr_reader :request, :params, :route_path
19
19
 
20
20
  def initialize(handler_class, args = nil)
21
21
  @status, @headers, @body = nil, Rack::Utils::HeaderHash.new, nil
22
22
 
23
+ @handler_class = handler_class
24
+ @handler = @handler_class.new(self)
25
+
23
26
  args ||= {}
27
+ @request = args[:request]
28
+ @route_path = args[:route_path].to_s
29
+ @params = args[:params] || {}
24
30
  @logger = args[:logger] || Deas::NullLogger.new
25
31
  @router = args[:router] || Deas::Router.new
26
32
  @template_source = args[:template_source] || Deas::NullTemplateSource.new
27
- @request = args[:request]
28
- @params = args[:params] || {}
29
- @route_path = args[:route_path].to_s
30
-
31
- @handler_class = handler_class
32
- @handler = @handler_class.new(self)
33
33
  end
34
34
 
35
35
  def splat
@@ -56,8 +56,10 @@ module Deas
56
56
 
57
57
  def body(value = nil)
58
58
  if !value.nil?
59
+ # http://www.rubydoc.info/github/rack/rack/master/file/SPEC#The_Body
60
+ # "The Body must respond to each and must only yield String values"
59
61
  # String#each is a thing in 1.8.7, so account for it here
60
- @body = !value.respond_to?(:each) || value.kind_of?(String) ? [*value] : value
62
+ @body = !value.respond_to?(:each) || value.kind_of?(String) ? [*value.to_s] : value
61
63
  end
62
64
  @body
63
65
  end
@@ -66,6 +68,14 @@ module Deas
66
68
  self.headers['Content-Type'] = get_content_type(extname, params)
67
69
  end
68
70
 
71
+ def set_cookie(name, value, opts = nil)
72
+ Rack::Utils.set_cookie_header!(
73
+ self.headers,
74
+ name,
75
+ (opts || {}).merge(:value => value)
76
+ )
77
+ end
78
+
69
79
  def halt(*args)
70
80
  self.status(args.shift) if args.first.instance_of?(::Fixnum)
71
81
  self.headers(args.shift) if args.first.kind_of?(::Hash)
@@ -6,6 +6,7 @@ require 'deas/router'
6
6
  require 'deas/show_exceptions'
7
7
  require 'deas/sinatra_app'
8
8
  require 'deas/template_source'
9
+ require 'deas/trailing_slashes'
9
10
 
10
11
  module Deas
11
12
 
@@ -194,11 +195,27 @@ module Deas
194
195
  # it is run before any other middleware
195
196
  self.middlewares.unshift([Rack::MethodOverride]) if self.method_override
196
197
 
197
- # append the show exceptions and logging middlewares last. This ensures
198
- # that the logging and exception showing happens just before the app gets
199
- # the request and just after the app sends a response.
198
+ # append the show exceptions and logging middlewares next-to-last. This
199
+ # ensures the logging and exception showing happens just before the
200
+ # optional trailing slashes handling. It should be just before the app
201
+ # gets the request and just after the app sends a response (except for
202
+ # trailing slashes - this should happen inside of the show exceptions
203
+ # and logging behavior).
200
204
  self.middlewares << [Deas::ShowExceptions] if self.show_exceptions
201
- self.middlewares << Deas::Logging.middleware_args(self.verbose_logging)
205
+ self.middlewares << [
206
+ Deas::Logging.middleware_type(self.verbose_logging),
207
+ self.logger
208
+ ]
209
+
210
+ # optionally add the trailing slashes middleware last b/c it should
211
+ # happen inside of show exceptions and logging. we want the behavior
212
+ # to feel like app behavior to the rest of the middleware stack so it
213
+ # needs to be just before the app gest the request and just after the
214
+ # app sends a response.
215
+ if self.router.trailing_slashes_set?
216
+ self.middlewares << [Deas::TrailingSlashes, self.router]
217
+ end
218
+
202
219
  self.middlewares.freeze
203
220
 
204
221
  @valid = true # if it made it this far, its valid!
@@ -43,13 +43,6 @@ module Deas
43
43
  set :environment, server_config.env
44
44
  set :root, server_config.root
45
45
 
46
- # TODO: sucks to have to do this but b/c of Rack there is no better way
47
- # to make the server data available to middleware. We should remove this
48
- # once we remove Sinatra. Whatever rack app implemenation will needs to
49
- # provide the server data or maybe the server data *will be* the rack app.
50
- # Not sure right now, just jotting down notes.
51
- set :deas_server_data, server_data
52
-
53
46
  # static settings - Deas doesn't care about these anymore so just
54
47
  # use some intelligent defaults
55
48
  set :views, server_config.root
@@ -84,8 +77,8 @@ module Deas
84
77
  RequestData.new({
85
78
  :request => request,
86
79
  :response => response,
87
- :params => params,
88
- :route_path => route.path
80
+ :route_path => route.path,
81
+ :params => params
89
82
  })
90
83
  )
91
84
  rescue *STANDARD_ERROR_CLASSES => err
@@ -95,11 +88,11 @@ module Deas
95
88
  :server_data => server_data,
96
89
  :request => request,
97
90
  :response => response,
91
+ :route_path => request.env['deas.route_path'],
98
92
  :handler_class => request.env['deas.handler_class'],
99
93
  :handler => request.env['deas.handler'],
100
94
  :params => request.env['deas.params'],
101
- :splat => request.env['deas.splat'],
102
- :route_path => request.env['deas.route_path']
95
+ :splat => request.env['deas.splat']
103
96
  })
104
97
  end
105
98
  end
@@ -109,7 +102,7 @@ module Deas
109
102
  # `self` is the sinatra call in this context
110
103
  if env['sinatra.error']
111
104
  env['deas.error'] = if env['sinatra.error'].instance_of?(::Sinatra::NotFound)
112
- Deas::NotFound.new(env['PATH_INFO']).tap do |e|
105
+ Deas::NotFound.new("#{env['REQUEST_METHOD']} #{env['PATH_INFO']}").tap do |e|
113
106
  e.set_backtrace(env['sinatra.error'].backtrace)
114
107
  end
115
108
  else
@@ -119,11 +112,11 @@ module Deas
119
112
  :server_data => server_data,
120
113
  :request => request,
121
114
  :response => response,
115
+ :route_path => request.env['deas.route_path'],
122
116
  :handler_class => request.env['deas.handler_class'],
123
117
  :handler => request.env['deas.handler'],
124
118
  :params => request.env['deas.params'],
125
- :splat => request.env['deas.splat'],
126
- :route_path => request.env['deas.route_path']
119
+ :splat => request.env['deas.splat']
127
120
  })
128
121
  end
129
122
  end
@@ -27,8 +27,8 @@ module Deas
27
27
  :router => a.delete(:router),
28
28
  :template_source => a.delete(:template_source),
29
29
  :request => a.delete(:request),
30
- :params => NormalizedParams.new(a.delete(:params) || {}).value,
31
- :route_path => a.delete(:route_path)
30
+ :route_path => a.delete(:route_path),
31
+ :params => NormalizedParams.new(a.delete(:params) || {}).value
32
32
  })
33
33
  @splat = a.delete(:splat)
34
34
  a.each{|key, value| self.handler.send("#{key}=", value) }
@@ -0,0 +1,85 @@
1
+ require 'rack/utils'
2
+
3
+ require 'deas/exceptions'
4
+ require 'deas/router'
5
+
6
+ module Deas
7
+
8
+ class TrailingSlashes
9
+
10
+ module RequireNoHandler; end
11
+ module AllowHandler; end
12
+
13
+ HANDLERS = {
14
+ Deas::Router::REMOVE_TRAILING_SLASHES => RequireNoHandler,
15
+ Deas::Router::ALLOW_TRAILING_SLASHES => AllowHandler
16
+ }
17
+
18
+ def initialize(app, router)
19
+ @app = app
20
+ @router = router
21
+
22
+ if !@router.trailing_slashes_set?
23
+ val = @router.trailing_slashes
24
+ desc = val.nil? ? 'no' : "an invalid (`#{val.inspect}`)"
25
+ raise ArgumentError, "TrailingSlashes middleware is in use but there is "\
26
+ "#{desc} trailing slashes router directive set."
27
+ end
28
+ end
29
+
30
+ # The Rack call interface. The receiver acts as a prototype and runs
31
+ # each request in a clone object unless the +rack.run_once+ variable is
32
+ # set in the environment. Ripped from:
33
+ # http://github.com/rtomayko/rack-cache/blob/master/lib/rack/cache/context.rb
34
+ def call(env)
35
+ if env['rack.run_once']
36
+ call! env
37
+ else
38
+ clone.call! env
39
+ end
40
+ end
41
+
42
+ # The real Rack call interface.
43
+ def call!(env)
44
+ HANDLERS[@router.trailing_slashes].run(env){ @app.call(env) }
45
+ end
46
+
47
+ module RequireNoHandler
48
+
49
+ def self.run(env)
50
+ if env['PATH_INFO'][-1..-1] == Deas::Router::SLASH
51
+ [302, { 'Location' => env['PATH_INFO'][0..-2] }, ['']]
52
+ else
53
+ yield
54
+ end
55
+ end
56
+
57
+ end
58
+
59
+ module AllowHandler
60
+
61
+ def self.run(env)
62
+ status, headers, body = yield
63
+ if env['deas.error'].kind_of?(Deas::NotFound)
64
+ # reset 'deas.error' state
65
+ env['deas.error'] = nil
66
+
67
+ # switching the trailing slash of the path info
68
+ env['PATH_INFO'] = if env['PATH_INFO'][-1..-1] == Deas::Router::SLASH
69
+ env['PATH_INFO'][0..-2]
70
+ else
71
+ env['PATH_INFO']+Deas::Router::SLASH
72
+ end
73
+
74
+ # retry
75
+ yield
76
+ else
77
+ [status, headers, body]
78
+ end
79
+ end
80
+
81
+ end
82
+
83
+ end
84
+
85
+ end