roda 3.56.0 → 3.59.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 +24 -0
- data/doc/conventions.rdoc +14 -11
- data/doc/release_notes/3.57.0.txt +34 -0
- data/doc/release_notes/3.58.0.txt +16 -0
- data/doc/release_notes/3.59.0.txt +17 -0
- data/lib/roda/plugins/additional_render_engines.rb +61 -0
- data/lib/roda/plugins/assets.rb +1 -1
- data/lib/roda/plugins/common_logger.rb +12 -1
- data/lib/roda/plugins/delete_empty_headers.rb +3 -3
- data/lib/roda/plugins/exception_page.rb +20 -4
- data/lib/roda/plugins/filter_common_logger.rb +46 -0
- data/lib/roda/plugins/hash_branch_view_subdir.rb +76 -0
- data/lib/roda/plugins/hash_branches.rb +145 -0
- data/lib/roda/plugins/hash_paths.rb +128 -0
- data/lib/roda/plugins/hash_routes.rb +13 -176
- data/lib/roda/plugins/heartbeat.rb +5 -10
- data/lib/roda/plugins/mail_processor.rb +4 -5
- data/lib/roda/plugins/multi_route.rb +1 -1
- data/lib/roda/plugins/multi_view.rb +0 -4
- data/lib/roda/plugins/named_routes.rb +1 -2
- data/lib/roda/plugins/static_routing.rb +1 -1
- data/lib/roda/version.rb +1 -1
- metadata +13 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 963e2d9ac538dbe97835ef65e2e2ba4184838664696bb084ce423a85ef23c205
|
|
4
|
+
data.tar.gz: d47a7f9c86357e99b0c9f470b6c33a32962df55189303020982af55d70907c7b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c84cb0ae8c66b537c0a7d32d83d9c5a639439b15fbc650d3569efab5e134e0883ba06183c7f17bfb379f4018060c7d276a5016431ae3e21d05c2db3801a85762
|
|
7
|
+
data.tar.gz: 0c9495ec5fe24b774512f6495f97f5bb4cf8189b964f21a04c5e880d4a536ade2e045c7131f0a54a7c1cd203fe142d53c6d7cde4cf223a8e8be4cb5c5475fede
|
data/CHANGELOG
CHANGED
|
@@ -1,3 +1,27 @@
|
|
|
1
|
+
= 3.59.0 (2022-08-12)
|
|
2
|
+
|
|
3
|
+
* Add additional_render_engines plugin, for considering multiple render engines for templates (jeremyevans)
|
|
4
|
+
|
|
5
|
+
* Fix typo in private method name in delete_empty_headers plugin (mculpt) (#279)
|
|
6
|
+
|
|
7
|
+
= 3.58.0 (2022-07-13)
|
|
8
|
+
|
|
9
|
+
* Add filter_common_logger plugin for skipping the logging of certain requests when using the common_logger plugin (jeremyevans)
|
|
10
|
+
|
|
11
|
+
* Make exception_page plugin use Exception#detailed_message on Ruby 3.2+ (jeremyevans)
|
|
12
|
+
|
|
13
|
+
* Make heartbeat plugin compatible with recent changes in the rack master branch (jeremyevans)
|
|
14
|
+
|
|
15
|
+
= 3.57.0 (2022-06-14)
|
|
16
|
+
|
|
17
|
+
* Make static_routing plugin depend on the hash_paths instead of the hash_routes plugin (jeremyevans)
|
|
18
|
+
|
|
19
|
+
* Split hash_branches and hash_paths plugins from hash_routes plugin (jeremyevans)
|
|
20
|
+
|
|
21
|
+
* Hex escape unprintable characters in common_logger plugin output (jeremyevans)
|
|
22
|
+
|
|
23
|
+
* Add hash_branch_view_subdir plugin for automatically appending a view subdirectory on a successful hash branch (jeremyevans)
|
|
24
|
+
|
|
1
25
|
= 3.56.0 (2022-05-13)
|
|
2
26
|
|
|
3
27
|
* Make status_303 plugin use 303 responses for HTTP/2 and higher versions (jeremyevans)
|
data/doc/conventions.rdoc
CHANGED
|
@@ -86,17 +86,17 @@ Large applications generally need more structure:
|
|
|
86
86
|
|
|
87
87
|
For larger apps, the +Rakefile+, +assets/+, +migrate+, +models.rb+, +models/+, +public/+, remain the same.
|
|
88
88
|
|
|
89
|
-
+app_name.rb+ should use the +
|
|
90
|
-
|
|
89
|
+
+app_name.rb+ should use the +hash_branch_view_subdir+ plugin (which builds on the +hash_branches+ and
|
|
90
|
+
+view_options+ plugin), or the +multi_run+ plugin.
|
|
91
|
+
The routes used by the +hash_branches+ or +multi_run+ should be stored in routing files in the +routes/+
|
|
91
92
|
directory, with one file per prefix.
|
|
92
93
|
|
|
93
94
|
For specs/tests, you should have +spec/models/+ and +spec/web/+, with one file per model in +spec/models/+
|
|
94
95
|
and one file per prefix in +spec/web/+. Substitute +spec+ with +test+ if that is what you are using as the
|
|
95
96
|
name of the directory.
|
|
96
97
|
|
|
97
|
-
You should have a separate view subdirectory per prefix.
|
|
98
|
-
|
|
99
|
-
specified on every call to view.
|
|
98
|
+
You should have a separate view subdirectory per prefix. With the +hash_branch_view_subdir+, the application
|
|
99
|
+
will automatically set a separate view subdirectory per routing tree branch.
|
|
100
100
|
|
|
101
101
|
+helpers/+ should be used to store helper methods for your application, that you call in your routing files
|
|
102
102
|
and views. In a small application, these methods should just be specified in +app_name.rb+
|
|
@@ -104,7 +104,7 @@ and views. In a small application, these methods should just be specified in +a
|
|
|
104
104
|
=== Really Large Applications
|
|
105
105
|
|
|
106
106
|
For very large applications, it's expected that there will be deviations from these conventions. However,
|
|
107
|
-
it is recommended to use the +
|
|
107
|
+
it is recommended to use the +hash_branch_view_subdir+ or +multi_run+ plugins to organize your application, and have
|
|
108
108
|
subdirectories in the +routes/+ directory, and nested subdirectories in the +views/+ directory.
|
|
109
109
|
|
|
110
110
|
== Roda Application File Layout
|
|
@@ -156,19 +156,22 @@ For larger applications, there are some slight changes to the Roda application f
|
|
|
156
156
|
|
|
157
157
|
plugin :render, escape: true, layout: './layout'
|
|
158
158
|
plugin :assets
|
|
159
|
-
plugin :
|
|
160
|
-
plugin :hash_routes
|
|
159
|
+
plugin :hash_branch_view_subdir
|
|
161
160
|
Dir['routes/*.rb'].each{|f| require_relative f}
|
|
162
161
|
|
|
163
162
|
route do |r|
|
|
164
|
-
r.
|
|
163
|
+
r.hash_branches('')
|
|
164
|
+
|
|
165
|
+
r.root do
|
|
166
|
+
# ...
|
|
167
|
+
end
|
|
165
168
|
end
|
|
166
169
|
|
|
167
170
|
Dir['helpers/*.rb'].each{|f| require_relative f}
|
|
168
171
|
end
|
|
169
172
|
|
|
170
|
-
After loading the +
|
|
173
|
+
After loading the +hash_branch_view_subdir+ plugin, you require all of your
|
|
171
174
|
routing files. Inside your route block, instead of defining your routes, you just call
|
|
172
|
-
+r.
|
|
175
|
+
+r.hash_branches+, which will dispatch to all of your routing files. After your route
|
|
173
176
|
block, you require all of your helper files containing the instance methods for your
|
|
174
177
|
route block or views, instead of defining the methods directly.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
= New Features
|
|
2
|
+
|
|
3
|
+
* hash_branches and hash_paths plugins have been split off from the
|
|
4
|
+
hash_routes plugin, allowing you to use only those parts instead
|
|
5
|
+
of all of hash_routes.
|
|
6
|
+
|
|
7
|
+
The hash_branches plugin supports the hash_branch class method
|
|
8
|
+
and r.hash_branches routing method.
|
|
9
|
+
|
|
10
|
+
The hash_paths plugin supports the hash_path class method and
|
|
11
|
+
r.hash_paths routing method.
|
|
12
|
+
|
|
13
|
+
The hash_routes plugin functions as it did previously by
|
|
14
|
+
requiring the hash_branches and hash_paths plugins. It adds
|
|
15
|
+
the hash_routes DSL and r.hash_routes routing method.
|
|
16
|
+
|
|
17
|
+
* A hash_branch_view_subdir has been added. It builds on the
|
|
18
|
+
view_options plugin and new hash_branches plugin, automatically
|
|
19
|
+
appending a view subdirectory for each successful hash branch.
|
|
20
|
+
This can DRY up code that uses a separate view subdirectory for
|
|
21
|
+
each branch.
|
|
22
|
+
|
|
23
|
+
= Other Improvements
|
|
24
|
+
|
|
25
|
+
* Unprintable characters are now hex escaped in the output of the
|
|
26
|
+
common_logger plugin. This can protect users who use software
|
|
27
|
+
that respects shell escape sequences to view the logs.
|
|
28
|
+
|
|
29
|
+
= Backwards Compatibility
|
|
30
|
+
|
|
31
|
+
* The static_routing plugin now depends on the hash_paths plugin
|
|
32
|
+
instead of the hash_routes plugin, so you will need to update
|
|
33
|
+
your application to explicitly load the hash_routes plugin if
|
|
34
|
+
you were relying on static_routing to implicitly load it.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
= New Features
|
|
2
|
+
|
|
3
|
+
* A filter_common_logger plugin has been added, allowing you to skip
|
|
4
|
+
logging of certain requests in the common_logger plugin. This
|
|
5
|
+
allows you to only log requests for certain paths, or only log
|
|
6
|
+
requests for certain types of responses.
|
|
7
|
+
|
|
8
|
+
= Other Improvements
|
|
9
|
+
|
|
10
|
+
* The heartbeat plugin is now compatible with recent changes in the
|
|
11
|
+
rack master branch (what will be rack 3).
|
|
12
|
+
|
|
13
|
+
* The exception_page plugin will now use Exception#detailed_message
|
|
14
|
+
on Ruby 3.2+, preserving the did_you_mean and error_highlight
|
|
15
|
+
information. Additionally, the display of exception messages
|
|
16
|
+
has been improved.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
= New Features
|
|
2
|
+
|
|
3
|
+
* An additional_render_engines plugin has been added, for considering
|
|
4
|
+
multiple render engines for templates. If the template path does not
|
|
5
|
+
exist for the default render engine, then each additional render
|
|
6
|
+
engine will be checked, returning the first path that exists:
|
|
7
|
+
|
|
8
|
+
plugin :additional_render_engines, ['haml', 'str']
|
|
9
|
+
|
|
10
|
+
This is similar to the additional_view_directories plugin added in
|
|
11
|
+
3.53.0. Both plugins can be used if you want to consider multiple
|
|
12
|
+
view directories and multiple render engines.
|
|
13
|
+
|
|
14
|
+
= Other Improvements
|
|
15
|
+
|
|
16
|
+
* A typo in a private method name in the delete_empty_headers plugin
|
|
17
|
+
has been fixed.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
class Roda
|
|
5
|
+
module RodaPlugins
|
|
6
|
+
# The additional_render_engines plugin allows for specifying additional render
|
|
7
|
+
# engines to consider for templates. When rendering a template, it will
|
|
8
|
+
# first try the default template engine specified in the render plugin. If the
|
|
9
|
+
# template file to be rendered does not exist, it will try each additional render
|
|
10
|
+
# engine specified in this plugin, in order, using the path to the first
|
|
11
|
+
# template file that exists in the file system. If no such path is found, it
|
|
12
|
+
# uses the default path specified by the render plugin.
|
|
13
|
+
#
|
|
14
|
+
# Example:
|
|
15
|
+
#
|
|
16
|
+
# plugin :render # default engine is 'erb'
|
|
17
|
+
# plugin :additional_render_engines, ['haml', 'str']
|
|
18
|
+
#
|
|
19
|
+
# route do |r|
|
|
20
|
+
# # Will check the following in order, using path for first
|
|
21
|
+
# # template file that exists:
|
|
22
|
+
# # * views/t.erb
|
|
23
|
+
# # * views/t.haml
|
|
24
|
+
# # * views/t.str
|
|
25
|
+
# render :t
|
|
26
|
+
# end
|
|
27
|
+
module AdditionalRenderEngines
|
|
28
|
+
def self.load_dependencies(app, render_engines)
|
|
29
|
+
app.plugin :render
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Set the additional render engines to consider.
|
|
33
|
+
def self.configure(app, render_engines)
|
|
34
|
+
app.opts[:additional_render_engines] = render_engines.dup.freeze
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
module InstanceMethods
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
# If the template path does not exist, try looking for the template
|
|
41
|
+
# using each of the render engines, in order, returning
|
|
42
|
+
# the first path that exists. If no template path exists for the
|
|
43
|
+
# default any or any additional engines, return the original path.
|
|
44
|
+
def template_path(opts)
|
|
45
|
+
orig_path = super
|
|
46
|
+
|
|
47
|
+
unless File.file?(orig_path)
|
|
48
|
+
self.opts[:additional_render_engines].each do |engine|
|
|
49
|
+
path = super(opts.merge(:engine=>engine))
|
|
50
|
+
return path if File.file?(path)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
orig_path
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
register_plugin(:additional_render_engines, AdditionalRenderEngines)
|
|
60
|
+
end
|
|
61
|
+
end
|
data/lib/roda/plugins/assets.rb
CHANGED
|
@@ -147,7 +147,7 @@ class Roda
|
|
|
147
147
|
# If you have the yuicompressor gem installed and working, it will be used
|
|
148
148
|
# automatically to compress your javascript and css assets. For javascript
|
|
149
149
|
# assets, if yuicompressor is not available, the plugin will check for
|
|
150
|
-
#
|
|
150
|
+
# closure-compiler, uglifier, and minjs and use the first one that works.
|
|
151
151
|
# If no compressors are available, the assets will just be concatenated
|
|
152
152
|
# together and not compressed during compilation. You can use the
|
|
153
153
|
# :css_compressor and :js_compressor options to specify the compressor to use.
|
|
@@ -20,6 +20,9 @@ class Roda
|
|
|
20
20
|
# plugin :common_logger, Logger.new('filename')
|
|
21
21
|
# plugin :common_logger, Logger.new('filename'), method: :debug
|
|
22
22
|
module CommonLogger
|
|
23
|
+
MUTATE_LINE = RUBY_VERSION < '2.3' || RUBY_VERSION >= '3'
|
|
24
|
+
private_constant :MUTATE_LINE
|
|
25
|
+
|
|
23
26
|
def self.configure(app, logger=nil, opts=OPTS)
|
|
24
27
|
app.opts[:common_logger] = logger || app.opts[:common_logger] || $stderr
|
|
25
28
|
app.opts[:common_logger_meth] = app.opts[:common_logger].method(opts.fetch(:method){logger.respond_to?(:write) ? :write : :<<})
|
|
@@ -53,7 +56,15 @@ class Roda
|
|
|
53
56
|
|
|
54
57
|
env = @_request.env
|
|
55
58
|
|
|
56
|
-
|
|
59
|
+
line = "#{env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-"} - #{env["REMOTE_USER"] || "-"} [#{Time.now.strftime("%d/%b/%Y:%H:%M:%S %z")}] \"#{env["REQUEST_METHOD"]} #{env["SCRIPT_NAME"]}#{env["PATH_INFO"]}#{"?#{env["QUERY_STRING"]}" if ((qs = env["QUERY_STRING"]) && !qs.empty?)} #{@_request.http_version}\" #{result[0]} #{((length = result[1]['Content-Length']) && (length unless length == '0')) || '-'} #{elapsed_time}\n"
|
|
60
|
+
if MUTATE_LINE
|
|
61
|
+
line.gsub!(/[^[:print:]\n]/){|c| sprintf("\\x%x", c.ord)}
|
|
62
|
+
# :nocov:
|
|
63
|
+
else
|
|
64
|
+
line = line.gsub(/[^[:print:]\n]/){|c| sprintf("\\x%x", c.ord)}
|
|
65
|
+
# :nocov:
|
|
66
|
+
end
|
|
67
|
+
opts[:common_logger_meth].call(line)
|
|
57
68
|
end
|
|
58
69
|
|
|
59
70
|
# Create timer instance used for timing
|
|
@@ -13,18 +13,18 @@ class Roda
|
|
|
13
13
|
module ResponseMethods
|
|
14
14
|
# Delete any empty headers when calling finish
|
|
15
15
|
def finish
|
|
16
|
-
|
|
16
|
+
delete_empty_headers(super)
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
# Delete any empty headers when calling finish_with_body
|
|
20
20
|
def finish_with_body(_)
|
|
21
|
-
|
|
21
|
+
delete_empty_headers(super)
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
private
|
|
25
25
|
|
|
26
26
|
# Delete any empty headers from response
|
|
27
|
-
def
|
|
27
|
+
def delete_empty_headers(res)
|
|
28
28
|
res[1].delete_if{|_, v| v.is_a?(String) && v.empty?}
|
|
29
29
|
res
|
|
30
30
|
end
|
|
@@ -127,7 +127,7 @@ div.context ol.context-line li span { float: right; }
|
|
|
127
127
|
div.commands { margin-left: 40px; }
|
|
128
128
|
div.commands a { color:black; text-decoration:none; }
|
|
129
129
|
#summary { background: #ffc; }
|
|
130
|
-
#summary h2 { font-weight: normal; color: #666; }
|
|
130
|
+
#summary h2 { font-weight: normal; color: #666; font-family: monospace; white-space: pre-wrap;}
|
|
131
131
|
#summary ul#quicklinks { list-style-type: none; margin-bottom: 2em; }
|
|
132
132
|
#summary ul#quicklinks li { float: left; padding: 0 1em; }
|
|
133
133
|
#summary ul#quicklinks>li+li { border-left: 1px #666 solid; }
|
|
@@ -196,12 +196,13 @@ END
|
|
|
196
196
|
# Designed to be used with the +json+ exception, which will
|
|
197
197
|
# automatically convert the hash to JSON format.
|
|
198
198
|
def exception_page(exception, opts=OPTS)
|
|
199
|
+
message = exception_page_exception_message(exception)
|
|
199
200
|
if opts[:json]
|
|
200
201
|
@_response['Content-Type'] = "application/json"
|
|
201
202
|
{
|
|
202
203
|
"exception"=>{
|
|
203
204
|
"class"=>exception.class.to_s,
|
|
204
|
-
"message"=>
|
|
205
|
+
"message"=>message,
|
|
205
206
|
"backtrace"=>exception.backtrace.map(&:to_s)
|
|
206
207
|
}
|
|
207
208
|
}
|
|
@@ -319,7 +320,7 @@ END
|
|
|
319
320
|
|
|
320
321
|
<div id="summary">
|
|
321
322
|
<h1>#{h exception.class} at #{h r.path}</h1>
|
|
322
|
-
<h2>#{h
|
|
323
|
+
<h2>#{h message}</h2>
|
|
323
324
|
<table><tr>
|
|
324
325
|
<th>Ruby</th>
|
|
325
326
|
<td>
|
|
@@ -394,7 +395,22 @@ END1
|
|
|
394
395
|
END
|
|
395
396
|
else
|
|
396
397
|
@_response['Content-Type'] = "text/plain"
|
|
397
|
-
"#{exception.class}: #{
|
|
398
|
+
"#{exception.class}: #{message}\n#{exception.backtrace.map{|l| "\t#{l}"}.join("\n")}"
|
|
399
|
+
end
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
private
|
|
403
|
+
|
|
404
|
+
# :nocov:
|
|
405
|
+
if RUBY_VERSION >= '3.2'
|
|
406
|
+
def exception_page_exception_message(exception)
|
|
407
|
+
exception.detailed_message(highlight: false).to_s
|
|
408
|
+
end
|
|
409
|
+
# :nocov:
|
|
410
|
+
else
|
|
411
|
+
# Return message to use for exception.
|
|
412
|
+
def exception_page_exception_message(exception)
|
|
413
|
+
exception.message.to_s
|
|
398
414
|
end
|
|
399
415
|
end
|
|
400
416
|
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
class Roda
|
|
5
|
+
module RodaPlugins
|
|
6
|
+
# The skip_common_logger plugin allows for skipping common_logger logging
|
|
7
|
+
# of some requests. You pass a block when loading the plugin, and the
|
|
8
|
+
# block will be called before logging each request. The block should return
|
|
9
|
+
# whether the request should be logged.
|
|
10
|
+
#
|
|
11
|
+
# Example:
|
|
12
|
+
#
|
|
13
|
+
# # Only log server errors
|
|
14
|
+
# plugin :filter_common_logger do |result|
|
|
15
|
+
# result[0] >= 500
|
|
16
|
+
# end
|
|
17
|
+
#
|
|
18
|
+
# # Don't log requests to certain paths
|
|
19
|
+
# plugin :filter_common_logger do |_|
|
|
20
|
+
# # Block is called in the same context as the route block
|
|
21
|
+
# !request.path.start_with?('/admin/')
|
|
22
|
+
# end
|
|
23
|
+
module FilterCommonLogger
|
|
24
|
+
def self.load_dependencies(app)
|
|
25
|
+
app.plugin :common_logger
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.configure(app, &block)
|
|
29
|
+
app.send(:define_method, :_common_log_request?, &block)
|
|
30
|
+
app.send(:private, :_common_log_request?)
|
|
31
|
+
app.send(:alias_method, :_common_log_request?, :_common_log_request?)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
module InstanceMethods
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
# Log request/response information in common log format to logger.
|
|
38
|
+
def _roda_after_90__common_logger(result)
|
|
39
|
+
super if result && _common_log_request?(result)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
register_plugin(:filter_common_logger, FilterCommonLogger)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
class Roda
|
|
5
|
+
module RodaPlugins
|
|
6
|
+
# The hash_branch_view_subdir plugin builds on the hash_branches and view_options
|
|
7
|
+
# plugins, automatically appending a view subdirectory for any matching hash branch
|
|
8
|
+
# taken. In cases where you are using a separate view subdirectory per hash branch,
|
|
9
|
+
# this can result in DRYer code. Example:
|
|
10
|
+
#
|
|
11
|
+
# plugin :hash_branch_view_subdir
|
|
12
|
+
#
|
|
13
|
+
# route do |r|
|
|
14
|
+
# r.hash_branches
|
|
15
|
+
# end
|
|
16
|
+
#
|
|
17
|
+
# hash_branch 'foo' do |r|
|
|
18
|
+
# # view subdirectory here is 'foo'
|
|
19
|
+
# r.hash_branches('foo')
|
|
20
|
+
# end
|
|
21
|
+
#
|
|
22
|
+
# hash_branch 'foo', 'bar' do |r|
|
|
23
|
+
# # view subdirectory here is 'foo/bar'
|
|
24
|
+
# end
|
|
25
|
+
module HashBranchViewSubdir
|
|
26
|
+
def self.load_dependencies(app)
|
|
27
|
+
app.plugin :hash_branches
|
|
28
|
+
app.plugin :view_options
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.configure(app)
|
|
32
|
+
app.opts[:hash_branch_view_subdir_methods] ||= {}
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
module ClassMethods
|
|
36
|
+
# Freeze the hash_branch_view_subdir metadata when freezing the app.
|
|
37
|
+
def freeze
|
|
38
|
+
opts[:hash_branch_view_subdir_methods].freeze.each_value(&:freeze)
|
|
39
|
+
super
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Duplicate hash_branch_view_subdir metadata in subclass.
|
|
43
|
+
def inherited(subclass)
|
|
44
|
+
super
|
|
45
|
+
|
|
46
|
+
h = subclass.opts[:hash_branch_view_subdir_methods]
|
|
47
|
+
opts[:hash_branch_view_subdir_methods].each do |namespace, routes|
|
|
48
|
+
h[namespace] = routes.dup
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Automatically append a view subdirectory for a successful hash_branch route,
|
|
53
|
+
# by modifying the generated method to append the view subdirectory before
|
|
54
|
+
# dispatching to the original block.
|
|
55
|
+
def hash_branch(namespace='', segment, &block)
|
|
56
|
+
meths = opts[:hash_branch_view_subdir_methods][namespace] ||= {}
|
|
57
|
+
|
|
58
|
+
if block
|
|
59
|
+
meth = meths[segment] = define_roda_method(meths[segment] || "_hash_branch_view_subdir_#{namespace}_#{segment}", 1, &convert_route_block(block))
|
|
60
|
+
super do |*_|
|
|
61
|
+
append_view_subdir(segment)
|
|
62
|
+
send(meth, @_request)
|
|
63
|
+
end
|
|
64
|
+
else
|
|
65
|
+
if meth = meths.delete(segment)
|
|
66
|
+
remove_method(meth)
|
|
67
|
+
end
|
|
68
|
+
super
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
register_plugin(:hash_branch_view_subdir, HashBranchViewSubdir)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
class Roda
|
|
5
|
+
module RodaPlugins
|
|
6
|
+
# The hash_branches plugin allows for O(1) dispatch to multiple route tree branches,
|
|
7
|
+
# based on the next segment in the remaining path:
|
|
8
|
+
#
|
|
9
|
+
# class App < Roda
|
|
10
|
+
# plugin :hash_branches
|
|
11
|
+
#
|
|
12
|
+
# hash_branch("a") do |r|
|
|
13
|
+
# # /a branch
|
|
14
|
+
# end
|
|
15
|
+
#
|
|
16
|
+
# hash_branch("b") do |r|
|
|
17
|
+
# # /b branch
|
|
18
|
+
# end
|
|
19
|
+
#
|
|
20
|
+
# route do |r|
|
|
21
|
+
# r.hash_branches
|
|
22
|
+
# end
|
|
23
|
+
# end
|
|
24
|
+
#
|
|
25
|
+
# With the above routing tree, the +r.hash_branches+ call in the main routing tree
|
|
26
|
+
# will dispatch requests for the +/a+ and +/b+ branches of the tree to the appropriate
|
|
27
|
+
# routing blocks.
|
|
28
|
+
#
|
|
29
|
+
# In this example, the hash branches for +/a+ and +/b+ are in the same file, but in larger
|
|
30
|
+
# applications, they are usually stored in separate files. This allows for easily splitting
|
|
31
|
+
# up the routing tree into a separate file per branch.
|
|
32
|
+
#
|
|
33
|
+
# The +hash_branch+ method supports namespaces, which allow for dispatching to sub-branches
|
|
34
|
+
# any level of the routing tree, fully supporting the needs of applications with large and
|
|
35
|
+
# deep routing branches:
|
|
36
|
+
#
|
|
37
|
+
# class App < Roda
|
|
38
|
+
# plugin :hash_branches
|
|
39
|
+
#
|
|
40
|
+
# # Only one argument used, so the namespace defaults to '', and the argument
|
|
41
|
+
# # specifies the route name
|
|
42
|
+
# hash_branch("a") do |r|
|
|
43
|
+
# # No argument given, so uses the already matched path as the namespace,
|
|
44
|
+
# # which is '/a' in this case.
|
|
45
|
+
# r.hash_branches
|
|
46
|
+
# end
|
|
47
|
+
#
|
|
48
|
+
# hash_branch("b") do |r|
|
|
49
|
+
# # uses :b as the namespace when looking up routes, as that was explicitly specified
|
|
50
|
+
# r.hash_branches(:b)
|
|
51
|
+
# end
|
|
52
|
+
#
|
|
53
|
+
# # Two arguments used, so first specifies the namespace and the second specifies
|
|
54
|
+
# # the branch name
|
|
55
|
+
# hash_branch("/a", "b") do |r|
|
|
56
|
+
# # /a/b path
|
|
57
|
+
# end
|
|
58
|
+
#
|
|
59
|
+
# hash_branch("/a", "c") do |r|
|
|
60
|
+
# # /a/c path
|
|
61
|
+
# end
|
|
62
|
+
#
|
|
63
|
+
# hash_branch(:b, "b") do |r|
|
|
64
|
+
# # /b/b path
|
|
65
|
+
# end
|
|
66
|
+
#
|
|
67
|
+
# hash_branch(:b, "c") do |r|
|
|
68
|
+
# # /b/c path
|
|
69
|
+
# end
|
|
70
|
+
#
|
|
71
|
+
# route do |r|
|
|
72
|
+
# # No argument given, so uses '' as the namespace, as no part of the path has
|
|
73
|
+
# # been matched yet
|
|
74
|
+
# r.hash_branches
|
|
75
|
+
# end
|
|
76
|
+
# end
|
|
77
|
+
#
|
|
78
|
+
# With the above routing tree, requests for the +/a+ and +/b+ branches will be
|
|
79
|
+
# dispatched to the appropriate +hash_branch+ block. Those blocks will the dispatch
|
|
80
|
+
# to the remaining +hash_branch+ blocks, with the +/a+ branch using the implicit namespace of
|
|
81
|
+
# +/a+, and the +/b+ branch using the explicit namespace of +:b+.
|
|
82
|
+
#
|
|
83
|
+
# It is best for performance to explicitly specify the namespace when calling
|
|
84
|
+
# +r.hash_branches+.
|
|
85
|
+
module HashBranches
|
|
86
|
+
def self.configure(app)
|
|
87
|
+
app.opts[:hash_branches] ||= {}
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
module ClassMethods
|
|
91
|
+
# Freeze the hash_branches metadata when freezing the app.
|
|
92
|
+
def freeze
|
|
93
|
+
opts[:hash_branches].freeze.each_value(&:freeze)
|
|
94
|
+
super
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Duplicate hash_branches metadata in subclass.
|
|
98
|
+
def inherited(subclass)
|
|
99
|
+
super
|
|
100
|
+
|
|
101
|
+
h = subclass.opts[:hash_branches]
|
|
102
|
+
opts[:hash_branches].each do |namespace, routes|
|
|
103
|
+
h[namespace] = routes.dup
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Add branch handler for the given namespace and segment. If called without
|
|
108
|
+
# a block, removes the existing branch handler if it exists.
|
|
109
|
+
def hash_branch(namespace='', segment, &block)
|
|
110
|
+
segment = "/#{segment}"
|
|
111
|
+
routes = opts[:hash_branches][namespace] ||= {}
|
|
112
|
+
if block
|
|
113
|
+
routes[segment] = define_roda_method(routes[segment] || "hash_branch_#{namespace}_#{segment}", 1, &convert_route_block(block))
|
|
114
|
+
elsif meth = routes.delete(segment)
|
|
115
|
+
remove_method(meth)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
module RequestMethods
|
|
121
|
+
# Checks the matching hash_branch namespace for a branch matching the next
|
|
122
|
+
# segment in the remaining path, and dispatch to that block if there is one.
|
|
123
|
+
def hash_branches(namespace=matched_path)
|
|
124
|
+
rp = @remaining_path
|
|
125
|
+
|
|
126
|
+
return unless rp.getbyte(0) == 47 # "/"
|
|
127
|
+
|
|
128
|
+
if routes = roda_class.opts[:hash_branches][namespace]
|
|
129
|
+
if segment_end = rp.index('/', 1)
|
|
130
|
+
if meth = routes[rp[0, segment_end]]
|
|
131
|
+
@remaining_path = rp[segment_end, 100000000]
|
|
132
|
+
always{scope.send(meth, self)}
|
|
133
|
+
end
|
|
134
|
+
elsif meth = routes[rp]
|
|
135
|
+
@remaining_path = ''
|
|
136
|
+
always{scope.send(meth, self)}
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
register_plugin(:hash_branches, HashBranches)
|
|
144
|
+
end
|
|
145
|
+
end
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
class Roda
|
|
5
|
+
module RodaPlugins
|
|
6
|
+
# The hash_paths plugin allows for O(1) dispatch to multiple routes at any point
|
|
7
|
+
# in the routing tree. It is useful when you have a large number of specific routes
|
|
8
|
+
# to dispatch to at any point in the routing tree.
|
|
9
|
+
#
|
|
10
|
+
# You configure the hash paths to dispatch to using the +hash_path+ class method,
|
|
11
|
+
# specifying the remaining path, with a block to handle that path. Then you dispatch
|
|
12
|
+
# to the configured paths using +r.hash_paths+:
|
|
13
|
+
#
|
|
14
|
+
# class App < Roda
|
|
15
|
+
# plugin :hash_paths
|
|
16
|
+
#
|
|
17
|
+
# hash_path("/a") do |r|
|
|
18
|
+
# # /a path
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# hash_path("/a/b") do |r|
|
|
22
|
+
# # /a/b path
|
|
23
|
+
# end
|
|
24
|
+
#
|
|
25
|
+
# route do |r|
|
|
26
|
+
# r.hash_paths
|
|
27
|
+
# end
|
|
28
|
+
# end
|
|
29
|
+
#
|
|
30
|
+
# With the above routing tree, the +r.hash_paths+ call will dispatch requests for the +/a+ and
|
|
31
|
+
# +/a/b+ request paths.
|
|
32
|
+
#
|
|
33
|
+
# The +hash_path+ class method supports namespaces, which allows +r.hash_paths+ to be used at
|
|
34
|
+
# any level of the routing tree. Here is an example that uses namespaces for sub-branches:
|
|
35
|
+
#
|
|
36
|
+
# class App < Roda
|
|
37
|
+
# plugin :hash_paths
|
|
38
|
+
#
|
|
39
|
+
# # Two arguments provided, so first argument is the namespace
|
|
40
|
+
# hash_path("/a", "/b") do |r|
|
|
41
|
+
# # /a/b path
|
|
42
|
+
# end
|
|
43
|
+
#
|
|
44
|
+
# hash_path("/a", "/c") do |r|
|
|
45
|
+
# # /a/c path
|
|
46
|
+
# end
|
|
47
|
+
#
|
|
48
|
+
# hash_path(:b, "/b") do |r|
|
|
49
|
+
# # /b/b path
|
|
50
|
+
# end
|
|
51
|
+
#
|
|
52
|
+
# hash_path(:b, "/c") do |r|
|
|
53
|
+
# # /b/c path
|
|
54
|
+
# end
|
|
55
|
+
#
|
|
56
|
+
# route do |r|
|
|
57
|
+
# r.on 'a' do
|
|
58
|
+
# # No argument given, so uses the already matched path as the namespace,
|
|
59
|
+
# # which is '/a' in this case.
|
|
60
|
+
# r.hash_paths
|
|
61
|
+
# end
|
|
62
|
+
#
|
|
63
|
+
# r.on 'b' do
|
|
64
|
+
# # uses :b as the namespace when looking up routes, as that was explicitly specified
|
|
65
|
+
# r.hash_paths(:b)
|
|
66
|
+
# end
|
|
67
|
+
# end
|
|
68
|
+
# end
|
|
69
|
+
#
|
|
70
|
+
# With the above routing tree, requests for the +/a+ branch will be handled by the first
|
|
71
|
+
# +r.hash_paths+ call, and requests for the +/b+ branch will be handled by the second
|
|
72
|
+
# +r.hash_paths+ call. Those will dispatch to the configured hash paths for the +/a+ and
|
|
73
|
+
# +:b+ namespaces.
|
|
74
|
+
#
|
|
75
|
+
# It is best for performance to explicitly specify the namespace when calling
|
|
76
|
+
# +r.hash_paths+.
|
|
77
|
+
module HashPaths
|
|
78
|
+
def self.configure(app)
|
|
79
|
+
app.opts[:hash_paths] ||= {}
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
module ClassMethods
|
|
83
|
+
# Freeze the hash_paths metadata when freezing the app.
|
|
84
|
+
def freeze
|
|
85
|
+
opts[:hash_paths].freeze.each_value(&:freeze)
|
|
86
|
+
super
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Duplicate hash_paths metadata in subclass.
|
|
90
|
+
def inherited(subclass)
|
|
91
|
+
super
|
|
92
|
+
|
|
93
|
+
h = subclass.opts[:hash_paths]
|
|
94
|
+
opts[:hash_paths].each do |namespace, routes|
|
|
95
|
+
h[namespace] = routes.dup
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Add path handler for the given namespace and path. When the
|
|
100
|
+
# r.hash_paths method is called, checks the matching namespace
|
|
101
|
+
# for the full remaining path, and dispatch to that block if
|
|
102
|
+
# there is one. If called without a block, removes the existing
|
|
103
|
+
# path handler if it exists.
|
|
104
|
+
def hash_path(namespace='', path, &block)
|
|
105
|
+
routes = opts[:hash_paths][namespace] ||= {}
|
|
106
|
+
if block
|
|
107
|
+
routes[path] = define_roda_method(routes[path] || "hash_path_#{namespace}_#{path}", 1, &convert_route_block(block))
|
|
108
|
+
elsif meth = routes.delete(path)
|
|
109
|
+
remove_method(meth)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
module RequestMethods
|
|
115
|
+
# Checks the matching hash_path namespace for a branch matching the
|
|
116
|
+
# remaining path, and dispatch to that block if there is one.
|
|
117
|
+
def hash_paths(namespace=matched_path)
|
|
118
|
+
if (routes = roda_class.opts[:hash_paths][namespace]) && (meth = routes[@remaining_path])
|
|
119
|
+
@remaining_path = ''
|
|
120
|
+
always{scope.send(meth, self)}
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
register_plugin(:hash_paths, HashPaths)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
@@ -3,58 +3,9 @@
|
|
|
3
3
|
#
|
|
4
4
|
class Roda
|
|
5
5
|
module RodaPlugins
|
|
6
|
-
# The hash_routes plugin
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
# is a static string.
|
|
10
|
-
#
|
|
11
|
-
# For a basic replacement of the multi_route plugin, you can replace class level
|
|
12
|
-
# <tt>route('segment')</tt> calls with <tt>hash_branch('segment')</tt>:
|
|
13
|
-
#
|
|
14
|
-
# class App < Roda
|
|
15
|
-
# plugin :hash_routes
|
|
16
|
-
#
|
|
17
|
-
# hash_branch("a") do |r|
|
|
18
|
-
# # /a branch
|
|
19
|
-
# end
|
|
20
|
-
#
|
|
21
|
-
# hash_branch("b") do |r|
|
|
22
|
-
# # /b branch
|
|
23
|
-
# end
|
|
24
|
-
#
|
|
25
|
-
# route do |r|
|
|
26
|
-
# r.hash_branches
|
|
27
|
-
# end
|
|
28
|
-
# end
|
|
29
|
-
#
|
|
30
|
-
# With the above routing tree, the +r.hash_branches+ call in the main routing tree,
|
|
31
|
-
# will dispatch requests for the +/a+ and +/b+ branches of the tree to the appropriate
|
|
32
|
-
# routing blocks.
|
|
33
|
-
#
|
|
34
|
-
# In addition to supporting routing via the next segment, you can also support similar
|
|
35
|
-
# routing for entire remaining path using the +hash_path+ class method:
|
|
36
|
-
#
|
|
37
|
-
# class App < Roda
|
|
38
|
-
# plugin :hash_routes
|
|
39
|
-
#
|
|
40
|
-
# hash_path("/a") do |r|
|
|
41
|
-
# # /a path
|
|
42
|
-
# end
|
|
43
|
-
#
|
|
44
|
-
# hash_path("/a/b") do |r|
|
|
45
|
-
# # /a/b path
|
|
46
|
-
# end
|
|
47
|
-
#
|
|
48
|
-
# route do |r|
|
|
49
|
-
# r.hash_paths
|
|
50
|
-
# end
|
|
51
|
-
# end
|
|
52
|
-
#
|
|
53
|
-
# With the above routing tree, the +r.hash_paths+ call will dispatch requests for the +/a+ and
|
|
54
|
-
# +/a/b+ request paths.
|
|
55
|
-
#
|
|
56
|
-
# You can combine the two approaches, and use +r.hash_routes+ to first try routing the
|
|
57
|
-
# full path, and then try routing the next segment:
|
|
6
|
+
# The hash_routes plugin builds on top of the hash_branches and hash_paths plugins, and adds
|
|
7
|
+
# a DSL for configuring hash branches and paths. It also adds an +r.hash_routes+ method for
|
|
8
|
+
# first attempting dispatch to the configured hash_paths, then to the configured hash_branches:
|
|
58
9
|
#
|
|
59
10
|
# class App < Roda
|
|
60
11
|
# plugin :hash_routes
|
|
@@ -84,60 +35,14 @@ class Roda
|
|
|
84
35
|
# +hash_path+ block. Other requests for the +/a+ branch, and all requests for the +/b+
|
|
85
36
|
# branch will be routed to the appropriate +hash_branch+ block.
|
|
86
37
|
#
|
|
87
|
-
#
|
|
88
|
-
#
|
|
89
|
-
#
|
|
90
|
-
# class App < Roda
|
|
91
|
-
# plugin :hash_routes
|
|
92
|
-
#
|
|
93
|
-
# # Only one argument used, so the namespace defaults to '', and the argument
|
|
94
|
-
# # specifies the route name
|
|
95
|
-
# hash_branch("a") do |r|
|
|
96
|
-
# # uses '/a' as the namespace when looking up routes,
|
|
97
|
-
# # as that part of the path has been routed now
|
|
98
|
-
# r.hash_routes
|
|
99
|
-
# end
|
|
100
|
-
#
|
|
101
|
-
# # Two arguments used, so first specifies the namespace and the second specifies
|
|
102
|
-
# # the route name
|
|
103
|
-
# hash_branch('', "b") do |r|
|
|
104
|
-
# # uses :b as the namespace when looking up routes, as that was explicitly specified
|
|
105
|
-
# r.hash_routes(:b)
|
|
106
|
-
# end
|
|
107
|
-
#
|
|
108
|
-
# hash_path("/a", "/b") do |r|
|
|
109
|
-
# # /a/b path
|
|
110
|
-
# end
|
|
111
|
-
#
|
|
112
|
-
# hash_path("/a", "/c") do |r|
|
|
113
|
-
# # /a/c path
|
|
114
|
-
# end
|
|
115
|
-
#
|
|
116
|
-
# hash_path(:b, "/b") do |r|
|
|
117
|
-
# # /b/b path
|
|
118
|
-
# end
|
|
119
|
-
#
|
|
120
|
-
# hash_path(:b, "/c") do |r|
|
|
121
|
-
# # /b/c path
|
|
122
|
-
# end
|
|
123
|
-
#
|
|
124
|
-
# route do |r|
|
|
125
|
-
# # uses '' as the namespace, as no part of the path has been routed yet
|
|
126
|
-
# r.hash_branches
|
|
127
|
-
# end
|
|
128
|
-
# end
|
|
129
|
-
#
|
|
130
|
-
# With the above routing tree, requests for the +/a+ and +/b+ branches will be
|
|
131
|
-
# dispatched to the appropriate +hash_branch+ block. Those blocks will the dispatch
|
|
132
|
-
# to the +hash_path+ blocks, with the +/a+ branch using the implicit namespace of
|
|
133
|
-
# +/a+, and the +/b+ branch using the explicit namespace of +:b+. In general, it
|
|
134
|
-
# is best for performance to explicitly specify the namespace when calling
|
|
135
|
-
# +r.hash_branches+, +r.hash_paths+, and +r.hash_routes+.
|
|
38
|
+
# It is best for performance to explicitly specify the namespace when calling
|
|
39
|
+
# +r.hash_routes+.
|
|
136
40
|
#
|
|
137
41
|
# Because specifying routes explicitly using the +hash_branch+ and +hash_path+
|
|
138
42
|
# class methods can get repetitive, the hash_routes plugin offers a DSL for DRYing
|
|
139
|
-
# the code up. This DSL is used by calling the +hash_routes+ class method.
|
|
140
|
-
#
|
|
43
|
+
# the code up. This DSL is used by calling the +hash_routes+ class method. The
|
|
44
|
+
# DSL used tries to mirror the standard Roda DSL, but it is not a normal routing
|
|
45
|
+
# tree (it's not possible to execute arbitrary code between branches during routing).
|
|
141
46
|
#
|
|
142
47
|
# class App < Roda
|
|
143
48
|
# plugin :hash_routes
|
|
@@ -264,9 +169,12 @@ class Roda
|
|
|
264
169
|
# * views
|
|
265
170
|
# * all verb methods (get, post, etc.)
|
|
266
171
|
module HashRoutes
|
|
172
|
+
def self.load_dependencies(app)
|
|
173
|
+
app.plugin :hash_branches
|
|
174
|
+
app.plugin :hash_paths
|
|
175
|
+
end
|
|
176
|
+
|
|
267
177
|
def self.configure(app)
|
|
268
|
-
app.opts[:hash_branches] ||= {}
|
|
269
|
-
app.opts[:hash_paths] ||= {}
|
|
270
178
|
app.opts[:hash_routes_methods] ||= {}
|
|
271
179
|
end
|
|
272
180
|
|
|
@@ -359,24 +267,10 @@ class Roda
|
|
|
359
267
|
module ClassMethods
|
|
360
268
|
# Freeze the hash_routes metadata when freezing the app.
|
|
361
269
|
def freeze
|
|
362
|
-
opts[:hash_branches].freeze.each_value(&:freeze)
|
|
363
|
-
opts[:hash_paths].freeze.each_value(&:freeze)
|
|
364
270
|
opts[:hash_routes_methods].freeze
|
|
365
271
|
super
|
|
366
272
|
end
|
|
367
273
|
|
|
368
|
-
# Duplicate hash_routes metadata in subclass.
|
|
369
|
-
def inherited(subclass)
|
|
370
|
-
super
|
|
371
|
-
|
|
372
|
-
[:hash_branches, :hash_paths].each do |k|
|
|
373
|
-
h = subclass.opts[k]
|
|
374
|
-
opts[k].each do |namespace, routes|
|
|
375
|
-
h[namespace] = routes.dup
|
|
376
|
-
end
|
|
377
|
-
end
|
|
378
|
-
end
|
|
379
|
-
|
|
380
274
|
# Invoke the DSL for configuring hash routes, see DSL for methods inside the
|
|
381
275
|
# block. If the block accepts an argument, yield the DSL instance. If the
|
|
382
276
|
# block does not accept an argument, instance_exec the block in the context
|
|
@@ -393,66 +287,9 @@ class Roda
|
|
|
393
287
|
|
|
394
288
|
dsl
|
|
395
289
|
end
|
|
396
|
-
|
|
397
|
-
# Add branch handler for the given namespace and segment. If called without
|
|
398
|
-
# a block, removes the existing branch handler if it exists.
|
|
399
|
-
def hash_branch(namespace='', segment, &block)
|
|
400
|
-
segment = "/#{segment}"
|
|
401
|
-
routes = opts[:hash_branches][namespace] ||= {}
|
|
402
|
-
if block
|
|
403
|
-
routes[segment] = define_roda_method(routes[segment] || "hash_branch_#{namespace}_#{segment}", 1, &convert_route_block(block))
|
|
404
|
-
elsif meth = routes[segment]
|
|
405
|
-
routes.delete(segment)
|
|
406
|
-
remove_method(meth)
|
|
407
|
-
end
|
|
408
|
-
end
|
|
409
|
-
|
|
410
|
-
# Add path handler for the given namespace and path. When the
|
|
411
|
-
# r.hash_paths method is called, checks the matching namespace
|
|
412
|
-
# for the full remaining path, and dispatch to that block if
|
|
413
|
-
# there is one. If called without a block, removes the existing
|
|
414
|
-
# path handler if it exists.
|
|
415
|
-
def hash_path(namespace='', path, &block)
|
|
416
|
-
routes = opts[:hash_paths][namespace] ||= {}
|
|
417
|
-
if block
|
|
418
|
-
routes[path] = define_roda_method(routes[path] || "hash_path_#{namespace}_#{path}", 1, &convert_route_block(block))
|
|
419
|
-
elsif meth = routes[path]
|
|
420
|
-
routes.delete(path)
|
|
421
|
-
remove_method(meth)
|
|
422
|
-
end
|
|
423
|
-
end
|
|
424
290
|
end
|
|
425
291
|
|
|
426
292
|
module RequestMethods
|
|
427
|
-
# Checks the matching hash_branch namespace for a branch matching the next
|
|
428
|
-
# segment in the remaining path, and dispatch to that block if there is one.
|
|
429
|
-
def hash_branches(namespace=matched_path)
|
|
430
|
-
rp = @remaining_path
|
|
431
|
-
|
|
432
|
-
return unless rp.getbyte(0) == 47 # "/"
|
|
433
|
-
|
|
434
|
-
if routes = roda_class.opts[:hash_branches][namespace]
|
|
435
|
-
if segment_end = rp.index('/', 1)
|
|
436
|
-
if meth = routes[rp[0, segment_end]]
|
|
437
|
-
@remaining_path = rp[segment_end, 100000000]
|
|
438
|
-
always{scope.send(meth, self)}
|
|
439
|
-
end
|
|
440
|
-
elsif meth = routes[rp]
|
|
441
|
-
@remaining_path = ''
|
|
442
|
-
always{scope.send(meth, self)}
|
|
443
|
-
end
|
|
444
|
-
end
|
|
445
|
-
end
|
|
446
|
-
|
|
447
|
-
# Checks the matching hash_path namespace for a branch matching the
|
|
448
|
-
# remaining path, and dispatch to that block if there is one.
|
|
449
|
-
def hash_paths(namespace=matched_path)
|
|
450
|
-
if (routes = roda_class.opts[:hash_paths][namespace]) && (meth = routes[@remaining_path])
|
|
451
|
-
@remaining_path = ''
|
|
452
|
-
always{scope.send(meth, self)}
|
|
453
|
-
end
|
|
454
|
-
end
|
|
455
|
-
|
|
456
293
|
# Check for matches in both the hash_path and hash_branch namespaces for
|
|
457
294
|
# a matching remaining path or next segment in the remaining path, respectively.
|
|
458
295
|
def hash_routes(namespace=matched_path)
|
|
@@ -14,13 +14,6 @@ class Roda
|
|
|
14
14
|
#
|
|
15
15
|
# plugin :heartbeat, path: '/status'
|
|
16
16
|
module Heartbeat
|
|
17
|
-
# :nocov:
|
|
18
|
-
HEADER_CLASS = (defined?(Rack::Headers) && Rack::Headers.is_a?(Class)) ? Rack::Headers : Hash
|
|
19
|
-
# :nocov:
|
|
20
|
-
private_constant :HEADER_CLASS
|
|
21
|
-
|
|
22
|
-
HEARTBEAT_RESPONSE = [200, {'Content-Type'=>'text/plain'}.freeze, ['OK'.freeze].freeze].freeze
|
|
23
|
-
|
|
24
17
|
# Set the heartbeat path to the given path.
|
|
25
18
|
def self.configure(app, opts=OPTS)
|
|
26
19
|
app.opts[:heartbeat_path] = (opts[:path] || app.opts[:heartbeat_path] || "/heartbeat").dup.freeze
|
|
@@ -32,9 +25,11 @@ class Roda
|
|
|
32
25
|
# If the request is for a heartbeat path, return the heartbeat response.
|
|
33
26
|
def _roda_before_20__heartbeat
|
|
34
27
|
if env['PATH_INFO'] == opts[:heartbeat_path]
|
|
35
|
-
response =
|
|
36
|
-
response
|
|
37
|
-
|
|
28
|
+
response = @_response
|
|
29
|
+
response.status = 200
|
|
30
|
+
response['Content-Type'] = 'text/plain'
|
|
31
|
+
response.write 'OK'
|
|
32
|
+
throw :halt, response.finish
|
|
38
33
|
end
|
|
39
34
|
end
|
|
40
35
|
end
|
|
@@ -465,7 +465,11 @@ class Roda
|
|
|
465
465
|
def #{field}(address, &block)
|
|
466
466
|
on(:#{field}=>address, &block)
|
|
467
467
|
end
|
|
468
|
+
END
|
|
469
|
+
|
|
470
|
+
next if [:rcpt, :text, :body, :subject].include?(field)
|
|
468
471
|
|
|
472
|
+
class_eval(<<-END, __FILE__, __LINE__+1)
|
|
469
473
|
private
|
|
470
474
|
|
|
471
475
|
def match_#{field}(address)
|
|
@@ -474,11 +478,6 @@ class Roda
|
|
|
474
478
|
END
|
|
475
479
|
end
|
|
476
480
|
|
|
477
|
-
undef_method :match_rcpt
|
|
478
|
-
undef_method :match_text
|
|
479
|
-
undef_method :match_body
|
|
480
|
-
undef_method :match_subject
|
|
481
|
-
|
|
482
481
|
# Same as +header+, but also mark the message as being handled.
|
|
483
482
|
def handle_header(key, value=nil)
|
|
484
483
|
header(key, value) do |*args|
|
|
@@ -8,7 +8,7 @@ class Roda
|
|
|
8
8
|
# which will check # if the first segment in the path matches a named route,
|
|
9
9
|
# and dispatch to that named route.
|
|
10
10
|
#
|
|
11
|
-
# The
|
|
11
|
+
# The hash_branches plugin offers a +r.hash_branches+ method that is similar to
|
|
12
12
|
# and performs better than the +r.multi_route+ method, and it is recommended
|
|
13
13
|
# to consider using that instead of this plugin.
|
|
14
14
|
#
|
|
@@ -16,10 +16,6 @@ class Roda
|
|
|
16
16
|
# +multi_view_compile+ class method that will take an array of view template
|
|
17
17
|
# names and construct a regexp that can be passed to +r.multi_view+.
|
|
18
18
|
#
|
|
19
|
-
# The hash_routes plugin offers a views method that is similar to and performs
|
|
20
|
-
# better than the +r.multi_view+ method, and it is recommended to consider
|
|
21
|
-
# using that instead of this plugin.
|
|
22
|
-
#
|
|
23
19
|
# Example:
|
|
24
20
|
#
|
|
25
21
|
# plugin :multi_view
|
|
@@ -145,8 +145,7 @@ class Roda
|
|
|
145
145
|
routes = opts[:namespaced_routes][namespace] ||= {}
|
|
146
146
|
if block
|
|
147
147
|
routes[name] = define_roda_method(routes[name] || "named_routes_#{namespace}_#{name}", 1, &convert_route_block(block))
|
|
148
|
-
elsif meth = routes
|
|
149
|
-
routes.delete(name)
|
|
148
|
+
elsif meth = routes.delete(name)
|
|
150
149
|
remove_method(meth)
|
|
151
150
|
end
|
|
152
151
|
else
|
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.59.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: 2022-
|
|
11
|
+
date: 2022-08-12 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rack
|
|
@@ -228,6 +228,9 @@ extra_rdoc_files:
|
|
|
228
228
|
- doc/release_notes/3.54.0.txt
|
|
229
229
|
- doc/release_notes/3.55.0.txt
|
|
230
230
|
- doc/release_notes/3.56.0.txt
|
|
231
|
+
- doc/release_notes/3.57.0.txt
|
|
232
|
+
- doc/release_notes/3.58.0.txt
|
|
233
|
+
- doc/release_notes/3.59.0.txt
|
|
231
234
|
- doc/release_notes/3.6.0.txt
|
|
232
235
|
- doc/release_notes/3.7.0.txt
|
|
233
236
|
- doc/release_notes/3.8.0.txt
|
|
@@ -291,6 +294,9 @@ files:
|
|
|
291
294
|
- doc/release_notes/3.54.0.txt
|
|
292
295
|
- doc/release_notes/3.55.0.txt
|
|
293
296
|
- doc/release_notes/3.56.0.txt
|
|
297
|
+
- doc/release_notes/3.57.0.txt
|
|
298
|
+
- doc/release_notes/3.58.0.txt
|
|
299
|
+
- doc/release_notes/3.59.0.txt
|
|
294
300
|
- doc/release_notes/3.6.0.txt
|
|
295
301
|
- doc/release_notes/3.7.0.txt
|
|
296
302
|
- doc/release_notes/3.8.0.txt
|
|
@@ -302,6 +308,7 @@ files:
|
|
|
302
308
|
- lib/roda/plugins/_before_hook.rb
|
|
303
309
|
- lib/roda/plugins/_optimized_matching.rb
|
|
304
310
|
- lib/roda/plugins/_symbol_regexp_matchers.rb
|
|
311
|
+
- lib/roda/plugins/additional_render_engines.rb
|
|
305
312
|
- lib/roda/plugins/additional_view_directories.rb
|
|
306
313
|
- lib/roda/plugins/all_verbs.rb
|
|
307
314
|
- lib/roda/plugins/assets.rb
|
|
@@ -334,10 +341,14 @@ files:
|
|
|
334
341
|
- lib/roda/plugins/error_handler.rb
|
|
335
342
|
- lib/roda/plugins/error_mail.rb
|
|
336
343
|
- lib/roda/plugins/exception_page.rb
|
|
344
|
+
- lib/roda/plugins/filter_common_logger.rb
|
|
337
345
|
- lib/roda/plugins/flash.rb
|
|
338
346
|
- lib/roda/plugins/h.rb
|
|
339
347
|
- lib/roda/plugins/halt.rb
|
|
348
|
+
- lib/roda/plugins/hash_branch_view_subdir.rb
|
|
349
|
+
- lib/roda/plugins/hash_branches.rb
|
|
340
350
|
- lib/roda/plugins/hash_matcher.rb
|
|
351
|
+
- lib/roda/plugins/hash_paths.rb
|
|
341
352
|
- lib/roda/plugins/hash_routes.rb
|
|
342
353
|
- lib/roda/plugins/head.rb
|
|
343
354
|
- lib/roda/plugins/header_matchers.rb
|