scorched 0.20 → 0.21
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES.md +7 -0
- data/Gemfile +0 -0
- data/LICENSE +0 -0
- data/README.md +2 -2
- data/docs/02_fundamentals/03_routing.md +17 -2
- data/examples/rails_style_routing.ru +47 -0
- data/lib/scorched/controller.rb +40 -25
- data/lib/scorched/dynamic_delegate.rb +0 -0
- data/lib/scorched/error.rb +0 -0
- data/lib/scorched/version.rb +1 -1
- data/spec/controller_spec.rb +13 -4
- data/spec/options_spec.rb +0 -0
- data/spec/public/static.txt +0 -0
- data/spec/request_spec.rb +0 -0
- data/spec/views/composer.erb +0 -0
- data/spec/views/layout.erb +0 -0
- data/spec/views/main.erb +0 -0
- data/spec/views/other.str +0 -0
- data/spec/views/partial.erb +0 -0
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: be73ae01518b83ec6ae0d6d84b693810dad8be8f
|
4
|
+
data.tar.gz: 67a18fa26a948a71c9c283deec4d064fc1575746
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bb3f359dcbe1f20fdfe70c029ab4beb8bcfc28c8242b5ceb7b9d8b239738c9b2a49f771a11524aaf232b896c7f4b4342ebad996cbf7ae4c06f7bbc307621a4c9
|
7
|
+
data.tar.gz: f89933d08e8e8f3348221b8e16471433c2819de51ad4ed1088505edfa0ec9aef6a21aefbed6d00443e472c5182ae54dd42f7b29e61572cf88662cc60302e0738
|
data/CHANGES.md
CHANGED
@@ -3,6 +3,13 @@ Changelog
|
|
3
3
|
|
4
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
5
|
|
6
|
+
### v0.21
|
7
|
+
* Named captures have changed again. The values are now passed as arguments to route proc's in favor of using the new `captures` convenience method for accessing named arguments as a hash.
|
8
|
+
* The `:redirect` option for `:strip_trailing_slashes` no longer cause a redirection loop when the request path is set to two or more forward slashes, e.g. '//'
|
9
|
+
* Changed default value of `render_defaults[:tilt]` to `{default_encoding: 'UTF-8'}`. This defaults _Tilt_ to using UTF-8 for loading template files, which is desirable 99% of the time.
|
10
|
+
* The `action` method has been split out into two new methods, `process` and `dispatch`. This provides the oppurtunity to override the dispatch logic where more control is needed over how mapping targets are invoked.
|
11
|
+
* `:failed_condition` condition now takes into account whether any mapping has actually handled the request. This allows for example, the correct status to be set when a mapping has been matched, but has `pass`ed the request.
|
12
|
+
|
6
13
|
### v0.20
|
7
14
|
* _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
15
|
* 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.
|
data/Gemfile
CHANGED
File without changes
|
data/LICENSE
CHANGED
File without changes
|
data/README.md
CHANGED
@@ -39,9 +39,9 @@ Scorched requires Ruby 2.0 or above. If you've got Ruby 2.0.0-p195 and newer, yo
|
|
39
39
|
|
40
40
|
The Errors of Our Past (and Present!)
|
41
41
|
----------------------
|
42
|
-
One of the mistakes made by a lot of other Ruby frameworks is to not leverage the power of the class.
|
42
|
+
One of the mistakes made by a lot of other Ruby frameworks is to not leverage the power of the class. Consequently, this makes for some awkwardness. Helpers for example, are a classic reinvention of what classes and modules are already made to solve. Scorched implements Controllers as classes, which in addition to having their own DSL, allow you to define and call whatever you need as standard instance methods. The decision to allow developers to implement helpers and other common functionality as standard instance methods not only makes Controllers somewhat more predictable and familiar, but also allows for such helpers to be inheritable via plain old class inheritance.
|
43
43
|
|
44
|
-
Another design oversight of other frameworks is the lack of consideration for the hierarchical nature of websites and the fact that sub-directories
|
44
|
+
Another design oversight of other frameworks is the lack of consideration for the hierarchical nature of websites and the fact that it's often desireable for sub-directories to inherit attributes of their parents. Scorched supports sub-controllers to any arbitrary depth, with each controller's configuration, filters, route conditions, etc. applied along the way. This can help in many areas of web development, including security, restful interfaces, and interchangeable content types.
|
45
45
|
|
46
46
|
|
47
47
|
Design Philosophy
|
@@ -27,7 +27,7 @@ route '/' do
|
|
27
27
|
end
|
28
28
|
|
29
29
|
route '/*', 5, method: ['POST', 'PUT', 'DELETE'] do |capture|
|
30
|
-
"Hmm trying to change #{capture}
|
30
|
+
"Hmm, I see you're trying to change the resource #{capture}"
|
31
31
|
end
|
32
32
|
```
|
33
33
|
|
@@ -62,11 +62,26 @@ String patterns are compiled into Regexp patterns corresponding to the following
|
|
62
62
|
* `:param` - Same as `*` except the capture is named to whatever the string following the single-colon.
|
63
63
|
* `::param` - Same as `**` except the capture is named to whatever the string following the double-colon.
|
64
64
|
* `?` - If placed directly after a wildcard capture, matches zero or more characters instead of one or more. For example, the patterns `/*?` and `/::title?` would match both `/` and `/about`.
|
65
|
-
* `$` - If placed at the end of a pattern, the pattern only matches if it matches the entire path. For patterns defined using the route helpers, e.g. `Controller.route`, `Controller.get`, this is implied.
|
65
|
+
* `$` - If placed at the end of a pattern, the pattern only matches if it matches the entire path. For patterns defined using the route helpers, e.g. `Controller.route`, `Controller.get`, this is implied.
|
66
66
|
|
67
67
|
###Regex Patterns
|
68
68
|
Regex patterns offer more power and flexibility than string patterns (naturally). The rules for Regex patterns are identical to String patterns, e.g. they must match from the beginning of the path, etc.
|
69
69
|
|
70
|
+
Captures
|
71
|
+
--------
|
72
|
+
Captures can be accessed as arguments on the route proc, or via the `captures` helper method, which is shorthand for `request.captures`.
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
get '/:id' do |id|
|
76
|
+
id == captures[:id]
|
77
|
+
end
|
78
|
+
|
79
|
+
get '/*/*' do |id, title|
|
80
|
+
[id, title] == captures
|
81
|
+
end
|
82
|
+
```
|
83
|
+
|
84
|
+
The above examples demonstrates the two methods of accessing captures, for both named and anonymous captures. You may notice that while named and anonymous captures are passed to the proc as arguments in the exact same way, the `captures` helper either returns either a `Hash` or an `Array` depending on whether the captures are named or not.
|
70
85
|
|
71
86
|
Conditions
|
72
87
|
----------
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require File.expand_path('../../lib/scorched.rb', __FILE__)
|
2
|
+
|
3
|
+
class App < Scorched::Controller
|
4
|
+
end
|
5
|
+
|
6
|
+
class Base < App
|
7
|
+
def self.inherited(klass)
|
8
|
+
klass.get('/') { invoke_action :index }
|
9
|
+
klass.post('/') { invoke_action :create }
|
10
|
+
klass.get('/:id') { invoke_action :show }
|
11
|
+
klass.get('/:id/edit') { invoke_action :edit }
|
12
|
+
klass.route('/:id', method: ['PATCH', 'PUT']) { invoke_action :update }
|
13
|
+
klass.delete('/:id') { invoke_action :delete }
|
14
|
+
end
|
15
|
+
|
16
|
+
def invoke_action(action)
|
17
|
+
respond_to?(action) ? send(action) : pass
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Root < Base
|
22
|
+
def index
|
23
|
+
'Hello'
|
24
|
+
end
|
25
|
+
|
26
|
+
def create
|
27
|
+
'Creating it now'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class Customer < Base
|
32
|
+
def index
|
33
|
+
'Hello customer'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class Order < Base
|
38
|
+
def index
|
39
|
+
'Me order'
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
App.controller '/customer', Customer
|
44
|
+
App.controller '/order', Order
|
45
|
+
App.controller '/', Root
|
46
|
+
|
47
|
+
run App
|
data/lib/scorched/controller.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
1
3
|
module Scorched
|
2
4
|
TemplateCache = Tilt::Cache.new
|
3
5
|
|
@@ -28,7 +30,7 @@ module Scorched
|
|
28
30
|
:layout => false, # The default layout template to use, relative to the view directory. Set to false for no default layout.
|
29
31
|
:engine => :erb,
|
30
32
|
:locals => {},
|
31
|
-
:tilt => {}, # Options intended for Tilt. This gets around
|
33
|
+
:tilt => {default_encoding: 'UTF-8'}, # Options intended for Tilt. This gets around potential key name conflicts between Scorched and the renderer invoked by Tilt.
|
32
34
|
}
|
33
35
|
|
34
36
|
if ENV['RACK_ENV'] == 'development'
|
@@ -52,7 +54,7 @@ module Scorched
|
|
52
54
|
[*encodings].any? { |encoding| env['rack-accept.request'].encoding? encoding }
|
53
55
|
},
|
54
56
|
failed_condition: proc { |conditions|
|
55
|
-
if !matches.empty? && matches.
|
57
|
+
if !matches.empty? && matches.any? { |m| m.failed_condition } && !@_handled
|
56
58
|
[*conditions].include? matches.first.failed_condition[0]
|
57
59
|
end
|
58
60
|
},
|
@@ -82,13 +84,13 @@ module Scorched
|
|
82
84
|
},
|
83
85
|
}
|
84
86
|
|
85
|
-
middleware << proc { |
|
87
|
+
middleware << proc { |controller|
|
86
88
|
use Rack::Head
|
87
89
|
use Rack::MethodOverride
|
88
90
|
use Rack::Accept
|
89
|
-
use Scorched::Static,
|
90
|
-
use Rack::Logger,
|
91
|
-
use Rack::ShowExceptions if
|
91
|
+
use Scorched::Static, controller.config[:static_dir] if controller.config[:static_dir]
|
92
|
+
use Rack::Logger, controller.config[:logger] if controller.config[:logger]
|
93
|
+
use Rack::ShowExceptions if controller.config[:show_exceptions]
|
92
94
|
}
|
93
95
|
|
94
96
|
class << self
|
@@ -104,7 +106,7 @@ module Scorched
|
|
104
106
|
def call(env)
|
105
107
|
loaded = env['scorched.middleware'] ||= Set.new
|
106
108
|
app = lambda do |env|
|
107
|
-
self.new(env).
|
109
|
+
self.new(env).process
|
108
110
|
end
|
109
111
|
|
110
112
|
builder = Rack::Builder.new
|
@@ -165,7 +167,8 @@ module Scorched
|
|
165
167
|
# patch(pattern = nil, priority = nil, **conds, &block)
|
166
168
|
def route(pattern = nil, priority = nil, **conds, &block)
|
167
169
|
target = lambda do
|
168
|
-
|
170
|
+
args = captures.respond_to?(:values) ? captures.values : captures
|
171
|
+
response.body = instance_exec(*args, &block)
|
169
172
|
response
|
170
173
|
end
|
171
174
|
[*pattern].compact.each do |pattern|
|
@@ -252,8 +255,9 @@ module Scorched
|
|
252
255
|
@response = Response.new
|
253
256
|
end
|
254
257
|
|
255
|
-
# This is where the magic happens.
|
256
|
-
|
258
|
+
# This is where the magic happens. Applies filters, matches mappings, applies error handlers, catches :halt and
|
259
|
+
# :pass, etc.
|
260
|
+
def process
|
257
261
|
inner_error = nil
|
258
262
|
rescue_block = proc do |e|
|
259
263
|
raise unless filters[:error].any? do |f|
|
@@ -265,7 +269,7 @@ module Scorched
|
|
265
269
|
|
266
270
|
begin
|
267
271
|
catch(:halt) do
|
268
|
-
if config[:strip_trailing_slash] == :redirect && request.path =~ %r{
|
272
|
+
if config[:strip_trailing_slash] == :redirect && request.path =~ %r{[^/]/+$}
|
269
273
|
redirect(request.path.chomp('/'), 307)
|
270
274
|
end
|
271
275
|
eligable_matches = matches.reject { |m| m.failed_condition }
|
@@ -284,18 +288,8 @@ module Scorched
|
|
284
288
|
}.reverse.each { |match,idx|
|
285
289
|
request.breadcrumb << match
|
286
290
|
catch(:pass) {
|
287
|
-
target = match.mapping[:target]
|
288
291
|
catch(:halt) do
|
289
|
-
|
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
|
298
|
-
end
|
292
|
+
dispatch(match)
|
299
293
|
end
|
300
294
|
@_handled = true
|
301
295
|
}
|
@@ -320,11 +314,27 @@ module Scorched
|
|
320
314
|
response.finish
|
321
315
|
end
|
322
316
|
|
317
|
+
# Dispatches the request to the matched target.
|
318
|
+
# Overriding this method provides the oppurtunity for one to have more control over how mapping targets are invoked.
|
319
|
+
def dispatch(match)
|
320
|
+
target = match.mapping[:target]
|
321
|
+
response.merge! begin
|
322
|
+
if Proc === target
|
323
|
+
instance_exec(&target)
|
324
|
+
else
|
325
|
+
target.call(env.merge(
|
326
|
+
'SCRIPT_NAME' => request.matched_path.chomp('/'),
|
327
|
+
'PATH_INFO' => request.unmatched_path[match.path.chomp('/').length..-1]
|
328
|
+
))
|
329
|
+
end
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
323
333
|
# Finds mappings that match the unmatched portion of the request path, returning an array of `Match` objects, or an
|
324
334
|
# empty array if no matches were found.
|
325
335
|
#
|
326
336
|
# The `:eligable` attribute of the `Match` object indicates whether the conditions for that mapping passed.
|
327
|
-
# The result is cached for the life time of the controller instance, for the sake of effecient
|
337
|
+
# The result is cached for the life time of the controller instance, for the sake of effecient recalling.
|
328
338
|
def matches
|
329
339
|
return @_matches if @_matches
|
330
340
|
to_match = request.unmatched_path
|
@@ -387,6 +397,10 @@ module Scorched
|
|
387
397
|
env['rack.session']
|
388
398
|
end
|
389
399
|
|
400
|
+
# Delegate a few common `request` methods for conveniance.
|
401
|
+
extend Forwardable
|
402
|
+
def_delegators :request, :captures
|
403
|
+
|
390
404
|
# Flash session storage helper.
|
391
405
|
# Stores session data until the next time this method is called with the same arguments, at which point it's reset.
|
392
406
|
# The typical use case is to provide feedback to the user on the previous action they performed.
|
@@ -449,11 +463,12 @@ module Scorched
|
|
449
463
|
tilt_options = options.merge(tilt || {})
|
450
464
|
tilt_engine = (derived_engine = Tilt[string_or_file.to_s]) || Tilt[engine]
|
451
465
|
raise Error, "Invalid or undefined template engine: #{engine.inspect}" unless tilt_engine
|
466
|
+
|
452
467
|
template = if Symbol === string_or_file
|
453
468
|
file = string_or_file.to_s
|
454
469
|
file = file << ".#{engine}" unless derived_engine
|
455
470
|
file = File.expand_path(file, dir) if dir
|
456
|
-
|
471
|
+
|
457
472
|
template_cache.fetch(:file, tilt_engine, file, tilt_options) do
|
458
473
|
tilt_engine.new(file, nil, tilt_options)
|
459
474
|
end
|
@@ -463,7 +478,7 @@ module Scorched
|
|
463
478
|
end
|
464
479
|
end
|
465
480
|
|
466
|
-
# The following
|
481
|
+
# The following is responsible for preventing the rendering of layouts within views.
|
467
482
|
begin
|
468
483
|
original_no_default_layout = @_no_default_layout
|
469
484
|
@_no_default_layout = true
|
File without changes
|
data/lib/scorched/error.rb
CHANGED
File without changes
|
data/lib/scorched/version.rb
CHANGED
data/spec/controller_spec.rb
CHANGED
@@ -271,12 +271,14 @@ module Scorched
|
|
271
271
|
end
|
272
272
|
|
273
273
|
it "provides wildcard captures as arguments" do
|
274
|
-
app.get('/*/**') { |a,b| "#{a}#{b}" }
|
275
|
-
rt.get('/hello/there/dude').body.should == '
|
274
|
+
app.get('/*/**') { |a,b| "#{a} #{b}" }
|
275
|
+
rt.get('/hello/there/dude').body.should == 'hello there/dude'
|
276
276
|
end
|
277
277
|
|
278
|
-
it "provides named captures as
|
279
|
-
app.get('/:given_name
|
278
|
+
it "provides named captures as individual arguments for each value" do
|
279
|
+
app.get('/:given_name') { |a| a }
|
280
|
+
app.get('/:given_name/::surname') { |a,b| "#{a} #{b}" }
|
281
|
+
rt.get('/bob').body.should == 'bob'
|
280
282
|
rt.get('/bob/smith').body.should == 'bob smith'
|
281
283
|
end
|
282
284
|
|
@@ -1217,6 +1219,13 @@ module Scorched
|
|
1217
1219
|
end
|
1218
1220
|
end
|
1219
1221
|
end
|
1222
|
+
|
1223
|
+
describe "delegators" do
|
1224
|
+
it "delegates captures" do
|
1225
|
+
app.get('/:id') { captures[:id] }
|
1226
|
+
rt.get('/587').body.should == '587'
|
1227
|
+
end
|
1228
|
+
end
|
1220
1229
|
|
1221
1230
|
end
|
1222
1231
|
end
|
data/spec/options_spec.rb
CHANGED
File without changes
|
data/spec/public/static.txt
CHANGED
File without changes
|
data/spec/request_spec.rb
CHANGED
File without changes
|
data/spec/views/composer.erb
CHANGED
File without changes
|
data/spec/views/layout.erb
CHANGED
File without changes
|
data/spec/views/main.erb
CHANGED
File without changes
|
data/spec/views/other.str
CHANGED
File without changes
|
data/spec/views/partial.erb
CHANGED
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.
|
4
|
+
version: '0.21'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tom Wardrop
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2014-02-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -123,6 +123,7 @@ files:
|
|
123
123
|
- docs/03_further_reading/running_unit_tests.md
|
124
124
|
- examples/file_upload.ru
|
125
125
|
- examples/media_types.ru
|
126
|
+
- examples/rails_style_routing.ru
|
126
127
|
- examples/simple.ru
|
127
128
|
- lib/scorched.rb
|
128
129
|
- lib/scorched/collection.rb
|
@@ -172,7 +173,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
172
173
|
version: '0'
|
173
174
|
requirements: []
|
174
175
|
rubyforge_project:
|
175
|
-
rubygems_version: 2.
|
176
|
+
rubygems_version: 2.1.11
|
176
177
|
signing_key:
|
177
178
|
specification_version: 4
|
178
179
|
summary: Light-weight, DRY as a desert, web framework for Ruby
|