deas 0.43.3 → 0.43.4

Sign up to get free protection for your applications and to get access to all the features.
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