scorched 0.19 → 0.20

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
  SHA1:
3
- metadata.gz: 126d0553639f9a33afab401c7084a573a28914c4
4
- data.tar.gz: ca8aaba8a210a5b48ec91be131ec9d5432be489a
3
+ metadata.gz: 5b78f42bbcf1ea4018406b7fcf95f31c78ed1c4e
4
+ data.tar.gz: 3a86547b8e623c089ec67a1a45f65a4ce2b1554d
5
5
  SHA512:
6
- metadata.gz: def1aa2f26ef50000e5a6689a4eeb9f120119968c6604915618d12f3d22211dbdf3e4ba080f4d79525c98848e8729b4b022a843bcab159fb870899efed7511db
7
- data.tar.gz: 35cb01c416ccb4ed8e328476b5a7a1a80b76570e5d0229b9c02e7082f4eb078fdc92e868adcfaf227546761f8827a6d8b658cf970be30ed1dda2ef8608c0d81a
6
+ metadata.gz: 2ff3c8bc993bffc2d5d94682fc53288a6293edfe94f9f7e9341efb08f6af71cd868cc6614541b286add86c2d0c2ff2c80021115957d088c7afc3bd1c8de075e6
7
+ data.tar.gz: e96714a418248738c3bb78d76bed6d8a41b28055bed3d1840bca2e2c1430bfb67960e8e5d6fcb6341dbec244bf06172cd2513d20c031c9fe950d311931a3ab9f
data/CHANGES.md CHANGED
@@ -1,8 +1,18 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
+ _Note that Scorched is yet to reach a v1.0 release. This means breaking changes may still be made. If upgrading Scorched version for your project, review this changelog carefully._
5
+
6
+ ### v0.20
7
+ * _After_ filters are now still processed when a request is halted. This restores the original behaviour prior to version 0.8. This fixes an issue where halting the request after setting flash session data would cause the request to bomb-out.
8
+ * As an extension of the change above, filters can now be marked as _forced_ using the new `force` option, which if true, means the filter will run even if the request is halted within another filter (including within another forced filter). This ensures a particular filter is run for every request. This required changing the public interface for the _filter_ method. The sugar methods _before_, _after_, and _error_ have remained backwards compatible.
9
+ * Named captures are now passed to route proc's as a single hash argument. The previous behaviour was an oversight.
10
+ * Halting within an error filter is now swallowed if the error filter is invoked by an error raised in a before or after filter.
11
+ * The `controller` helper can now be used to map predefined controllers simply by omitting the block, which is now optional. This a more convenient means of mapping controllers as compared to the more barebones `map` method.
12
+ * `log` method now returns the log object. Method arguments are also now optional, meaning you can use the more natural logger interface if you prefer, e.g. `log.info "hello"`.
13
+
4
14
  ### v0.19
5
- * The behaviour of wildcards '*' and '**' (and their _named_ equivalents) have been reverted to their original behviour of matching one or more characters, instead of zero or more. This means `/*` will no longer match `/`. Adding a question mark directly after a wildcard will have that wildcard match zero or more character, instead of one or more. So the pattern `/*?` will match both `/` and `/about`.
15
+ * The behaviour of wildcards `*` and `**` (and their _named_ equivalents) have been reverted to their original behaviour of matching one or more characters, instead of zero or more. This means `/*` will no longer match `/`. Adding a question mark directly after a wildcard will have that wildcard match zero or more character, instead of one or more. So the pattern `/*?` will match both `/` and `/about`.
6
16
 
7
17
  ### v0.18
8
18
  * Redirects now use a 303 or 302 HTTP status code by default, depending on HTTP version (similar logic to Sinatra). Trailing slash redirects (triggered by :strip_trailing_slash) still uses a 307 status.
data/Gemfile CHANGED
File without changes
data/LICENSE CHANGED
File without changes
@@ -88,17 +88,10 @@ You can use nesting and inheritance exclusively, or together, depending on your
88
88
 
89
89
  Controller Helper
90
90
  -----------------
