roda 2.23.0 → 2.24.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
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