scorched 0.20 → 0.21

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: 5b78f42bbcf1ea4018406b7fcf95f31c78ed1c4e
4
- data.tar.gz: 3a86547b8e623c089ec67a1a45f65a4ce2b1554d
3
+ metadata.gz: be73ae01518b83ec6ae0d6d84b693810dad8be8f
4
+ data.tar.gz: 67a18fa26a948a71c9c283deec4d064fc1575746
5
5
  SHA512:
6
- metadata.gz: 2ff3c8bc993bffc2d5d94682fc53288a6293edfe94f9f7e9341efb08f6af71cd868cc6614541b286add86c2d0c2ff2c80021115957d088c7afc3bd1c8de075e6
7
- data.tar.gz: e96714a418248738c3bb78d76bed6d8a41b28055bed3d1840bca2e2c1430bfb67960e8e5d6fcb6341dbec244bf06172cd2513d20c031c9fe950d311931a3ab9f
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. Consequentially, this makes for some awkwardness. Helpers, for example, are a classical 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.
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 are often expected 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.
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} I see"
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
@@ -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 potentialkey name conflicts between Scorched and the renderer invoked by Tilt. For example, if you had to specify an `:engine` for the renderer, this allows you to do that without.
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.all? { |m| m.failed_condition }
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 { |this|
87
+ middleware << proc { |controller|
86
88
  use Rack::Head
87
89
  use Rack::MethodOverride
88
90
  use Rack::Accept
89
- use Scorched::Static, this.config[:static_dir] if this.config[:static_dir]
90
- use Rack::Logger, this.config[:logger] if this.config[:logger]
91
- use Rack::ShowExceptions if this.config[:show_exceptions]
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).action
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
- response.body = instance_exec(*[request.captures].flatten, &block)
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
- def action
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
- 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
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-recalling.
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
- # Tilt still has unresolved file encoding issues. Until that's fixed, we read the file manually.
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 chunk of code is responsible for preventing the rendering of layouts within views.
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
File without changes
@@ -1,3 +1,3 @@
1
1
  module Scorched
2
- VERSION = '0.20'
2
+ VERSION = '0.21'
3
3
  end
@@ -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 == 'hellothere/dude'
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 a single hash argument" do
279
- app.get('/:given_name/::surname') { |h| "#{h[:given_name]} #{h[:surname]}" }
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
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.20'
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: 2013-12-22 00:00:00.000000000 Z
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.0.6
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