91
- There is a more succinct way of defining sub-controllers, and that's with the `controller` helper. Here's the previous example re-written using the `controller` helper.
91
+ There is a more succinct way of mapping and defining sub-controllers, and that's with the `controller` helper. Here's the previous example cut-down and re-written using the `controller` helper.
92
92
 
93
93
  ``` ruby
94
94
  class MyApp < Scorched::Controller
95
- render_defaults[:dir] = 'views'
96
- render_defaults[:layout] = :main
97
-
98
- conditions[:user] = proc { |usernames|
99
- [*usernames].include? 'bob'
100
- }
101
-
102
95
  get '/' do
103
96
  bold "Hello there"
104
97
  end
@@ -117,7 +110,7 @@ class MyApp < Scorched::Controller
117
110
  end
118
111
  ```
119
112
 
120
- The `controller` helper takes an optional URL pattern as it's first argument, an optional parent class as it's second, and finally a mapping hash as its third optional argument, where you can define a priority, conditions, or override the URL pattern. Of course, the `controller` helper takes a block as well, which defines the body of the new controller class.
113
+ The `controller` helper takes an optional URL pattern as it's first argument, an optional parent class as its second, and finally a mapping hash as its third optional argument, where you can define a priority, conditions, or override the URL pattern. Of course, the `controller` helper takes a block as well, which defines the body of the new controller class.
121
114
 
122
115
  The optional URL pattern defaults to `'/'` which means it's essentially a match-all mapping. In addition, the generated controller has `:auto_pass` set to `true` by default (refer to configuration documentation for more information). This is a handy combination for grouping a set of routes in their own scope, with their own methods, filters, configuration, etc.
123
116
 
@@ -153,6 +146,18 @@ end
153
146
 
154
147
  That example, while serving no practical purpose, hopefully demonstrates how you can combine various constructs with sub-controllers, to come up with DRY creative solutions.
155
148
 
149
+ Finally, the `controller` helper can also be used a shortcut to map one controller to another, where the given class is mapped directly, instead of being the parent of a new controller class. These two examples are essentially equivalent:
150
+
151
+ ``` ruby
152
+ ControllerA << {pattern: '/sub', target: ControllerB}
153
+
154
+ # Or
155
+
156
+ class ControllerA
157
+ controller '/', ControllerB # Note that no block was given
158
+ end
159
+ ```
160
+
156
161
 
157
162
  The Root Controller
158
163
  -------------------
@@ -20,9 +20,9 @@ Halting Requests
20
20
  ----------------
21
21
  There may be instances we're you want to shortcut out-of processing the current request. The `halt` method allows you to do this, though it's worth clarifying its behaviour.
22
22
 
23
- When `halt` is called within a route, it simply exists out of that route, and begins processing any _after_ filters. Halt can also be used within a _before_ or _after_ filter, in which case any remaining filters in the current controller are skipped.
23
+ When `halt` is called within a route, it simply exits out of that route, and begins processing any _after_ filters. Halt can also be used within a _before_ or _after_ filter, in which case any remaining filters in the current controller are skipped, with the exception of _forced_ filters (see documentation on filters).
24
24
 
25
- Calls to `halt` don't propagate up the controller chain. They're local to the controller. A call to `halt` is equivalent to doing a manual `throw :halt`. Calling `halt` is often preferred though because as well as being syntactically sweeter, it can take an optional argument to set the response status and body, which is something you likely want to do when halting a request.
25
+ Calls to `halt` don't propagate up the controller chain. They're caught within the controller they're thrown. A call to `halt` is equivalent to doing a manual `throw :halt`. Calling `halt` is often preferred though because as well as being syntactically sweeter, it can take an optional argument to set the response status and body, which is something you likely want to do when halting a request.
26
26
 
27
27
 
28
28
  Passing Requests
@@ -11,7 +11,7 @@ Before and After filters allow pre- and post-processing of requests. They are ex
11
11
 
