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 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