roda 3.59.0 → 3.61.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 +10 -0
- data/README.rdoc +40 -15
- data/doc/release_notes/3.60.0.txt +56 -0
- data/doc/release_notes/3.61.0.txt +24 -0
- data/lib/roda/plugins/_optimized_matching.rb +2 -2
- data/lib/roda/plugins/all_verbs.rb +0 -2
- data/lib/roda/plugins/common_logger.rb +1 -1
- data/lib/roda/plugins/disallow_file_uploads.rb +0 -2
- data/lib/roda/plugins/indifferent_params.rb +2 -8
- data/lib/roda/plugins/json_parser.rb +1 -3
- data/lib/roda/plugins/link_to.rb +83 -0
- data/lib/roda/plugins/multi_public.rb +0 -4
- data/lib/roda/plugins/not_allowed.rb +0 -2
- data/lib/roda/plugins/public.rb +0 -6
- data/lib/roda/plugins/render.rb +3 -13
- data/lib/roda/plugins/render_each.rb +0 -2
- data/lib/roda/plugins/render_locals.rb +0 -2
- data/lib/roda/plugins/sinatra_helpers.rb +0 -6
- data/lib/roda/plugins/status_handler.rb +1 -3
- data/lib/roda/plugins/typecast_params.rb +102 -53
- data/lib/roda/plugins/view_options.rb +0 -2
- data/lib/roda/request.rb +4 -8
- data/lib/roda/response.rb +0 -4
- data/lib/roda/version.rb +1 -1
- metadata +11 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 337e7cc074ffe0c300a4dbf9a14f72552caa5b9446fdf0ca04b9f22d9117c9c7
|
|
4
|
+
data.tar.gz: 53a8b4ec124df8ef0efdc7efcc7bec3e0e7ea7d13fa60fb5d6adf06f50ed081e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 78671d90145cf431a7deff333e6b75ad681252cae159b5ff09dfaba70d4b3fc17ec1f7362c52454c3f8e94e2343bd4b4d419a5c23738a6b525e9fdd79dc8846c
|
|
7
|
+
data.tar.gz: 55b5bc0be878d58d66e0834110b47313f522e1f109b95c23348b2cb09430b01383595197db8f8db6587f10d3d9e4a7a353a2d70fb1ed4a099573309a5aabcae6
|
data/CHANGELOG
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
= 3.61.0 (2022-10-12)
|
|
2
|
+
|
|
3
|
+
* Make Integer matcher limit integer segments to 100 characters by default (jeremyevans)
|
|
4
|
+
|
|
5
|
+
* Limit input bytesize by default for integer, float, and date/time typecasts in typecast_params (jeremyevans)
|
|
6
|
+
|
|
7
|
+
= 3.60.0 (2022-09-13)
|
|
8
|
+
|
|
9
|
+
* Add link_to plugin with link_to method for creating HTML links (jeremyevans)
|
|
10
|
+
|
|
1
11
|
= 3.59.0 (2022-08-12)
|
|
2
12
|
|
|
3
13
|
* Add additional_render_engines plugin, for considering multiple render engines for templates (jeremyevans)
|
data/README.rdoc
CHANGED
|
@@ -1,9 +1,35 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
rdoc-image:https://roda.jeremyevans.net/images/roda-logo.svg
|
|
2
|
+
|
|
3
|
+
A routing tree web toolkit, designed for building fast and maintainable web applications in Ruby.
|
|
4
|
+
|
|
5
|
+
== Table of contents
|
|
6
|
+
|
|
7
|
+
- {Installation}[#label-Installation]
|
|
8
|
+
- {Resources}[#label-Resources]
|
|
9
|
+
- {Goals}[#label-Goals]
|
|
10
|
+
- {Usage}[#label-Usage]
|
|
11
|
+
- {Running the application}[#label-Running+the+Application]
|
|
12
|
+
- {The routing tree}[#label-The+Routing+Tree]
|
|
13
|
+
- {Matchers}[#label-Matchers]
|
|
14
|
+
- {Optional segments}[#label-Optional+segments]
|
|
15
|
+
- {Match/Route Block Return Values}[#label-Match-2FRoute+Block+Return+Values]
|
|
16
|
+
- {Status codes}[#label-Status+Codes]
|
|
17
|
+
- {Verb methods}[#label-Verb+Methods]
|
|
18
|
+
- {Root method}[#label-Root+Method]
|
|
19
|
+
- {Request and Response}[#label-Request+and+Response]
|
|
20
|
+
- {Pollution}[#label-Pollution]
|
|
21
|
+
- {Composition}[#label-Composition]
|
|
22
|
+
- {Testing}[#label-Testing]
|
|
23
|
+
- {Settings}[#label-Settings]
|
|
24
|
+
- {Rendering}[#label-Rendering]
|
|
25
|
+
- {Security}[#label-Security]
|
|
26
|
+
- {Code Reloading}[#label-Code+Reloading]
|
|
27
|
+
- {Plugins}[#label-Plugins]
|
|
28
|
+
- {No introspection}[#label-No+Introspection]
|
|
29
|
+
- {Inspiration}[#label-Inspiration]
|
|
30
|
+
- {Ruby Support Policy}[#label-Ruby+Support+Policy]
|
|
31
|
+
|
|
32
|
+
== Installation
|
|
7
33
|
|
|
8
34
|
$ gem install roda
|
|
9
35
|
|
|
@@ -140,13 +166,11 @@ for every request.
|
|
|
140
166
|
== Running the Application
|
|
141
167
|
|
|
142
168
|
Running a Roda application is similar to running any other rack-based application
|
|
143
|
-
that uses a +config.ru+ file. You can start a basic server using +rackup
|
|
169
|
+
that uses a +config.ru+ file. You can start a basic server using +rackup+, +puma+,
|
|
170
|
+
+unicorn+, +passenger+, or any other webserver that can handle +config.ru+ files:
|
|
144
171
|
|
|
145
172
|
$ rackup
|
|
146
173
|
|
|
147
|
-
Ruby web servers such as Unicorn and Puma also ship with their own programs
|
|
148
|
-
that you can use to run a Roda application.
|
|
149
|
-
|
|
150
174
|
== The Routing Tree
|
|
151
175
|
|
|
152
176
|
Roda is called a routing tree web toolkit because the way most sites are structured,
|
|
@@ -454,7 +478,7 @@ shared branch:
|
|
|
454
478
|
|
|
455
479
|
This works well for many cases, but there are also cases where you really want to
|
|
456
480
|
treat it as one route with an optional segment. One simple way to do that is to
|
|
457
|
-
use a parameter instead of an optional segment (e.g.
|
|
481
|
+
use a parameter instead of an optional segment (e.g. <tt>/items/123?opt=456</tt>).
|
|
458
482
|
|
|
459
483
|
r.is "items", Integer do |item_id|
|
|
460
484
|
optional_data = r.params['opt'].to_s
|
|
@@ -647,14 +671,14 @@ If you have a lot of rack applications that you want to dispatch to, and
|
|
|
647
671
|
which one to dispatch to is based on the request path prefix, look into the
|
|
648
672
|
+multi_run+ plugin.
|
|
649
673
|
|
|
650
|
-
===
|
|
674
|
+
=== hash_branches plugin
|
|
651
675
|
|
|
652
676
|
If you are just looking to split up the main route block up by branches,
|
|
653
|
-
you should use the +
|
|
677
|
+
you should use the +hash_branches+ plugin,
|
|
654
678
|
which keeps the current scope of the +route+ block:
|
|
655
679
|
|
|
656
680
|
class App < Roda
|
|
657
|
-
plugin :
|
|
681
|
+
plugin :hash_branches
|
|
658
682
|
|
|
659
683
|
hash_branch "api" do |r|
|
|
660
684
|
r.is do
|
|
@@ -663,7 +687,7 @@ which keeps the current scope of the +route+ block:
|
|
|
663
687
|
end
|
|
664
688
|
|
|
665
689
|
route do |r|
|
|
666
|
-
r.
|
|
690
|
+
r.hash_branches
|
|
667
691
|
end
|
|
668
692
|
end
|
|
669
693
|
|
|
@@ -1120,3 +1144,4 @@ MIT
|
|
|
1120
1144
|
== Maintainer
|
|
1121
1145
|
|
|
1122
1146
|
Jeremy Evans <code@jeremyevans.net>
|
|
1147
|
+
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
= New Features
|
|
2
|
+
|
|
3
|
+
* A link_to plugin has been added with a link_to method for
|
|
4
|
+
creating HTML links.
|
|
5
|
+
|
|
6
|
+
The simplest usage of link_to is passing the body and the location
|
|
7
|
+
to link to as strings:
|
|
8
|
+
|
|
9
|
+
# Instance level
|
|
10
|
+
link_to("body", "/path")
|
|
11
|
+
# => "<a href=\"/path\">body</a>"
|
|
12
|
+
|
|
13
|
+
The link_to plugin depends on the path plugin, and allows you to
|
|
14
|
+
pass symbols for named paths:
|
|
15
|
+
|
|
16
|
+
# Class level
|
|
17
|
+
path :foo, "/path/to/too"
|
|
18
|
+
|
|
19
|
+
# Instance level
|
|
20
|
+
link_to("body", :foo)
|
|
21
|
+
# => "<a href=\"/path/to/foo\">body</a>"
|
|
22
|
+
|
|
23
|
+
It also allows you to pass instances of classes that you have
|
|
24
|
+
registered with the path plugin:
|
|
25
|
+
|
|
26
|
+
# Class level
|
|
27
|
+
A = Struct.new(:id)
|
|
28
|
+
path A do
|
|
29
|
+
"/path/to/a/#{id}"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Instance level
|
|
33
|
+
link_to("body", A.new(1))
|
|
34
|
+
# => "<a href=\"/path/to/a/1\">body</a>"
|
|
35
|
+
|
|
36
|
+
To set additional HTML attributes on the tag, you can pass them as
|
|
37
|
+
an options hash:
|
|
38
|
+
|
|
39
|
+
link_to("body", "/path", foo: "bar")
|
|
40
|
+
# => "<a href=\"/path\" foo=\"bar\">body</a>"
|
|
41
|
+
|
|
42
|
+
If the body is nil, it will be set to the same as the path:
|
|
43
|
+
|
|
44
|
+
link_to(nil, "/path")
|
|
45
|
+
# => "<a href=\"/path\">/path</a>"
|
|
46
|
+
|
|
47
|
+
The plugin will automatically HTML escape the path and any HTML
|
|
48
|
+
attribute values, using the h plugin:
|
|
49
|
+
|
|
50
|
+
link_to("body", "/path?a=1&b=2", foo: '"bar"')
|
|
51
|
+
# => "<a href=\"/path?a=1&b=2\" foo=\""bar"\">body</a>"
|
|
52
|
+
|
|
53
|
+
= Other Improvements
|
|
54
|
+
|
|
55
|
+
* Coverage testing has been expanded to multiple rack versions, instead
|
|
56
|
+
of just the current rack release.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
= Improvements
|
|
2
|
+
|
|
3
|
+
* The typecast_params plugin now limits input bytesize for integer,
|
|
4
|
+
float, and date/time typecasts. If the input is over the allowed
|
|
5
|
+
bytesize, typecasting will fail. This prevents issues with trying
|
|
6
|
+
to typecast arbitrarily large input.
|
|
7
|
+
|
|
8
|
+
* The default Integer class matcher now limits integer segments to
|
|
9
|
+
100 characters by default, also to prevent issues with typecasting
|
|
10
|
+
arbitrarily large input. Segments larger than 100 characters will
|
|
11
|
+
no longer be matched by the Integer class matcher.
|
|
12
|
+
|
|
13
|
+
= Backwards Compatibility
|
|
14
|
+
|
|
15
|
+
* If the input bytesize limits in the typecast_params plugin cause
|
|
16
|
+
issues in your application, you can use the :skip_bytesize_checking
|
|
17
|
+
option when loading the plugin to disable the checks.
|
|
18
|
+
|
|
19
|
+
* If the default Integer class matcher limit causes problems in your
|
|
20
|
+
application, you can use the class_matchers plugin to override the
|
|
21
|
+
matcher to not use a limit:
|
|
22
|
+
|
|
23
|
+
plugin :class_matchers
|
|
24
|
+
class_matcher(Integer, /(\d+)/){|a| [a.to_i]}
|
|
@@ -52,7 +52,7 @@ class Roda
|
|
|
52
52
|
end
|
|
53
53
|
end
|
|
54
54
|
elsif matcher == Integer
|
|
55
|
-
if matchdata = /\A\/(\d
|
|
55
|
+
if matchdata = /\A\/(\d{1,100})(?=\/|\z)/.match(@remaining_path)
|
|
56
56
|
@remaining_path = matchdata.post_match
|
|
57
57
|
always{yield(matchdata[1].to_i)}
|
|
58
58
|
end
|
|
@@ -151,7 +151,7 @@ class Roda
|
|
|
151
151
|
always{yield rp[1, len]}
|
|
152
152
|
end
|
|
153
153
|
elsif matcher == Integer
|
|
154
|
-
if matchdata = /\A\/(\d
|
|
154
|
+
if matchdata = /\A\/(\d{1,100})\z/.match(@remaining_path)
|
|
155
155
|
@remaining_path = ''
|
|
156
156
|
always{yield(matchdata[1].to_i)}
|
|
157
157
|
end
|
|
@@ -34,9 +34,7 @@ class Roda
|
|
|
34
34
|
module AllVerbs
|
|
35
35
|
module RequestMethods
|
|
36
36
|
%w'delete head options link patch put trace unlink'.each do |verb|
|
|
37
|
-
# :nocov:
|
|
38
37
|
if ::Rack::Request.method_defined?("#{verb}?")
|
|
39
|
-
# :nocov:
|
|
40
38
|
class_eval(<<-END, __FILE__, __LINE__+1)
|
|
41
39
|
def #{verb}(*args, &block)
|
|
42
40
|
_verb(args, &block) if #{verb}?
|
|
@@ -11,7 +11,7 @@ class Roda
|
|
|
11
11
|
# * Doesn't include middleware timing
|
|
12
12
|
# * Doesn't proxy the body
|
|
13
13
|
# * Doesn't support different capitalization of the Content-Length response header
|
|
14
|
-
# * Logs to
|
|
14
|
+
# * Logs to +$stderr+ instead of <tt>env['rack.errors']</tt> if explicit logger not passed
|
|
15
15
|
#
|
|
16
16
|
# Example:
|
|
17
17
|
#
|
|
@@ -52,14 +52,12 @@ class Roda
|
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
class Params < Rack::QueryParser::Params
|
|
55
|
-
|
|
56
|
-
if Rack.release >= '2.3'
|
|
55
|
+
if Rack.release >= '3'
|
|
57
56
|
def initialize
|
|
58
57
|
@size = 0
|
|
59
58
|
@params = Hash.new(&INDIFFERENT_PROC)
|
|
60
59
|
end
|
|
61
60
|
else
|
|
62
|
-
# :nocov:
|
|
63
61
|
def initialize(limit = Rack::Utils.key_space_limit)
|
|
64
62
|
@limit = limit
|
|
65
63
|
@size = 0
|
|
@@ -71,9 +69,7 @@ class Roda
|
|
|
71
69
|
end
|
|
72
70
|
|
|
73
71
|
module RequestMethods
|
|
74
|
-
|
|
75
|
-
query_parser = Rack.release >= '2.3' ? QueryParser.new(QueryParser::Params, 32) : QueryParser.new(QueryParser::Params, 65536, 32)
|
|
76
|
-
# :nocov:
|
|
72
|
+
query_parser = Rack.release >= '3' ? QueryParser.new(QueryParser::Params, 32) : QueryParser.new(QueryParser::Params, 65536, 32)
|
|
77
73
|
QUERY_PARSER = Rack::Utils.default_query_parser = query_parser
|
|
78
74
|
|
|
79
75
|
private
|
|
@@ -89,7 +85,6 @@ class Roda
|
|
|
89
85
|
end
|
|
90
86
|
end
|
|
91
87
|
else
|
|
92
|
-
# :nocov:
|
|
93
88
|
module InstanceMethods
|
|
94
89
|
# A copy of the request params that will automatically
|
|
95
90
|
# convert symbols to strings.
|
|
@@ -115,7 +110,6 @@ class Roda
|
|
|
115
110
|
end
|
|
116
111
|
end
|
|
117
112
|
end
|
|
118
|
-
# :nocov:
|
|
119
113
|
end
|
|
120
114
|
end
|
|
121
115
|
|
|
@@ -86,12 +86,10 @@ class Roda
|
|
|
86
86
|
|
|
87
87
|
|
|
88
88
|
# Rack 3 dropped requirement that input be rewindable
|
|
89
|
-
if Rack.release >= '
|
|
90
|
-
# :nocov:
|
|
89
|
+
if Rack.release >= '3'
|
|
91
90
|
def _read_json_input(input)
|
|
92
91
|
input.read
|
|
93
92
|
end
|
|
94
|
-
# :nocov:
|
|
95
93
|
else
|
|
96
94
|
def _read_json_input(input)
|
|
97
95
|
input.rewind
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
class Roda
|
|
5
|
+
module RodaPlugins
|
|
6
|
+
# The link_to plugin adds the +link_to+ instance method, which can be used for constructing
|
|
7
|
+
# HTML links (+a+ tag with +href+ attribute).
|
|
8
|
+
#
|
|
9
|
+
# The simplest usage of +link_to+ is passing the body and the location to link to as strings:
|
|
10
|
+
#
|
|
11
|
+
# link_to("body", "/path")
|
|
12
|
+
# # => "<a href=\"/path\">body</a>"
|
|
13
|
+
#
|
|
14
|
+
# The link_to plugin depends on the path plugin, and allows you to pass symbols for named paths:
|
|
15
|
+
#
|
|
16
|
+
# # Class level
|
|
17
|
+
# path :foo, "/path/to/too"
|
|
18
|
+
#
|
|
19
|
+
# # Instance level
|
|
20
|
+
# link_to("body", :foo)
|
|
21
|
+
# # => "<a href=\"/path/to/foo\">body</a>"
|
|
22
|
+
#
|
|
23
|
+
# It also allows you to pass instances of classes that you have registered with the path plugin:
|
|
24
|
+
#
|
|
25
|
+
# # Class level
|
|
26
|
+
# A = Struct.new(:id)
|
|
27
|
+
# path A do
|
|
28
|
+
# "/path/to/a/#{id}"
|
|
29
|
+
# end
|
|
30
|
+
#
|
|
31
|
+
# # Instance level
|
|
32
|
+
# link_to("body", A.new(1))
|
|
33
|
+
# # => "<a href=\"/path/to/a/1\">body</a>"
|
|
34
|
+
#
|
|
35
|
+
# To set additional HTML attributes on the +a+ tag, you can pass them as an options hash:
|
|
36
|
+
#
|
|
37
|
+
# link_to("body", "/path", foo: "bar")
|
|
38
|
+
# # => "<a href=\"/path\" foo=\"bar\">body</a>"
|
|
39
|
+
#
|
|
40
|
+
# If the body is nil, it will be set to the same as the path:
|
|
41
|
+
#
|
|
42
|
+
# link_to(nil, "/path")
|
|
43
|
+
# # => "<a href=\"/path\">/path</a>"
|
|
44
|
+
#
|
|
45
|
+
# The plugin will automatically HTML escape the path and any HTML attribute values, using the h plugin:
|
|
46
|
+
#
|
|
47
|
+
# link_to("body", "/path?a=1&b=2", foo: '"bar"')
|
|
48
|
+
# # => "<a href=\"/path?a=1&b=2\" foo=\""bar"\">body</a>"
|
|
49
|
+
module LinkTo
|
|
50
|
+
def self.load_dependencies(app)
|
|
51
|
+
app.plugin :h
|
|
52
|
+
app.plugin :path
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
module InstanceMethods
|
|
56
|
+
# Return a string with an HTML +a+ tag with an +href+ attribute. See LinkTo
|
|
57
|
+
# module documentation for details.
|
|
58
|
+
def link_to(body, href, attributes=OPTS)
|
|
59
|
+
case href
|
|
60
|
+
when Symbol
|
|
61
|
+
href = public_send(:"#{href}_path")
|
|
62
|
+
when String
|
|
63
|
+
# nothing
|
|
64
|
+
else
|
|
65
|
+
href = path(href)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
href = h(href)
|
|
69
|
+
|
|
70
|
+
body = href if body.nil?
|
|
71
|
+
|
|
72
|
+
buf = String.new << "<a href=\"#{href}\""
|
|
73
|
+
attributes.each do |k, v|
|
|
74
|
+
buf << " " << k.to_s << "=\"" << h(v) << "\""
|
|
75
|
+
end
|
|
76
|
+
buf << ">" << body << "</a>"
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
register_plugin(:link_to, LinkTo)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -3,9 +3,7 @@
|
|
|
3
3
|
begin
|
|
4
4
|
require 'rack/files'
|
|
5
5
|
rescue LoadError
|
|
6
|
-
# :nocov:
|
|
7
6
|
require 'rack/file'
|
|
8
|
-
# :nocov:
|
|
9
7
|
end
|
|
10
8
|
|
|
11
9
|
#
|
|
@@ -66,9 +64,7 @@ class Roda
|
|
|
66
64
|
# end
|
|
67
65
|
# end
|
|
68
66
|
module MultiPublic
|
|
69
|
-
# :nocov:
|
|
70
67
|
RACK_FILES = defined?(Rack::Files) ? Rack::Files : Rack::File
|
|
71
|
-
# :nocov:
|
|
72
68
|
|
|
73
69
|
def self.load_dependencies(app, _, opts=OPTS)
|
|
74
70
|
app.plugin(:public, opts)
|
|
@@ -116,9 +116,7 @@ class Roda
|
|
|
116
116
|
# arguments, record the verb used. If given an argument, add an is
|
|
117
117
|
# check with the arguments.
|
|
118
118
|
%w'get post delete head options link patch put trace unlink'.each do |verb|
|
|
119
|
-
# :nocov:
|
|
120
119
|
if ::Rack::Request.method_defined?("#{verb}?")
|
|
121
|
-
# :nocov:
|
|
122
120
|
class_eval(<<-END, __FILE__, __LINE__+1)
|
|
123
121
|
def #{verb}(*args, &block)
|
|
124
122
|
if (empty = args.empty?) && @_is_verbs
|
data/lib/roda/plugins/public.rb
CHANGED
|
@@ -5,9 +5,7 @@ require 'uri'
|
|
|
5
5
|
begin
|
|
6
6
|
require 'rack/files'
|
|
7
7
|
rescue LoadError
|
|
8
|
-
# :nocov:
|
|
9
8
|
require 'rack/file'
|
|
10
|
-
# :nocov:
|
|
11
9
|
end
|
|
12
10
|
|
|
13
11
|
#
|
|
@@ -45,9 +43,7 @@ class Roda
|
|
|
45
43
|
module Public
|
|
46
44
|
SPLIT = Regexp.union(*[File::SEPARATOR, File::ALT_SEPARATOR].compact)
|
|
47
45
|
PARSER = URI::DEFAULT_PARSER
|
|
48
|
-
# :nocov:
|
|
49
46
|
RACK_FILES = defined?(Rack::Files) ? Rack::Files : Rack::File
|
|
50
|
-
# :nocov:
|
|
51
47
|
|
|
52
48
|
# Use options given to setup a Rack::File instance for serving files. Options:
|
|
53
49
|
# :default_mime :: The default mime type to use if the mime type is not recognized.
|
|
@@ -142,13 +138,11 @@ class Roda
|
|
|
142
138
|
server.serving(self, path)
|
|
143
139
|
end
|
|
144
140
|
else
|
|
145
|
-
# :nocov:
|
|
146
141
|
def public_serve(server, path)
|
|
147
142
|
server = server.dup
|
|
148
143
|
server.path = path
|
|
149
144
|
server.serving(env)
|
|
150
145
|
end
|
|
151
|
-
# :nocov:
|
|
152
146
|
end
|
|
153
147
|
end
|
|
154
148
|
end
|
data/lib/roda/plugins/render.rb
CHANGED
|
@@ -214,18 +214,18 @@ class Roda
|
|
|
214
214
|
tilt_compiled_method_support = defined?(Tilt::VERSION) && Tilt::VERSION >= '1.2' &&
|
|
215
215
|
([1, -2].include?(((compiled_method_arity = Tilt::Template.instance_method(:compiled_method).arity) rescue false)))
|
|
216
216
|
NO_CACHE = {:cache=>false}.freeze
|
|
217
|
-
COMPILED_METHOD_SUPPORT = RUBY_VERSION >= '2.3' && tilt_compiled_method_support
|
|
217
|
+
COMPILED_METHOD_SUPPORT = RUBY_VERSION >= '2.3' && tilt_compiled_method_support && ENV['RODA_RENDER_COMPILED_METHOD_SUPPORT'] != 'no'
|
|
218
218
|
|
|
219
219
|
if compiled_method_arity == -2
|
|
220
220
|
def self.tilt_template_compiled_method(template, locals_keys, scope_class)
|
|
221
221
|
template.send(:compiled_method, locals_keys, scope_class)
|
|
222
222
|
end
|
|
223
|
+
# :nocov:
|
|
223
224
|
else
|
|
224
|
-
# :nocov:
|
|
225
225
|
def self.tilt_template_compiled_method(template, locals_keys, scope_class)
|
|
226
226
|
template.send(:compiled_method, locals_keys)
|
|
227
227
|
end
|
|
228
|
-
|
|
228
|
+
# :nocov:
|
|
229
229
|
end
|
|
230
230
|
|
|
231
231
|
# Setup default rendering options. See Render for details.
|
|
@@ -366,9 +366,7 @@ class Roda
|
|
|
366
366
|
false
|
|
367
367
|
end
|
|
368
368
|
|
|
369
|
-
# :nocov:
|
|
370
369
|
if COMPILED_METHOD_SUPPORT
|
|
371
|
-
# :nocov:
|
|
372
370
|
# Compile a method in the given module with the given name that will
|
|
373
371
|
# call the compiled template method, updating the compiled template method
|
|
374
372
|
def define_compiled_method(roda_class, method_name, locals_keys=EMPTY_ARRAY)
|
|
@@ -412,9 +410,7 @@ class Roda
|
|
|
412
410
|
end
|
|
413
411
|
|
|
414
412
|
module ClassMethods
|
|
415
|
-
# :nocov:
|
|
416
413
|
if COMPILED_METHOD_SUPPORT
|
|
417
|
-
# :nocov:
|
|
418
414
|
# If using compiled methods and there is an optimized layout, speed up
|
|
419
415
|
# access to the layout method to improve the performance of view.
|
|
420
416
|
def freeze
|
|
@@ -437,9 +433,7 @@ class Roda
|
|
|
437
433
|
def inherited(subclass)
|
|
438
434
|
super
|
|
439
435
|
opts = subclass.opts[:render] = subclass.opts[:render].dup
|
|
440
|
-
# :nocov:
|
|
441
436
|
if COMPILED_METHOD_SUPPORT
|
|
442
|
-
# :nocov:
|
|
443
437
|
opts[:template_method_cache] = (opts[:cache_class] || RodaCache).new
|
|
444
438
|
end
|
|
445
439
|
opts[:cache] = opts[:cache].dup
|
|
@@ -459,9 +453,7 @@ class Roda
|
|
|
459
453
|
instance = allocate
|
|
460
454
|
instance.send(:retrieve_template, instance.send(:view_layout_opts, OPTS))
|
|
461
455
|
|
|
462
|
-
# :nocov:
|
|
463
456
|
if COMPILED_METHOD_SUPPORT
|
|
464
|
-
# :nocov:
|
|
465
457
|
if (layout_template = render_opts[:optimize_layout]) && !opts[:render][:optimized_layout_method_created]
|
|
466
458
|
instance.send(:retrieve_template, :template=>layout_template, :cache_key=>nil, :template_method_cache_key => :_roda_layout)
|
|
467
459
|
layout_method = opts[:render][:template_method_cache][:_roda_layout]
|
|
@@ -610,7 +602,6 @@ class Roda
|
|
|
610
602
|
end
|
|
611
603
|
end
|
|
612
604
|
else
|
|
613
|
-
# :nocov:
|
|
614
605
|
def _cached_template_method(_)
|
|
615
606
|
nil
|
|
616
607
|
end
|
|
@@ -630,7 +621,6 @@ class Roda
|
|
|
630
621
|
def _optimized_view_content(template)
|
|
631
622
|
nil
|
|
632
623
|
end
|
|
633
|
-
# :nocov:
|
|
634
624
|
end
|
|
635
625
|
|
|
636
626
|
|
|
@@ -43,9 +43,7 @@ class Roda
|
|
|
43
43
|
module InstanceMethods
|
|
44
44
|
private
|
|
45
45
|
|
|
46
|
-
# :nocov:
|
|
47
46
|
if Render::COMPILED_METHOD_SUPPORT
|
|
48
|
-
# :nocov:
|
|
49
47
|
# Disable use of cached templates, since it assumes a render/view call with no
|
|
50
48
|
# options will have no locals.
|
|
51
49
|
def _cached_template_method(template)
|
|
@@ -4,9 +4,7 @@ require 'rack/mime'
|
|
|
4
4
|
begin
|
|
5
5
|
require 'rack/files'
|
|
6
6
|
rescue LoadError
|
|
7
|
-
# :nocov:
|
|
8
7
|
require 'rack/file'
|
|
9
|
-
# :nocov:
|
|
10
8
|
end
|
|
11
9
|
|
|
12
10
|
|
|
@@ -225,9 +223,7 @@ class Roda
|
|
|
225
223
|
ISO88591_ENCODING = Encoding.find('ISO-8859-1')
|
|
226
224
|
BINARY_ENCODING = Encoding.find('BINARY')
|
|
227
225
|
|
|
228
|
-
# :nocov:
|
|
229
226
|
RACK_FILES = defined?(Rack::Files) ? Rack::Files : Rack::File
|
|
230
|
-
# :nocov:
|
|
231
227
|
|
|
232
228
|
# Depend on the status_303 plugin.
|
|
233
229
|
def self.load_dependencies(app, _opts = nil)
|
|
@@ -351,10 +347,8 @@ class Roda
|
|
|
351
347
|
s, h, b = if Rack.release > '2'
|
|
352
348
|
file.serving(self, path)
|
|
353
349
|
else
|
|
354
|
-
# :nocov:
|
|
355
350
|
file.path = path
|
|
356
351
|
file.serving(@env)
|
|
357
|
-
# :nocov:
|
|
358
352
|
end
|
|
359
353
|
|
|
360
354
|
res.status = opts[:status] || s
|
|
@@ -45,11 +45,9 @@ class Roda
|
|
|
45
45
|
when nil, false
|
|
46
46
|
CLEAR_HEADERS
|
|
47
47
|
when Array
|
|
48
|
-
|
|
49
|
-
if Rack.release >= '2.3'
|
|
48
|
+
if Rack.release >= '3'
|
|
50
49
|
keep_headers = keep_headers.map(&:downcase)
|
|
51
50
|
end
|
|
52
|
-
# :nocov:
|
|
53
51
|
lambda{|headers| headers.delete_if{|k,_| !keep_headers.include?(k)}}
|
|
54
52
|
else
|
|
55
53
|
raise RodaError, "Invalid :keep_headers option"
|
|
@@ -5,34 +5,26 @@ require 'time'
|
|
|
5
5
|
|
|
6
6
|
class Roda
|
|
7
7
|
module RodaPlugins
|
|
8
|
-
# The typecast_params plugin allows for
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
# cases it makes sense to explicitly convert the parameter to the
|
|
8
|
+
# The typecast_params plugin allows for type conversion of submitted parameters.
|
|
9
|
+
# Submitted parameters should be considered untrusted input, and in standard use
|
|
10
|
+
# with browsers, parameters are # submitted as strings (or a hash/array containing
|
|
11
|
+
# strings). In most # cases it makes sense to explicitly convert the parameter to the
|
|
13
12
|
# desired type. While this can be done via manual conversion:
|
|
14
13
|
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
14
|
+
# val = request.params['key'].to_i
|
|
15
|
+
# val = nil unless val > 0
|
|
17
16
|
#
|
|
18
17
|
# the typecast_params plugin adds a friendlier interface:
|
|
19
18
|
#
|
|
20
|
-
#
|
|
19
|
+
# val = typecast_params.pos_int('key')
|
|
21
20
|
#
|
|
22
|
-
# As +typecast_params+ is a fairly long method name,
|
|
23
|
-
# consider aliasing it to something more terse in your
|
|
24
|
-
# such as +tp+.
|
|
25
|
-
#
|
|
26
|
-
# One advantage of using typecast_params is that access or conversion
|
|
27
|
-
# errors are raised as a specific exception class
|
|
28
|
-
# (+Roda::RodaPlugins::TypecastParams::Error+). This allows you to handle
|
|
29
|
-
# this specific exception class globally and return an appropriate 4xx
|
|
30
|
-
# response to the client. You can use the Error#param_name and Error#reason
|
|
31
|
-
# methods to get more information about the error.
|
|
21
|
+
# As +typecast_params+ is a fairly long method name, and may be a method you call
|
|
22
|
+
# frequently, you may want to consider aliasing it to something more terse in your
|
|
23
|
+
# application, such as +tp+.
|
|
32
24
|
#
|
|
33
25
|
# typecast_params offers support for default values:
|
|
34
26
|
#
|
|
35
|
-
#
|
|
27
|
+
# val = typecast_params.pos_int('key', 1)
|
|
36
28
|
#
|
|
37
29
|
# The default value is only used if no value has been submitted for the parameter,
|
|
38
30
|
# or if the conversion of the value results in +nil+. Handling defaults for parameter
|
|
@@ -43,35 +35,41 @@ class Roda
|
|
|
43
35
|
# In many cases, parameters should be required, and if they aren't submitted, that
|
|
44
36
|
# should be considered an error. typecast_params handles this with ! methods:
|
|
45
37
|
#
|
|
46
|
-
#
|
|
38
|
+
# val = typecast_params.pos_int!('key')
|
|
47
39
|
#
|
|
48
40
|
# These ! methods raise an error instead of returning +nil+, and do not allow defaults.
|
|
49
41
|
#
|
|
42
|
+
# The errors raised by this plugin use a specific exception class,
|
|
43
|
+
# +Roda::RodaPlugins::TypecastParams::Error+. This allows you to handle
|
|
44
|
+
# this specific exception class globally and return an appropriate 4xx
|
|
45
|
+
# response to the client. You can use the Error#param_name and Error#reason
|
|
46
|
+
# methods to get more information about the error.
|
|
47
|
+
#
|
|
50
48
|
# To make it easy to handle cases where many parameters need the same conversion
|
|
51
49
|
# done, you can pass an array of keys to a conversion method, and it will return an array
|
|
52
50
|
# of converted values:
|
|
53
51
|
#
|
|
54
|
-
#
|
|
52
|
+
# val1, val2 = typecast_params.pos_int(['key1', 'key2'])
|
|
55
53
|
#
|
|
56
54
|
# This is equivalent to:
|
|
57
55
|
#
|
|
58
|
-
#
|
|
59
|
-
#
|
|
56
|
+
# val1 = typecast_params.pos_int('key1')
|
|
57
|
+
# val2 = typecast_params.pos_int('key2')
|
|
60
58
|
#
|
|
61
59
|
# The ! methods also support arrays, ensuring that all parameters have a value:
|
|
62
60
|
#
|
|
63
|
-
#
|
|
61
|
+
# val1, val2 = typecast_params.pos_int!(['key1', 'key2'])
|
|
64
62
|
#
|
|
65
63
|
# For handling of array parameters, where all entries in the array use the
|
|
66
64
|
# same conversion, there is an +array+ method which takes the type as the first argument
|
|
67
65
|
# and the keys to convert as the second argument:
|
|
68
66
|
#
|
|
69
|
-
#
|
|
67
|
+
# vals = typecast_params.array(:pos_int, 'keys')
|
|
70
68
|
#
|
|
71
69
|
# If you want to ensure that all entries in the array are converted successfully and that
|
|
72
70
|
# there is a value for the array itself, you can use +array!+:
|
|
73
71
|
#
|
|
74
|
-
#
|
|
72
|
+
# vals = typecast_params.array!(:pos_int, 'keys')
|
|
75
73
|
#
|
|
76
74
|
# This will raise an exception if any of the values in the array for parameter +keys+ cannot
|
|
77
75
|
# be converted to integer.
|
|
@@ -79,8 +77,8 @@ class Roda
|
|
|
79
77
|
# Both +array+ and +array!+ support default values which are used if no value is present
|
|
80
78
|
# for the parameter:
|
|
81
79
|
#
|
|
82
|
-
#
|
|
83
|
-
#
|
|
80
|
+
# vals1 = typecast_params.array(:pos_int, 'keys1', [])
|
|
81
|
+
# vals2 = typecast_params.array!(:pos_int, 'keys2', [])
|
|
84
82
|
#
|
|
85
83
|
# You can also pass an array of keys to +array+ or +array!+, if you would like to perform
|
|
86
84
|
# the same conversion on multiple arrays:
|
|
@@ -88,7 +86,7 @@ class Roda
|
|
|
88
86
|
# foo_ids, bar_ids = typecast_params.array!(:pos_int, ['foo_ids', 'bar_ids'])
|
|
89
87
|
#
|
|
90
88
|
# The previous examples have shown use of the +pos_int+ method, which uses +to_i+ to convert the
|
|
91
|
-
# value to an integer, but returns nil if the resulting integer is not positive. Unless you need
|
|
89
|
+
# value to an integer, but returns +nil+ if the resulting integer is not positive. Unless you need
|
|
92
90
|
# to handle negative numbers, it is recommended to use +pos_int+ instead of +int+ as +int+ will
|
|
93
91
|
# convert invalid values to 0 (since that is how <tt>String#to_i</tt> works).
|
|
94
92
|
#
|
|
@@ -224,10 +222,10 @@ class Roda
|
|
|
224
222
|
# # }
|
|
225
223
|
#
|
|
226
224
|
# Using the +:symbolize+ option makes it simpler to transition from untrusted external
|
|
227
|
-
# data (string keys), to
|
|
228
|
-
# the expected types are used).
|
|
225
|
+
# data (string keys), to semitrusted data that can be used internally (trusted in the sense that
|
|
226
|
+
# the expected types are used, not that you trust the values).
|
|
229
227
|
#
|
|
230
|
-
# Note that if there are multiple conversion
|
|
228
|
+
# Note that if there are multiple conversion errors raised inside a +convert!+ or +convert_each!+
|
|
231
229
|
# block, they are recorded and a single TypecastParams::Error instance is raised after
|
|
232
230
|
# processing the block. TypecastParams::Error#param_names can be called on the exception to
|
|
233
231
|
# get an array of all parameter names with conversion issues, and TypecastParams::Error#all_errors
|
|
@@ -245,14 +243,18 @@ class Roda
|
|
|
245
243
|
# specific to the Roda application. You can add support for custom types by passing a block
|
|
246
244
|
# when loading the typecast_params plugin. This block is executed in the context of the
|
|
247
245
|
# subclass, and calling +handle_type+ in the block can be used to add conversion methods.
|
|
248
|
-
# +handle_type+ accepts a type name and the block used to convert the type
|
|
246
|
+
# +handle_type+ accepts a type name, an options hash, and the block used to convert the type.
|
|
247
|
+
# The only currently supported option is +:max_input_bytesize+, specifying the maximum bytesize of
|
|
248
|
+
# string input. You can also override the max input bytesize of an existing type using the
|
|
249
|
+
# +max_input_bytesize+ method.
|
|
249
250
|
#
|
|
250
251
|
# plugin :typecast_params do
|
|
251
|
-
# handle_type(:album) do |value|
|
|
252
|
+
# handle_type(:album, max_input_bytesize: 100) do |value|
|
|
252
253
|
# if id = convert_pos_int(val)
|
|
253
254
|
# Album[id]
|
|
254
255
|
# end
|
|
255
256
|
# end
|
|
257
|
+
# max_input_bytesize(:date, 256)
|
|
256
258
|
# end
|
|
257
259
|
#
|
|
258
260
|
# By default, the typecast_params conversion procs are passed the parameter value directly
|
|
@@ -260,10 +262,18 @@ class Roda
|
|
|
260
262
|
# strip leading and trailing whitespace from parameter string values before processing, which
|
|
261
263
|
# you can do by passing the <tt>strip: :all</tt> option when loading the plugin.
|
|
262
264
|
#
|
|
263
|
-
# By default, the
|
|
264
|
-
#
|
|
265
|
+
# By default, the typecasting methods for some types check whether the bytesize of input
|
|
266
|
+
# strings is over the maximum expected values, and raise an error in such cases. The input
|
|
267
|
+
# bytesize is checked prior to any type conversion. If you would like to skip this check
|
|
268
|
+
# and allow any bytesize when doing type conversion for param string values, you can do so by
|
|
269
|
+
# passing the # <tt>:skip_bytesize_checking</tt> option when loading the plugin. By default,
|
|
270
|
+
# there is an 100 byte limit on integer input, an 1000 byte input on float input, and a 128
|
|
271
|
+
# byte limit on date/time input.
|
|
272
|
+
#
|
|
273
|
+
# By default, the typecasting methods check whether input strings have null bytes, and raise
|
|
274
|
+
# an error in such cases. This check for null bytes occurs prior to any type conversion.
|
|
265
275
|
# If you would like to skip this check and allow null bytes in param string values,
|
|
266
|
-
# you can do by passing the <tt>:allow_null_bytes</tt> option when loading the plugin.
|
|
276
|
+
# you can do so by passing the <tt>:allow_null_bytes</tt> option when loading the plugin.
|
|
267
277
|
#
|
|
268
278
|
# You can use the :date_parse_input_handler option to specify custom handling of date
|
|
269
279
|
# parsing input. Modern versions of Ruby and the date gem internally raise if the input to
|
|
@@ -282,6 +292,11 @@ class Roda
|
|
|
282
292
|
# string.b[0, 128]
|
|
283
293
|
# }
|
|
284
294
|
#
|
|
295
|
+
# The +date_parse_input_handler+ is only called if the value is under the max input
|
|
296
|
+
# bytesize, so you may need to call +max_input_bytesize+ for the +:date+, +:time+, and
|
|
297
|
+
# +:datetime+ methods to override the max input bytesize if you want to use this option
|
|
298
|
+
# for input strings over 128 bytes.
|
|
299
|
+
#
|
|
285
300
|
# By design, typecast_params only deals with string keys, it is not possible to use
|
|
286
301
|
# symbol keys as arguments to the conversion methods and have them converted.
|
|
287
302
|
module TypecastParams
|
|
@@ -409,6 +424,14 @@ class Roda
|
|
|
409
424
|
end
|
|
410
425
|
end
|
|
411
426
|
|
|
427
|
+
module SkipBytesizeChecking
|
|
428
|
+
private
|
|
429
|
+
|
|
430
|
+
# Do not check max input bytesize
|
|
431
|
+
def check_allowed_bytesize(v, max)
|
|
432
|
+
end
|
|
433
|
+
end
|
|
434
|
+
|
|
412
435
|
# Class handling conversion of submitted parameters to desired types.
|
|
413
436
|
class Params
|
|
414
437
|
# Handle conversions for the given type using the given block.
|
|
@@ -422,23 +445,29 @@ class Roda
|
|
|
422
445
|
# This method is used to define all type conversions, even the built
|
|
423
446
|
# in ones. It can be called in subclasses to setup subclass-specific
|
|
424
447
|
# types.
|
|
425
|
-
def self.handle_type(type, &block)
|
|
448
|
+
def self.handle_type(type, opts=OPTS, &block)
|
|
426
449
|
convert_meth = :"convert_#{type}"
|
|
427
450
|
define_method(convert_meth, &block)
|
|
428
451
|
|
|
452
|
+
max_input_bytesize = opts[:max_input_bytesize]
|
|
453
|
+
max_input_bytesize_meth = :"_max_input_bytesize_for_#{type}"
|
|
454
|
+
define_method(max_input_bytesize_meth){max_input_bytesize}
|
|
455
|
+
|
|
429
456
|
convert_array_meth = :"_convert_array_#{type}"
|
|
430
457
|
define_method(convert_array_meth) do |v|
|
|
431
458
|
raise Error, "expected array but received #{v.inspect}" unless v.is_a?(Array)
|
|
432
459
|
v.map! do |val|
|
|
460
|
+
check_allowed_bytesize(val, send(max_input_bytesize_meth))
|
|
433
461
|
check_null_byte(val)
|
|
434
462
|
send(convert_meth, val)
|
|
435
463
|
end
|
|
436
464
|
end
|
|
437
465
|
|
|
438
|
-
private convert_meth, convert_array_meth
|
|
466
|
+
private convert_meth, convert_array_meth, max_input_bytesize_meth
|
|
467
|
+
alias_method max_input_bytesize_meth, max_input_bytesize_meth
|
|
439
468
|
|
|
440
469
|
define_method(type) do |key, default=nil|
|
|
441
|
-
process_arg(convert_meth, key, default) if require_hash!
|
|
470
|
+
process_arg(convert_meth, key, default, send(max_input_bytesize_meth)) if require_hash!
|
|
442
471
|
end
|
|
443
472
|
|
|
444
473
|
define_method(:"#{type}!") do |key|
|
|
@@ -446,6 +475,15 @@ class Roda
|
|
|
446
475
|
end
|
|
447
476
|
end
|
|
448
477
|
|
|
478
|
+
# Override the maximum input bytesize for the given type. This is mostly useful
|
|
479
|
+
# for overriding the sizes for the default input types.
|
|
480
|
+
def self.max_input_bytesize(type, bytesize)
|
|
481
|
+
max_input_bytesize_meth = :"_max_input_bytesize_for_#{type}"
|
|
482
|
+
define_method(max_input_bytesize_meth){bytesize}
|
|
483
|
+
private max_input_bytesize_meth
|
|
484
|
+
alias_method max_input_bytesize_meth, max_input_bytesize_meth
|
|
485
|
+
end
|
|
486
|
+
|
|
449
487
|
# Create a new instance with the given object and nesting level.
|
|
450
488
|
# +obj+ should be an array or hash, and +nesting+ should be an
|
|
451
489
|
# array. Designed for internal use, should not be called by
|
|
@@ -485,17 +523,17 @@ class Roda
|
|
|
485
523
|
end
|
|
486
524
|
end
|
|
487
525
|
|
|
488
|
-
handle_type(:int) do |v|
|
|
526
|
+
handle_type(:int, :max_input_bytesize=>100) do |v|
|
|
489
527
|
string_or_numeric!(v) && v.to_i
|
|
490
528
|
end
|
|
491
529
|
|
|
492
|
-
handle_type(:pos_int) do |v|
|
|
530
|
+
handle_type(:pos_int, :max_input_bytesize=>100) do |v|
|
|
493
531
|
if (v = convert_int(v)) && v > 0
|
|
494
532
|
v
|
|
495
533
|
end
|
|
496
534
|
end
|
|
497
535
|
|
|
498
|
-
handle_type(:Integer) do |v|
|
|
536
|
+
handle_type(:Integer, :max_input_bytesize=>100) do |v|
|
|
499
537
|
if string_or_numeric!(v)
|
|
500
538
|
case v
|
|
501
539
|
when String
|
|
@@ -510,11 +548,11 @@ class Roda
|
|
|
510
548
|
end
|
|
511
549
|
end
|
|
512
550
|
|
|
513
|
-
handle_type(:float) do |v|
|
|
551
|
+
handle_type(:float, :max_input_bytesize=>1000) do |v|
|
|
514
552
|
string_or_numeric!(v) && v.to_f
|
|
515
553
|
end
|
|
516
554
|
|
|
517
|
-
handle_type(:Float) do |v|
|
|
555
|
+
handle_type(:Float, :max_input_bytesize=>1000) do |v|
|
|
518
556
|
string_or_numeric!(v) && ::Kernel::Float(v)
|
|
519
557
|
end
|
|
520
558
|
|
|
@@ -523,15 +561,15 @@ class Roda
|
|
|
523
561
|
v
|
|
524
562
|
end
|
|
525
563
|
|
|
526
|
-
handle_type(:date) do |v|
|
|
564
|
+
handle_type(:date, :max_input_bytesize=>128) do |v|
|
|
527
565
|
parse!(::Date, v)
|
|
528
566
|
end
|
|
529
567
|
|
|
530
|
-
handle_type(:time) do |v|
|
|
568
|
+
handle_type(:time, :max_input_bytesize=>128) do |v|
|
|
531
569
|
parse!(::Time, v)
|
|
532
570
|
end
|
|
533
571
|
|
|
534
|
-
handle_type(:datetime) do |v|
|
|
572
|
+
handle_type(:datetime, :max_input_bytesize=>128) do |v|
|
|
535
573
|
parse!(::DateTime, v)
|
|
536
574
|
end
|
|
537
575
|
|
|
@@ -712,7 +750,7 @@ class Roda
|
|
|
712
750
|
def array(type, key, default=nil)
|
|
713
751
|
meth = :"_convert_array_#{type}"
|
|
714
752
|
raise ProgrammerError, "no typecast_params type registered for #{type.inspect}" unless respond_to?(meth, true)
|
|
715
|
-
process_arg(meth, key, default) if require_hash!
|
|
753
|
+
process_arg(meth, key, default, send(:"_max_input_bytesize_for_#{type}")) if require_hash!
|
|
716
754
|
end
|
|
717
755
|
|
|
718
756
|
# Call +array+ with the +type+, +key+, and +default+, but if the return value is nil or any value in
|
|
@@ -945,10 +983,10 @@ class Roda
|
|
|
945
983
|
|
|
946
984
|
# If +key+ is not an array, convert the value at the given +key+ using the +meth+ method and +default+
|
|
947
985
|
# value. If +key+ is an array, return an array with the conversion done for each respective member of +key+.
|
|
948
|
-
def process_arg(meth, key, default)
|
|
986
|
+
def process_arg(meth, key, default, max_input_bytesize=nil)
|
|
949
987
|
case key
|
|
950
988
|
when String
|
|
951
|
-
v = process(meth, key, default)
|
|
989
|
+
v = process(meth, key, default, max_input_bytesize)
|
|
952
990
|
|
|
953
991
|
if @capture
|
|
954
992
|
key = key.to_sym if symbolize?
|
|
@@ -961,13 +999,20 @@ class Roda
|
|
|
961
999
|
when Array
|
|
962
1000
|
key.map do |k|
|
|
963
1001
|
raise ProgrammerError, "non-String element in array argument passed to typecast_params: #{k.inspect}" unless k.is_a?(String)
|
|
964
|
-
process_arg(meth, k, default)
|
|
1002
|
+
process_arg(meth, k, default, max_input_bytesize)
|
|
965
1003
|
end
|
|
966
1004
|
else
|
|
967
1005
|
raise ProgrammerError, "Unsupported argument for typecast_params conversion method: #{key.inspect}"
|
|
968
1006
|
end
|
|
969
1007
|
end
|
|
970
1008
|
|
|
1009
|
+
# Raise an Error if the value is a string with bytesize over max (if max is given)
|
|
1010
|
+
def check_allowed_bytesize(v, max)
|
|
1011
|
+
if max && v.is_a?(String) && v.bytesize > max
|
|
1012
|
+
handle_error(nil, :too_long, "string parameter is too long for type", true)
|
|
1013
|
+
end
|
|
1014
|
+
end
|
|
1015
|
+
|
|
971
1016
|
# Raise an Error if the value is a string containing a null byte.
|
|
972
1017
|
def check_null_byte(v)
|
|
973
1018
|
if v.is_a?(String) && v.index("\0")
|
|
@@ -977,10 +1022,11 @@ class Roda
|
|
|
977
1022
|
|
|
978
1023
|
# Get the value of +key+ for the object, and convert it to the expected type using +meth+.
|
|
979
1024
|
# If the value either before or after conversion is nil, return the +default+ value.
|
|
980
|
-
def process(meth, key, default)
|
|
1025
|
+
def process(meth, key, default, max_input_bytesize=nil)
|
|
981
1026
|
v = param_value(key)
|
|
982
1027
|
|
|
983
1028
|
unless v.nil?
|
|
1029
|
+
check_allowed_bytesize(v, max_input_bytesize)
|
|
984
1030
|
check_null_byte(v)
|
|
985
1031
|
v = send(meth, v)
|
|
986
1032
|
end
|
|
@@ -1049,6 +1095,9 @@ class Roda
|
|
|
1049
1095
|
if opts[:allow_null_bytes]
|
|
1050
1096
|
app::TypecastParams.send(:include, AllowNullByte)
|
|
1051
1097
|
end
|
|
1098
|
+
if opts[:skip_bytesize_checking]
|
|
1099
|
+
app::TypecastParams.send(:include, SkipBytesizeChecking)
|
|
1100
|
+
end
|
|
1052
1101
|
if opts[:date_parse_input_handler]
|
|
1053
1102
|
app::TypecastParams.class_eval do
|
|
1054
1103
|
include DateParseInputHandler
|
|
@@ -126,9 +126,7 @@ class Roda
|
|
|
126
126
|
|
|
127
127
|
private
|
|
128
128
|
|
|
129
|
-
# :nocov:
|
|
130
129
|
if Render::COMPILED_METHOD_SUPPORT
|
|
131
|
-
# :nocov:
|
|
132
130
|
# Return nil if using custom view or layout options.
|
|
133
131
|
# If using a view subdir, prefix the template key with the subdir.
|
|
134
132
|
def _cached_template_method_key(template)
|
data/lib/roda/request.rb
CHANGED
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
# frozen-string-literal: true
|
|
2
2
|
|
|
3
|
-
# :nocov:
|
|
4
3
|
begin
|
|
5
4
|
require "rack/version"
|
|
6
5
|
rescue LoadError
|
|
7
6
|
require "rack"
|
|
8
7
|
else
|
|
9
|
-
if Rack.release >= '
|
|
8
|
+
if Rack.release >= '3'
|
|
10
9
|
require "rack/request"
|
|
11
10
|
else
|
|
12
11
|
require "rack"
|
|
13
12
|
end
|
|
14
13
|
end
|
|
15
|
-
# :nocov:
|
|
16
14
|
|
|
17
15
|
require_relative "cache"
|
|
18
16
|
|
|
@@ -129,8 +127,7 @@ class Roda
|
|
|
129
127
|
"#<#{self.class.inspect} #{@env["REQUEST_METHOD"]} #{path}>"
|
|
130
128
|
end
|
|
131
129
|
|
|
132
|
-
|
|
133
|
-
if Rack.release >= '2.3'
|
|
130
|
+
if Rack.release >= '3'
|
|
134
131
|
def http_version
|
|
135
132
|
# Prefer SERVER_PROTOCOL as it is required in Rack 3.
|
|
136
133
|
# Still fall back to HTTP_VERSION if SERVER_PROTOCOL
|
|
@@ -139,7 +136,6 @@ class Roda
|
|
|
139
136
|
@env['SERVER_PROTOCOL'] || @env['HTTP_VERSION']
|
|
140
137
|
end
|
|
141
138
|
else
|
|
142
|
-
# :nocov:
|
|
143
139
|
# What HTTP version the request was submitted with.
|
|
144
140
|
def http_version
|
|
145
141
|
# Prefer HTTP_VERSION as it is backwards compatible
|
|
@@ -444,10 +440,10 @@ class Roda
|
|
|
444
440
|
hash.all?{|k,v| send("match_#{k}", v)}
|
|
445
441
|
end
|
|
446
442
|
|
|
447
|
-
# Match integer segment, and yield resulting value as an
|
|
443
|
+
# Match integer segment of up to 100 decimal characters, and yield resulting value as an
|
|
448
444
|
# integer.
|
|
449
445
|
def _match_class_Integer
|
|
450
|
-
consume(/\A\/(\d
|
|
446
|
+
consume(/\A\/(\d{1,100})(?=\/|\z)/){|i| [i.to_i]}
|
|
451
447
|
end
|
|
452
448
|
|
|
453
449
|
# Match only if all of the arguments in the given array match.
|
data/lib/roda/response.rb
CHANGED
|
@@ -42,7 +42,6 @@ class Roda
|
|
|
42
42
|
# code for non-empty responses and a 404 code for empty responses.
|
|
43
43
|
attr_accessor :status
|
|
44
44
|
|
|
45
|
-
# :nocov:
|
|
46
45
|
if defined?(Rack::Headers) && Rack::Headers.is_a?(Class)
|
|
47
46
|
# Set the default headers when creating a response.
|
|
48
47
|
def initialize
|
|
@@ -51,7 +50,6 @@ class Roda
|
|
|
51
50
|
@length = 0
|
|
52
51
|
end
|
|
53
52
|
else
|
|
54
|
-
# :nocov:
|
|
55
53
|
# Set the default headers when creating a response.
|
|
56
54
|
def initialize
|
|
57
55
|
@headers = {}
|
|
@@ -173,7 +171,6 @@ class Roda
|
|
|
173
171
|
|
|
174
172
|
private
|
|
175
173
|
|
|
176
|
-
# :nocov:
|
|
177
174
|
if Rack.release < '2.0.2'
|
|
178
175
|
# Don't use a content length for empty 205 responses on
|
|
179
176
|
# rack 1, as it violates Rack::Lint in that version.
|
|
@@ -181,7 +178,6 @@ class Roda
|
|
|
181
178
|
headers.delete("Content-Type")
|
|
182
179
|
headers.delete("Content-Length")
|
|
183
180
|
end
|
|
184
|
-
# :nocov:
|
|
185
181
|
else
|
|
186
182
|
# Set the content length for empty 205 responses to 0
|
|
187
183
|
def empty_205_headers(headers)
|
data/lib/roda/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: roda
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.61.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jeremy Evans
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2022-
|
|
11
|
+
date: 2022-10-12 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rack
|
|
@@ -164,7 +164,7 @@ dependencies:
|
|
|
164
164
|
- - ">="
|
|
165
165
|
- !ruby/object:Gem::Version
|
|
166
166
|
version: '0'
|
|
167
|
-
description:
|
|
167
|
+
description:
|
|
168
168
|
email:
|
|
169
169
|
- code@jeremyevans.net
|
|
170
170
|
executables: []
|
|
@@ -232,6 +232,8 @@ extra_rdoc_files:
|
|
|
232
232
|
- doc/release_notes/3.58.0.txt
|
|
233
233
|
- doc/release_notes/3.59.0.txt
|
|
234
234
|
- doc/release_notes/3.6.0.txt
|
|
235
|
+
- doc/release_notes/3.60.0.txt
|
|
236
|
+
- doc/release_notes/3.61.0.txt
|
|
235
237
|
- doc/release_notes/3.7.0.txt
|
|
236
238
|
- doc/release_notes/3.8.0.txt
|
|
237
239
|
- doc/release_notes/3.9.0.txt
|
|
@@ -298,6 +300,8 @@ files:
|
|
|
298
300
|
- doc/release_notes/3.58.0.txt
|
|
299
301
|
- doc/release_notes/3.59.0.txt
|
|
300
302
|
- doc/release_notes/3.6.0.txt
|
|
303
|
+
- doc/release_notes/3.60.0.txt
|
|
304
|
+
- doc/release_notes/3.61.0.txt
|
|
301
305
|
- doc/release_notes/3.7.0.txt
|
|
302
306
|
- doc/release_notes/3.8.0.txt
|
|
303
307
|
- doc/release_notes/3.9.0.txt
|
|
@@ -359,6 +363,7 @@ files:
|
|
|
359
363
|
- lib/roda/plugins/inject_erb.rb
|
|
360
364
|
- lib/roda/plugins/json.rb
|
|
361
365
|
- lib/roda/plugins/json_parser.rb
|
|
366
|
+
- lib/roda/plugins/link_to.rb
|
|
362
367
|
- lib/roda/plugins/mail_processor.rb
|
|
363
368
|
- lib/roda/plugins/mailer.rb
|
|
364
369
|
- lib/roda/plugins/match_affix.rb
|
|
@@ -433,7 +438,7 @@ metadata:
|
|
|
433
438
|
documentation_uri: https://roda.jeremyevans.net/documentation.html
|
|
434
439
|
mailing_list_uri: https://github.com/jeremyevans/roda/discussions
|
|
435
440
|
source_code_uri: https://github.com/jeremyevans/roda
|
|
436
|
-
post_install_message:
|
|
441
|
+
post_install_message:
|
|
437
442
|
rdoc_options: []
|
|
438
443
|
require_paths:
|
|
439
444
|
- lib
|
|
@@ -449,7 +454,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
449
454
|
version: '0'
|
|
450
455
|
requirements: []
|
|
451
456
|
rubygems_version: 3.3.7
|
|
452
|
-
signing_key:
|
|
457
|
+
signing_key:
|
|
453
458
|
specification_version: 4
|
|
454
459
|
summary: Routing tree web toolkit
|
|
455
460
|
test_files: []
|