12
12
  ```ruby
13
13
  before do
14
- raise Error, "Must be logged in to access this site" unless session[:logged_in] == true
14
+ raise "Must be logged in to access this site" unless session[:logged_in] == true
15
15
  end
16
16
  ```
17
17
 
@@ -31,7 +31,15 @@ after status: 404 do
31
31
  end
32
32
  ```
33
33
 
34
- Your imagination is the only limitation.
34
+ An optional _force_ option exists which ensures the filter is always run, even if another filter halts the request.
35
+
36
+ ```ruby
37
+ after force: true do
38
+ # Close open file handles or something
39
+ end
40
+ ```
41
+
42
+ How you use filters is up to your own imagination and creative problem solving.
35
43
 
36
44
 
37
45
  Error Filters
@@ -40,10 +48,10 @@ Error filters are processed like regular filters, except they're called whenever
40
48
 
41
49
  Error filters can handle exceptions raised from within the request target, as well as those raised within _before_ and _after_ filters. The way error filters have been implemented, allows exceptions raised within the request target to be handled before running the _after_ filters. This means that _after_ filters are still run as long as exceptions that occurred within the request target are handled.
42
50
 
43
- Error filters can target only specific types of exception class, in much the same way as a typical Ruby rescue block.
51
+ Error filters can target only specific types of exception class, in much the same way as a typical Ruby rescue block. In the following example, the error filter is only executed when an uncaught PermissionError exception is raised, and when a made-up `catch_exceptions` condition evaluates to true.
44
52
 
45
53
  ```ruby
46
- error PermissionError do |e|
54
+ error PermissionError, catch_exceptions: true do |e|
47
55
  flash[:error] = "You do not have the appropriate permission to perform that action: #{e.message}"
48
56
  end
49
57
  ```
@@ -21,4 +21,11 @@ When a layout is given, a subsequent call to `render` is made, with the rendered
21
21
 
22
22
  Partials
23
23
  --------
24
- There are cases where you may want a view to be composed of more than just a layout and a single view. The view may contain one or more sub-views, commonly referred to as partials. Scorched makes provisions for this by ignoring the default layout when `render` is called within a view, hence negating the requirement to explicitly override the layout.
24
+ There are cases where you may want a view to be composed of more than just a layout and a single view. The view may contain one or more sub-views, commonly referred to as partials. Scorched makes provisions for this by ignoring the default layout when `render` is called within a view, hence negating the requirement to explicitly override the layout.
25
+
26
+ Helpers
27
+ -------
28
+ No explicit distinction is made between a controller helper and a view helper, as both share the same context. There are however some helper methods intended primarily for views.
29
+
30
+ * `absolute` - Returns the absolute URL of the web application root, with the optional argument joined to the end. For example, if you're application was mounted under example.com/myapp/: `absolute '/about' #=> /myapp/about`
31
+ * `url` - Same as absolute, except returns the full URL, e.g. `url '/about' #=> http://example.com/myapp/about`.
@@ -15,7 +15,7 @@ module Scorched
15
15
  config << {
16
16
  :auto_pass => false, # Automatically _pass_ request back to outer controller if no route matches.
17
17
  :cache_templates => true,
18
- :logger => nil,
18
+ :logger => Logger.new(STDOUT),
19
19
  :show_exceptions => false,
20
20
  :show_http_error_pages => false, # If true, shows the default Scorched HTTP error page.
21
21
  :static_dir => false, # The directory Scorched should serve static files from. Set to false if web server or anything else is serving static files.
@@ -32,7 +32,6 @@ module Scorched
32
32
  }
33
33
 
34
34
  if ENV['RACK_ENV'] == 'development'
35
- config[:logger] = Logger.new(STDOUT)
36
35
  config[:show_exceptions] = true
37
36
  config[:static_dir] = 'public'
38
37
  config[:cache_templates] = false
@@ -136,20 +135,19 @@ module Scorched
136
135
  end
137
136
  alias :<< :map
138
137
 
139
- # Creates a new controller as a sub-class of self (by default), mapping it to self using the provided mapping
140
- # hash if one is provided. Returns the new anonymous controller class.
138
+ # Maps a new ad-hoc or predefined controller.
141
139
  #
