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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c091dcf6b8bb44694c2ea16a6244e3534662e2ed
4
- data.tar.gz: cff4ce05dd629994658d4470bf2d3f339b6c11c7
3
+ metadata.gz: b17a05c593a91658be1194fb29c9564dcd6e3fe2
4
+ data.tar.gz: 153d3095509dd43d37e9297c963aea4e39ef2cbe
5
5
  SHA512:
6
- metadata.gz: 4571d9602f0a17a330110bfcb4c319ff1c36476b16f8a488c0c081c5e651ddcbd8fe2df7c6d01f667dcc30445946f91131820e48fc22340c32a3465e21d5b756
7
- data.tar.gz: d7d6fc717405752f1b4eabd14fc23587f4cf2d9c25db32ab6f9425f1a67cda19ea0cb84a0c42173f07cdf722b0c8ab0b962d74fb898fc47fd5ae9382406c7995
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)
@@ -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 &#39; instead of &#x27;.
@@ -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
- builder = Rack::Builder.new
224
- @middleware.each{|a, b| builder.use(*a, &b)}
225
- builder.run lambda{|env| new(env).call(&block)}
226
- @app = builder.to_app
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
@@ -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] = File.expand_path(opts[:path]||"assets", app.opts[:root]).freeze
358
- opts[:public] = File.expand_path(opts[:public]||"public", app.opts[:root]).freeze
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|
@@ -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
- ::Rack::Utils.set_cookie_header!(@headers, key, value)
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
+
@@ -14,23 +14,37 @@ class Roda
14
14
  # h('<foo>')
15
15
  # end
16
16
  module H
17
- # A Hash of entities and their escaped equivalents,
18
- # to be escaped by h().
19
- ESCAPE_HTML = {
20
- "&" => "&amp;".freeze,
21
- "<" => "&lt;".freeze,
22
- ">" => "&gt;".freeze,
23
- "'" => "&#x27;".freeze,
24
- '"' => "&quot;".freeze,
25
- }.freeze
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
+ "&" => "&amp;".freeze,
35
+ "<" => "&lt;".freeze,
36
+ ">" => "&gt;".freeze,
37
+ "'" => "&#39;".freeze,
38
+ '"' => "&quot;".freeze,
39
+ }.freeze
26
40
 
27
- # A Regexp of HTML entities to match for escaping.
28
- ESCAPE_HTML_PATTERN = Regexp.union(*ESCAPE_HTML.keys)
41
+ # A Regexp of HTML entities to match for escaping.
42
+ ESCAPE_HTML_PATTERN = Regexp.union(*ESCAPE_HTML.keys)
29
43
 
30
- module InstanceMethods
31
- # HTML escape the input and return the escaped version.
32
- def h(string)
33
- string.to_s.gsub(ESCAPE_HTML_PATTERN){|c| ESCAPE_HTML[c] }
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
 
@@ -71,7 +71,7 @@ class Roda
71
71
  compile_opts = if pattern.is_a?(Hash)
72
72
  [opts]
73
73
  else
74
- Dir[pattern].map{|file| opts.merge(:path=>File.expand_path(file))}
74
+ Dir[pattern].map{|file| opts.merge(:path=>File.expand_path(file, nil))}
75
75
  end
76
76
 
77
77
  instance = allocate
@@ -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 = File.expand_path(opts[:root]||"public", app.opts[: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
@@ -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] = File.expand_path(opts[:views]||"views", app.opts[:root]).freeze
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| ::File.expand_path(f)}.uniq.freeze
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 cache_class = opts[:cache_class]
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 = ::File.expand_path(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
@@ -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] = File.expand_path(opts[:root]||"public", app.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
- r = @_request
109
- if route = self.class.static_route_for(r.request_method, r.path_info)
110
- catch(:halt){r.static_route(&route)}
111
- else
112
- super
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
@@ -4,7 +4,7 @@ class Roda
4
4
  RodaMajorVersion = 2
5
5
 
6
6
  # The minor version of Roda, updated for new feature releases of Roda.
7
- RodaMinorVersion = 23
7
+ RodaMinorVersion = 24
8
8
 
9
9
  # The patch version of Roda, updated only for bug fixes from the last
10
10
  # feature release.
@@ -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
@@ -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
@@ -6,6 +6,6 @@ describe "h plugin" do
6
6
  h("<form>") + h(:form) + h("test&<>/'")
7
7
  end
8
8
 
9
- body.must_equal '&lt;form&gt;formtest&amp;&lt;&gt;/&#x27;'
9
+ body.must_equal '&lt;form&gt;formtest&amp;&lt;&gt;/&#39;'
10
10
  end
11
11
  end
@@ -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
@@ -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 same render_opts as superclass when inheriting if :inherit_cache option is used" do
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.23.0
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-02-24 00:00:00.000000000 Z
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