roda 2.23.0 → 2.24.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 +20 -0
- data/README.rdoc +1 -0
- data/doc/release_notes/2.24.0.txt +65 -0
- data/lib/roda.rb +12 -4
- data/lib/roda/plugins/assets.rb +2 -2
- data/lib/roda/plugins/cookies.rb +14 -2
- data/lib/roda/plugins/disallow_file_uploads.rb +38 -0
- data/lib/roda/plugins/h.rb +29 -15
- data/lib/roda/plugins/middleware.rb +43 -5
- data/lib/roda/plugins/precompile_templates.rb +1 -1
- data/lib/roda/plugins/public.rb +1 -1
- data/lib/roda/plugins/render.rb +7 -4
- data/lib/roda/plugins/static.rb +1 -1
- data/lib/roda/plugins/static_routing.rb +10 -6
- data/lib/roda/plugins/strip_path_prefix.rb +33 -0
- data/lib/roda/version.rb +1 -1
- data/spec/integration_spec.rb +21 -0
- data/spec/plugin/cookies_spec.rb +26 -0
- data/spec/plugin/disallow_file_uploads_spec.rb +25 -0
- data/spec/plugin/h_spec.rb +1 -1
- data/spec/plugin/middleware_spec.rb +37 -0
- data/spec/plugin/render_spec.rb +14 -1
- data/spec/plugin/static_routing_spec.rb +20 -0
- data/spec/plugin/strip_path_prefix_spec.rb +24 -0
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b17a05c593a91658be1194fb29c9564dcd6e3fe2
|
4
|
+
data.tar.gz: 153d3095509dd43d37e9297c963aea4e39ef2cbe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d3871a3650c8490435fd94d13148a5ba90c636779cb1235c94c0bb61d76ef35efa8cf5c504e29271a3e34bcc818c179ed8a709e1576ccbeb53fb8c4825da2272
|
7
|
+
data.tar.gz: 0ab43b3207200bfc783a00d714b058d04220a1ab1dd333670b8c2d590530cab87564e9323f4023a6c8e48f63a50fd3cf82f842429ae395bdf8f192905fb035bd
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,23 @@
|
|
1
|
+
= 2.24.0 (2017-03-15)
|
2
|
+
|
3
|
+
* Have h plugin use cgi/escape if available for faster escaping (jeremyevans)
|
4
|
+
|
5
|
+
* Add disallow_file_uploads plugin for raising an exception if a multipart file upload is attempted (jeremyevans)
|
6
|
+
|
7
|
+
* Add strip_path_prefix plugin for stripping prefixes off of internal absolute paths, making them relative paths (jeremyevans)
|
8
|
+
|
9
|
+
* Add Roda.expand_path method to DRY up path expansion (jeremyevans)
|
10
|
+
|
11
|
+
* Support :freeze_middleware option, which freezes all middleware instances when building the rack app (jeremyevans)
|
12
|
+
|
13
|
+
* Allow middleware plugin to accept a block that will be used to configure the application when used as middleware (jeremyevans)
|
14
|
+
|
15
|
+
* Support an options hash when loading the cookies plugin, that will be used as the defaults for setting and deleting cookies (mwpastore, jeremyevans) (#112)
|
16
|
+
|
17
|
+
* Make the static_routing plugin work with the hooks plugin if the hooks plugin is loaded first (jeremyevans) (#110)
|
18
|
+
|
19
|
+
* Do not modify the render plugin's cache if loading the plugin multiple times (jeremyevans)
|
20
|
+
|
1
21
|
= 2.23.0 (2017-02-24)
|
2
22
|
|
3
23
|
* Add :inherit_cache render plugin option, to create a copy of the cache for subclasses, instead of using an empty cache (jeremyevans)
|
data/README.rdoc
CHANGED
@@ -635,6 +635,7 @@ The following options are respected by the default library or multiple plugins:
|
|
635
635
|
|
636
636
|
:add_script_name :: Prepend the SCRIPT_NAME for the request to paths. This is
|
637
637
|
useful if you mount the app as a path under another app.
|
638
|
+
:freeze_middleware :: Whether to freeze all middleware when building the rack app.
|
638
639
|
:root :: Set the root path for the app. This defaults to the current working
|
639
640
|
directory of the process.
|
640
641
|
:unsupported_block_result :: If set to :raise, raises an error if a match or
|
@@ -0,0 +1,65 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* The middleware plugin now accepts a block that can be used to
|
4
|
+
implement configurable middleware. This allows you to load the
|
5
|
+
Roda application as middleware in another application, and provide
|
6
|
+
options/block that are passed to the block you passed when loading
|
7
|
+
the middleware plugin. Example:
|
8
|
+
|
9
|
+
class Mid < Roda
|
10
|
+
plugin :middleware do |middleware, *args, &block|
|
11
|
+
middleware.opts[:middleware_args] = args
|
12
|
+
block.call(middleware)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class App < Roda
|
17
|
+
use Mid, :foo, :bar do |middleware|
|
18
|
+
middleware.opts[:middleware_args] << :baz
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
Note that when passing a block when loading the middleware plugin,
|
23
|
+
using the middleware in another rack application will actually
|
24
|
+
load a subclass of the middleware (Mid in the example above).
|
25
|
+
This allows you to use the Roda middleware multiple times in the
|
26
|
+
same process with different configurations.
|
27
|
+
|
28
|
+
* The cookies plugin now accepts options that are used as the default
|
29
|
+
options when setting and deleting cookies:
|
30
|
+
|
31
|
+
plugin :cookies, :path => '/foo', :domain => 'example.com'
|
32
|
+
|
33
|
+
* A strip_path_prefix plugin has been added, which can be used to
|
34
|
+
strip prefixes from internal paths. Internally Sequel stores
|
35
|
+
most paths as absolute paths, but there are cases where this
|
36
|
+
doesn't work well, such as symlink changes and chroot. This
|
37
|
+
plugin supports those scenarios.
|
38
|
+
|
39
|
+
* A disallow_file_uploads plugin has been added, which raises
|
40
|
+
an exception if the user attempts a multipart file upload.
|
41
|
+
More exactly, it makes the application raise an exception if
|
42
|
+
attempting to parse a request body when the user has submitted
|
43
|
+
a multipart file upload. This is useful if you don't need to
|
44
|
+
support file uploads in your application, or cases where no
|
45
|
+
paths are writable by the application (due to chroot or system call
|
46
|
+
filtering).
|
47
|
+
|
48
|
+
* The :freeze_middleware option has been added, which freezes all
|
49
|
+
middleware instances when building the rack application. This
|
50
|
+
can help find thread safety issues in middleware.
|
51
|
+
|
52
|
+
= Other Improvements
|
53
|
+
|
54
|
+
* The h plugin is now about 6x faster on ruby 2.3+ due to the use
|
55
|
+
of cgi/escape.
|
56
|
+
|
57
|
+
* The static_routing plugin now works with the hooks plugin if the
|
58
|
+
hooks plugin is loaded first.
|
59
|
+
|
60
|
+
* The render plugin's render cache is no longer cleared when loading
|
61
|
+
the plugin multiple times.
|
62
|
+
|
63
|
+
= Backwards Compatibility
|
64
|
+
|
65
|
+
* The h plugin now escapes ' as ' instead of '.
|
data/lib/roda.rb
CHANGED
@@ -123,6 +123,11 @@ class Roda
|
|
123
123
|
build_rack_app
|
124
124
|
end
|
125
125
|
|
126
|
+
# Expand the given path, using the root argument as the base directory.
|
127
|
+
def expand_path(path, root=opts[:root])
|
128
|
+
::File.expand_path(path, root)
|
129
|
+
end
|
130
|
+
|
126
131
|
# Freeze the internal state of the class, to avoid thread safety issues at runtime.
|
127
132
|
# It's optional to call this method, as nothing should be modifying the
|
128
133
|
# internal state at runtime anyway, but this makes sure an exception will
|
@@ -220,10 +225,13 @@ class Roda
|
|
220
225
|
# Build the rack app to use
|
221
226
|
def build_rack_app
|
222
227
|
if block = @route_block
|
223
|
-
|
224
|
-
@middleware.
|
225
|
-
|
226
|
-
|
228
|
+
app = lambda{|env| new(env).call(&block)}
|
229
|
+
@middleware.reverse_each do |args, bl|
|
230
|
+
mid, *args = args
|
231
|
+
app = mid.new(app, *args, &bl)
|
232
|
+
app.freeze if opts[:freeze_middleware]
|
233
|
+
end
|
234
|
+
@app = app
|
227
235
|
end
|
228
236
|
end
|
229
237
|
end
|
data/lib/roda/plugins/assets.rb
CHANGED
@@ -354,8 +354,8 @@ class Roda
|
|
354
354
|
app.opts[:assets][:orig_opts] = opts
|
355
355
|
end
|
356
356
|
opts = app.opts[:assets]
|
357
|
-
opts[:path] =
|
358
|
-
opts[:public] =
|
357
|
+
opts[:path] = app.expand_path(opts[:path]||"assets").freeze
|
358
|
+
opts[:public] = app.expand_path(opts[:public]||"public").freeze
|
359
359
|
|
360
360
|
# Combine multiple values into a path, ignoring trailing slashes
|
361
361
|
j = lambda do |*v|
|
data/lib/roda/plugins/cookies.rb
CHANGED
@@ -9,7 +9,18 @@ class Roda
|
|
9
9
|
#
|
10
10
|
# response.set_cookie('foo', 'bar')
|
11
11
|
# response.delete_cookie('foo')
|
12
|
+
#
|
13
|
+
# Pass a hash of cookie options when loading the plugin to set some
|
14
|
+
# defaults for all cookies upon setting and deleting. This is particularly
|
15
|
+
# useful for configuring the +domain+ and +path+ of all cookies.
|
16
|
+
#
|
17
|
+
# plugin :cookies, :domain=>'example.com', :path=>'/api'
|
12
18
|
module Cookies
|
19
|
+
# Allow setting default cookie options when loading the cookies plugin.
|
20
|
+
def self.configure(app, opts={})
|
21
|
+
app.opts[:cookies_opts] = (app.opts[:cookies_opts]||{}).merge(opts).freeze
|
22
|
+
end
|
23
|
+
|
13
24
|
module ResponseMethods
|
14
25
|
# Modify the headers to include a Set-Cookie value that
|
15
26
|
# deletes the cookie. A value hash can be provided to
|
@@ -19,7 +30,7 @@ class Roda
|
|
19
30
|
# response.delete_cookie('foo')
|
20
31
|
# response.delete_cookie('foo', :domain=>'example.org')
|
21
32
|
def delete_cookie(key, value = {})
|
22
|
-
::Rack::Utils.delete_cookie_header!(@headers, key, value)
|
33
|
+
::Rack::Utils.delete_cookie_header!(@headers, key, roda_class.opts[:cookies_opts].merge(value))
|
23
34
|
end
|
24
35
|
|
25
36
|
# Set the cookie with the given key in the headers.
|
@@ -27,7 +38,8 @@ class Roda
|
|
27
38
|
# response.set_cookie('foo', 'bar')
|
28
39
|
# response.set_cookie('foo', :value=>'bar', :domain=>'example.org')
|
29
40
|
def set_cookie(key, value)
|
30
|
-
|
41
|
+
value = { :value=>value } unless value.respond_to?(:keys)
|
42
|
+
::Rack::Utils.set_cookie_header!(@headers, key, roda_class.opts[:cookies_opts].merge(value))
|
31
43
|
end
|
32
44
|
end
|
33
45
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
raise LoadError, "disallow_file_uploads plugin not supported on Rack <1.6" if Rack.release < '1.6'
|
4
|
+
|
5
|
+
#
|
6
|
+
class Roda
|
7
|
+
module RodaPlugins
|
8
|
+
# The disallow_file_uploads plugin raises a Roda::RodaPlugins::DisallowFileUploads::Error
|
9
|
+
# if there is an attempt to upload a file. This plugin is useful for applications where
|
10
|
+
# multipart file uploads are not expected and you want to remove the ability for rack
|
11
|
+
# to create temporary files. Example:
|
12
|
+
#
|
13
|
+
# plugin :disallow_file_uploads
|
14
|
+
#
|
15
|
+
# This plugin is only supported on Rack 1.6+. This plugin does not technically
|
16
|
+
# block users from uploading files, it only blocks the parsing of request bodies containing
|
17
|
+
# multipart file uploads. So if you do not call +r.POST+ (or something that calls it such as
|
18
|
+
# +r.params+), then Roda will not attempt to parse the request body, and an exception will not
|
19
|
+
# be raised.
|
20
|
+
module DisallowFileUploads
|
21
|
+
# Exception class used when a multipart file upload is attempted.
|
22
|
+
class Error < RodaError; end
|
23
|
+
|
24
|
+
NO_TEMPFILE = lambda{|_,_| raise Error, "Support for uploading files has been disabled"}
|
25
|
+
|
26
|
+
module RequestMethods
|
27
|
+
# HTML escape the input and return the escaped version.
|
28
|
+
def initialize(_, env)
|
29
|
+
env['rack.multipart.tempfile_factory'] = NO_TEMPFILE
|
30
|
+
super
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
register_plugin(:disallow_file_uploads, DisallowFileUploads)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
data/lib/roda/plugins/h.rb
CHANGED
@@ -14,23 +14,37 @@ class Roda
|
|
14
14
|
# h('<foo>')
|
15
15
|
# end
|
16
16
|
module H
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
17
|
+
begin
|
18
|
+
require 'cgi/escape'
|
19
|
+
unless CGI.respond_to?(:escapeHTML) # work around for JRuby 9.1
|
20
|
+
CGI = Object.new
|
21
|
+
CGI.extend(::CGI::Util)
|
22
|
+
end
|
23
|
+
|
24
|
+
module InstanceMethods
|
25
|
+
# HTML escape the input and return the escaped version.
|
26
|
+
def h(string)
|
27
|
+
CGI.escapeHTML(string.to_s)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
rescue LoadError
|
31
|
+
# A Hash of entities and their escaped equivalents,
|
32
|
+
# to be escaped by h().
|
33
|
+
ESCAPE_HTML = {
|
34
|
+
"&" => "&".freeze,
|
35
|
+
"<" => "<".freeze,
|
36
|
+
">" => ">".freeze,
|
37
|
+
"'" => "'".freeze,
|
38
|
+
'"' => """.freeze,
|
39
|
+
}.freeze
|
26
40
|
|
27
|
-
|
28
|
-
|
41
|
+
# A Regexp of HTML entities to match for escaping.
|
42
|
+
ESCAPE_HTML_PATTERN = Regexp.union(*ESCAPE_HTML.keys)
|
29
43
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
44
|
+
module InstanceMethods
|
45
|
+
def h(string)
|
46
|
+
string.to_s.gsub(ESCAPE_HTML_PATTERN){|c| ESCAPE_HTML[c] }
|
47
|
+
end
|
34
48
|
end
|
35
49
|
end
|
36
50
|
end
|
@@ -35,22 +35,60 @@ class Roda
|
|
35
35
|
#
|
36
36
|
# It is possible to use the Roda app as a regular app even when using
|
37
37
|
# the middleware plugin.
|
38
|
+
#
|
39
|
+
# You can support configurable middleware by passing a block when loading
|
40
|
+
# the plugin:
|
41
|
+
#
|
42
|
+
# class Mid < Roda
|
43
|
+
# plugin :middleware do |middleware, *args, &block|
|
44
|
+
# middleware.opts[:middleware_args] = args
|
45
|
+
# block.call(middleware)
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# route do |r|
|
49
|
+
# r.is "mid" do
|
50
|
+
# opts[:middleware_args].join(' ')
|
51
|
+
# end
|
52
|
+
# end
|
53
|
+
# end
|
54
|
+
#
|
55
|
+
# class App < Roda
|
56
|
+
# use Mid, :foo, :bar do |middleware|
|
57
|
+
# middleware.opts[:middleware_args] << :baz
|
58
|
+
# end
|
59
|
+
# end
|
60
|
+
#
|
61
|
+
# # Request to App for /mid returns
|
62
|
+
# # "foo bar baz"
|
63
|
+
#
|
64
|
+
# Note that when supporting configurable middleware via a block, the middleware
|
65
|
+
# used is a subclass of the class loading the plugin, instead of the class itself.
|
66
|
+
# This is done so the same class can be used as middleware with multiple separate
|
67
|
+
# configurations.
|
38
68
|
module Middleware
|
39
69
|
# Configure the middleware plugin. Options:
|
40
70
|
# :env_var :: Set the environment variable to use to indicate to the roda
|
41
71
|
# application that the current request is a middleware request.
|
42
72
|
# You should only need to override this if you are using multiple
|
43
73
|
# roda middleware in the same application.
|
44
|
-
def self.configure(app, opts={})
|
74
|
+
def self.configure(app, opts={}, &block)
|
45
75
|
app.opts[:middleware_env_var] = opts[:env_var] if opts.has_key?(:env_var)
|
46
76
|
app.opts[:middleware_env_var] ||= 'roda.forward_next'
|
77
|
+
app.opts[:middleware_configure] = block if block
|
47
78
|
end
|
48
79
|
|
49
80
|
# Forward instances are what is actually used as middleware.
|
50
81
|
class Forwarder
|
51
82
|
# Store the current middleware and the next middleware to call.
|
52
|
-
def initialize(mid, app)
|
53
|
-
@mid = mid
|
83
|
+
def initialize(mid, app, *args, &block)
|
84
|
+
@mid = if configure = mid.opts[:middleware_configure]
|
85
|
+
mid = Class.new(mid)
|
86
|
+
configure.call(mid, *args, &block)
|
87
|
+
mid
|
88
|
+
else
|
89
|
+
raise RodaError, "cannot provide middleware args or block unless loading middleware plugin with a block" if block || !args.empty?
|
90
|
+
mid
|
91
|
+
end
|
54
92
|
@app = app
|
55
93
|
end
|
56
94
|
|
@@ -76,11 +114,11 @@ class Roda
|
|
76
114
|
|
77
115
|
module ClassMethods
|
78
116
|
# Create a Forwarder instead of a new instance if a non-Hash is given.
|
79
|
-
def new(app)
|
117
|
+
def new(app, *args, &block)
|
80
118
|
if app.is_a?(Hash)
|
81
119
|
super
|
82
120
|
else
|
83
|
-
Forwarder.new(self, app)
|
121
|
+
Forwarder.new(self, app, *args, &block)
|
84
122
|
end
|
85
123
|
end
|
86
124
|
|
data/lib/roda/plugins/public.rb
CHANGED
@@ -32,7 +32,7 @@ class Roda
|
|
32
32
|
# :headers :: A hash of headers to use for statically served files
|
33
33
|
# :root :: Use this option for the root of the public directory (default: "public")
|
34
34
|
def self.configure(app, opts={})
|
35
|
-
root =
|
35
|
+
root = app.expand_path(opts[:root]||"public")
|
36
36
|
app.opts[:public_server] = ::Rack::File.new(root, opts[:headers]||{}, opts[:default_mime] || 'text/plain')
|
37
37
|
app.opts[:public_gzip] = opts[:gzip]
|
38
38
|
end
|
data/lib/roda/plugins/render.rb
CHANGED
@@ -157,6 +157,7 @@ class Roda
|
|
157
157
|
# Setup default rendering options. See Render for details.
|
158
158
|
def self.configure(app, opts=OPTS)
|
159
159
|
if app.opts[:render]
|
160
|
+
orig_cache = app.opts[:render][:cache]
|
160
161
|
opts = app.opts[:render][:orig_opts].merge(opts)
|
161
162
|
end
|
162
163
|
app.opts[:render] = opts.dup
|
@@ -164,12 +165,14 @@ class Roda
|
|
164
165
|
|
165
166
|
opts = app.opts[:render]
|
166
167
|
opts[:engine] = (opts[:engine] || opts[:ext] || "erb").dup.freeze
|
167
|
-
opts[:views] =
|
168
|
+
opts[:views] = app.expand_path(opts[:views]||"views").freeze
|
168
169
|
opts[:allowed_paths] ||= [opts[:views]].freeze
|
169
|
-
opts[:allowed_paths] = opts[:allowed_paths].map{|f|
|
170
|
+
opts[:allowed_paths] = opts[:allowed_paths].map{|f| app.expand_path(f, nil)}.uniq.freeze
|
170
171
|
|
171
172
|
if opts.fetch(:cache, true)
|
172
|
-
if
|
173
|
+
if orig_cache
|
174
|
+
opts[:cache] = orig_cache
|
175
|
+
elsif cache_class = opts[:cache_class]
|
173
176
|
opts[:cache] = cache_class.new
|
174
177
|
else
|
175
178
|
opts[:cache] = app.thread_safe_cache
|
@@ -396,7 +399,7 @@ class Roda
|
|
396
399
|
def template_path(opts)
|
397
400
|
path = "#{opts[:views]}/#{template_name(opts)}.#{opts[:engine]}"
|
398
401
|
if opts.fetch(:check_paths){render_opts[:check_paths]}
|
399
|
-
full_path =
|
402
|
+
full_path = self.class.expand_path(path)
|
400
403
|
unless render_opts[:allowed_paths].any?{|f| full_path.start_with?(f)}
|
401
404
|
raise RodaError, "attempt to render path not in allowed_paths: #{path} (allowed: #{render_opts[:allowed_paths].join(', ')})"
|
402
405
|
end
|
data/lib/roda/plugins/static.rb
CHANGED
@@ -30,7 +30,7 @@ class Roda
|
|
30
30
|
def self.configure(app, paths, opts={})
|
31
31
|
opts = opts.dup
|
32
32
|
opts[:urls] = paths
|
33
|
-
opts[:root] =
|
33
|
+
opts[:root] = app.expand_path(opts[:root]||"public")
|
34
34
|
app.use ::Rack::Static, opts
|
35
35
|
end
|
36
36
|
end
|
@@ -48,6 +48,9 @@ class Roda
|
|
48
48
|
# As shown above, you can use Roda's routing tree methods inside the
|
49
49
|
# static_route block to have shared behavior for different request methods,
|
50
50
|
# while still having handling the request methods differently.
|
51
|
+
#
|
52
|
+
# Note that if you want to use the static_routing plugin and the hooks
|
53
|
+
# plugin at the same time, you should load the hooks plugin first.
|
51
54
|
module StaticRouting
|
52
55
|
def self.configure(app)
|
53
56
|
app.opts[:static_routes] = {}
|
@@ -104,12 +107,13 @@ class Roda
|
|
104
107
|
module InstanceMethods
|
105
108
|
# If there is a static routing method for the given path, call it
|
106
109
|
# instead having the routing tree handle the request.
|
107
|
-
def call
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
110
|
+
def call(&block)
|
111
|
+
super do |r|
|
112
|
+
if route = self.class.static_route_for(r.request_method, r.path_info)
|
113
|
+
r.static_route(&route)
|
114
|
+
else
|
115
|
+
instance_exec(r, &block)
|
116
|
+
end
|
113
117
|
end
|
114
118
|
end
|
115
119
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
class Roda
|
5
|
+
module RodaPlugins
|
6
|
+
# The strip_path_prefix plugin makes Roda strip a given prefix off internal absolute paths,
|
7
|
+
# turning them to relative paths. Roda by default stores internal paths as absolute paths.
|
8
|
+
# The main reason to use this plugin is when the internal absolute path could change at
|
9
|
+
# runtime, either due to a symlink change or chroot call, or you really want to use
|
10
|
+
# relative links instead of absolute links.
|
11
|
+
#
|
12
|
+
# Examples:
|
13
|
+
#
|
14
|
+
# plugin :strip_path_prefix # Defaults to Dir.pwd
|
15
|
+
# plugin :strip_path_prefix, File.dirname(Dir.pwd)
|
16
|
+
module StripPathPrefix
|
17
|
+
# Set the regexp to use when stripping prefixes from internal paths.
|
18
|
+
def self.configure(app, prefix=Dir.pwd)
|
19
|
+
prefix += '/' unless prefix=~ /\/\z/
|
20
|
+
app.opts[:strip_path_prefix] = /\A#{Regexp.escape(prefix)}/
|
21
|
+
end
|
22
|
+
|
23
|
+
module ClassMethods
|
24
|
+
# Strip the path prefix from the gien path if it starts with the prefix.
|
25
|
+
def expand_path(path, root=opts[:root])
|
26
|
+
super.sub(opts[:strip_path_prefix], '')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
register_plugin(:strip_path_prefix, StripPathPrefix)
|
32
|
+
end
|
33
|
+
end
|
data/lib/roda/version.rb
CHANGED
data/spec/integration_spec.rb
CHANGED
@@ -54,6 +54,27 @@ describe "integration" do
|
|
54
54
|
body('/hello').must_equal 'D '
|
55
55
|
end
|
56
56
|
|
57
|
+
it "should freeze middleware if opts[:freeze_middleware] is true" do
|
58
|
+
c = Class.new do
|
59
|
+
def initialize(app) @app = app end
|
60
|
+
def call(env) @a = 1; @app.call(env) end
|
61
|
+
end
|
62
|
+
|
63
|
+
app do
|
64
|
+
"D"
|
65
|
+
end
|
66
|
+
|
67
|
+
body.must_equal 'D'
|
68
|
+
|
69
|
+
app.use c
|
70
|
+
body.must_equal 'D'
|
71
|
+
|
72
|
+
app.clear_middleware!
|
73
|
+
app.opts[:freeze_middleware] = true
|
74
|
+
app.use c
|
75
|
+
proc{body}.must_raise RuntimeError, TypeError
|
76
|
+
end
|
77
|
+
|
57
78
|
it "should support adding middleware using use after route block setup" do
|
58
79
|
c = @c
|
59
80
|
app(:bare) do
|
data/spec/plugin/cookies_spec.rb
CHANGED
@@ -22,4 +22,30 @@ describe "cookies plugin" do
|
|
22
22
|
header('Set-Cookie').must_match(/foo=; (max-age=0; )?expires=Thu, 01[ -]Jan[ -]1970 00:00:00 (-0000|GMT)/)
|
23
23
|
body.must_equal 'Hello'
|
24
24
|
end
|
25
|
+
|
26
|
+
it "should pass default cookie options when setting" do
|
27
|
+
app.plugin :cookies, :path => '/foo'
|
28
|
+
app.route { response.set_cookie("foo", "bar") }
|
29
|
+
header('Set-Cookie').must_equal "foo=bar; path=/foo"
|
30
|
+
|
31
|
+
app.route { response.set_cookie("foo", :value=>"bar", :path=>'/baz') }
|
32
|
+
header('Set-Cookie').must_equal "foo=bar; path=/baz"
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should pass default cookie options when deleting" do
|
36
|
+
app.plugin :cookies, :domain => 'example.com'
|
37
|
+
app.route { response.delete_cookie("foo") }
|
38
|
+
header('Set-Cookie').must_match(/foo=; domain=example.com; (max-age=0; )?expires=Thu, 01[ -]Jan[ -]1970 00:00:00 (-0000|GMT)/)
|
39
|
+
|
40
|
+
app.route { response.delete_cookie("foo", :domain=>'bar.com') }
|
41
|
+
header('Set-Cookie').must_match(/foo=; domain=bar.com; (max-age=0; )?expires=Thu, 01[ -]Jan[ -]1970 00:00:00 (-0000|GMT)/)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should not override existing default cookie options" do
|
45
|
+
app.plugin :cookies, :path => '/foo'
|
46
|
+
app.plugin :cookies
|
47
|
+
app.route { response.set_cookie("foo", "bar") }
|
48
|
+
|
49
|
+
header('Set-Cookie').must_equal "foo=bar; path=/foo"
|
50
|
+
end
|
25
51
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
|
2
|
+
|
3
|
+
if Rack.release < '1.6'
|
4
|
+
warn "Rack #{Rack.release} used, skipping disallow_file_uploads plugin test"
|
5
|
+
else
|
6
|
+
describe "disallow_file_uploads plugin" do
|
7
|
+
it "disallows the uploading of files" do
|
8
|
+
app do |r|
|
9
|
+
r['foo'][:tempfile].read
|
10
|
+
end
|
11
|
+
|
12
|
+
request_body = StringIO.new("------WebKitFormBoundarymwHIM9XjTTVHn3YP\r\nContent-Disposition: form-data; name=\"foo\"; filename=\"bar.txt\"\r\nContent-Type: text/plain\r\n\r\nfoo\n\r\n------WebKitFormBoundarymwHIM9XjTTVHn3YP--\r\n")
|
13
|
+
|
14
|
+
h = {
|
15
|
+
'rack.input'=>request_body,
|
16
|
+
'CONTENT_TYPE'=>'multipart/form-data; boundary=----WebKitFormBoundarymwHIM9XjTTVHn3YP',
|
17
|
+
'CONTENT_LENGTH'=>'184',
|
18
|
+
'REQUEST_METHOD'=>'POST'
|
19
|
+
}
|
20
|
+
body(h.dup).must_equal "foo\n"
|
21
|
+
app.plugin :disallow_file_uploads
|
22
|
+
proc{body(h.dup)}.must_raise Roda::RodaPlugins::DisallowFileUploads::Error
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/spec/plugin/h_spec.rb
CHANGED
@@ -57,6 +57,43 @@ describe "middleware plugin" do
|
|
57
57
|
body.must_equal 'a'
|
58
58
|
end
|
59
59
|
|
60
|
+
it "should raise error if attempting to use options for Roda application that does not support configurable middleware" do
|
61
|
+
a1 = app(:bare){plugin :middleware}
|
62
|
+
proc{app(:bare){use a1, :foo; route{}; build_rack_app}}.must_raise Roda::RodaError
|
63
|
+
proc{app(:bare){use(a1){}; route{}; build_rack_app}}.must_raise Roda::RodaError
|
64
|
+
end
|
65
|
+
|
66
|
+
it "supports configuring middleware via a block" do
|
67
|
+
a1 = app(:bare) do
|
68
|
+
plugin :middleware do |mid, *args, &block|
|
69
|
+
mid.opts[:a] = args.concat(block.call(:quux)).join(' ')
|
70
|
+
end
|
71
|
+
opts[:a] = 'a1'
|
72
|
+
|
73
|
+
route do |r|
|
74
|
+
r.is 'a' do opts[:a] end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
body('/a').must_equal 'a1'
|
79
|
+
|
80
|
+
app(:bare) do
|
81
|
+
use a1, :foo, :bar do |baz|
|
82
|
+
[baz, :a1]
|
83
|
+
end
|
84
|
+
|
85
|
+
route do
|
86
|
+
'b1'
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
body.must_equal 'b1'
|
91
|
+
body('/a').must_equal 'foo bar quux a1'
|
92
|
+
|
93
|
+
@app = a1
|
94
|
+
body('/a').must_equal 'a1'
|
95
|
+
end
|
96
|
+
|
60
97
|
it "is compatible with the multi_route plugin" do
|
61
98
|
app(:bare) do
|
62
99
|
plugin :multi_route
|
data/spec/plugin/render_spec.rb
CHANGED
@@ -499,7 +499,7 @@ describe "render plugin" do
|
|
499
499
|
sc.render_opts[:cache][:foo].must_be_nil
|
500
500
|
end
|
501
501
|
|
502
|
-
it "should use
|
502
|
+
it "should use a copy of superclass's cache when inheriting if :inherit_cache option is used" do
|
503
503
|
c = Class.new(Roda)
|
504
504
|
c.plugin :render, :inherit_cache=>true
|
505
505
|
c.render_opts[:cache][:foo] = 1
|
@@ -510,6 +510,19 @@ describe "render plugin" do
|
|
510
510
|
sc.render_opts[:cache][:foo].must_equal 1
|
511
511
|
end
|
512
512
|
|
513
|
+
it "should not modifying existing cache if loading the plugin a separate time" do
|
514
|
+
c = Class.new(Roda)
|
515
|
+
c.plugin :render
|
516
|
+
cache = c.render_opts[:cache]
|
517
|
+
c.plugin :render
|
518
|
+
c.render_opts[:cache].must_be_same_as cache
|
519
|
+
|
520
|
+
c.plugin :render, :cache=>false
|
521
|
+
c.render_opts[:cache].must_equal false
|
522
|
+
c.plugin :render
|
523
|
+
c.render_opts[:cache].must_equal false
|
524
|
+
end
|
525
|
+
|
513
526
|
it "render plugin call should not override existing options" do
|
514
527
|
c = Class.new(Roda)
|
515
528
|
c.plugin :render, :layout=>:foo
|
@@ -46,6 +46,26 @@ describe "static_routing plugin" do
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
+
it "works with hooks plugin if loaded after" do
|
50
|
+
a = []
|
51
|
+
app(:bare) do
|
52
|
+
plugin :hooks
|
53
|
+
plugin :static_routing
|
54
|
+
|
55
|
+
before{a << 1}
|
56
|
+
after{a << 2}
|
57
|
+
|
58
|
+
static_route "/foo" do |r|
|
59
|
+
a << 3
|
60
|
+
"bar"
|
61
|
+
end
|
62
|
+
|
63
|
+
route{}
|
64
|
+
end
|
65
|
+
body('/foo').must_equal 'bar'
|
66
|
+
a.must_equal [1,3,2]
|
67
|
+
end
|
68
|
+
|
49
69
|
it "does not allow placeholders in static routes" do
|
50
70
|
app(:bare) do
|
51
71
|
plugin :static_routing
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
|
2
|
+
|
3
|
+
describe "strip_path_prefix plugin" do
|
4
|
+
it "strips path prefix when expanding paths" do
|
5
|
+
app(:bare){}
|
6
|
+
abs_dir = app.expand_path('spec')
|
7
|
+
|
8
|
+
app.plugin :strip_path_prefix
|
9
|
+
app.expand_path('spec').must_equal 'spec'
|
10
|
+
File.expand_path(app.expand_path('spec'), Dir.pwd).must_equal abs_dir
|
11
|
+
|
12
|
+
app.expand_path('/foo').must_equal '/foo'
|
13
|
+
app.expand_path('bar', '/foo').must_equal '/foo/bar'
|
14
|
+
|
15
|
+
app.opts[:root] = '/foo'
|
16
|
+
app.expand_path('bar').must_equal '/foo/bar'
|
17
|
+
app.plugin :strip_path_prefix, '/foo'
|
18
|
+
app.expand_path('bar').must_equal 'bar'
|
19
|
+
|
20
|
+
app.opts[:root] = '/foo/bar'
|
21
|
+
app.expand_path('baz').must_equal 'bar/baz'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
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.24.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-
|
11
|
+
date: 2017-03-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -203,6 +203,7 @@ extra_rdoc_files:
|
|
203
203
|
- doc/release_notes/2.21.0.txt
|
204
204
|
- doc/release_notes/2.22.0.txt
|
205
205
|
- doc/release_notes/2.23.0.txt
|
206
|
+
- doc/release_notes/2.24.0.txt
|
206
207
|
files:
|
207
208
|
- CHANGELOG
|
208
209
|
- MIT-LICENSE
|
@@ -230,6 +231,7 @@ files:
|
|
230
231
|
- doc/release_notes/2.21.0.txt
|
231
232
|
- doc/release_notes/2.22.0.txt
|
232
233
|
- doc/release_notes/2.23.0.txt
|
234
|
+
- doc/release_notes/2.24.0.txt
|
233
235
|
- doc/release_notes/2.3.0.txt
|
234
236
|
- doc/release_notes/2.4.0.txt
|
235
237
|
- doc/release_notes/2.5.0.txt
|
@@ -255,6 +257,7 @@ files:
|
|
255
257
|
- lib/roda/plugins/delay_build.rb
|
256
258
|
- lib/roda/plugins/delegate.rb
|
257
259
|
- lib/roda/plugins/delete_empty_headers.rb
|
260
|
+
- lib/roda/plugins/disallow_file_uploads.rb
|
258
261
|
- lib/roda/plugins/drop_body.rb
|
259
262
|
- lib/roda/plugins/empty_root.rb
|
260
263
|
- lib/roda/plugins/environments.rb
|
@@ -307,6 +310,7 @@ files:
|
|
307
310
|
- lib/roda/plugins/static_routing.rb
|
308
311
|
- lib/roda/plugins/status_handler.rb
|
309
312
|
- lib/roda/plugins/streaming.rb
|
313
|
+
- lib/roda/plugins/strip_path_prefix.rb
|
310
314
|
- lib/roda/plugins/symbol_matchers.rb
|
311
315
|
- lib/roda/plugins/symbol_status.rb
|
312
316
|
- lib/roda/plugins/symbol_views.rb
|
@@ -343,6 +347,7 @@ files:
|
|
343
347
|
- spec/plugin/delay_build_spec.rb
|
344
348
|
- spec/plugin/delegate_spec.rb
|
345
349
|
- spec/plugin/delete_empty_headers_spec.rb
|
350
|
+
- spec/plugin/disallow_file_uploads_spec.rb
|
346
351
|
- spec/plugin/drop_body_spec.rb
|
347
352
|
- spec/plugin/empty_root_spec.rb
|
348
353
|
- spec/plugin/environments_spec.rb
|
@@ -394,6 +399,7 @@ files:
|
|
394
399
|
- spec/plugin/static_spec.rb
|
395
400
|
- spec/plugin/status_handler_spec.rb
|
396
401
|
- spec/plugin/streaming_spec.rb
|
402
|
+
- spec/plugin/strip_path_prefix_spec.rb
|
397
403
|
- spec/plugin/symbol_matchers_spec.rb
|
398
404
|
- spec/plugin/symbol_status_spec.rb
|
399
405
|
- spec/plugin/symbol_views_spec.rb
|