roda 2.6.0 → 2.7.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 +4 -4
- data/CHANGELOG +14 -0
- data/doc/release_notes/2.7.0.txt +75 -0
- data/lib/roda.rb +28 -30
- data/lib/roda/plugins/assets.rb +12 -0
- data/lib/roda/plugins/default_status.rb +33 -0
- data/lib/roda/plugins/halt.rb +7 -1
- data/lib/roda/plugins/header_matchers.rb +15 -2
- data/lib/roda/plugins/path_rewriter.rb +35 -6
- data/lib/roda/plugins/response_request.rb +26 -0
- data/lib/roda/plugins/run_handler.rb +44 -0
- data/lib/roda/version.rb +1 -1
- data/spec/plugin/default_status_spec.rb +65 -0
- data/spec/plugin/halt_spec.rb +10 -0
- data/spec/plugin/header_matchers_spec.rb +25 -1
- data/spec/plugin/path_rewriter_spec.rb +9 -1
- data/spec/plugin/response_request_spec.rb +12 -0
- data/spec/plugin/run_handler_spec.rb +53 -0
- metadata +10 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ba4bf20bc11b1e1d4482325c64d4ae439a7665dd
|
4
|
+
data.tar.gz: 2f17d46442001a41ba9223db2b71545d11e7ac3e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 747c00137a2db4494558398d0e85a659c8d247ef330431239787d135cd7213c91ef5cfdb5585c315b76676b8a016a64f33ee4c6d711927e04ed95839e537430e
|
7
|
+
data.tar.gz: 930fd61e842f5ca4bbbbf19c9efcfb881e10651f2e2f9f7029388d9c0d84fc27d1cbae5f364e7248aa33edb011d8fbf82518a3a7074844990a58e818fb517fb6
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
= 2.7.0 (2015-10-13)
|
2
|
+
|
3
|
+
* Add run_handler plugin for modifying rack response arrays when using r.run, and continuing routing for 404 responses (jeremyevans)
|
4
|
+
|
5
|
+
* Add response_request plugin allowing response object access to request object (jeremyevans)
|
6
|
+
|
7
|
+
* Add default_status plugin for overriding the default response status (celsworth) (#47)
|
8
|
+
|
9
|
+
* Make RodaCache synchronize access on MRI (jeremyevans)
|
10
|
+
|
11
|
+
* Support opts[:host_matcher_captures] = true to make :host=>/regexp/ matcher yield captures in the header_matchers plugin (jeremyevans)
|
12
|
+
|
13
|
+
* Allow Roda.rewrite_path to take a block in the path_rewriter plugin (Freaky) (#45)
|
14
|
+
|
1
15
|
= 2.6.0 (2015-09-14)
|
2
16
|
|
3
17
|
* Add :params and :params! matchers to param_matchers plugin (jeremyevans)
|
@@ -0,0 +1,75 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* A default_status plugin has been added for changing the default
|
4
|
+
status for responses. Previously, the default status was hard
|
5
|
+
coded to 200, this plugin allows you to change it. The plugin
|
6
|
+
takes a block which is instance_evaled in the context of the
|
7
|
+
response:
|
8
|
+
|
9
|
+
plugin :default_status do
|
10
|
+
headers['Content-Type'] == 'foo' ? 201 : 200
|
11
|
+
end
|
12
|
+
|
13
|
+
Note that the default status for empty responses (used when no
|
14
|
+
route handles the response) is still 404, this just changes the
|
15
|
+
default for non-empty responses.
|
16
|
+
|
17
|
+
* A response_request plugin has been added for giving the response
|
18
|
+
instance access to the related request. This can be useful in
|
19
|
+
conjunction with the default_status plugin, if you want the
|
20
|
+
default status of the response to depend on the request, such as
|
21
|
+
using a different status for different request methods:
|
22
|
+
|
23
|
+
plugin :response_request
|
24
|
+
plugin :default_status do
|
25
|
+
request.post? ? 201 : 200
|
26
|
+
end
|
27
|
+
|
28
|
+
* A run_handler plugin has been added, for modifying rack response
|
29
|
+
arrays before returning them when using r.run. Additionally, it
|
30
|
+
allows for continuing with routing if the response returned by
|
31
|
+
r.run is a 404 response, using the :not_found=>:pass option:
|
32
|
+
|
33
|
+
plugin :run_handler
|
34
|
+
route do |r|
|
35
|
+
# Keep running code if RackAppFoo returns a 404 response
|
36
|
+
r.run RackAppFoo, :not_found=>:pass
|
37
|
+
|
38
|
+
# Change response status codes before returning.
|
39
|
+
r.run(RackAppBar) do |response|
|
40
|
+
response[0] = 200 if response[0] == 201
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
* Roda.rewite_path in the path_rewriter extension now accepts a block
|
45
|
+
to allow for dynamic replacements. The block is yielded a MatchData
|
46
|
+
instance:
|
47
|
+
|
48
|
+
rewrite_path(/\A\/a/(\w+)/){|match| match[1].capitalize}
|
49
|
+
# PATH_INFO '/a/moo' => remaining_path '/a/Moo'
|
50
|
+
|
51
|
+
rewrite_path(/\A\/a/(\w+)/, :path_info => true) do |match|
|
52
|
+
match[1].capitalize
|
53
|
+
end
|
54
|
+
# PATH_INFO '/a/moo' => PATH_INFO '/a/Moo'
|
55
|
+
|
56
|
+
* The :host matcher in the header_matchers plugin will now yield the
|
57
|
+
regexp captures to the block if given a regexp when the
|
58
|
+
:host_matcher_captures application option is set. This behavior
|
59
|
+
will become the default behavior in Roda 3. This will allow for
|
60
|
+
code like:
|
61
|
+
|
62
|
+
opts[:host_matcher_captures] = true
|
63
|
+
route do |r|
|
64
|
+
r.on :host=>/\A(\w+).example.com\z/ do |subdomain|
|
65
|
+
# ...
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
= Other Improvements
|
70
|
+
|
71
|
+
* RodaCache now uses a mutex to synchronize access on MRI.
|
72
|
+
Previously, it relied on the global interpreter lock, but testing
|
73
|
+
has shown that is not reliable in all cases. RodaCache has always
|
74
|
+
used a mutex for synchronization on other ruby implementations,
|
75
|
+
this just extends that code to MRI as well.
|
data/lib/roda.rb
CHANGED
@@ -9,33 +9,24 @@ class Roda
|
|
9
9
|
# Error class raised by Roda
|
10
10
|
class RodaError < StandardError; end
|
11
11
|
|
12
|
-
|
13
|
-
#
|
14
|
-
|
15
|
-
#
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
@mutex = Mutex.new
|
21
|
-
@hash = {}
|
22
|
-
end
|
12
|
+
# A thread safe cache class, offering only #[] and #[]= methods,
|
13
|
+
# each protected by a mutex.
|
14
|
+
class RodaCache
|
15
|
+
# Create a new thread safe cache.
|
16
|
+
def initialize
|
17
|
+
@mutex = Mutex.new
|
18
|
+
@hash = {}
|
19
|
+
end
|
23
20
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
21
|
+
# Make getting value from underlying hash thread safe.
|
22
|
+
def [](key)
|
23
|
+
@mutex.synchronize{@hash[key]}
|
24
|
+
end
|
28
25
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
end
|
26
|
+
# Make setting value in underlying hash thread safe.
|
27
|
+
def []=(key, value)
|
28
|
+
@mutex.synchronize{@hash[key] = value}
|
33
29
|
end
|
34
|
-
# :nocov:
|
35
|
-
else
|
36
|
-
# Hashes are already thread-safe in MRI, due to the GVL, so they
|
37
|
-
# can safely be used as a cache.
|
38
|
-
RodaCache = Hash
|
39
30
|
end
|
40
31
|
|
41
32
|
# Base class used for Roda requests. The instance methods for this
|
@@ -233,7 +224,7 @@ class Roda
|
|
233
224
|
# This is the same object yielded by Roda.route.
|
234
225
|
# response :: The instance of the response class related to this request.
|
235
226
|
module InstanceMethods
|
236
|
-
# Create a request and response of the
|
227
|
+
# Create a request and response of the appropriate class
|
237
228
|
def initialize(env)
|
238
229
|
klass = self.class
|
239
230
|
@_request = klass::RodaRequest.new(self, env)
|
@@ -882,9 +873,9 @@ class Roda
|
|
882
873
|
|
883
874
|
# Return the rack response array of status, headers, and body
|
884
875
|
# for the current response. If the status has not been set,
|
885
|
-
# uses
|
886
|
-
# uses a 404 status.
|
887
|
-
# size of the response body.
|
876
|
+
# uses the return value of default_status if the body has
|
877
|
+
# been written to, otherwise uses a 404 status.
|
878
|
+
# Adds the Content-Length header to the size of the response body.
|
888
879
|
#
|
889
880
|
# Example:
|
890
881
|
#
|
@@ -894,7 +885,7 @@ class Roda
|
|
894
885
|
# # []]
|
895
886
|
def finish
|
896
887
|
b = @body
|
897
|
-
s = (@status ||= b.empty? ? 404 :
|
888
|
+
s = (@status ||= b.empty? ? 404 : default_status)
|
898
889
|
set_default_headers
|
899
890
|
h = @headers
|
900
891
|
h[CONTENT_LENGTH] ||= @length.to_s
|
@@ -907,7 +898,14 @@ class Roda
|
|
907
898
|
# body.
|
908
899
|
def finish_with_body(body)
|
909
900
|
set_default_headers
|
910
|
-
[@status ||
|
901
|
+
[@status || default_status, @headers, body]
|
902
|
+
end
|
903
|
+
|
904
|
+
# Return the default response status to be used when the body
|
905
|
+
# has been written to. This is split out to make overriding
|
906
|
+
# easier in plugins.
|
907
|
+
def default_status
|
908
|
+
200
|
911
909
|
end
|
912
910
|
|
913
911
|
# Show response class, status code, response headers, and response body
|
data/lib/roda/plugins/assets.rb
CHANGED
@@ -185,6 +185,18 @@ class Roda
|
|
185
185
|
# end
|
186
186
|
# end
|
187
187
|
#
|
188
|
+
# == External Assets/Assets from Gems
|
189
|
+
#
|
190
|
+
# The assets plugin only supports loading assets files underneath the assets
|
191
|
+
# path. You cannot pass an absolute path to an asset file and have it
|
192
|
+
# work. If you would like to reference asset files that are outside the assets
|
193
|
+
# path, you have the following options:
|
194
|
+
#
|
195
|
+
# * Copy, hard link, or symlink the external assets files into the assets path.
|
196
|
+
# * Use tilt-indirect or another method of indirection (such as an erb template that loads
|
197
|
+
# the external asset file) so that a file inside the assets path can reference files
|
198
|
+
# outside the assets path.
|
199
|
+
#
|
188
200
|
# == Plugin Options
|
189
201
|
#
|
190
202
|
# :add_suffix :: Whether to append a .css or .js extension to asset routes in non-compiled mode
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class Roda
|
2
|
+
module RodaPlugins
|
3
|
+
# The default_status plugin accepts a block which should
|
4
|
+
# return a response status integer. This integer will be used as
|
5
|
+
# the default response status (usually 200) if the body has been
|
6
|
+
# written to, and you have not explicitly set a response status.
|
7
|
+
# The block given to the block is instance_execed in the context
|
8
|
+
# of the response.
|
9
|
+
#
|
10
|
+
# Example:
|
11
|
+
#
|
12
|
+
# # Use 201 default response status for all requests
|
13
|
+
# plugin :default_status do
|
14
|
+
# 201
|
15
|
+
# end
|
16
|
+
module DefaultStatus
|
17
|
+
def self.configure(app, &block)
|
18
|
+
raise RodaError, "default_status plugin requires a block" unless block
|
19
|
+
app.opts[:default_status] = block
|
20
|
+
end
|
21
|
+
|
22
|
+
module ResponseMethods
|
23
|
+
# instance_exec the default_status plugin block to get the response
|
24
|
+
# status.
|
25
|
+
def default_status
|
26
|
+
instance_exec(&roda_class.opts[:default_status])
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
register_plugin(:default_status, DefaultStatus)
|
32
|
+
end
|
33
|
+
end
|
data/lib/roda/plugins/halt.rb
CHANGED
@@ -31,7 +31,13 @@ class Roda
|
|
31
31
|
# r.halt(403, {'Content-Type'=>'text/csv'}, 'body')
|
32
32
|
# end
|
33
33
|
#
|
34
|
-
#
|
34
|
+
# As supported by default, you can still pass an array which contains a rack response:
|
35
|
+
#
|
36
|
+
# route do |r|
|
37
|
+
# r.halt([403, {'Content-Type'=>'text/csv'}, ['body']])
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# Note that there is a difference between providing status, headers, and body as separate
|
35
41
|
# arguments and providing them as a single rack response array. With a rack response array,
|
36
42
|
# the values are used directly, while with 3 arguments, the headers given are merged into
|
37
43
|
# the existing headers and the given body is written to the existing response body.
|
@@ -15,7 +15,14 @@ class Roda
|
|
15
15
|
#
|
16
16
|
# r.on :host=>'foo.example.com' do
|
17
17
|
# end
|
18
|
-
# r.on :host=>/\A\w+.example.com/ do
|
18
|
+
# r.on :host=>/\A\w+.example.com\z/ do
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# By default the +:host+ matcher does not yield matchers, but if you use a regexp
|
22
|
+
# and set the +:host_matcher_captures+ option for the application, it will
|
23
|
+
# yield regexp captures:
|
24
|
+
#
|
25
|
+
# r.on :host=>/\A(\w+).example.com\z/ do |subdomain|
|
19
26
|
# end
|
20
27
|
#
|
21
28
|
# It adds a +:user_agent+ matcher for matching on a user agent patterns, which
|
@@ -52,7 +59,13 @@ class Roda
|
|
52
59
|
# Match if the host of the request is the same as the hostname. +hostname+
|
53
60
|
# can be a regexp or a string.
|
54
61
|
def match_host(hostname)
|
55
|
-
hostname
|
62
|
+
if hostname.is_a?(Regexp) && roda_class.opts[:host_matcher_captures]
|
63
|
+
if match = hostname.match(host)
|
64
|
+
@captures.concat(match.captures)
|
65
|
+
end
|
66
|
+
else
|
67
|
+
hostname === host
|
68
|
+
end
|
56
69
|
end
|
57
70
|
|
58
71
|
# Match the submitted user agent to the given pattern, capturing any
|
@@ -30,6 +30,14 @@ class Roda
|
|
30
30
|
# # PATH_INFO '/a' => remaining_path '/b'
|
31
31
|
# # PATH_INFO '/a/c' => remaining_path '/a/c', no change
|
32
32
|
#
|
33
|
+
# Patterns can be rewritten dynamically by providing a block accepting a MatchData
|
34
|
+
# object and evaluating to the replacement.
|
35
|
+
#
|
36
|
+
# rewrite_path(/\A\/a/(\w+)/){|match| match[1].capitalize}
|
37
|
+
# # PATH_INFO '/a/moo' => remaining_path '/a/Moo'
|
38
|
+
# rewrite_path(/\A\/a/(\w+)/, :path_info => true){|match| match[1].capitalize}
|
39
|
+
# # PATH_INFO '/a/moo' => PATH_INFO '/a/Moo'
|
40
|
+
#
|
33
41
|
# All path rewrites are applied in order, so if a path is rewritten by one rewrite,
|
34
42
|
# it can be rewritten again by a later rewrite. Note that PATH_INFO rewrites are
|
35
43
|
# processed before remaining_path rewrites.
|
@@ -54,7 +62,18 @@ class Roda
|
|
54
62
|
|
55
63
|
# Record a path rewrite from path +was+ to path +is+. Options:
|
56
64
|
# :path_info :: Modify PATH_INFO, not just remaining path.
|
57
|
-
def rewrite_path(was, is, opts=OPTS)
|
65
|
+
def rewrite_path(was, is = nil, opts=OPTS, &block)
|
66
|
+
if is.is_a? Hash
|
67
|
+
raise RodaError, "cannot provide two hashes to rewrite_path" unless opts.empty?
|
68
|
+
opts = is
|
69
|
+
is = nil
|
70
|
+
end
|
71
|
+
|
72
|
+
if block
|
73
|
+
raise RodaError, "cannot provide both block and string replacement to rewrite_path" if is
|
74
|
+
is = block
|
75
|
+
end
|
76
|
+
|
58
77
|
was = /\A#{Regexp.escape(was)}/ unless was.is_a?(Regexp)
|
59
78
|
array = @opts[opts[:path_info] ? :path_info_rewrites : :remaining_path_rewrites]
|
60
79
|
array << [was, is.dup.freeze].freeze
|
@@ -65,13 +84,23 @@ class Roda
|
|
65
84
|
# Rewrite remaining_path and/or PATH_INFO based on the path rewrites.
|
66
85
|
def initialize(scope, env)
|
67
86
|
path_info = env[PATH_INFO]
|
68
|
-
|
69
|
-
|
70
|
-
end
|
87
|
+
|
88
|
+
rewrite_path(scope.class.opts[:path_info_rewrites], path_info)
|
71
89
|
super
|
72
90
|
remaining_path = @remaining_path = @remaining_path.dup
|
73
|
-
scope.class.opts[:remaining_path_rewrites]
|
74
|
-
|
91
|
+
rewrite_path(scope.class.opts[:remaining_path_rewrites], remaining_path)
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
# Rewrite the given path using the given replacements.
|
97
|
+
def rewrite_path(replacements, path)
|
98
|
+
replacements.each do |was, is|
|
99
|
+
if is.is_a?(Proc)
|
100
|
+
path.sub!(was){is.call($~)}
|
101
|
+
else
|
102
|
+
path.sub!(was, is)
|
103
|
+
end
|
75
104
|
end
|
76
105
|
end
|
77
106
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class Roda
|
2
|
+
module RodaPlugins
|
3
|
+
# The response_request plugin gives the response access to the
|
4
|
+
# related request instance via the #request method.
|
5
|
+
#
|
6
|
+
# Example:
|
7
|
+
#
|
8
|
+
# plugin :response_request
|
9
|
+
module ResponseRequest
|
10
|
+
module InstanceMethods
|
11
|
+
# Set the response's request to the current request.
|
12
|
+
def initialize(env)
|
13
|
+
super
|
14
|
+
@_response.request = @_request
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module ResponseMethods
|
19
|
+
# The request related to this response.
|
20
|
+
attr_accessor :request
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
register_plugin(:response_request, ResponseRequest)
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
class Roda
|
2
|
+
module RodaPlugins
|
3
|
+
# The run_handler plugin allows r.run to take a block, which is yielded
|
4
|
+
# the rack response array, before it returns it as a response.
|
5
|
+
#
|
6
|
+
# Additionally, r.run also takes a options hash, and you can provide a
|
7
|
+
# <tt>:not_found=>:pass</tt> option to keep routing normally if the rack
|
8
|
+
# app returns a 404 response.
|
9
|
+
#
|
10
|
+
#
|
11
|
+
# plugin :run_handler
|
12
|
+
#
|
13
|
+
# route do |r|
|
14
|
+
# r.on 'a' do
|
15
|
+
# # Keep running code if RackAppFoo doesn't return a result
|
16
|
+
# r.run RackAppFoo, :not_found=>:pass
|
17
|
+
#
|
18
|
+
# # Change response status codes before returning.
|
19
|
+
# r.run(RackAppBar) do |response|
|
20
|
+
# response[0] = 200 if response[0] == 201
|
21
|
+
# end
|
22
|
+
# end
|
23
|
+
# end
|
24
|
+
module RunHandler
|
25
|
+
OPTS = {}.freeze
|
26
|
+
|
27
|
+
module RequestMethods
|
28
|
+
# If a block is given, yield the rack response array to it. The response can
|
29
|
+
# be modified before it is returned by the current app.
|
30
|
+
#
|
31
|
+
# If the <tt>:not_found=>:pass</tt> option is given, and the rack response
|
32
|
+
# returned by the app is a 404 response, do not return the response, continue
|
33
|
+
# routing normally.
|
34
|
+
def run(app, opts=OPTS)
|
35
|
+
res = catch(:halt){super(app)}
|
36
|
+
yield res if block_given?
|
37
|
+
throw(:halt, res) unless opts[:not_found] == :pass && res[0] == 404
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
register_plugin(:run_handler, RunHandler)
|
43
|
+
end
|
44
|
+
end
|
data/lib/roda/version.rb
CHANGED
@@ -0,0 +1,65 @@
|
|
1
|
+
require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
|
2
|
+
|
3
|
+
describe "default_status plugin" do
|
4
|
+
it "sets the default response status to use for the response" do
|
5
|
+
app(:bare) do
|
6
|
+
plugin :default_status do
|
7
|
+
201
|
8
|
+
end
|
9
|
+
route do |r|
|
10
|
+
r.halt response.finish_with_body([])
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
status.must_equal 201
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should instance_exec the plugin block" do
|
18
|
+
app(:bare) do
|
19
|
+
plugin :default_status do
|
20
|
+
200 + @body[0].length
|
21
|
+
end
|
22
|
+
route do |r|
|
23
|
+
r.path_info
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
status.must_equal 201
|
28
|
+
status('/foo').must_equal 204
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should not override existing response" do
|
32
|
+
app(:bare) do
|
33
|
+
plugin :default_status do
|
34
|
+
201
|
35
|
+
end
|
36
|
+
|
37
|
+
route do |r|
|
38
|
+
response.status = 202
|
39
|
+
r.halt response.finish_with_body([])
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
status.must_equal 202
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should work correctly in subclasses" do
|
47
|
+
app(:bare) do
|
48
|
+
plugin :default_status do
|
49
|
+
201
|
50
|
+
end
|
51
|
+
|
52
|
+
route do |r|
|
53
|
+
r.halt response.finish_with_body([])
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
@app = Class.new(@app)
|
58
|
+
|
59
|
+
status.must_equal 201
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should raise if not given a block" do
|
63
|
+
proc{app(:default_status)}.must_raise Roda::RodaError
|
64
|
+
end
|
65
|
+
end
|
data/spec/plugin/halt_spec.rb
CHANGED
@@ -69,6 +69,16 @@ describe "halt plugin" do
|
|
69
69
|
body.must_equal "foo"
|
70
70
|
end
|
71
71
|
|
72
|
+
it "should consider an array as a rack response" do
|
73
|
+
app(:halt) do |r|
|
74
|
+
r.halt [300, {'a'=>'b'}, ["foo"]]
|
75
|
+
end
|
76
|
+
|
77
|
+
status.must_equal 300
|
78
|
+
header('a').must_equal 'b'
|
79
|
+
body.must_equal "foo"
|
80
|
+
end
|
81
|
+
|
72
82
|
it "should handle 3rd of 3 arguments similar to block bodies" do
|
73
83
|
app(:bare) do
|
74
84
|
plugin :halt
|
@@ -64,7 +64,7 @@ describe "host matcher" do
|
|
64
64
|
status("HTTP_HOST" => "foo.com").must_equal 404
|
65
65
|
end
|
66
66
|
|
67
|
-
it "doesn't yield
|
67
|
+
it "doesn't yield host" do
|
68
68
|
app(:header_matchers) do |r|
|
69
69
|
r.on :host=>"example.com" do |*args|
|
70
70
|
args.size.to_s
|
@@ -73,6 +73,30 @@ describe "host matcher" do
|
|
73
73
|
|
74
74
|
body("HTTP_HOST" => "example.com").must_equal '0'
|
75
75
|
end
|
76
|
+
|
77
|
+
it "yields host if passed a regexp and opts[:host_matcher_captures] is set" do
|
78
|
+
app(:header_matchers) do |r|
|
79
|
+
r.on :host=>/\A(.*)\.example\.com\z/ do |*m|
|
80
|
+
m.empty? ? '0' : m[0]
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
body("HTTP_HOST" => "foo.example.com").must_equal '0'
|
85
|
+
app.opts[:host_matcher_captures] = true
|
86
|
+
body("HTTP_HOST" => "foo.example.com").must_equal 'foo'
|
87
|
+
end
|
88
|
+
|
89
|
+
it "doesn't yields host if passed a string and opts[:host_matcher_captures] is set" do
|
90
|
+
app(:header_matchers) do |r|
|
91
|
+
r.on :host=>'example.com' do |*m|
|
92
|
+
m.empty? ? '0' : m[0]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
body("HTTP_HOST" => "example.com").must_equal '0'
|
97
|
+
app.opts[:host_matcher_captures] = true
|
98
|
+
body("HTTP_HOST" => "example.com").must_equal '0'
|
99
|
+
end
|
76
100
|
end
|
77
101
|
|
78
102
|
describe "user_agent matcher" do
|
@@ -11,6 +11,12 @@ describe "path_rewriter plugin" do
|
|
11
11
|
rewrite_path '/3', '/h'
|
12
12
|
rewrite_path '/3', '/g', :path_info=>true
|
13
13
|
rewrite_path(/\A\/e\z/, '/f')
|
14
|
+
rewrite_path(/\A\/(dynamic1)/){|match| "/#{match[1].capitalize}"}
|
15
|
+
rewrite_path(/\A\/(dynamic2)/, :path_info=>true){|match| "/#{match[1].capitalize}"}
|
16
|
+
proc{rewrite_path('/a', '/z'){|match| "/x"}}.must_raise(Roda::RodaError)
|
17
|
+
proc{rewrite_path('/a', {:path_info=>true}, :path_info=>true)}.must_raise(Roda::RodaError)
|
18
|
+
proc{rewrite_path('/a', {:path_info=>true}, :path_info=>true){|match| "/x"}}.must_raise(Roda::RodaError)
|
19
|
+
|
14
20
|
route do |r|
|
15
21
|
"#{r.path_info}:#{r.remaining_path}"
|
16
22
|
end
|
@@ -29,7 +35,9 @@ describe "path_rewriter plugin" do
|
|
29
35
|
body('/2').must_equal '/1:/b'
|
30
36
|
body('/2/f').must_equal '/1/f:/b/f'
|
31
37
|
body('/3').must_equal '/g:/g'
|
32
|
-
|
38
|
+
body('/dynamic1').must_equal '/dynamic1:/Dynamic1'
|
39
|
+
body('/dynamic2').must_equal '/Dynamic2:/Dynamic2'
|
40
|
+
|
33
41
|
app.freeze
|
34
42
|
body('/a').must_equal '/a:/b'
|
35
43
|
proc{app.rewrite_path '/a', '/b'}.must_raise
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
|
2
|
+
|
3
|
+
describe "response_request plugin" do
|
4
|
+
it "gives the response access to the request" do
|
5
|
+
app(:response_request) do
|
6
|
+
response.request.post? ? "b" : "a"
|
7
|
+
end
|
8
|
+
|
9
|
+
body.must_equal "a"
|
10
|
+
body('REQUEST_METHOD'=>'POST').must_equal "b"
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
|
2
|
+
|
3
|
+
describe "run_handler plugin" do
|
4
|
+
it "makes r.run :not_found=>:pass keep going on 404" do
|
5
|
+
pr = proc{|env| [(env['PATH_INFO'] == '/a' ? 404 : 201), {}, ['b']]}
|
6
|
+
app(:run_handler) do |r|
|
7
|
+
r.run pr, :not_found=>:pass
|
8
|
+
'a'
|
9
|
+
end
|
10
|
+
|
11
|
+
status.must_equal 201
|
12
|
+
body.must_equal 'b'
|
13
|
+
status('/a').must_equal 200
|
14
|
+
body('/a').must_equal 'a'
|
15
|
+
end
|
16
|
+
|
17
|
+
it "makes r.run with a block yield rack app to block, and have it be thrown afterward" do
|
18
|
+
pr = proc{|env| [(env['PATH_INFO'] == '/a' ? 404 : 201), {}, ['b']]}
|
19
|
+
app(:run_handler) do |r|
|
20
|
+
r.run(pr){|a| a[0] *= 2}
|
21
|
+
'a'
|
22
|
+
end
|
23
|
+
|
24
|
+
status.must_equal 402
|
25
|
+
status('/a').must_equal 808
|
26
|
+
end
|
27
|
+
|
28
|
+
it "works when both :not_found=>:pass and block are given" do
|
29
|
+
pr = proc{|env| [(env['PATH_INFO'] == '/a' ? 202 : 201), {}, ['b']]}
|
30
|
+
app(:run_handler) do |r|
|
31
|
+
r.run(pr, :not_found=>:pass){|a| a[0] *= 2}
|
32
|
+
'a'
|
33
|
+
end
|
34
|
+
|
35
|
+
status.must_equal 402
|
36
|
+
body.must_equal 'b'
|
37
|
+
status('/a').must_equal 200
|
38
|
+
body('/a').must_equal 'a'
|
39
|
+
end
|
40
|
+
|
41
|
+
it "makes r.run work normally if not given an option or block" do
|
42
|
+
pr = proc{|env| [(env['PATH_INFO'] == '/a' ? 404 : 201), {}, ['b']]}
|
43
|
+
app(:run_handler) do |r|
|
44
|
+
r.run pr
|
45
|
+
'a'
|
46
|
+
end
|
47
|
+
|
48
|
+
status.must_equal 201
|
49
|
+
body.must_equal 'b'
|
50
|
+
status('/a').must_equal 404
|
51
|
+
body('/a').must_equal 'b'
|
52
|
+
end
|
53
|
+
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: 2.
|
4
|
+
version: 2.7.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: 2015-
|
11
|
+
date: 2015-10-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -144,6 +144,7 @@ extra_rdoc_files:
|
|
144
144
|
- doc/release_notes/2.5.0.txt
|
145
145
|
- doc/release_notes/2.5.1.txt
|
146
146
|
- doc/release_notes/2.6.0.txt
|
147
|
+
- doc/release_notes/2.7.0.txt
|
147
148
|
files:
|
148
149
|
- CHANGELOG
|
149
150
|
- MIT-LICENSE
|
@@ -162,6 +163,7 @@ files:
|
|
162
163
|
- doc/release_notes/2.5.0.txt
|
163
164
|
- doc/release_notes/2.5.1.txt
|
164
165
|
- doc/release_notes/2.6.0.txt
|
166
|
+
- doc/release_notes/2.7.0.txt
|
165
167
|
- lib/roda.rb
|
166
168
|
- lib/roda/plugins/_erubis_escaping.rb
|
167
169
|
- lib/roda/plugins/all_verbs.rb
|
@@ -174,6 +176,7 @@ files:
|
|
174
176
|
- lib/roda/plugins/cookies.rb
|
175
177
|
- lib/roda/plugins/csrf.rb
|
176
178
|
- lib/roda/plugins/default_headers.rb
|
179
|
+
- lib/roda/plugins/default_status.rb
|
177
180
|
- lib/roda/plugins/delay_build.rb
|
178
181
|
- lib/roda/plugins/delegate.rb
|
179
182
|
- lib/roda/plugins/delete_empty_headers.rb
|
@@ -213,6 +216,8 @@ files:
|
|
213
216
|
- lib/roda/plugins/precompile_templates.rb
|
214
217
|
- lib/roda/plugins/render.rb
|
215
218
|
- lib/roda/plugins/render_each.rb
|
219
|
+
- lib/roda/plugins/response_request.rb
|
220
|
+
- lib/roda/plugins/run_handler.rb
|
216
221
|
- lib/roda/plugins/shared_vars.rb
|
217
222
|
- lib/roda/plugins/sinatra_helpers.rb
|
218
223
|
- lib/roda/plugins/slash_path_empty.rb
|
@@ -247,6 +252,7 @@ files:
|
|
247
252
|
- spec/plugin/cookies_spec.rb
|
248
253
|
- spec/plugin/csrf_spec.rb
|
249
254
|
- spec/plugin/default_headers_spec.rb
|
255
|
+
- spec/plugin/default_status_spec.rb
|
250
256
|
- spec/plugin/delay_build_spec.rb
|
251
257
|
- spec/plugin/delegate_spec.rb
|
252
258
|
- spec/plugin/delete_empty_headers_spec.rb
|
@@ -286,6 +292,8 @@ files:
|
|
286
292
|
- spec/plugin/precompile_templates_spec.rb
|
287
293
|
- spec/plugin/render_each_spec.rb
|
288
294
|
- spec/plugin/render_spec.rb
|
295
|
+
- spec/plugin/response_request_spec.rb
|
296
|
+
- spec/plugin/run_handler_spec.rb
|
289
297
|
- spec/plugin/shared_vars_spec.rb
|
290
298
|
- spec/plugin/sinatra_helpers_spec.rb
|
291
299
|
- spec/plugin/slash_path_empty_spec.rb
|