deas 0.42.0 → 0.43.0

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
- SHA1:
3
- metadata.gz: 1105d3943d8f80cbcfd722a4fd5796b015fb7e0c
4
- data.tar.gz: 4847313602ffd9942d2b287f36052c0946e03c06
5
2
  SHA512:
6
- metadata.gz: 431fac2fe7deafdf9523882813e5caf34b57035b5077cf8152270503e6a0c914fa501015a47c5ead7d5c221d788e216389449a087c16167915ed82d769f9deab
7
- data.tar.gz: 45cc5441925322a35fd7ad112ecf5b297113782ff14afb9d621b50735c6ee492ea163ec915e8b8cdef3d0b5da1d9653f0513f35a18aa8564855a6a9cf133b7a7
3
+ data.tar.gz: 3520f4645edbc6b6b89e470cc2025f436e6031989c7c7ebf60f9b2c50ec4ec9e68b938fd0149603c8addd798ccd9fc895a0a68e58b89b12c51e2aad0e6f84f1d
4
+ metadata.gz: ba28b9b16f9011f958849afdb5a774a1f559d5c258447b6f0e3ba551328ebbad70d36d84f462af46cce1fcd9f9fec2ebc5fc57d3da01d86aa9f7d778bc181a7f
5
+ SHA1:
6
+ data.tar.gz: 91a77987b3fb3b6d87a728643150a787b620c04a
7
+ metadata.gz: 69eab40552eaaf3cb49bf1d9dd1bc3f4cadb8533
data/deas.gemspec CHANGED
@@ -22,7 +22,7 @@ Gem::Specification.new do |gem|
22
22
  gem.add_development_dependency("assert-rack-test", ["~> 1.0.4"])
23
23
 
24
24
  gem.add_dependency("much-plugin", ["~> 0.2.0"])
25
- gem.add_dependency("rack", ["~> 1.1"])
25
+ gem.add_dependency("rack", ["~> 1.6.4"])
26
26
  gem.add_dependency("sinatra", ["~> 1.2"])
27
27
 
28
28
  end
@@ -35,25 +35,30 @@ module Deas
35
35
  class Context
36
36
 
37
37
  attr_reader :server_data
38
- attr_reader :request, :response, :handler_class, :handler, :params
38
+ attr_reader :request, :response, :handler_class, :handler
39
+ attr_reader :params, :splat, :route_path
39
40
 
40
41
  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]
42
+ @server_data = args.fetch(:server_data)
43
+ @request = args.fetch(:request)
44
+ @response = args.fetch(:response)
45
+ @handler_class = args.fetch(:handler_class)
46
+ @handler = args.fetch(:handler)
47
+ @params = args.fetch(:params)
48
+ @splat = args.fetch(:splat)
49
+ @route_path = args.fetch(:route_path)
47
50
  end
48
51
 
49
52
  def ==(other)
50
53
  if other.kind_of?(self.class)
51
- self.server_data == other.server_data &&
54
+ self.server_data == other.server_data &&
52
55
  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
56
+ self.request == other.request &&
57
+ self.response == other.response &&
58
+ self.handler == other.handler &&
59
+ self.params == other.params &&
60
+ self.splat == other.splat &&
61
+ self.route_path == other.route_path
57
62
  else
58
63
  super
59
64
  end
@@ -15,29 +15,26 @@ module Deas
15
15
  raise NotImplementedError
16
16
  end
17
17
 
18
- def run(server_data, sinatra_call)
19
- # captures are not part of Deas' intended behavior and route matching
20
- # they are a side-effects of using Sinatra. remove them so they won't
21
- # be relied upon in Deas apps.
22
- sinatra_call.params.delete(:captures)
23
- sinatra_call.params.delete('captures')
24
-
25
- # splat will be provided to the handlers via a special `splat` helper.
26
- # only single splat values are allowed (see router `url` method). this
27
- # takes the last splat value from Sinatra and provides it standalone to
28
- # the runner.
29
- splat_sym_param = sinatra_call.params.delete(:splat)
30
- splat_string_param = sinatra_call.params.delete('splat')
31
- splat_value = (splat_sym_param || splat_string_param || []).last
18
+ def run(server_data, request_data)
19
+ # captures are not part of Deas' intended behavior and route matching -
20
+ # they are a side-effect of using Sinatra. remove them so they won't
21
+ # be relied upon in Deas apps. Remove all of this when Sinatra is removed.
22
+ request_data.params.delete(:captures)
23
+ request_data.params.delete('captures')
24
+
25
+ # splats that Sinatra provides aren't used by Deas - they are a
26
+ # side-effect of using Sinatra. remove them so they won't be relied upon
27
+ # in Deas apps. Remove all of this when Sinatra is removed.
28
+ request_data.params.delete(:splat)
29
+ request_data.params.delete('splat')
32
30
 
33
31
  runner = DeasRunner.new(self.handler_class, {
34
32
  :logger => server_data.logger,
35
33
  :router => server_data.router,
36
34
  :template_source => server_data.template_source,
37
- :request => sinatra_call.request,
38
- :session => sinatra_call.session,
39
- :params => sinatra_call.params,
40
- :splat => splat_value
35
+ :request => request_data.request,
36
+ :params => request_data.params,
37
+ :route_path => request_data.route_path
41
38
  })
42
39
 
43
40
  runner.request.env.tap do |env|
@@ -48,11 +45,14 @@ module Deas
48
45
  env['deas.handler_class'] = self.handler_class
49
46
  env['deas.handler'] = runner.handler
50
47
  env['deas.params'] = runner.params
48
+ env['deas.splat'] = runner.splat
49
+ env['deas.route_path'] = runner.route_path
51
50
 
52
51
  # this handles the verbose logging (it is a no-op if summary logging)
53
52
  env['deas.logging'].call " Handler: #{self.handler_class.name}"
54
53
  env['deas.logging'].call " Params: #{runner.params.inspect}"
55
54
  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
data/lib/deas/logging.rb CHANGED
@@ -4,8 +4,8 @@ require 'sinatra/base'
4
4
  module Deas
5
5
 
6
6
  module Logging
7
- def self.middleware(verbose)
8
- verbose ? VerboseLogging : SummaryLogging
7
+ def self.middleware_args(verbose)
8
+ verbose ? [VerboseLogging] : [SummaryLogging]
9
9
  end
10
10
  end
11
11
 
@@ -102,6 +102,7 @@ module Deas
102
102
  'method' => request.request_method,
103
103
  'path' => request.path,
104
104
  'params' => env['deas.params'],
105
+ 'splat' => env['deas.splat'],
105
106
  'time' => env['deas.time_taken'],
106
107
  'status' => status
107
108
  }
@@ -119,7 +120,7 @@ module Deas
119
120
 
120
121
  module SummaryLine
121
122
  def self.keys
122
- %w{time status method path handler params redir}
123
+ %w{time status method path handler params splat redir}
123
124
  end
124
125
  def self.new(line_attrs)
125
126
  self.keys.select{ |k| line_attrs.key?(k) }.
@@ -0,0 +1,33 @@
1
+ module Deas
2
+
3
+ class RequestData
4
+
5
+ # The rack app uses this to "compile" the request-related data. The goal
6
+ # here is to wrap up these (and any future) request objects into a struct
7
+ # object to make them available to the runner/handler. This is also to
8
+ # decouple the rack app from the handlers (we can use any rack app as long
9
+ # as they provide this data).
10
+
11
+ attr_reader :request, :response, :params, :route_path
12
+
13
+ def initialize(args)
14
+ @request = args[:request]
15
+ @response = args[:response]
16
+ @params = args[:params]
17
+ @route_path = args[:route_path]
18
+ end
19
+
20
+ def ==(other_request_data)
21
+ if other_request_data.kind_of?(RequestData)
22
+ self.request == other_request_data.request &&
23
+ self.response == other_request_data.response &&
24
+ self.params == other_request_data.params &&
25
+ self.route_path == other_request_data.route_path
26
+ else
27
+ super
28
+ end
29
+ end
30
+
31
+ end
32
+
33
+ end
data/lib/deas/route.rb CHANGED
@@ -16,15 +16,13 @@ module Deas
16
16
  end
17
17
  end
18
18
 
19
- def run(server_data, sinatra_call)
20
- type = server_data.router.request_type_name(sinatra_call.request)
21
- proxy = begin
22
- @handler_proxies[type]
19
+ def run(server_data, request_data)
20
+ request_type_name = server_data.router.request_type_name(request_data.request)
21
+ begin
22
+ @handler_proxies[request_type_name].run(server_data, request_data)
23
23
  rescue HandlerProxyNotFound
24
- sinatra_call.halt(404)
24
+ [404, Rack::Utils::HeaderHash.new, []]
25
25
  end
26
- # TODO: eventually stop sending sinatra call (part of phasing out Sinatra)
27
- proxy.run(server_data, sinatra_call)
28
26
  end
29
27
 
30
28
  end
data/lib/deas/router.rb CHANGED
@@ -43,15 +43,15 @@ module Deas
43
43
  end
44
44
 
45
45
  def url(name, path, options = nil)
46
+ if !name.kind_of?(::Symbol)
47
+ raise ArgumentError, "invalid name `#{name.inspect}` - "\
48
+ "named urls must be defined with Symbol names"
49
+ end
46
50
  if !path.kind_of?(::String)
47
51
  raise ArgumentError, "invalid path `#{path.inspect}` - "\
48
52
  "named urls must be defined with String paths"
49
53
  end
50
- if path =~ /\*(?!$)/ # splat not at end of path
51
- raise ArgumentError, "invalid path `#{path.inspect}` - "\
52
- "named urls can only have a single splat at the end of the path"
53
- end
54
- add_url(name.to_sym, path, options || {})
54
+ add_url(name, path, options || {})
55
55
  end
56
56
 
57
57
  def url_for(name, *args)
@@ -179,11 +179,21 @@ module Deas
179
179
  end
180
180
 
181
181
  def add_route(http_method, path, proxies)
182
+ # splat not at end of path OR
183
+ # at end of path without preceding forward slash
184
+ if path =~ /\*(?!$)|[^\/]\*/
185
+ raise InvalidSplatError, "invalid path `#{path.inspect}` - "\
186
+ "routes can only have a single splat and " \
187
+ "it must be the last path segment " \
188
+ "(ie '/something/*')"
189
+ end
182
190
  proxies = HandlerProxies.new(proxies, self.default_request_type_name)
183
191
  require 'deas/route'
184
192
  Deas::Route.new(http_method, path, proxies).tap{ |r| self.routes.push(r) }
185
193
  end
186
194
 
195
+ InvalidSplatError = Class.new(ArgumentError)
196
+
187
197
  class HandlerProxies
188
198
 
189
199
  attr_reader :default_type
data/lib/deas/runner.rb CHANGED
@@ -15,24 +15,27 @@ module Deas
15
15
 
16
16
  attr_reader :handler_class, :handler
17
17
  attr_reader :logger, :router, :template_source
18
- attr_reader :request, :session, :params, :splat
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
23
  args ||= {}
24
- @logger = args[:logger] || Deas::NullLogger.new
25
- @router = args[:router] || Deas::Router.new
24
+ @logger = args[:logger] || Deas::NullLogger.new
25
+ @router = args[:router] || Deas::Router.new
26
26
  @template_source = args[:template_source] || Deas::NullTemplateSource.new
27
27
  @request = args[:request]
28
- @session = args[:session]
29
- @params = args[:params] || {}
30
- @splat = args[:splat]
28
+ @params = args[:params] || {}
29
+ @route_path = args[:route_path].to_s
31
30
 
32
31
  @handler_class = handler_class
33
32
  @handler = @handler_class.new(self)
34
33
  end
35
34
 
35
+ def splat
36
+ @splat ||= parse_splat_value
37
+ end
38
+
36
39
  def run
37
40
  raise NotImplementedError
38
41
  end
@@ -138,6 +141,46 @@ module Deas
138
141
  File.join("#{request.env['rack.url_scheme']}://#{request.env['HTTP_HOST']}", url)
139
142
  end
140
143
 
144
+ def parse_splat_value
145
+ return nil if request.nil? || (path_info = request.env['PATH_INFO']).nil?
146
+
147
+ regex = splat_value_parse_regex
148
+ match = regex.match(path_info)
149
+ if match.nil?
150
+ raise SplatParseError, "could not parse the splat value: " \
151
+ "the PATH_INFO, `#{path_info.inspect}`, " \
152
+ "doesn't match " \
153
+ "the route, `#{self.route_path.inspect}`, " \
154
+ "using the route regex, `#{regex.inspect}`"
155
+ end
156
+ match.captures.first
157
+ end
158
+
159
+ SplatParseError = Class.new(RuntimeError)
160
+
161
+ def splat_value_parse_regex
162
+ # `sub` should suffice as only a single trailing splat is allowed. Replace
163
+ # any splat with a capture placholder so we can extract the splat value.
164
+ /^#{route_path_with_regex_placeholders.sub('*', '(.+?)')}$/
165
+ end
166
+
167
+ def route_path_with_regex_placeholders
168
+ # Replace param values with regex placeholders b/c we don't care what
169
+ # the values are just that "a param value goes here". Use gsub in the odd
170
+ # case a placeholder is used more than once in a route. This is to help
171
+ # ensure matching and capturing splats even on nonsense paths.
172
+ sorted_param_names.inject(self.route_path) do |path, name|
173
+ path.gsub(":#{name}", '.+?')
174
+ end
175
+ end
176
+
177
+ def sorted_param_names
178
+ # Sort longer key names first so they will be processed first. This
179
+ # ensures that shorter param names that compose longer param names won't
180
+ # be subbed in the longer param name's place.
181
+ self.params.keys.sort{ |a, b| b.size <=> a.size }
182
+ end
183
+
141
184
  class NormalizedParams
142
185
 
143
186
  attr_reader :value
data/lib/deas/server.rb CHANGED
@@ -31,7 +31,12 @@ module Deas
31
31
  # TODO: needed while Deas is powered by Sinatra
32
32
  # eventually do an initialize method more like Sanford does
33
33
  def new
34
- Deas::SinatraApp.new(self.config)
34
+ begin
35
+ Deas::SinatraApp.new(self.config)
36
+ rescue Router::InvalidSplatError => e
37
+ # reset the exception backtrace to hide Deas internals
38
+ raise e.class, e.message, caller
39
+ end
35
40
  end
36
41
 
37
42
  def config
@@ -48,44 +53,19 @@ module Deas
48
53
  self.config.root
49
54
  end
50
55
 
51
- def views_path(value = nil)
52
- self.config.views_path = value if !value.nil?
53
- self.config.views_path
54
- end
55
-
56
- def views_root
57
- self.config.views_root
58
- end
59
-
60
- def public_path(value = nil)
61
- self.config.public_path = value if !value.nil?
62
- self.config.public_path
63
- end
64
-
65
- def public_root
66
- self.config.public_root
67
- end
68
-
69
- def default_encoding(value = nil)
70
- self.config.default_encoding = value if !value.nil?
71
- self.config.default_encoding
72
- end
73
-
74
- def set(name, value)
75
- self.config.settings[name.to_sym] = value
76
- end
77
-
78
- def settings
79
- self.config.settings
56
+ def method_override(value = nil)
57
+ self.config.method_override = value if !value.nil?
58
+ self.config.method_override
80
59
  end
81
60
 
82
- def template_helpers(*helper_modules)
83
- helper_modules.each{ |m| self.config.template_helpers << m }
84
- self.config.template_helpers
61
+ def show_exceptions(value = nil)
62
+ self.config.show_exceptions = value if !value.nil?
63
+ self.config.show_exceptions
85
64
  end
86
65
 
87
- def template_helper?(helper_module)
88
- self.config.template_helpers.include?(helper_module)
66
+ def verbose_logging(value = nil)
67
+ self.config.verbose_logging = value if !value.nil?
68
+ self.config.verbose_logging
89
69
  end
90
70
 
91
71
  def use(*args)
@@ -132,94 +112,33 @@ module Deas
132
112
 
133
113
  def url_for(*args, &block); self.router.url_for(*args, &block); end
134
114
 
135
- # flags
136
-
137
- def dump_errors(value = nil)
138
- self.config.dump_errors = value if !value.nil?
139
- self.config.dump_errors
140
- end
141
-
142
- def method_override(value = nil)
143
- self.config.method_override = value if !value.nil?
144
- self.config.method_override
145
- end
146
-
147
- def reload_templates(value = nil)
148
- self.config.reload_templates = value if !value.nil?
149
- self.config.reload_templates
150
- end
151
-
152
- def sessions(value = nil)
153
- self.config.sessions = value if !value.nil?
154
- self.config.sessions
155
- end
156
-
157
- def show_exceptions(value = nil)
158
- self.config.show_exceptions = value if !value.nil?
159
- self.config.show_exceptions
160
- end
161
-
162
- def static_files(value = nil)
163
- self.config.static_files = value if !value.nil?
164
- self.config.static_files
165
- end
166
-
167
- def verbose_logging(value = nil)
168
- self.config.verbose_logging = value if !value.nil?
169
- self.config.verbose_logging
170
- end
171
-
172
115
  end
