scorched 0.11.1 → 0.12

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: 215f4c437938c37e309b465ec6fd685887e41fd5
4
- data.tar.gz: 38795c31fe7f0395a78ebddc403e2fef586dcd9a
3
+ metadata.gz: 83445d0e97b4717ff0d830c7e72f09bf0c8f8fa1
4
+ data.tar.gz: eb90cc0893a4b53d180d7dca083f04340026b964
5
5
  SHA512:
6
- metadata.gz: 4117536f2e361a2c432b49386f2def80528340d4d28055a50e5713e316fecdeffd93b2daa843af8dfdb8df447eb38b5379fa7fa7fc4253518ba7ad3129213829
7
- data.tar.gz: 70dab536b62e324347442e19ca08fc7a13c02e7102d5fcdb051a41e32aa3c3286d008bf3fdea73b0f508360ce5cbc9cb1989d9401e1928b63c3705c2351cd725
6
+ metadata.gz: d90ea66b9a3a280fa2746d0a9cf12a0697c6169e035fdcba9753612bf3297bd01c87b4ca7fa286740c24441e6ec539105cb4621c99a4b28facacdfe2deedeade
7
+ data.tar.gz: db597065e5a9d1dd93be4a738f998bea0cc85be0b057cd8b107eaad98f5932cc4ebf51aefc03a7b11c5ee412672f2c695e687e755cab5ed69c5f1c93f200d522
@@ -1,10 +1,12 @@
1
- Milestones
2
- ==========
3
-
4
1
  Changelog
5
- ---------
2
+ =========
3
+
4
+ ### v0.12
5
+ * Halt can now take an optional response body (typically a string).
6
+ * Controller now returns a valid rack array, rather than a Scorched::Response.
7
+
6
8
  ### v0.11.1
