roda 3.3.0 → 3.4.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 +5 -5
- data/CHANGELOG +6 -0
- data/doc/release_notes/3.4.0.txt +24 -0
- data/lib/roda/plugins/assets.rb +2 -0
- data/lib/roda/plugins/head.rb +24 -1
- data/lib/roda/plugins/middleware_stack.rb +101 -0
- data/lib/roda/version.rb +1 -1
- data/spec/plugin/head_spec.rb +17 -0
- data/spec/plugin/middleware_stack_spec.rb +81 -0
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: fc5d44e8586188ebb590d75d204b22fdd5b547c35e8631d31cf184ef5bfc0908
|
4
|
+
data.tar.gz: 03f023b11296d7cdebe72229d47988f3cf644ef3e80f243030e3b678c0ee9cb3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 942fb33ceeb0faef69467fa5b705bfd8b73f92bfb27557f58c9d459a9c1f1d4447eec1ac10c5cbf4eacf09293f20a4562e997125a41a226c5fb673154b85524d
|
7
|
+
data.tar.gz: e5d5f9533bed9c0c126a1203a9715bda8461b587bfb647a70a8aae1dd0a951cb5512d189c2e205ccbc1d67086afaa4e2a8c7104c741824c0ab3f8003fe999a98
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
= 3.4.0 (2018-01-12)
|
2
|
+
|
3
|
+
* Add middleware_stack plugin for removing middleware and inserting middleware before the end of the stack (jeremyevans)
|
4
|
+
|
5
|
+
* Make head plugin handle closing existing response bodies if the body responds to close (Eric Wong)
|
6
|
+
|
1
7
|
= 3.3.0 (2017-12-14)
|
2
8
|
|
3
9
|
* Add typecast_params plugin for converting param values to explicit types (jeremyevans)
|
@@ -0,0 +1,24 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* A middleware_stack plugin has been added for more detailed control
|
4
|
+
over middleware, allowing for the removal of middleware and the
|
5
|
+
insertion of middleware before existing middleware. Example:
|
6
|
+
|
7
|
+
plugin :middleware_stack
|
8
|
+
|
9
|
+
# Remove csrf middleware
|
10
|
+
middleware_stack.remove{|m, *args| m == Rack::Csrf}
|
11
|
+
|
12
|
+
# Insert csrf middleware before logger middleware
|
13
|
+
middleware_stack.before{|m, *args| m == Rack::CommonLogger}.
|
14
|
+
use(Rack::Csrf, raise: true)
|
15
|
+
|
16
|
+
# Insert csrf middleware after logger middleware
|
17
|
+
middleware_stack.after{|m, *args| m == Rack::CommonLogger}.
|
18
|
+
use(Rack::Csrf, raise: true)
|
19
|
+
|
20
|
+
= Other Improvements
|
21
|
+
|
22
|
+
* The head plugin now calls close on the response body if the body
|
23
|
+
responds to close. Previously an existing response body was
|
24
|
+
just ignored.
|
data/lib/roda/plugins/assets.rb
CHANGED
data/lib/roda/plugins/head.rb
CHANGED
@@ -37,13 +37,36 @@ class Roda
|
|
37
37
|
# this plugin those HEAD requests will return a 404 status, which
|
38
38
|
# may prevent search engines from crawling your website.
|
39
39
|
module Head
|
40
|
+
|
41
|
+
# used to ensure proper resource release on HEAD requests
|
42
|
+
# we do not respond to a to_path method, here.
|
43
|
+
class CloseLater
|
44
|
+
def initialize(body)
|
45
|
+
@body = body
|
46
|
+
end
|
47
|
+
|
48
|
+
# yield nothing
|
49
|
+
def each
|
50
|
+
end
|
51
|
+
|
52
|
+
# this should be called by the Rack server
|
53
|
+
def close
|
54
|
+
@body.close
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
40
58
|
module InstanceMethods
|
41
59
|
# Always use an empty response body for head requests, with a
|
42
60
|
# content length of 0.
|
43
61
|
def call(*)
|
44
62
|
res = super
|
45
63
|
if @_request.head?
|
46
|
-
res[2]
|
64
|
+
body = res[2]
|
65
|
+
if body.respond_to?(:close)
|
66
|
+
res[2] = CloseLater.new(body)
|
67
|
+
else
|
68
|
+
res[2] = EMPTY_ARRAY
|
69
|
+
end
|
47
70
|
end
|
48
71
|
res
|
49
72
|
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
class Roda
|
5
|
+
module RodaPlugins
|
6
|
+
# The middleware_stack plugin adds methods to remove middleware
|
7
|
+
# from the middleware stack, and insert new middleware at specific
|
8
|
+
# positions in the middleware stack.
|
9
|
+
#
|
10
|
+
# plugin :middleware_stack
|
11
|
+
#
|
12
|
+
# # Remove csrf middleware
|
13
|
+
# middleware_stack.remove{|m, *args| m == Rack::Csrf}
|
14
|
+
#
|
15
|
+
# # Insert csrf middleware
|
16
|
+
# middleware_stack.before{|m, *args| m == Rack::CommonLogger}.use(Rack::Csrf, raise: true)
|
17
|
+
# middleware_stack.after{|m, *args| m == Rack::CommonLogger}.use(Rack::Csrf, raise: true)
|
18
|
+
module MiddlewareStack
|
19
|
+
# Represents a specific position in the application's middleware stack where new
|
20
|
+
# middleware can be inserted.
|
21
|
+
class StackPosition
|
22
|
+
def initialize(app, middleware, position)
|
23
|
+
@app = app
|
24
|
+
@middleware = middleware
|
25
|
+
@position = position
|
26
|
+
end
|
27
|
+
|
28
|
+
# Insert a new middleware into the current position in the middleware stack.
|
29
|
+
# Increments the position so that calling this multiple times adds later
|
30
|
+
# middleware after earlier middleware, similar to how +Roda.use+ works.
|
31
|
+
def use(*args, &block)
|
32
|
+
@middleware.insert(@position, [args, block])
|
33
|
+
@app.send(:build_rack_app)
|
34
|
+
@position += 1
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Represents the applications middleware as a stack, allowing for easily
|
40
|
+
# removing middleware or finding places to insert new middleware.
|
41
|
+
class Stack
|
42
|
+
def initialize(app, middleware)
|
43
|
+
@app = app
|
44
|
+
@middleware = middleware
|
45
|
+
end
|
46
|
+
|
47
|
+
# Return a StackPosition representing the position after the middleware where
|
48
|
+
# the block returns true. Yields the middleware and any middleware arguments
|
49
|
+
# given, but not the middleware block.
|
50
|
+
# It the block never returns true, returns a StackPosition that will insert
|
51
|
+
# new middleware at the end of the stack.
|
52
|
+
def after(&block)
|
53
|
+
handle(1, &block)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Return a StackPosition representing the position before the middleware where
|
57
|
+
# the block returns true. Yields the middleware and any middleware arguments
|
58
|
+
# given, but not the middleware block.
|
59
|
+
# It the block never returns true, returns a StackPosition that will insert
|
60
|
+
# new middleware at the end of the stack.
|
61
|
+
def before(&block)
|
62
|
+
handle(0, &block)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Removes any middleware where the block returns true. Yields the middleware
|
66
|
+
# and any middleware arguments given, but not the middleware block
|
67
|
+
def remove
|
68
|
+
@middleware.delete_if do |m, _|
|
69
|
+
yield(*m)
|
70
|
+
end
|
71
|
+
@app.send(:build_rack_app)
|
72
|
+
nil
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
# Internals of before and after.
|
78
|
+
def handle(offset)
|
79
|
+
@middleware.each_with_index do |(m, _), i|
|
80
|
+
if yield(*m)
|
81
|
+
return StackPosition.new(@app, @middleware, i+offset)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
StackPosition.new(@app, @middleware, @middleware.length)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
module ClassMethods
|
90
|
+
# Return a new Stack that allows removing middleware and inserting
|
91
|
+
# middleware at specific places in the stack.
|
92
|
+
def middleware_stack
|
93
|
+
Stack.new(self, @middleware)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
register_plugin(:middleware_stack, MiddlewareStack)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
data/lib/roda/version.rb
CHANGED
data/spec/plugin/head_spec.rb
CHANGED
@@ -32,4 +32,21 @@ describe "head plugin" do
|
|
32
32
|
body('/b').must_equal 'b'
|
33
33
|
status('/b', 'REQUEST_METHOD' => 'HEAD').must_equal 200
|
34
34
|
end
|
35
|
+
|
36
|
+
it "releases resources via body.close" do
|
37
|
+
body = StringIO.new('hi')
|
38
|
+
app(:head) do |r|
|
39
|
+
r.root do
|
40
|
+
r.halt [ 200, {}, body ]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
s, h, b = req('REQUEST_METHOD' => 'HEAD')
|
44
|
+
s.must_equal 200
|
45
|
+
res = String.new
|
46
|
+
body.closed?.must_equal false
|
47
|
+
b.each { |buf| res << buf }
|
48
|
+
b.close
|
49
|
+
body.closed?.must_equal true
|
50
|
+
res.must_equal ''
|
51
|
+
end
|
35
52
|
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require_relative "../spec_helper"
|
2
|
+
|
3
|
+
describe "middleware_stack plugin" do
|
4
|
+
it "adds middleware_stack method for removing and inserting into middleware stack" do
|
5
|
+
make_middleware = lambda do |name|
|
6
|
+
Class.new do
|
7
|
+
define_singleton_method(:name){name}
|
8
|
+
|
9
|
+
attr_reader :app
|
10
|
+
attr_reader :args
|
11
|
+
attr_reader :block
|
12
|
+
def initialize(app, *args, &block)
|
13
|
+
@app = app
|
14
|
+
@args = args
|
15
|
+
@block = block
|
16
|
+
end
|
17
|
+
|
18
|
+
def call(env)
|
19
|
+
(env[:record] ||= []) << [self.class.name, args, block]
|
20
|
+
app.call(env)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
recorded = nil
|
26
|
+
|
27
|
+
app(:middleware_stack) do |r|
|
28
|
+
recorded = env[:record]
|
29
|
+
nil
|
30
|
+
end
|
31
|
+
|
32
|
+
status.must_equal 404
|
33
|
+
recorded.must_be_nil
|
34
|
+
|
35
|
+
called = false
|
36
|
+
app.middleware_stack.before{called = true}.use(make_middleware[:m1], :a1).must_be_nil
|
37
|
+
called.must_equal false
|
38
|
+
|
39
|
+
status.must_equal 404
|
40
|
+
recorded.must_equal [[:m1, [:a1], nil]]
|
41
|
+
|
42
|
+
app.middleware_stack.before{|m, *a| m.name == :m1}.use(make_middleware[:m2]).must_be_nil
|
43
|
+
|
44
|
+
status.must_equal 404
|
45
|
+
recorded.must_equal [[:m2, [], nil], [:m1, [:a1], nil]]
|
46
|
+
|
47
|
+
b = lambda{}
|
48
|
+
app.middleware_stack.before{|m, *a| m.name == :m1}.use(make_middleware[:m3], :a2, :a3, &b).must_be_nil
|
49
|
+
|
50
|
+
status.must_equal 404
|
51
|
+
recorded.must_equal [[:m2, [], nil], [:m3, [:a2, :a3], b], [:m1, [:a1], nil]]
|
52
|
+
|
53
|
+
app.middleware_stack.after{|m, *a| m.name == :m4}.use(make_middleware[:m4]).must_be_nil
|
54
|
+
status.must_equal 404
|
55
|
+
recorded.must_equal [[:m2, [], nil], [:m3, [:a2, :a3], b], [:m1, [:a1], nil], [:m4, [], nil]]
|
56
|
+
|
57
|
+
app.middleware_stack.after{|m, *a| m.name == :m4}.use(make_middleware[:m5]).must_be_nil
|
58
|
+
status.must_equal 404
|
59
|
+
recorded.must_equal [[:m2, [], nil], [:m3, [:a2, :a3], b], [:m1, [:a1], nil], [:m4, [], nil], [:m5, [], nil]]
|
60
|
+
|
61
|
+
app.middleware_stack.after{|m, *a| a == [:a1]}.use(make_middleware[:m6]).must_be_nil
|
62
|
+
status.must_equal 404
|
63
|
+
recorded.must_equal [[:m2, [], nil], [:m3, [:a2, :a3], b], [:m1, [:a1], nil], [:m6, [], nil], [:m4, [], nil], [:m5, [], nil]]
|
64
|
+
|
65
|
+
app.middleware_stack.remove{|m, *a| a.empty?}.must_be_nil
|
66
|
+
status.must_equal 404
|
67
|
+
recorded.must_equal [[:m3, [:a2, :a3], b], [:m1, [:a1], nil]]
|
68
|
+
|
69
|
+
sp = app.middleware_stack.after{|m, *a| m.name == :m3}
|
70
|
+
sp.use(make_middleware[:m7])
|
71
|
+
sp.use(make_middleware[:m8])
|
72
|
+
status.must_equal 404
|
73
|
+
recorded.must_equal [[:m3, [:a2, :a3], b], [:m7, [], nil], [:m8, [], nil], [:m1, [:a1], nil]]
|
74
|
+
|
75
|
+
sp = app.middleware_stack.before{|m, *a| m.name == :m8}
|
76
|
+
sp.use(make_middleware[:m9])
|
77
|
+
sp.use(make_middleware[:m10])
|
78
|
+
status.must_equal 404
|
79
|
+
recorded.must_equal [[:m3, [:a2, :a3], b], [:m7, [], nil], [:m9, [], nil], [:m10, [], nil], [:m8, [], nil], [:m1, [:a1], nil]]
|
80
|
+
end
|
81
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: roda
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremy Evans
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-01-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -199,6 +199,7 @@ extra_rdoc_files:
|
|
199
199
|
- doc/release_notes/3.1.0.txt
|
200
200
|
- doc/release_notes/3.2.0.txt
|
201
201
|
- doc/release_notes/3.3.0.txt
|
202
|
+
- doc/release_notes/3.4.0.txt
|
202
203
|
files:
|
203
204
|
- CHANGELOG
|
204
205
|
- MIT-LICENSE
|
@@ -244,6 +245,7 @@ files:
|
|
244
245
|
- doc/release_notes/3.1.0.txt
|
245
246
|
- doc/release_notes/3.2.0.txt
|
246
247
|
- doc/release_notes/3.3.0.txt
|
248
|
+
- doc/release_notes/3.4.0.txt
|
247
249
|
- lib/roda.rb
|
248
250
|
- lib/roda/plugins/_symbol_regexp_matchers.rb
|
249
251
|
- lib/roda/plugins/all_verbs.rb
|
@@ -284,6 +286,7 @@ files:
|
|
284
286
|
- lib/roda/plugins/mailer.rb
|
285
287
|
- lib/roda/plugins/match_affix.rb
|
286
288
|
- lib/roda/plugins/middleware.rb
|
289
|
+
- lib/roda/plugins/middleware_stack.rb
|
287
290
|
- lib/roda/plugins/module_include.rb
|
288
291
|
- lib/roda/plugins/multi_route.rb
|
289
292
|
- lib/roda/plugins/multi_run.rb
|
@@ -377,6 +380,7 @@ files:
|
|
377
380
|
- spec/plugin/mailer_spec.rb
|
378
381
|
- spec/plugin/match_affix_spec.rb
|
379
382
|
- spec/plugin/middleware_spec.rb
|
383
|
+
- spec/plugin/middleware_stack_spec.rb
|
380
384
|
- spec/plugin/module_include_spec.rb
|
381
385
|
- spec/plugin/multi_route_spec.rb
|
382
386
|
- spec/plugin/multi_run_spec.rb
|
@@ -463,7 +467,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
463
467
|
version: '0'
|
464
468
|
requirements: []
|
465
469
|
rubyforge_project:
|
466
|
-
rubygems_version: 2.
|
470
|
+
rubygems_version: 2.7.3
|
467
471
|
signing_key:
|
468
472
|
specification_version: 4
|
469
473
|
summary: Routing tree web toolkit
|