ultra-max-sys 0.0.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.
Potentially problematic release.
This version of ultra-max-sys might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/roda-3.106.0/MIT-LICENSE +21 -0
- data/roda-3.106.0/lib/roda/cache.rb +42 -0
- data/roda-3.106.0/lib/roda/plugins/Integer_matcher_max.rb +55 -0
- data/roda-3.106.0/lib/roda/plugins/_after_hook.rb +11 -0
- data/roda-3.106.0/lib/roda/plugins/_base64.rb +34 -0
- data/roda-3.106.0/lib/roda/plugins/_before_hook.rb +41 -0
- data/roda-3.106.0/lib/roda/plugins/_optimized_matching.rb +227 -0
- data/roda-3.106.0/lib/roda/plugins/_symbol_class_matchers.rb +108 -0
- data/roda-3.106.0/lib/roda/plugins/_symbol_regexp_matchers.rb +24 -0
- data/roda-3.106.0/lib/roda/plugins/additional_render_engines.rb +61 -0
- data/roda-3.106.0/lib/roda/plugins/additional_view_directories.rb +66 -0
- data/roda-3.106.0/lib/roda/plugins/all_verbs.rb +50 -0
- data/roda-3.106.0/lib/roda/plugins/assets.rb +902 -0
- data/roda-3.106.0/lib/roda/plugins/assets_preloading.rb +89 -0
- data/roda-3.106.0/lib/roda/plugins/assume_ssl.rb +28 -0
- data/roda-3.106.0/lib/roda/plugins/autoload_hash_branches.rb +79 -0
- data/roda-3.106.0/lib/roda/plugins/autoload_named_routes.rb +65 -0
- data/roda-3.106.0/lib/roda/plugins/backtracking_array.rb +88 -0
- data/roda-3.106.0/lib/roda/plugins/bearer_token.rb +28 -0
- data/roda-3.106.0/lib/roda/plugins/branch_locals.rb +76 -0
- data/roda-3.106.0/lib/roda/plugins/break.rb +43 -0
- data/roda-3.106.0/lib/roda/plugins/caching.rb +206 -0
- data/roda-3.106.0/lib/roda/plugins/capture_erb.rb +94 -0
- data/roda-3.106.0/lib/roda/plugins/chunked.rb +333 -0
- data/roda-3.106.0/lib/roda/plugins/class_level_routing.rb +111 -0
- data/roda-3.106.0/lib/roda/plugins/class_matchers.rb +155 -0
- data/roda-3.106.0/lib/roda/plugins/common_logger.rb +82 -0
- data/roda-3.106.0/lib/roda/plugins/conditional_sessions.rb +67 -0
- data/roda-3.106.0/lib/roda/plugins/content_for.rb +105 -0
- data/roda-3.106.0/lib/roda/plugins/content_security_policy.rb +333 -0
- data/roda-3.106.0/lib/roda/plugins/cookie_flags.rb +157 -0
- data/roda-3.106.0/lib/roda/plugins/cookies.rb +51 -0
- data/roda-3.106.0/lib/roda/plugins/csrf.rb +81 -0
- data/roda-3.106.0/lib/roda/plugins/custom_block_results.rb +85 -0
- data/roda-3.106.0/lib/roda/plugins/custom_matchers.rb +89 -0
- data/roda-3.106.0/lib/roda/plugins/default_headers.rb +64 -0
- data/roda-3.106.0/lib/roda/plugins/default_status.rb +35 -0
- data/roda-3.106.0/lib/roda/plugins/delay_build.rb +18 -0
- data/roda-3.106.0/lib/roda/plugins/delegate.rb +77 -0
- data/roda-3.106.0/lib/roda/plugins/delete_empty_headers.rb +36 -0
- data/roda-3.106.0/lib/roda/plugins/direct_call.rb +38 -0
- data/roda-3.106.0/lib/roda/plugins/disallow_file_uploads.rb +38 -0
- data/roda-3.106.0/lib/roda/plugins/drop_body.rb +43 -0
- data/roda-3.106.0/lib/roda/plugins/each_part.rb +87 -0
- data/roda-3.106.0/lib/roda/plugins/early_hints.rb +25 -0
- data/roda-3.106.0/lib/roda/plugins/empty_root.rb +49 -0
- data/roda-3.106.0/lib/roda/plugins/environments.rb +79 -0
- data/roda-3.106.0/lib/roda/plugins/erb_h.rb +43 -0
- data/roda-3.106.0/lib/roda/plugins/error_email.rb +152 -0
- data/roda-3.106.0/lib/roda/plugins/error_handler.rb +133 -0
- data/roda-3.106.0/lib/roda/plugins/error_mail.rb +149 -0
- data/roda-3.106.0/lib/roda/plugins/exception_page.rb +445 -0
- data/roda-3.106.0/lib/roda/plugins/filter_common_logger.rb +46 -0
- data/roda-3.106.0/lib/roda/plugins/flash.rb +119 -0
- data/roda-3.106.0/lib/roda/plugins/h.rb +59 -0
- data/roda-3.106.0/lib/roda/plugins/halt.rb +108 -0
- data/roda-3.106.0/lib/roda/plugins/hash_branch_view_subdir.rb +76 -0
- data/roda-3.106.0/lib/roda/plugins/hash_branches.rb +145 -0
- data/roda-3.106.0/lib/roda/plugins/hash_matcher.rb +39 -0
- data/roda-3.106.0/lib/roda/plugins/hash_paths.rb +128 -0
- data/roda-3.106.0/lib/roda/plugins/hash_public.rb +125 -0
- data/roda-3.106.0/lib/roda/plugins/hash_public_cache.rb +91 -0
- data/roda-3.106.0/lib/roda/plugins/hash_routes.rb +304 -0
- data/roda-3.106.0/lib/roda/plugins/head.rb +99 -0
- data/roda-3.106.0/lib/roda/plugins/header_matchers.rb +88 -0
- data/roda-3.106.0/lib/roda/plugins/heartbeat.rb +41 -0
- data/roda-3.106.0/lib/roda/plugins/hmac_paths.rb +413 -0
- data/roda-3.106.0/lib/roda/plugins/hooks.rb +92 -0
- data/roda-3.106.0/lib/roda/plugins/host_authorization.rb +155 -0
- data/roda-3.106.0/lib/roda/plugins/host_routing.rb +194 -0
- data/roda-3.106.0/lib/roda/plugins/hsts.rb +35 -0
- data/roda-3.106.0/lib/roda/plugins/indifferent_params.rb +126 -0
- data/roda-3.106.0/lib/roda/plugins/inject_erb.rb +33 -0
- data/roda-3.106.0/lib/roda/plugins/invalid_request_body.rb +107 -0
- data/roda-3.106.0/lib/roda/plugins/ip_from_header.rb +34 -0
- data/roda-3.106.0/lib/roda/plugins/json.rb +119 -0
- data/roda-3.106.0/lib/roda/plugins/json_parser.rb +107 -0
- data/roda-3.106.0/lib/roda/plugins/link_to.rb +83 -0
- data/roda-3.106.0/lib/roda/plugins/mail_processor.rb +629 -0
- data/roda-3.106.0/lib/roda/plugins/mailer.rb +285 -0
- data/roda-3.106.0/lib/roda/plugins/map_matcher.rb +48 -0
- data/roda-3.106.0/lib/roda/plugins/match_affix.rb +64 -0
- data/roda-3.106.0/lib/roda/plugins/match_hook.rb +34 -0
- data/roda-3.106.0/lib/roda/plugins/match_hook_args.rb +93 -0
- data/roda-3.106.0/lib/roda/plugins/middleware.rb +225 -0
- data/roda-3.106.0/lib/roda/plugins/middleware_stack.rb +101 -0
- data/roda-3.106.0/lib/roda/plugins/module_include.rb +96 -0
- data/roda-3.106.0/lib/roda/plugins/multi_public.rb +95 -0
- data/roda-3.106.0/lib/roda/plugins/multi_route.rb +156 -0
- data/roda-3.106.0/lib/roda/plugins/multi_run.rb +144 -0
- data/roda-3.106.0/lib/roda/plugins/multi_view.rb +83 -0
- data/roda-3.106.0/lib/roda/plugins/multibyte_string_matcher.rb +57 -0
- data/roda-3.106.0/lib/roda/plugins/named_routes.rb +167 -0
- data/roda-3.106.0/lib/roda/plugins/named_templates.rb +102 -0
- data/roda-3.106.0/lib/roda/plugins/not_allowed.rb +151 -0
- data/roda-3.106.0/lib/roda/plugins/not_found.rb +55 -0
- data/roda-3.106.0/lib/roda/plugins/optimized_segment_matchers.rb +53 -0
- data/roda-3.106.0/lib/roda/plugins/optimized_string_matchers.rb +50 -0
- data/roda-3.106.0/lib/roda/plugins/padrino_render.rb +42 -0
- data/roda-3.106.0/lib/roda/plugins/param_matchers.rb +83 -0
- data/roda-3.106.0/lib/roda/plugins/params_capturing.rb +118 -0
- data/roda-3.106.0/lib/roda/plugins/part.rb +76 -0
- data/roda-3.106.0/lib/roda/plugins/partials.rb +74 -0
- data/roda-3.106.0/lib/roda/plugins/pass.rb +47 -0
- data/roda-3.106.0/lib/roda/plugins/path.rb +255 -0
- data/roda-3.106.0/lib/roda/plugins/path_matchers.rb +54 -0
- data/roda-3.106.0/lib/roda/plugins/path_rewriter.rb +111 -0
- data/roda-3.106.0/lib/roda/plugins/permissions_policy.rb +340 -0
- data/roda-3.106.0/lib/roda/plugins/placeholder_string_matchers.rb +48 -0
- data/roda-3.106.0/lib/roda/plugins/plain_hash_response_headers.rb +32 -0
- data/roda-3.106.0/lib/roda/plugins/precompile_templates.rb +156 -0
- data/roda-3.106.0/lib/roda/plugins/public.rb +182 -0
- data/roda-3.106.0/lib/roda/plugins/r.rb +35 -0
- data/roda-3.106.0/lib/roda/plugins/recheck_precompiled_assets.rb +107 -0
- data/roda-3.106.0/lib/roda/plugins/redirect_http_to_https.rb +99 -0
- data/roda-3.106.0/lib/roda/plugins/redirect_path.rb +59 -0
- data/roda-3.106.0/lib/roda/plugins/relative_path.rb +76 -0
- data/roda-3.106.0/lib/roda/plugins/render.rb +998 -0
- data/roda-3.106.0/lib/roda/plugins/render_coverage.rb +105 -0
- data/roda-3.106.0/lib/roda/plugins/render_each.rb +224 -0
- data/roda-3.106.0/lib/roda/plugins/render_locals.rb +102 -0
- data/roda-3.106.0/lib/roda/plugins/request_aref.rb +77 -0
- data/roda-3.106.0/lib/roda/plugins/request_headers.rb +83 -0
- data/roda-3.106.0/lib/roda/plugins/response_attachment.rb +94 -0
- data/roda-3.106.0/lib/roda/plugins/response_content_type.rb +79 -0
- data/roda-3.106.0/lib/roda/plugins/response_request.rb +36 -0
- data/roda-3.106.0/lib/roda/plugins/route_block_args.rb +50 -0
- data/roda-3.106.0/lib/roda/plugins/route_csrf.rb +388 -0
- data/roda-3.106.0/lib/roda/plugins/run_append_slash.rb +51 -0
- data/roda-3.106.0/lib/roda/plugins/run_handler.rb +51 -0
- data/roda-3.106.0/lib/roda/plugins/run_require_slash.rb +46 -0
- data/roda-3.106.0/lib/roda/plugins/sec_fetch_site_csrf.rb +131 -0
- data/roda-3.106.0/lib/roda/plugins/send_file.rb +127 -0
- data/roda-3.106.0/lib/roda/plugins/sessions.rb +537 -0
- data/roda-3.106.0/lib/roda/plugins/shape_friendly.rb +232 -0
- data/roda-3.106.0/lib/roda/plugins/shared_vars.rb +86 -0
- data/roda-3.106.0/lib/roda/plugins/sinatra_helpers.rb +444 -0
- data/roda-3.106.0/lib/roda/plugins/slash_path_empty.rb +26 -0
- data/roda-3.106.0/lib/roda/plugins/static.rb +42 -0
- data/roda-3.106.0/lib/roda/plugins/static_routing.rb +89 -0
- data/roda-3.106.0/lib/roda/plugins/status_303.rb +34 -0
- data/roda-3.106.0/lib/roda/plugins/status_handler.rb +86 -0
- data/roda-3.106.0/lib/roda/plugins/streaming.rb +170 -0
- data/roda-3.106.0/lib/roda/plugins/strip_path_prefix.rb +33 -0
- data/roda-3.106.0/lib/roda/plugins/symbol_matchers.rb +188 -0
- data/roda-3.106.0/lib/roda/plugins/symbol_status.rb +32 -0
- data/roda-3.106.0/lib/roda/plugins/symbol_views.rb +37 -0
- data/roda-3.106.0/lib/roda/plugins/timestamp_public.rb +78 -0
- data/roda-3.106.0/lib/roda/plugins/type_routing.rb +217 -0
- data/roda-3.106.0/lib/roda/plugins/typecast_params.rb +1185 -0
- data/roda-3.106.0/lib/roda/plugins/typecast_params_sized_integers.rb +108 -0
- data/roda-3.106.0/lib/roda/plugins/unescape_path.rb +41 -0
- data/roda-3.106.0/lib/roda/plugins/view_options.rb +203 -0
- data/roda-3.106.0/lib/roda/plugins/view_subdir_leading_slash.rb +49 -0
- data/roda-3.106.0/lib/roda/plugins.rb +68 -0
- data/roda-3.106.0/lib/roda/request.rb +722 -0
- data/roda-3.106.0/lib/roda/response.rb +221 -0
- data/roda-3.106.0/lib/roda/session_middleware.rb +184 -0
- data/roda-3.106.0/lib/roda/version.rb +18 -0
- data/roda-3.106.0/lib/roda.rb +741 -0
- data/ultra-max-sys.gemspec +12 -0
- metadata +202 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
class Roda
|
|
5
|
+
module RodaPlugins
|
|
6
|
+
# The halt plugin augments the standard request +halt+ method to allow the response
|
|
7
|
+
# status, body, or headers to be changed when halting.
|
|
8
|
+
#
|
|
9
|
+
# After loading the halt plugin:
|
|
10
|
+
#
|
|
11
|
+
# plugin :halt
|
|
12
|
+
#
|
|
13
|
+
# You can call the halt method with an integer to set the response status and return:
|
|
14
|
+
#
|
|
15
|
+
# route do |r|
|
|
16
|
+
# r.halt(403)
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
# Or set the response body and return:
|
|
20
|
+
#
|
|
21
|
+
# route do |r|
|
|
22
|
+
# r.halt('body')
|
|
23
|
+
# end
|
|
24
|
+
#
|
|
25
|
+
# Or set both:
|
|
26
|
+
#
|
|
27
|
+
# route do |r|
|
|
28
|
+
# r.halt(403, 'body')
|
|
29
|
+
# end
|
|
30
|
+
#
|
|
31
|
+
# Or set response status, headers, and body:
|
|
32
|
+
#
|
|
33
|
+
# route do |r|
|
|
34
|
+
# r.halt(403, {'Content-Type'=>'text/csv'}, 'body')
|
|
35
|
+
# end
|
|
36
|
+
#
|
|
37
|
+
# As supported by default, you can still pass an array which contains a rack response:
|
|
38
|
+
#
|
|
39
|
+
# route do |r|
|
|
40
|
+
# r.halt([403, {'Content-Type'=>'text/csv'}, ['body']])
|
|
41
|
+
# end
|
|
42
|
+
#
|
|
43
|
+
# Note that there is a difference between providing status, headers, and body as separate
|
|
44
|
+
# arguments and providing them as a single rack response array. With a rack response array,
|
|
45
|
+
# the values are used directly, while with 3 arguments, the headers given are merged into
|
|
46
|
+
# the existing headers and the given body is written to the existing response body.
|
|
47
|
+
#
|
|
48
|
+
# If using other plugins that recognize additional types of match block responses, such
|
|
49
|
+
# as +symbol_views+ and +json+, you can pass those additional types to +r.halt+:
|
|
50
|
+
#
|
|
51
|
+
# plugin :halt
|
|
52
|
+
# plugin :symbol_views
|
|
53
|
+
# plugin :json
|
|
54
|
+
# route do |r|
|
|
55
|
+
# # symbol_views plugin, specifying template file to render as body
|
|
56
|
+
# r.halt(:template) if r.params['a']
|
|
57
|
+
#
|
|
58
|
+
# # symbol_views plugin, specifying status code, headers, and template file to render as body
|
|
59
|
+
# r.halt(500, {'header'=>'value'}, :other_template) if r.params['c']
|
|
60
|
+
#
|
|
61
|
+
# # json plugin, specifying status code and JSON body
|
|
62
|
+
# r.halt(500, [{'error'=>'foo'}]) if r.params['b']
|
|
63
|
+
# end
|
|
64
|
+
#
|
|
65
|
+
# Note that when using the +json+ plugin with the +halt+ plugin, you cannot return a
|
|
66
|
+
# array as a single argument and have it be converted to json, since it would be interpreted
|
|
67
|
+
# as a rack response. You must use call +r.halt+ with either two or three argument forms
|
|
68
|
+
# in that case.
|
|
69
|
+
module Halt
|
|
70
|
+
module RequestMethods
|
|
71
|
+
# Expand default halt method to handle status codes, headers, and bodies. See Halt.
|
|
72
|
+
def halt(*res)
|
|
73
|
+
case res.length
|
|
74
|
+
when 0 # do nothing
|
|
75
|
+
when 1
|
|
76
|
+
case v = res[0]
|
|
77
|
+
when Integer
|
|
78
|
+
response.status = v
|
|
79
|
+
when Array
|
|
80
|
+
throw :halt, v
|
|
81
|
+
else
|
|
82
|
+
if result = block_result_body(v)
|
|
83
|
+
response.write(result)
|
|
84
|
+
else
|
|
85
|
+
raise Roda::RodaError, "singular argument given to #halt not handled: #{v.inspect}"
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
when 2
|
|
89
|
+
resp = response
|
|
90
|
+
resp.status = res[0]
|
|
91
|
+
resp.write(block_result_body(res[1]))
|
|
92
|
+
when 3
|
|
93
|
+
resp = response
|
|
94
|
+
resp.status = res[0]
|
|
95
|
+
resp.headers.merge!(res[1])
|
|
96
|
+
resp.write(block_result_body(res[2]))
|
|
97
|
+
else
|
|
98
|
+
raise Roda::RodaError, "too many arguments given to #halt (accepts 0-3, received #{res.length})"
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
super()
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
register_plugin(:halt, Halt)
|
|
107
|
+
end
|
|
108
|
+
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,39 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
class Roda
|
|
5
|
+
module RodaPlugins
|
|
6
|
+
# The hash_matcher plugin adds the hash_matcher class method, which
|
|
7
|
+
# allows for easily defining hash matchers:
|
|
8
|
+
#
|
|
9
|
+
# class App < Roda
|
|
10
|
+
# plugin :hash_matcher
|
|
11
|
+
#
|
|
12
|
+
# hash_matcher(:foo) do |v|
|
|
13
|
+
# params['foo'] == v
|
|
14
|
+
# end
|
|
15
|
+
#
|
|
16
|
+
# route do
|
|
17
|
+
# r.on foo: 'bar' do
|
|
18
|
+
# # matches when param foo has value bar
|
|
19
|
+
# end
|
|
20
|
+
# end
|
|
21
|
+
# end
|
|
22
|
+
module HashMatcher
|
|
23
|
+
module ClassMethods
|
|
24
|
+
# Create a match_#{key} method in the request class using the given
|
|
25
|
+
# block, so that using a hash key in a request match method will
|
|
26
|
+
# call the block. The block should return nil or false to not
|
|
27
|
+
# match, and anything else to match. See the HashMatcher module
|
|
28
|
+
# documentation for an example.
|
|
29
|
+
def hash_matcher(key, &block)
|
|
30
|
+
meth = :"match_#{key}"
|
|
31
|
+
self::RodaRequest.send(:define_method, meth, &block)
|
|
32
|
+
self::RodaRequest.send(:private, meth)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
register_plugin(:hash_matcher, HashMatcher)
|
|
38
|
+
end
|
|
39
|
+
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
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
class Roda
|
|
4
|
+
module RodaPlugins
|
|
5
|
+
# The hash_public plugin adds a +hash_path+ method for constructing
|
|
6
|
+
# content-hash-based paths, and a +r.hash_public+ routing method to serve
|
|
7
|
+
# static files from a directory (using the public plugin). This plugin is
|
|
8
|
+
# useful when you want to modify the path to static files when the content
|
|
9
|
+
# of the file changes, ensuring that requests for the static file will not
|
|
10
|
+
# be cached.
|
|
11
|
+
#
|
|
12
|
+
# Unlike the timestamp_public plugin, which uses file modification times,
|
|
13
|
+
# hash_public uses a SHA256 digest of the file content. This makes paths
|
|
14
|
+
# stable across different build environments (e.g. Docker images built in
|
|
15
|
+
# CI/CD pipelines), where file modification times may vary even when the
|
|
16
|
+
# file content has not changed.
|
|
17
|
+
#
|
|
18
|
+
# Note that while this plugin will not serve files outside of the public
|
|
19
|
+
# directory, for performance reasons it does not check the path of the file
|
|
20
|
+
# is inside the public directory when computing the content hash. If the
|
|
21
|
+
# +hash_path+ method is called with untrusted input, it is possible for an
|
|
22
|
+
# attacker to read the content hash of any file on the file system.
|
|
23
|
+
#
|
|
24
|
+
# This plugin caches the digest of file content on first read. That means
|
|
25
|
+
# if you change the file after that, it will continue to show the old hash.
|
|
26
|
+
# This can cause problems in development mode if you are modifying the
|
|
27
|
+
# content of files served by the plugin. You can use the hash_public_cache
|
|
28
|
+
# plugin to scan the public directory in and store the digests in a file,
|
|
29
|
+
# avoiding the need for the process to read files to calculate the digest.
|
|
30
|
+
#
|
|
31
|
+
# Examples:
|
|
32
|
+
#
|
|
33
|
+
# # Use public folder as location of files, and static as the path prefix
|
|
34
|
+
# plugin :hash_public
|
|
35
|
+
#
|
|
36
|
+
# # Use /path/to/app/static as location of files, and public as the path prefix
|
|
37
|
+
# opts[:root] = '/path/to/app'
|
|
38
|
+
# plugin :hash_public, root: 'static', prefix: 'public'
|
|
39
|
+
#
|
|
40
|
+
# # Assuming public is the location of files, and static as the path prefix
|
|
41
|
+
# route do
|
|
42
|
+
# # Make GET /static/any-string/images/foo.png look for public/images/foo.png
|
|
43
|
+
# r.hash_public
|
|
44
|
+
#
|
|
45
|
+
# r.get "example" do
|
|
46
|
+
# # "/static/sha256-url-safe-base64-encoded-file-digest-/images/foo.png"
|
|
47
|
+
# hash_path("images/foo.png")
|
|
48
|
+
# end
|
|
49
|
+
# end
|
|
50
|
+
module HashPublic
|
|
51
|
+
Digest = begin
|
|
52
|
+
require 'openssl'
|
|
53
|
+
::OpenSSL::Digest
|
|
54
|
+
# :nocov:
|
|
55
|
+
rescue LoadError
|
|
56
|
+
require 'digest/sha2'
|
|
57
|
+
::Digest
|
|
58
|
+
# :nocov:
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def self.load_dependencies(app, opts = OPTS)
|
|
62
|
+
app.plugin :public, opts
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Use options given to setup content-hash-based file serving. The
|
|
66
|
+
# following options are recognized by the plugin:
|
|
67
|
+
#
|
|
68
|
+
# :prefix :: The prefix for paths, before the hash segment
|
|
69
|
+
# :length :: The number of characters of the digest to use in paths
|
|
70
|
+
# (default: full 43-character SHA256 URL safe base64 digest)
|
|
71
|
+
#
|
|
72
|
+
# The options given are also passed to the public plugin.
|
|
73
|
+
def self.configure(app, opts = {})
|
|
74
|
+
app.opts[:hash_public_prefix] = (opts[:prefix] || app.opts[:hash_public_prefix] || 'static').dup.freeze
|
|
75
|
+
app.opts[:hash_public_length] = opts[:length] || app.opts[:hash_public_length]
|
|
76
|
+
app.opts[:hash_public_mutex] ||= Mutex.new
|
|
77
|
+
app.opts[:hash_public_cache] ||= {}
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
module ClassMethods
|
|
81
|
+
# The digest for the given file to use in hash_path.
|
|
82
|
+
def hash_path_digest(file)
|
|
83
|
+
opts = self.opts
|
|
84
|
+
cache = opts[:hash_public_cache]
|
|
85
|
+
mutex = opts[:hash_public_mutex]
|
|
86
|
+
unless digest = mutex.synchronize{cache[file]}
|
|
87
|
+
digest = Digest::SHA256.file(File.join(opts[:public_root], file)).base64digest
|
|
88
|
+
digest.chomp!("=")
|
|
89
|
+
digest.tr!("+/", "-_")
|
|
90
|
+
if length = opts[:hash_public_length]
|
|
91
|
+
digest = digest[0, length]
|
|
92
|
+
end
|
|
93
|
+
digest.freeze
|
|
94
|
+
mutex.synchronize{cache[file] = digest}
|
|
95
|
+
end
|
|
96
|
+
digest
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
module InstanceMethods
|
|
101
|
+
# Return a path to the static file that could be served by r.hash_public.
|
|
102
|
+
# This does not check the file is inside the directory for performance
|
|
103
|
+
# reasons, so this should not be called with untrusted input.
|
|
104
|
+
def hash_path(file)
|
|
105
|
+
"/#{opts[:hash_public_prefix]}/#{self.class.hash_path_digest(file)}/#{file}"
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
module RequestMethods
|
|
110
|
+
# Serve files from the public directory if the file exists,
|
|
111
|
+
# it includes the hash_public prefix segment followed by
|
|
112
|
+
# a string segment for the content hash, and this is a GET request.
|
|
113
|
+
def hash_public
|
|
114
|
+
if is_get?
|
|
115
|
+
on roda_class.opts[:hash_public_prefix], String do |_|
|
|
116
|
+
public
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
register_plugin(:hash_public, HashPublic)
|
|
124
|
+
end
|
|
125
|
+
end
|