142
- # Takes three optional arguments and a block: a pattern, a parent class from which the generated controller class
143
- # inherits from, a mapping hash for setting conditions and so on, and of course a block which defines the
144
- # controller class.
145
- #
146
- # It's worth noting, however obvious, that the resulting class will only be a Scorched::Controller if the parent
147
- # class is, or inherits from, a Scorched::Controller.
148
- def controller(pattern = '/', parent_class = self, **mapping, &block)
149
- c = Class.new(parent_class, &block)
150
- c.config[:auto_pass] = true if parent_class < Scorched::Controller
151
- self << {pattern: pattern, target: c}.merge(mapping)
152
- c
140
+ # If a block is given, creates a new controller as a sub-class of _klass_ (_self_ by default), otherwise maps
141
+ # _klass_ itself. Returns the new anonymous controller class if a block is given, or _klass_ otherwise.
142
+ def controller(pattern = '/', klass = self, **mapping, &block)
143
+ if block_given?
144
+ controller = Class.new(klass, &block)
145
+ controller.config[:auto_pass] = true if klass < Scorched::Controller
146
+ else
147
+ controller = klass
148
+ end
149
+ self << {pattern: pattern, target: controller}.merge(mapping)
150
+ controller
153
151
  end
154
152
 
155
153
  # Generates and returns a new route proc from the given block, and optionally maps said proc using the given args.
@@ -167,7 +165,7 @@ module Scorched
167
165
  # patch(pattern = nil, priority = nil, **conds, &block)
168
166
  def route(pattern = nil, priority = nil, **conds, &block)
169
167
  target = lambda do
170
- response.body = instance_exec(*request.captures, &block)
168
+ response.body = instance_exec(*[request.captures].flatten, &block)
171
169
  response
172
170
  end
173
171
  [*pattern].compact.each do |pattern|
@@ -184,22 +182,31 @@ module Scorched
184
182
  end
185
183
  end
186
184
 
187
- # Defines a filter of +type+. Helper methods are provided as syntactic sugar for each filter type.
188
- # +args+ is used internally by Scorched for passing additional arguments to the filter, such as the exception in
189
- # the case of error blocks.
190
- # :call-seq:
191
- # filter(type, *args, **conds, &block)
192
- # before(*args, **conds, &block)
193
- # after(*args, **conds, &block)
194
- # error(*args, **conds, &block)
195
- def filter(type, *args, **conds, &block)
196
- filters[type.to_sym] << {args: args, conditions: conds, proc: block}
185
+ # Defines a filter of +type+.
186
+ # +args+ is used internally by Scorched for passing additional arguments to some filters, such as the exception in
187
+ # the case of error filters.
188
+ def filter(type, args: nil, force: nil, conditions: nil, **more_conditions, &block)
189
+ more_conditions.merge!(conditions || {})
190
+ filters[type.to_sym] << {args: args, force: force, conditions: more_conditions, proc: block}
197
191
  end
198
192
 
199
- ['before', 'after', 'error'].each do |type|
200
- define_method(type) do |*args, &block|
201
- filter(type, *args, &block)
202
- end
193
+ # Syntactic sugar for defining a before filter.
194
+ # If +force+ is true, the filter is run even if another filter halts the request.
195
+ def before(force: false, **conditions, &block)
196
+ filter(:before, force: force, conditions: conditions, &block)
197
+ end
198
+
199
+ # Syntactic sugar for defining an after filter.
200
+ # If +force+ is true, the filter is run even if another filter halts the request.
201
+ def after(force: false, **conditions, &block)
202
+ filter(:after, force: force, conditions: conditions, &block)
203
+ end
204
+
205
+ # Syntactic sugar for defining an error filter.
206
+ # Takes one or more optional exception classes for which this error filter should handle. Handles all exceptions
207
+ # by default.
208
+ def error(*classes, **conditions, &block)
209
+ filter(:error, args: classes, conditions: conditions, &block)
203
210
  end
