actionpack 7.0.8.7 → 7.2.2.1
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.md +90 -537
- data/MIT-LICENSE +1 -1
- data/README.rdoc +2 -2
- data/lib/abstract_controller/asset_paths.rb +2 -0
- data/lib/abstract_controller/base.rb +119 -106
- data/lib/abstract_controller/caching/fragments.rb +51 -52
- data/lib/abstract_controller/caching.rb +2 -0
- data/lib/abstract_controller/callbacks.rb +94 -67
- data/lib/abstract_controller/collector.rb +6 -6
- data/lib/abstract_controller/deprecator.rb +9 -0
- data/lib/abstract_controller/error.rb +2 -0
- data/lib/abstract_controller/helpers.rb +121 -91
- data/lib/abstract_controller/logger.rb +2 -0
- data/lib/abstract_controller/railties/routes_helpers.rb +3 -16
- data/lib/abstract_controller/rendering.rb +14 -13
- data/lib/abstract_controller/translation.rb +12 -30
- data/lib/abstract_controller/url_for.rb +9 -5
- data/lib/abstract_controller.rb +8 -0
- data/lib/action_controller/api/api_rendering.rb +2 -0
- data/lib/action_controller/api.rb +78 -73
- data/lib/action_controller/base.rb +199 -141
- data/lib/action_controller/caching.rb +16 -11
- data/lib/action_controller/deprecator.rb +9 -0
- data/lib/action_controller/form_builder.rb +21 -16
- data/lib/action_controller/log_subscriber.rb +19 -5
- data/lib/action_controller/metal/allow_browser.rb +123 -0
- data/lib/action_controller/metal/basic_implicit_render.rb +2 -0
- data/lib/action_controller/metal/conditional_get.rb +187 -174
- data/lib/action_controller/metal/content_security_policy.rb +26 -25
- data/lib/action_controller/metal/cookies.rb +4 -2
- data/lib/action_controller/metal/data_streaming.rb +65 -54
- data/lib/action_controller/metal/default_headers.rb +6 -2
- data/lib/action_controller/metal/etag_with_flash.rb +4 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +18 -14
- data/lib/action_controller/metal/exceptions.rb +19 -9
- data/lib/action_controller/metal/flash.rb +12 -10
- data/lib/action_controller/metal/head.rb +20 -16
- data/lib/action_controller/metal/helpers.rb +64 -67
- data/lib/action_controller/metal/http_authentication.rb +212 -199
- data/lib/action_controller/metal/implicit_render.rb +21 -17
- data/lib/action_controller/metal/instrumentation.rb +22 -12
- data/lib/action_controller/metal/live.rb +125 -92
- data/lib/action_controller/metal/logging.rb +6 -4
- data/lib/action_controller/metal/mime_responds.rb +151 -142
- data/lib/action_controller/metal/parameter_encoding.rb +34 -32
- data/lib/action_controller/metal/params_wrapper.rb +58 -58
- data/lib/action_controller/metal/permissions_policy.rb +14 -13
- data/lib/action_controller/metal/rate_limiting.rb +62 -0
- data/lib/action_controller/metal/redirecting.rb +110 -84
- data/lib/action_controller/metal/renderers.rb +50 -49
- data/lib/action_controller/metal/rendering.rb +103 -82
- data/lib/action_controller/metal/request_forgery_protection.rb +279 -161
- data/lib/action_controller/metal/rescue.rb +12 -8
- data/lib/action_controller/metal/streaming.rb +174 -132
- data/lib/action_controller/metal/strong_parameters.rb +598 -473
- data/lib/action_controller/metal/testing.rb +2 -0
- data/lib/action_controller/metal/url_for.rb +23 -14
- data/lib/action_controller/metal.rb +145 -61
- data/lib/action_controller/railtie.rb +25 -9
- data/lib/action_controller/railties/helpers.rb +2 -0
- data/lib/action_controller/renderer.rb +105 -66
- data/lib/action_controller/template_assertions.rb +4 -2
- data/lib/action_controller/test_case.rb +157 -128
- data/lib/action_controller.rb +17 -3
- data/lib/action_dispatch/constants.rb +34 -0
- data/lib/action_dispatch/deprecator.rb +9 -0
- data/lib/action_dispatch/http/cache.rb +28 -29
- data/lib/action_dispatch/http/content_disposition.rb +2 -0
- data/lib/action_dispatch/http/content_security_policy.rb +48 -45
- data/lib/action_dispatch/http/filter_parameters.rb +18 -8
- data/lib/action_dispatch/http/filter_redirect.rb +22 -1
- data/lib/action_dispatch/http/headers.rb +23 -21
- data/lib/action_dispatch/http/mime_negotiation.rb +37 -48
- data/lib/action_dispatch/http/mime_type.rb +60 -30
- data/lib/action_dispatch/http/mime_types.rb +5 -1
- data/lib/action_dispatch/http/parameters.rb +12 -10
- data/lib/action_dispatch/http/permissions_policy.rb +32 -27
- data/lib/action_dispatch/http/rack_cache.rb +4 -0
- data/lib/action_dispatch/http/request.rb +132 -79
- data/lib/action_dispatch/http/response.rb +136 -103
- data/lib/action_dispatch/http/upload.rb +19 -15
- data/lib/action_dispatch/http/url.rb +75 -73
- data/lib/action_dispatch/journey/formatter.rb +19 -6
- data/lib/action_dispatch/journey/gtg/builder.rb +4 -3
- data/lib/action_dispatch/journey/gtg/simulator.rb +2 -0
- data/lib/action_dispatch/journey/gtg/transition_table.rb +10 -8
- data/lib/action_dispatch/journey/nfa/dot.rb +2 -0
- data/lib/action_dispatch/journey/nodes/node.rb +6 -5
- data/lib/action_dispatch/journey/parser.rb +4 -3
- data/lib/action_dispatch/journey/parser_extras.rb +2 -0
- data/lib/action_dispatch/journey/path/pattern.rb +18 -15
- data/lib/action_dispatch/journey/route.rb +12 -9
- data/lib/action_dispatch/journey/router/utils.rb +16 -15
- data/lib/action_dispatch/journey/router.rb +13 -10
- data/lib/action_dispatch/journey/routes.rb +6 -4
- data/lib/action_dispatch/journey/scanner.rb +4 -2
- data/lib/action_dispatch/journey/visitors.rb +2 -0
- data/lib/action_dispatch/journey.rb +2 -0
- data/lib/action_dispatch/log_subscriber.rb +25 -0
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +7 -6
- data/lib/action_dispatch/middleware/assume_ssl.rb +27 -0
- data/lib/action_dispatch/middleware/callbacks.rb +4 -0
- data/lib/action_dispatch/middleware/cookies.rb +192 -194
- data/lib/action_dispatch/middleware/debug_exceptions.rb +36 -27
- data/lib/action_dispatch/middleware/debug_locks.rb +18 -13
- data/lib/action_dispatch/middleware/debug_view.rb +9 -2
- data/lib/action_dispatch/middleware/exception_wrapper.rb +181 -27
- data/lib/action_dispatch/middleware/executor.rb +9 -1
- data/lib/action_dispatch/middleware/flash.rb +65 -46
- data/lib/action_dispatch/middleware/host_authorization.rb +22 -17
- data/lib/action_dispatch/middleware/public_exceptions.rb +12 -8
- data/lib/action_dispatch/middleware/reloader.rb +9 -5
- data/lib/action_dispatch/middleware/remote_ip.rb +88 -83
- data/lib/action_dispatch/middleware/request_id.rb +15 -8
- data/lib/action_dispatch/middleware/server_timing.rb +8 -6
- data/lib/action_dispatch/middleware/session/abstract_store.rb +7 -0
- data/lib/action_dispatch/middleware/session/cache_store.rb +14 -7
- data/lib/action_dispatch/middleware/session/cookie_store.rb +32 -25
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +9 -3
- data/lib/action_dispatch/middleware/show_exceptions.rb +42 -28
- data/lib/action_dispatch/middleware/ssl.rb +60 -45
- data/lib/action_dispatch/middleware/stack.rb +15 -9
- data/lib/action_dispatch/middleware/static.rb +40 -34
- data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +8 -1
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +7 -7
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +17 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +16 -12
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +3 -0
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +47 -38
- data/lib/action_dispatch/railtie.rb +12 -4
- data/lib/action_dispatch/request/session.rb +39 -27
- data/lib/action_dispatch/request/utils.rb +10 -3
- data/lib/action_dispatch/routing/endpoint.rb +2 -0
- data/lib/action_dispatch/routing/inspector.rb +59 -9
- data/lib/action_dispatch/routing/mapper.rb +686 -639
- data/lib/action_dispatch/routing/polymorphic_routes.rb +70 -61
- data/lib/action_dispatch/routing/redirection.rb +52 -38
- data/lib/action_dispatch/routing/route_set.rb +106 -62
- data/lib/action_dispatch/routing/routes_proxy.rb +16 -19
- data/lib/action_dispatch/routing/url_for.rb +131 -122
- data/lib/action_dispatch/routing.rb +152 -150
- data/lib/action_dispatch/system_test_case.rb +91 -81
- data/lib/action_dispatch/system_testing/browser.rb +27 -19
- data/lib/action_dispatch/system_testing/driver.rb +16 -22
- data/lib/action_dispatch/system_testing/server.rb +2 -0
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +53 -31
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +2 -0
- data/lib/action_dispatch/testing/assertion_response.rb +9 -7
- data/lib/action_dispatch/testing/assertions/response.rb +36 -26
- data/lib/action_dispatch/testing/assertions/routing.rb +203 -95
- data/lib/action_dispatch/testing/assertions.rb +5 -1
- data/lib/action_dispatch/testing/integration.rb +240 -229
- data/lib/action_dispatch/testing/request_encoder.rb +6 -1
- data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
- data/lib/action_dispatch/testing/test_process.rb +14 -9
- data/lib/action_dispatch/testing/test_request.rb +4 -2
- data/lib/action_dispatch/testing/test_response.rb +34 -19
- data/lib/action_dispatch.rb +52 -21
- data/lib/action_pack/gem_version.rb +6 -4
- data/lib/action_pack/version.rb +3 -1
- data/lib/action_pack.rb +18 -17
- metadata +86 -27
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
require "rack/session/abstract/id"
|
4
6
|
|
5
7
|
module ActionDispatch
|
@@ -78,6 +80,8 @@ module ActionDispatch
|
|
78
80
|
@loaded = false
|
79
81
|
@exists = nil # We haven't checked yet.
|
80
82
|
@enabled = enabled
|
83
|
+
@id_was = nil
|
84
|
+
@id_was_initialized = false
|
81
85
|
end
|
82
86
|
|
83
87
|
def id
|
@@ -105,8 +109,8 @@ module ActionDispatch
|
|
105
109
|
end
|
106
110
|
end
|
107
111
|
|
108
|
-
# Returns value of the key stored in the session or
|
109
|
-
#
|
112
|
+
# Returns value of the key stored in the session or `nil` if the given key is
|
113
|
+
# not found in the session.
|
110
114
|
def [](key)
|
111
115
|
load_for_read!
|
112
116
|
key = key.to_s
|
@@ -118,8 +122,8 @@ module ActionDispatch
|
|
118
122
|
end
|
119
123
|
end
|
120
124
|
|
121
|
-
# Returns the nested value specified by the sequence of keys, returning
|
122
|
-
#
|
125
|
+
# Returns the nested value specified by the sequence of keys, returning `nil` if
|
126
|
+
# any intermediate step is `nil`.
|
123
127
|
def dig(*keys)
|
124
128
|
load_for_read!
|
125
129
|
keys = keys.map.with_index { |key, i| i.zero? ? key.to_s : key }
|
@@ -167,18 +171,23 @@ module ActionDispatch
|
|
167
171
|
|
168
172
|
# Updates the session with given Hash.
|
169
173
|
#
|
170
|
-
#
|
171
|
-
#
|
174
|
+
# session.to_hash
|
175
|
+
# # => {"session_id"=>"e29b9ea315edf98aad94cc78c34cc9b2"}
|
172
176
|
#
|
173
|
-
#
|
174
|
-
#
|
177
|
+
# session.update({ "foo" => "bar" })
|
178
|
+
# # => {"session_id"=>"e29b9ea315edf98aad94cc78c34cc9b2", "foo" => "bar"}
|
175
179
|
#
|
176
|
-
#
|
177
|
-
#
|
180
|
+
# session.to_hash
|
181
|
+
# # => {"session_id"=>"e29b9ea315edf98aad94cc78c34cc9b2", "foo" => "bar"}
|
178
182
|
def update(hash)
|
183
|
+
unless hash.respond_to?(:to_hash)
|
184
|
+
raise TypeError, "no implicit conversion of #{hash.class.name} into Hash"
|
185
|
+
end
|
186
|
+
|
179
187
|
load_for_write!
|
180
|
-
@delegate.update hash.stringify_keys
|
188
|
+
@delegate.update hash.to_hash.stringify_keys
|
181
189
|
end
|
190
|
+
alias :merge! :update
|
182
191
|
|
183
192
|
# Deletes given key from the session.
|
184
193
|
def delete(key)
|
@@ -186,20 +195,20 @@ module ActionDispatch
|
|
186
195
|
@delegate.delete key.to_s
|
187
196
|
end
|
188
197
|
|
189
|
-
# Returns value of the given key from the session, or raises
|
190
|
-
#
|
191
|
-
#
|
198
|
+
# Returns value of the given key from the session, or raises `KeyError` if can't
|
199
|
+
# find the given key and no default value is set. Returns default value if
|
200
|
+
# specified.
|
192
201
|
#
|
193
|
-
#
|
194
|
-
#
|
202
|
+
# session.fetch(:foo)
|
203
|
+
# # => KeyError: key not found: "foo"
|
195
204
|
#
|
196
|
-
#
|
197
|
-
#
|
205
|
+
# session.fetch(:foo, :bar)
|
206
|
+
# # => :bar
|
198
207
|
#
|
199
|
-
#
|
200
|
-
#
|
201
|
-
#
|
202
|
-
#
|
208
|
+
# session.fetch(:foo) do
|
209
|
+
# :bar
|
210
|
+
# end
|
211
|
+
# # => :bar
|
203
212
|
def fetch(key, default = Unspecified, &block)
|
204
213
|
load_for_read!
|
205
214
|
if default == Unspecified
|
@@ -232,15 +241,15 @@ module ActionDispatch
|
|
232
241
|
@delegate.empty?
|
233
242
|
end
|
234
243
|
|
235
|
-
def merge!(other)
|
236
|
-
load_for_write!
|
237
|
-
@delegate.merge!(other)
|
238
|
-
end
|
239
|
-
|
240
244
|
def each(&block)
|
241
245
|
to_hash.each(&block)
|
242
246
|
end
|
243
247
|
|
248
|
+
def id_was
|
249
|
+
load_for_read!
|
250
|
+
@id_was
|
251
|
+
end
|
252
|
+
|
244
253
|
private
|
245
254
|
def load_for_read!
|
246
255
|
load! if !loaded? && exists?
|
@@ -260,10 +269,13 @@ module ActionDispatch
|
|
260
269
|
|
261
270
|
def load!
|
262
271
|
if enabled?
|
272
|
+
@id_was_initialized = true unless exists?
|
263
273
|
id, session = @by.load_session @req
|
264
274
|
options[:id] = id
|
265
275
|
@delegate.replace(session.stringify_keys)
|
276
|
+
@id_was = id unless @id_was_initialized
|
266
277
|
end
|
278
|
+
@id_was_initialized = true
|
267
279
|
@loaded = true
|
268
280
|
end
|
269
281
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
require "active_support/core_ext/hash/indifferent_access"
|
4
6
|
|
5
7
|
module ActionDispatch
|
@@ -55,9 +57,11 @@ module ActionDispatch
|
|
55
57
|
if params.has_key?(:tempfile)
|
56
58
|
ActionDispatch::Http::UploadedFile.new(params)
|
57
59
|
else
|
58
|
-
|
59
|
-
|
60
|
-
|
60
|
+
hwia = ActiveSupport::HashWithIndifferentAccess.new
|
61
|
+
params.each_pair do |key, val|
|
62
|
+
hwia[key] = normalize_encode_params(val)
|
63
|
+
end
|
64
|
+
hwia
|
61
65
|
end
|
62
66
|
else
|
63
67
|
params
|
@@ -83,6 +87,9 @@ module ActionDispatch
|
|
83
87
|
return params unless controller && controller.valid_encoding? && encoding_template = action_encoding_template(request, controller, action)
|
84
88
|
params.except(:controller, :action).each do |key, value|
|
85
89
|
ActionDispatch::Request::Utils.each_param_value(value) do |param|
|
90
|
+
# If `param` is frozen, it comes from the router defaults
|
91
|
+
next if param.frozen?
|
92
|
+
|
86
93
|
if encoding_template[key.to_s]
|
87
94
|
param.force_encoding(encoding_template[key.to_s])
|
88
95
|
end
|
@@ -1,13 +1,28 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
require "delegate"
|
4
6
|
require "io/console/size"
|
5
7
|
|
6
8
|
module ActionDispatch
|
7
9
|
module Routing
|
8
10
|
class RouteWrapper < SimpleDelegator # :nodoc:
|
11
|
+
def matches_filter?(filter, value)
|
12
|
+
return __getobj__.path.match(value) if filter == :exact_path_match
|
13
|
+
|
14
|
+
value.match?(public_send(filter))
|
15
|
+
end
|
16
|
+
|
9
17
|
def endpoint
|
10
|
-
|
18
|
+
case
|
19
|
+
when app.dispatcher?
|
20
|
+
"#{controller}##{action}"
|
21
|
+
when rack_app.is_a?(Proc)
|
22
|
+
"Inline handler (Proc/Lambda)"
|
23
|
+
else
|
24
|
+
rack_app.inspect
|
25
|
+
end
|
11
26
|
end
|
12
27
|
|
13
28
|
def constraints
|
@@ -53,8 +68,8 @@ module ActionDispatch
|
|
53
68
|
|
54
69
|
##
|
55
70
|
# This class is just used for displaying route information when someone
|
56
|
-
# executes `bin/rails routes` or looks at the RoutingError page.
|
57
|
-
#
|
71
|
+
# executes `bin/rails routes` or looks at the RoutingError page. People should
|
72
|
+
# not use this class.
|
58
73
|
class RoutesInspector # :nodoc:
|
59
74
|
def initialize(routes)
|
60
75
|
@engines = {}
|
@@ -85,8 +100,18 @@ module ActionDispatch
|
|
85
100
|
if filter[:controller]
|
86
101
|
{ controller: /#{filter[:controller].underscore.sub(/_?controller\z/, "")}/ }
|
87
102
|
elsif filter[:grep]
|
88
|
-
|
89
|
-
|
103
|
+
grep_pattern = Regexp.new(filter[:grep])
|
104
|
+
path = RFC2396_PARSER.escape(filter[:grep])
|
105
|
+
normalized_path = ("/" + path).squeeze("/")
|
106
|
+
|
107
|
+
{
|
108
|
+
controller: grep_pattern,
|
109
|
+
action: grep_pattern,
|
110
|
+
verb: grep_pattern,
|
111
|
+
name: grep_pattern,
|
112
|
+
path: grep_pattern,
|
113
|
+
exact_path_match: normalized_path,
|
114
|
+
}
|
90
115
|
end
|
91
116
|
end
|
92
117
|
|
@@ -94,7 +119,7 @@ module ActionDispatch
|
|
94
119
|
if filter
|
95
120
|
@routes.select do |route|
|
96
121
|
route_wrapper = RouteWrapper.new(route)
|
97
|
-
filter.any? { |
|
122
|
+
filter.any? { |filter_type, value| route_wrapper.matches_filter?(filter_type, value) }
|
98
123
|
end
|
99
124
|
else
|
100
125
|
@routes
|
@@ -110,7 +135,8 @@ module ActionDispatch
|
|
110
135
|
{ name: route.name,
|
111
136
|
verb: route.verb,
|
112
137
|
path: route.path,
|
113
|
-
reqs: route.reqs
|
138
|
+
reqs: route.reqs,
|
139
|
+
source_location: route.source_location }
|
114
140
|
end
|
115
141
|
end
|
116
142
|
|
@@ -216,13 +242,16 @@ module ActionDispatch
|
|
216
242
|
private
|
217
243
|
def draw_expanded_section(routes)
|
218
244
|
routes.map.each_with_index do |r, i|
|
219
|
-
<<~MESSAGE.chomp
|
245
|
+
route_rows = <<~MESSAGE.chomp
|
220
246
|
#{route_header(index: i + 1)}
|
221
247
|
Prefix | #{r[:name]}
|
222
248
|
Verb | #{r[:verb]}
|
223
249
|
URI | #{r[:path]}
|
224
250
|
Controller#Action | #{r[:reqs]}
|
225
251
|
MESSAGE
|
252
|
+
source_location = "\nSource Location | #{r[:source_location]}"
|
253
|
+
route_rows += source_location if r[:source_location].present?
|
254
|
+
route_rows
|
226
255
|
end
|
227
256
|
end
|
228
257
|
|
@@ -230,6 +259,27 @@ module ActionDispatch
|
|
230
259
|
"--[ Route #{index} ]".ljust(@width, "-")
|
231
260
|
end
|
232
261
|
end
|
262
|
+
|
263
|
+
class Unused < Sheet
|
264
|
+
def header(routes)
|
265
|
+
@buffer << <<~MSG
|
266
|
+
Found #{routes.count} unused #{"route".pluralize(routes.count)}:
|
267
|
+
MSG
|
268
|
+
|
269
|
+
super
|
270
|
+
end
|
271
|
+
|
272
|
+
def no_routes(routes, filter)
|
273
|
+
@buffer <<
|
274
|
+
if filter.none?
|
275
|
+
"No unused routes found."
|
276
|
+
elsif filter.key?(:controller)
|
277
|
+
"No unused routes found for this controller."
|
278
|
+
elsif filter.key?(:grep)
|
279
|
+
"No unused routes found for this grep pattern."
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
233
283
|
end
|
234
284
|
|
235
285
|
class HtmlTableFormatter
|
@@ -239,7 +289,7 @@ module ActionDispatch
|
|
239
289
|
end
|
240
290
|
|
241
291
|
def section_title(title)
|
242
|
-
@buffer << %(<tr><th colspan="
|
292
|
+
@buffer << %(<tr><th colspan="5">#{title}</th></tr>)
|
243
293
|
end
|
244
294
|
|
245
295
|
def section(routes)
|