7
- * Fixed issue where multiple nested render calls would incorrectly render the layout (issue #9).
9
+ * Fixed an issue where subsequent nested render calls would render the default layout, which they shouldn't (issue #9).
8
10
  * Bumped Tilt dependancy to v1.4 and removed work-around for Tilt encoding issue.
9
11
 
10
12
  ### v0.11
@@ -93,24 +95,3 @@ Changelog
93
95
  * Mechanism for handling exceptions in routes and before/after filters.
94
96
  * Added static resource serving. E.g. public folder.
95
97
 
96
-
97
-
98
- To Do
99
- -----
100
- Some of these remaining features may be reconsidered and either left out, or put into some kind of contrib library.
101
-
102
- * If one or more matches are found, but their conditions don't pass, a 403 should be returned instead of a 404.
103
- * Make specs for Collection and Options classes more thorough, e.g. test all non-reading modifiers such as clear, delete, etc.
104
-
105
-
106
- Unlikely
107
- --------
108
- These features are unlikely to be implemented unless someone provides a good reason.
109
-
110
- * Mutex locking option - I'm of the opinion that the web server should be configured for the concurrency model of the application, rather than the framework.
111
- * Using Rack::Protection by default - The problem here is that a good portion of Rack::Protection involves sessions, and given that Scorched doesn't itself load any session middleware, these components of Rack::Protection would have to be excluded. I wouldn't want to invoke a false sense of security
112
- * Filter priorities - They're technically possible, but I believe it would introduce the potential for _filter hell_; badly written filters and mass confusion. Filter order has to be logical and predictable. Adding prioritisation would undermine that, and make for lazy use of filters. By not having prioritisation, there's incentive to design filters to be order-agnostic.
113
- * Verbose logging - I originally intended to add some form of debug-style logging to show the complete flow of a request as it traverses through filters and controllers, etc. For a couple of reasons, I've decided to leave this out of Scorched. For those unfamiliar with the order in which filters and routes are invoked, it's better to learn through first-hand experience writing little test applications, rather than depending on debug logging.
114
-
115
-
116
- More things will be added as they're thought of and considered.
data/Gemfile CHANGED
@@ -1,2 +1,2 @@
1
- source :rubygems
1
+ source 'https://rubygems.org'
2
2
  gemspec
data/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  Scorched is a generic, unopinionated, DRY, light-weight web framework for Ruby. It provides a generic yet powerful set of constructs for processing HTTP requests, with which websites and applications of almost any scale can be built.
5
5
 
6
- If you've used a light-weight DSL-based Ruby web framework before, such as Sinatra, it should look familiar. Scorched is a true evolutionary enhancement of Sinatra, with more power, focus, and less clutter.
6
+ If you've used a light-weight DSL-based Ruby web framework before, such as Sinatra, Scorched should look quite familiar. Scorched is a true evolutionary enhancement of Sinatra, with more power, focus, and less clutter.
7
7
 
8
8
  Getting Started
9
9
  ---------------
@@ -35,7 +35,7 @@ $ rackup hello_world.ru
35
35
 
36
36
  #### A Note on Requirements
37
37
 
38
- Scorched requires Ruby 2.0 as it makes use of a few new features. It is however, important to ensure your version of Ruby 2.0 includes [changeset 39919](http://bugs.ruby-lang.org/projects/ruby-trunk/repository/revisions/39919) in order to avoid suffering from [random segmentation faults](http://bugs.ruby-lang.org/issues/8100). The first official patch release of 2.0.0 should include the fix, otherwise you can patch it during installation with RVM.
38
+ Scorched requires Ruby 2.0 or above. It's important to ensure your version of Ruby 2.0 includes [changeset 39919](http://bugs.ruby-lang.org/projects/ruby-trunk/repository/revisions/39919) in order to avoid suffering from [random segmentation faults](http://bugs.ruby-lang.org/issues/8100). Ruby 2.0.0-p195 and newer include the fix, otherwise you can manually patch it during installation.
39
39
 
40
40
  The Errors of Our Past
41
41
  ----------------------
@@ -107,11 +107,6 @@ This API shouldn't look too foreign to anyone familiar with frameworks like Sina
107
107
  * Conditions - Conditions are merely procs defined on the controller which are inherited (and can be overriden) by child controllers. When a request comes in, mappings that match the requested URL, first have their conditions evaluated in the context of the controller instance, before control is handed off to the target associated with that mapping. It's a very simple implementation that comes with a lot of flexibility.
108
108
 
109
109
 
110
- Development Progress
111
- --------------------
112
- Please refer to [Milestones.md](Milestones.md) for a breakdown of development progress.
113
-
114
-
115
110
  Links
116
111
  -----
117
112
  * [Website](http://scorchedrb.com)
data/TODO.md ADDED
@@ -0,0 +1,17 @@
1
+ To Do
2
+ =====
3
+ * If one or more matches are found, but their conditions don't pass, a 403 should be returned instead of a 404.
4
+ * Make specs for Collection and Options classes more thorough, e.g. test all non-reading modifiers such as clear, delete, etc.
5
+
6
+
7
+ Unlikely
8
+ ========
9
+ These features are unlikely to be implemented unless someone provides a good reason.
10
+
11
+ * Mutex locking option - I'm of the opinion that the web server should be configured for the concurrency model of the application, rather than the framework.
12
+ * Using Rack::Protection by default - The problem here is that a good portion of Rack::Protection involves sessions, and given that Scorched doesn't itself load any session middleware, these components of Rack::Protection would have to be excluded. I wouldn't want to invoke a false sense of security
13
+ * Filter priorities - They're technically possible, but I believe it would introduce the potential for _filter hell_; badly written filters and mass confusion. Filter order has to be logical and predictable. Adding prioritisation would undermine that, and make for lazy use of filters. By not having prioritisation, there's incentive to design filters to be order-agnostic.
14
+ * Verbose logging - I originally intended to add some form of debug-style logging to show the complete flow of a request as it traverses through filters and controllers, etc. For a couple of reasons, I've decided to leave this out of Scorched. For those unfamiliar with the order in which filters and routes are invoked, it's better to learn through first-hand experience writing little test applications, rather than depending on debug logging.
15
+
16
+
17
+ More things will be added as they're thought of and considered.
@@ -85,7 +85,7 @@ As of v0.11, Scorched also supports inverted/negated conditions by adding a trai
85
85
  Like configuration options, conditions are implemented using the `Scorched::Options` class, so they're inherited and can be overridden by child classes. You may easily add your own conditions as the example below demonstrates.
86
86
 
87
87
  ```ruby
88
- condition[:has_permission] = proc { |v|
88
+ conditions[:has_permission] = proc { |v|
89
89
  user.has_permission == v
90
90
  }
91
91
 
@@ -100,8 +100,7 @@ module Scorched
100
100
  def call(env)
101
101
  loaded = env['scorched.middleware'] ||= Set.new
102
102
  app = lambda do |env|
103
- instance = self.new(env)
104
- instance.action
103
+ self.new(env).action
105
104
  end
106
105
 
107
106
  builder = Rack::Builder.new
@@ -151,6 +150,18 @@ module Scorched
151
150
  end
152
151
 
153
152
  # Generates and returns a new route proc from the given block, and optionally maps said proc using the given args.
153
+ # Helper methods are provided for each HTTP method which automatically define the appropriate _:method_
154
+ # condition.
155
+ #
156
+ # :call-seq:
157
+ # route(pattern = nil, priority = nil, **conds, &block)
158
+ # get(pattern = nil, priority = nil, **conds, &block)
159
+ # post(pattern = nil, priority = nil, **conds, &block)
160
+ # put(pattern = nil, priority = nil, **conds, &block)
161
+ # delete(pattern = nil, priority = nil, **conds, &block)
162
+ # head(pattern = nil, priority = nil, **conds, &block)
163
+ # options(pattern = nil, priority = nil, **conds, &block)
164
+ # patch(pattern = nil, priority = nil, **conds, &block)
154
165
  def route(pattern = nil, priority = nil, **conds, &block)
155
166
  target = lambda do |env|
156
167
  env['scorched.response'].body = instance_exec(*env['scorched.request'].captures, &block)
@@ -159,7 +170,7 @@ module Scorched
159
170
  self << {pattern: compile(pattern, true), priority: priority, conditions: conds, target: target} if pattern
160
171
  target
161
172
  end
162
-
173
+
163
174
  ['get', 'post', 'put', 'delete', 'head', 'options', 'patch'].each do |method|
164
175
  methods = (method == 'get') ? ['GET', 'HEAD'] : [method.upcase]
165
176
  define_method(method) do |*args, **conds, &block|
@@ -168,11 +179,18 @@ module Scorched
168
179
  end
169
180
  end
170
181
 
182
+ # Defines a filter of +type+. Helper methods are provided as syntactic sugar for each filter type.
183
+ # +args+ is used internally by Scorched for passing additional arguments to the filter, such as the exception in
184
+ # the case of error blocks.
185
+ # :call-seq:
186
+ # filter(type, *args, **conds, &block)
187
+ # before(*args, **conds, &block)
188
+ # after(*args, **conds, &block)
189
+ # error(*args, **conds, &block)
171
190
  def filter(type, *args, **conds, &block)
172
191
  filters[type.to_sym] << {args: args, conditions: conds, proc: block}
173
192
  end
174
193
 
175
- # A bit of syntactic sugar for #filter.
176
194
  ['before', 'after', 'error'].each do |type|
177
195
  define_method(type) do |*args, &block|
178
196
  filter(type, *args, &block)
@@ -218,6 +236,8 @@ module Scorched
218
236
  end
219
237
  env['scorched.request'] ||= Request.new(env)
220
238
  env['scorched.response'] ||= Response.new
239
+ @_log_prefix = ' ' * (4 * request.breadcrumb.length)
240
+ log :debug, "#{log_object(self)} instantiated"
221
241
  end
222
242
 
223
243
  def action
@@ -236,6 +256,7 @@ module Scorched
236
256
  redirect(request.path.chomp('/'))
237
257
  end
238
258
 
259
+ log :debug, :matches
239
260
  if matches.all? { |m| m.failed_condition }
240
261
  pass if config[:auto_pass]
241
262
  response.status = matches.empty? ? 404 : 403
@@ -246,21 +267,23 @@ module Scorched
246
267
  @_matched = true == matches.each { |match|
247
268
  next if match.failed_condition
248
269
  request.breadcrumb << match
270
+ target = match.mapping[:target]
249
271
  break if catch(:pass) {
250
- target = match.mapping[:target]
272
+ log :debug, "Invoking target: #{log_object(target)}"
251
273
  response.merge! (Proc === target) ? instance_exec(env, &target) : target.call(env)
252
274
  }
253
275
  request.breadcrumb.pop
276
+ log :debug, "#{log_object(target)} passed the request"
254
277
  }
255
278
  rescue => inner_error
256
279
  rescue_block.call(inner_error)
257
280
  end
258
281
  run_filters(:after)
259
- end
282
+ end || log(:debug, "Request halted")
260
283
  rescue => outer_error
261
284
  outer_error == inner_error ? raise : rescue_block.call(outer_error)
262
285
  end
263
- response
286
+ response.finish
264
287
  end
265
288
 
266
289
  # Finds mappings that match the unmatched portion of the request path, returning an array of `Match` objects, or an
@@ -308,8 +331,16 @@ module Scorched
308
331
  halt(status)
309
332
  end
310
333
 
311
- def halt(status = 200)
312
- response.status = status
334
+ # call-seq:
335
+ # halt(status=nil, body=nil)
336
+ # halt(body)
337
+ def halt(status=nil, body=nil)
338
+ unless status.nil? || Integer === status
339
+ body = status
340
+ status = nil
341
+ end
342
+ response.status = status if status
343
+ response.body = body if body
313
344
  throw :halt
314
345
  end
315
346
 
@@ -489,18 +520,52 @@ module Scorched
489
520
 
490
521
  def run_filters(type)
491
522
  tracker = env['scorched.executed_filters'] ||= {before: Set.new, after: Set.new}
492
- filters[type].reject{ |f| tracker[type].include? f }.each do |f|
493
- unless check_for_failed_condition(f[:conditions])
494
- tracker[type] << f
495
- instance_exec(&f[:proc])
523
+ eligable = filters[type].reject{ |f| tracker[type].include? f || check_for_failed_condition(f[:conditions])}
524
+ log :debug, "Running #{eligable.length} eligable #{type} filters:"
525
+ eligable.each do |f|
526
+ log :debug, " #{f[:conditions]} => #{log_object f[:proc]}"
527
+ tracker[type] << f
528
+ instance_exec(&f[:proc])
529
+ end
530
+ end
531
+
532
+ def log(type, message = nil, *args)
533
+ if config[:logger]
534
+ logger = config[:logger]
535
+ type = Logger.const_get(type.to_s.upcase)
536
+ logger.progname ||= 'Scorched'
537
+
538
+ case message
539
+ when :matches
540
+ message = []
541
+ eligable = matches.reject { |m| m.failed_condition }
542
+ message << "#{matches.length} mappings matched #{request.unmatched_path.inspect}:"
543
+ matches.each do |m|
544
+ message << " #{m.mapping[:pattern].inspect} => #{log_object m.mapping[:target]}"
545
+ end
546
+ message << "#{eligable.length} of which are eligable:"
547
+ eligable.each do |m|
548
+ message << " #{m.mapping[:pattern].inspect} => #{log_object m.mapping[:target]}"
549
+ end
550
+ end
551
+
552
+ [*message].each do |v|
553
+ v.insert(0, @_log_prefix)
554
+ logger.add(type, v)
496
555
  end
497
556
  end
557
+ true # Makes it safe to use in conditions
498
558
  end
499
559
 
500
- def log(type, message)
501
- config[:logger].progname ||= 'Scorched'
502
- type = Logger.const_get(type.to_s.upcase)
503
- config[:logger].add(type, message)
560
+ def log_object(obj)
561
+ case obj
562
+ when Proc
563
+ "<Proc @#{obj.source_location.join(':')}>"
564
+ when Array
565
+ obj.map { |v| log_object(v) }.inspect
566
+ else
567
+ obj.class.name || Controller === obj.class ? 'Anonymous controller' : 'Anonymous class'
568
+ end
504
569
  end
505
570
  end
506
571
  end
@@ -1,5 +1,4 @@
1
1
  module Scorched
2
- class Error < StandardError
3
-
2
+ class Error < RuntimeError
4
3
  end
5
4
  end
@@ -5,26 +5,32 @@ module Scorched
5
5
  def merge!(response)
6
6
  return self if response == self
7
7
  if Rack::Response === response
8
- response.finish
9
- self.status = response.status
10
- self.header.merge!(response.header)
11
- self.body = []
12
- response.each { |v| self.body << v }
13
- else
14
- self.status, @header, self.body = response
8
+ response = [response.status, response.header, response]
15
9
  end
10
+ self.status, self.body = response[0], response[2]
11
+ self.header.merge!(response[1])
12
+ self
16
13
  end
17
14
 
18
15
  # Automatically wraps the assigned value in an array if it doesn't respond to ``each``.
19
16
  # Also filters out non-true values and empty strings.
20
17
  def body=(value)
21
- value = [] if !value || value == ''
18
+ value = [] if !value || value == ''
22
19
  super(value.respond_to?(:each) ? value : [value.to_s])
23
20
  end
24
21
 
22
+ # Override finish to avoid using BodyProxy
25
23
  def finish(*args, &block)
26
24
  self['Content-Type'] ||= 'text/html;charset=utf-8'
27
- super
25
+ @block = block if block
26
+ if [204, 205, 304].include?(status.to_i)
27
+ header.delete "Content-Type"
28
+ header.delete "Content-Length"
29
+ close
30
+ [status.to_i, header, []]
31
+ else
32
+ [status.to_i, header, body]
33
+ end
28
34
  end
29
35
 
30
36
  alias :to_a :finish
@@ -1,3 +1,3 @@
1
1
  module Scorched
2
- VERSION = '0.11.1'
2
+ VERSION = '0.12'
3
3
  end
@@ -2,11 +2,12 @@ $LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
2
2
  require 'scorched/version'
3
3
 
4
4
  Gem::Specification.new 'scorched', Scorched::VERSION do |s|
5
- s.summary = "Light-weight, DRY as a desert, web framework for Ruby"
6
- s.description = "A lightweight Sinatra-inspired web framework for web sites and applications of any size."
7
- s.authors = ["Tom Wardrop"]
8
- s.email = "tom@tomwardrop.com"
9
- s.homepage = "http://scorchedrb.com"
5
+ s.summary = 'Light-weight, DRY as a desert, web framework for Ruby'
6
+ s.description = 'A light-weight Sinatra-inspired web framework for web sites and applications of any size.'
7
+ s.authors = ['Tom Wardrop']
8
+ s.email = 'tom@tomwardrop.com'
9
+ s.homepage = 'http://scorchedrb.com'
10
+ s.license = 'MIT'
10
11
  s.files = Dir.glob(`git ls-files`.split("\n") - %w[.gitignore])
11
12
  s.test_files = Dir.glob('spec/**/*_spec.rb')
12
13
  s.rdoc_options = %w[--line-numbers --inline-source --title Scorched --encoding=UTF-8]
@@ -537,6 +537,17 @@ module Scorched
537
537
  rt.get('/').status.should == 401
538
538
  end
539
539
 
540
+ it "takes an optional response body" do
541
+ app.get('/') { halt 'cool' }
542
+ rt.get('/').body.should == 'cool'
543
+ end
544
+
545
+ it "can take a status and a response body" do
546
+ app.get('/') { halt 401, 'cool' }
547
+ rt.get('/').status.should == 401
548
+ rt.get('/').body.should == 'cool'
549
+ end
550
+
540
551
  it "skips processing filters" do
541
552
  app.after { response.status = 403 }
542
553
  app.get('/') { halt }
@@ -546,7 +557,7 @@ module Scorched
546
557
  it "short circuits filters if halted within filter" do
547
558
  app.before { halt }
548
559
  app.after { response.status = 403 }
549
- rt.get('/').status.should == 200
560
+ rt.get('/').status.should_not == 403
550
561
  end
551
562
  end
552
563
 
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.11.1
4
+ version: '0.12'
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-05-10 00:00:00.000000000 Z
11
+ date: 2013-05-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -80,17 +80,18 @@ dependencies:
80
80
  - - ~>
81
81
  - !ruby/object:Gem::Version
82
82
  version: '2.9'
83
- description: A lightweight Sinatra-inspired web framework for web sites and applications
83
+ description: A light-weight Sinatra-inspired web framework for web sites and applications
84
84
  of any size.
85
85
  email: tom@tomwardrop.com
86
86
  executables: []
87
87
  extensions: []
88
88
  extra_rdoc_files: []
89
89
  files:
90
+ - CHANGES.md
90
91
  - Gemfile
91
92
  - LICENSE
92
- - Milestones.md
93
93
  - README.md
94
+ - TODO.md
94
95
  - docs/01_preface.md
95
96
  - docs/02_fundamentals/01_the_controller.md
96
97
  - docs/02_fundamentals/02_configuration.md
@@ -131,7 +132,8 @@ files:
131
132
  - spec/views/other.str
132
133
  - spec/views/partial.erb
133
134
  homepage: http://scorchedrb.com
134
- licenses: []
135
+ licenses:
136
+ - MIT
135
137
  metadata: {}
136
138
  post_install_message:
137
139
  rdoc_options: