deas 0.42.0 → 0.43.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.
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