173
116
 
174
117
  class Config
175
118
 
176
- DEFAULT_ENV = 'development'.freeze
177
- DEFAULT_VIEWS_PATH = 'views'.freeze
178
- DEFAULT_PUBLIC_PATH = 'public'.freeze
179
- DEFAULT_ENCODING = 'utf-8'.freeze
180
-
181
- attr_accessor :env, :root, :views_path, :public_path, :default_encoding
182
- attr_accessor :settings, :template_helpers, :middlewares
183
- attr_accessor :init_procs, :error_procs, :template_source, :logger, :router
119
+ DEFAULT_ENV = 'development'.freeze
184
120
 
185
- attr_accessor :dump_errors, :method_override, :reload_templates
186
- attr_accessor :sessions, :show_exceptions, :static_files
187
- attr_accessor :verbose_logging
121
+ attr_accessor :env, :root
122
+ attr_accessor :method_override, :show_exceptions, :verbose_logging
123
+ attr_accessor :middlewares, :init_procs, :error_procs
124
+ attr_accessor :template_source, :logger, :router
188
125
 
189
126
  def initialize
190
- @env = DEFAULT_ENV
191
- @root = ENV['PWD']
192
- @views_path = DEFAULT_VIEWS_PATH
193
- @public_path = DEFAULT_PUBLIC_PATH
194
- @default_encoding = DEFAULT_ENCODING
195
- @settings = {}
196
- @template_helpers = []
197
- @middlewares = []
198
- @init_procs = []
199
- @error_procs = []
200
- @template_source = nil
201
- @logger = Deas::NullLogger.new
202
- @router = Deas::Router.new
203
-
204
- @dump_errors = false
205
- @method_override = true
206
- @reload_templates = false
207
- @sessions = false
208
- @show_exceptions = false
209
- @static_files = true
210
- @verbose_logging = true
127
+ @env = DEFAULT_ENV
128
+ @root = ENV['PWD']
129
+ @method_override = true
130
+ @show_exceptions = false
131
+ @verbose_logging = true
132
+ @middlewares = []
133
+ @init_procs = []
134
+ @error_procs = []
135
+ @template_source = nil
136
+ @logger = Deas::NullLogger.new
137
+ @router = Deas::Router.new
211
138
 
212
139
  @valid = nil
213
140
  end
214
141
 
215
- def views_root
216
- File.expand_path(@views_path.to_s, @root.to_s)
217
- end
218
-
219
- def public_root
220
- File.expand_path(@public_path.to_s, @root.to_s)
221
- end
222
-
223
142
  def template_source
224
143
  @template_source ||= Deas::NullTemplateSource.new(self.root)
225
144
  end
@@ -242,19 +161,26 @@ module Deas
242
161
  def validate!
243
162
  return @valid if !@valid.nil? # only need to run this once per config
244
163
 
245
- # ensure all user and plugin configs/settings are applied
164
+ # ensure all user and plugin configs are applied
246
165
  self.init_procs.each(&:call)
247
166
  raise Deas::ServerRootError if self.root.nil?
248
167
 
249
168
  # validate the router
250
169
  self.router.validate!
251
170
 
171
+ # TODO: build final middleware stack when building the rack app, not here
172
+ # (once Sinatra is removed)
173
+
174
+ # prepend the method override middleware first. This ensures that the
175
+ # it is run before any other middleware
176
+ self.middlewares.unshift([Rack::MethodOverride]) if self.method_override
177
+
252
178
  # append the show exceptions and logging middlewares last. This ensures
253
179
  # that the logging and exception showing happens just before the app gets
254
180
  # the request and just after the app sends a response.
255
181
  self.middlewares << [Deas::ShowExceptions] if self.show_exceptions
256
- logging_mw_args = [*Deas::Logging.middleware(self.verbose_logging)]
257
- self.middlewares << logging_mw_args
182
+ self.middlewares << Deas::Logging.middleware_args(self.verbose_logging)
183
+ self.middlewares.freeze
258
184
 
259
185
  @valid = true # if it made it this far, its valid!
260
186
  end