roda 3.3.0 → 3.4.0

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
- SHA1:
3
- metadata.gz: 992b12eaa4153e29ad448ac21c6afafe12f9a89a
4
- data.tar.gz: 3ce43ca69c2faeb4a335968959365f33309460ed
2
+ SHA256:
3
+ metadata.gz: fc5d44e8586188ebb590d75d204b22fdd5b547c35e8631d31cf184ef5bfc0908
4
+ data.tar.gz: 03f023b11296d7cdebe72229d47988f3cf644ef3e80f243030e3b678c0ee9cb3
5
5
  SHA512:
6
- metadata.gz: af0baab222fcd78a7b49e5d8d2e204139ab7b1ec25cf75d001c2715777d31effa1527dda7ecfe9e1f18082978f77ad419d5c0b020667c6265d9273038d441483
7
- data.tar.gz: 4b7da58d7c976ed77f7da407090c14f38f912ac0dbead739cc676a16b10665d09866a95f739c852139919a260805e839a80ea0c9797a3da8413aaef632109046
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.
@@ -564,7 +564,9 @@ class Roda
564
564
  begin
565
565
  require 'uglifier'
566
566
  rescue => e
567
+ # :nocov:
567
568
  raise CompressorNotFound, "#{e.class}: #{e.message}", e.backtrace
569
+ # :nocov:
568
570
  end
569
571
 
570
572
  Uglifier.compile(content)
@@ -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] = EMPTY_ARRAY
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
@@ -4,7 +4,7 @@ class Roda
4
4
  RodaMajorVersion = 3
5
5
 
6
6
  # The minor version of Roda, updated for new feature releases of Roda.
7
- RodaMinorVersion = 3
7
+ RodaMinorVersion = 4
8
8
 
9
9
  # The patch version of Roda, updated only for bug fixes from the last
10
10
  # feature release.
@@ -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.3.0
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: 2017-12-14 00:00:00.000000000 Z
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.6.13
470
+ rubygems_version: 2.7.3
467
471
  signing_key:
468
472
  specification_version: 4
469
473
  summary: Routing tree web toolkit