204
211
 
205
212
  private
@@ -211,7 +218,7 @@ module Scorched
211
218
  return pattern if Regexp === pattern
212
219
  raise Error, "Can't compile URL of type #{pattern.class}. Must be String or Regexp." unless String === pattern
213
220
  match_to_end = !!pattern.sub!(/\$$/, '') || match_to_end
214
- compiled_pattern = pattern.split(%r{(\*{1,2}\??|(?<!\\):{1,2}[^/*$]+\??)}).each_slice(2).map { |unmatched, match|
221
+ compiled = pattern.split(%r{(\*{1,2}\??|(?<!\\):{1,2}[^/*$]+\??)}).each_slice(2).map { |unmatched, match|
215
222
  Regexp.escape(unmatched) << begin
216
223
  op = (match && match[-1] == '?' && match.chomp!('?')) ? '*' : '+'
217
224
  if %w{* **}.include? match
@@ -223,8 +230,8 @@ module Scorched
223
230
  end
224
231
  end
225
232
  }.join
226
- compiled_pattern << '$' if match_to_end
227
- Regexp.new(compiled_pattern)
233
+ compiled << '$' if match_to_end
234
+ Regexp.new(compiled)
228
235
  end
229
236
  end
230
237
 
@@ -245,13 +252,14 @@ module Scorched
245
252
  @response = Response.new
246
253
  end
247
254
 
255
+ # This is where the magic happens.
248
256
  def action
249
257
  inner_error = nil
250
258
  rescue_block = proc do |e|
251
259
  raise unless filters[:error].any? do |f|
252
- (f[:args].empty? || f[:args].any? { |type| e.is_a?(type) }) &&
253
- !check_for_failed_condition(f[:conditions]) &&
254
- instance_exec(e, &f[:proc])
260
+ if !f[:args] || f[:args].empty? || f[:args].any? { |type| e.is_a?(type) }
261
+ instance_exec(e, &f[:proc]) unless check_for_failed_condition(f[:conditions])
262
+ end
255
263
  end
256
264
  end
257
265
 
@@ -273,22 +281,25 @@ module Scorched
273
281
  }.max || 0,
274
282
  -idx
275
283
  ]
276
- }.reverse.each { |match,|
284
+ }.reverse.each { |match,idx|
277
285
  request.breadcrumb << match
278
- break if catch(:pass) {
286
+ catch(:pass) {
279
287
  target = match.mapping[:target]
280
- response.merge! begin
281
- if Proc === target
282
- instance_exec(&target)
283
- else
284
- target.call(env.merge(
285
- 'SCRIPT_NAME' => request.matched_path.chomp('/'),
286
- 'PATH_INFO' => request.unmatched_path[match.path.chomp('/').length..-1]
287
- ))
288
+ catch(:halt) do
289
+ response.merge! begin
290
+ if Proc === target
291
+ instance_exec(&target)
292
+ else
293
+ target.call(env.merge(
294
+ 'SCRIPT_NAME' => request.matched_path.chomp('/'),
295
+ 'PATH_INFO' => request.unmatched_path[match.path.chomp('/').length..-1]
296
+ ))
297
+ end
288
298
  end
289
299
  end
290
300
  @_handled = true
291
301
  }
302
+ break if @_handled
292
303
  request.breadcrumb.pop
293
304
  }
294
305
  unless @_handled
@@ -298,9 +309,13 @@ module Scorched
298
309
  rescue_block.call(inner_error)
299
310
  end
300
311
  run_filters(:after)
312
+ true
313
+ end || begin
314
+ run_filters(:before, true)
315
+ run_filters(:after, true)
301
316
  end
302
317
  rescue => outer_error
303
- outer_error == inner_error ? raise : rescue_block.call(outer_error)
318
+ outer_error == inner_error ? raise : catch(:halt) { rescue_block.call(outer_error) }
304
319
  end
305
320
  response.finish
306
321
  end
@@ -388,7 +403,7 @@ module Scorched
388
403
  session[key]
389
404
  end
390
405
 
391
- after do
406
+ after(force: true) do
392
407
  env['scorched.flash'].each { |k,v| session[k] = v } if session && env['scorched.flash']
393
408
  end
394
409
 
@@ -527,20 +542,29 @@ module Scorched
527
542
 
528
543
  private
529
544
 
530
- def run_filters(type)
545
+ def run_filters(type, forced_only = false)
531
546
  tracker = env['scorched.executed_filters'] ||= {before: Set.new, after: Set.new}
532
- filters[type].reject{ |f| tracker[type].include? f }.each do |f|
547
+ filters[type].reject{ |f| tracker[type].include?(f) || (forced_only && !f[:force]) }.each do |f|
533
548
  unless check_for_failed_condition(f[:conditions])
534
549
  tracker[type] << f
535
- instance_exec(&f[:proc])
550
+ if forced_only
551
+ catch(:halt) do
552
+ instance_exec(&f[:proc]); true
553
+ end or log.warn "Ignored halt while running forced filters."
554
+ else
555
+ instance_exec(&f[:proc])
556
+ end
536
557
  end
537
558
  end
538
559
  end
539
560
 
540
- def log(type, message)
561
+ def log(type = nil, message = nil)
541
562
  config[:logger].progname ||= 'Scorched'
542
- type = Logger.const_get(type.to_s.upcase)
543
- config[:logger].add(type, message)
563
+ if(type)
564
+ type = Logger.const_get(type.to_s.upcase)
565
+ config[:logger].add(type, message)
566
+ end
567
+ config[:logger]
544
568
  end
545
569
  end
546
570
  end
File without changes
File without changes
@@ -11,4 +11,4 @@ module Scorched
11
11
  response[0] >= 400 ? @app.call(env) : response
12
12
  end
13
13
  end
14
- end
14
+ end
@@ -1,3 +1,3 @@
1
1
  module Scorched
2
- VERSION = '0.19'
2
+ VERSION = '0.20'
3
3
  end
@@ -270,6 +270,16 @@ module Scorched
270
270
  end
271
271
  end
272
272
 
273
+ it "provides wildcard captures as arguments" do
274
+ app.get('/*/**') { |a,b| "#{a}#{b}" }
275
+ rt.get('/hello/there/dude').body.should == 'hellothere/dude'
276
+ end
277
+
278
+ it "provides named captures as a single hash argument" do
279
+ app.get('/:given_name/::surname') { |h| "#{h[:given_name]} #{h[:surname]}" }
280
+ rt.get('/bob/smith').body.should == 'bob smith'
281
+ end
282
+
273
283
  it "always matches to the end of the URL (implied $)" do
274
284
  app.get('/') { 'awesome '}
275
285
  rt.get('/dog').status.should == 404
@@ -352,9 +362,9 @@ module Scorched
352
362
  response.body.should == 'roof'
353
363
  end
354
364
 
355
- it "inherits from parent class, or any other class" do
356
- app.controller.superclass.should == app
357
- app.controller('/', String).superclass.should == String
365
+ it "inherits from parent class, or otherwise the specified class" do
366
+ app.controller{}.superclass.should == app
367
+ app.controller('/', String){}.superclass.should == String
358
368
  end
359
369
 
360
370
  it "can take mapping options" do
@@ -377,6 +387,14 @@ module Scorched
377
387
  rt.get('/').body.should == 'hello'
378
388
  filters_run.should == 0
379
389
  end
390
+
391
+ it "can be used to map a predefined controller" do
392
+ person_controller = Class.new(Scorched::Controller) do
393
+ get('/name') { 'George' }
394
+ end
395
+ app.controller '/person', person_controller
396
+ rt.get('/person/name').body.should == 'George'
397
+ end
380
398
  end
381
399
  end
382
400
 
@@ -547,6 +565,12 @@ module Scorched
547
565
  rt.get('/').body.should == 'StandardErrorArgumentError'
548
566
  end
549
567
 
568
+ they "swallow halts when executed in an outer context" do
569
+ app.before { raise "Big bad error" }
570
+ app.error { throw :halt }
571
+ rt.get('/') # Would otherwise bomb out with uncaught throw.
572
+ end
573
+
550
574
  they "only get called once per error" do
551
575
  times_called = 0
552
576
  app.error { times_called += 1 }
@@ -621,8 +645,8 @@ module Scorched
621
645
  end
622
646
 
623
647
  it "takes an optional status" do
624
- app.get('/') { halt 401 }
625
- rt.get('/').status.should == 401
648
+ app.get('/') { halt 600 }
649
+ rt.get('/').status.should == 600
626
650
  end
627
651
 
628
652
  it "takes an optional response body" do
@@ -631,21 +655,41 @@ module Scorched
631
655
  end
632
656
 
633
657
  it "can take a status and a response body" do
634
- app.get('/') { halt 401, 'cool' }
635
- rt.get('/').status.should == 401
658
+ app.get('/') { halt 600, 'cool' }
659
+ rt.get('/').status.should == 600
636
660
  rt.get('/').body.should == 'cool'
637
661
  end
638
662
 
639
- it "skips processing filters" do
640
- app.after { response.status = 403 }
663
+ it "still processes filters" do
664
+ app.after { response.status = 600 }
641
665
  app.get('/') { halt }
642
- rt.get('/').status.should == 200
666
+ rt.get('/').status.should == 600
643
667
  end
644
668
 
645
- it "short circuits filters if halted within filter" do
646
- app.before { halt }
647
- app.after { response.status = 403 }
648
- rt.get('/').status.should_not == 403
669
+ describe "within filters" do
670
+ it "short circuits filters if halted within filter" do
671
+ app.before { halt }
672
+ app.after { response.status = 600 }
673
+ rt.get('/').status.should_not == 600
674
+ end
675
+
676
+ it "forced filters are always run" do
677
+ app.before { halt }
678
+ app.after(force: true) { response.status = 600 }
679
+ app.after { response.status = 700 } # Shouldn't run because it's not forced
680
+ app.get('/') { 'hello' }
681
+ rt.get('/').status.should == 600
682
+ end
683
+
684
+ it "halting within a forced filter still runs other forced filters" do
685
+ app.before { halt }
686
+ app.before(force: true) { halt }
687
+ app.before(force: true) { response.status = 600 }
688
+ app.get('/') { 'hello' }
689
+ rt.get('/').status.should == 600
690
+ app.after(force: true) { response.status = 700 }
691
+ rt.get('/').status.should == 700
692
+ end
649
693
  end
650
694
  end
651
695
 
@@ -674,6 +718,14 @@ module Scorched
674
718
  rt.get('/')
675
719
  var.should == false
676
720
  end
721
+
722
+ it "works in filters" do
723
+ app.error { redirect '/somewhere' }
724
+ app.get('/') { raise "Some error" }
725
+ rt.get('/').location.should == '/somewhere'
726
+ app.before { redirect '/somewhere_else' }
727
+ rt.get('/').location.should == '/somewhere_else'
728
+ end
677
729
  end
678
730
 
679
731
  describe "passing" do
@@ -3,6 +3,8 @@ ENV['RACK_ENV'] = 'production'
3
3
  require 'rack/test'
4
4
  require_relative '../lib/scorched.rb'
5
5
 
6
+ Scorched::Controller.config[:logger] = Logger.new(nil)
7
+
6
8
  module Scorched
7
9
  class SimpleCounter
8
10
  def initialize(app)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scorched
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.19'
4
+ version: '0.20'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tom Wardrop
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-11-29 00:00:00.000000000 Z
11
+ date: 2013-12-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -172,7 +172,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
172
172
  version: '0'
173
173
  requirements: []
174
174
  rubyforge_project:
175
- rubygems_version: 2.0.3
175
+ rubygems_version: 2.0.6
176
176
  signing_key:
177
177
  specification_version: 4
178
178
  summary: Light-weight, DRY as a desert, web framework for Ruby