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.

Files changed (163) hide show
  1. checksums.yaml +7 -0
  2. data/roda-3.106.0/MIT-LICENSE +21 -0
  3. data/roda-3.106.0/lib/roda/cache.rb +42 -0
  4. data/roda-3.106.0/lib/roda/plugins/Integer_matcher_max.rb +55 -0
  5. data/roda-3.106.0/lib/roda/plugins/_after_hook.rb +11 -0
  6. data/roda-3.106.0/lib/roda/plugins/_base64.rb +34 -0
  7. data/roda-3.106.0/lib/roda/plugins/_before_hook.rb +41 -0
  8. data/roda-3.106.0/lib/roda/plugins/_optimized_matching.rb +227 -0
  9. data/roda-3.106.0/lib/roda/plugins/_symbol_class_matchers.rb +108 -0
  10. data/roda-3.106.0/lib/roda/plugins/_symbol_regexp_matchers.rb +24 -0
  11. data/roda-3.106.0/lib/roda/plugins/additional_render_engines.rb +61 -0
  12. data/roda-3.106.0/lib/roda/plugins/additional_view_directories.rb +66 -0
  13. data/roda-3.106.0/lib/roda/plugins/all_verbs.rb +50 -0
  14. data/roda-3.106.0/lib/roda/plugins/assets.rb +902 -0
  15. data/roda-3.106.0/lib/roda/plugins/assets_preloading.rb +89 -0
  16. data/roda-3.106.0/lib/roda/plugins/assume_ssl.rb +28 -0
  17. data/roda-3.106.0/lib/roda/plugins/autoload_hash_branches.rb +79 -0
  18. data/roda-3.106.0/lib/roda/plugins/autoload_named_routes.rb +65 -0
  19. data/roda-3.106.0/lib/roda/plugins/backtracking_array.rb +88 -0
  20. data/roda-3.106.0/lib/roda/plugins/bearer_token.rb +28 -0
  21. data/roda-3.106.0/lib/roda/plugins/branch_locals.rb +76 -0
  22. data/roda-3.106.0/lib/roda/plugins/break.rb +43 -0
  23. data/roda-3.106.0/lib/roda/plugins/caching.rb +206 -0
  24. data/roda-3.106.0/lib/roda/plugins/capture_erb.rb +94 -0
  25. data/roda-3.106.0/lib/roda/plugins/chunked.rb +333 -0
  26. data/roda-3.106.0/lib/roda/plugins/class_level_routing.rb +111 -0
  27. data/roda-3.106.0/lib/roda/plugins/class_matchers.rb +155 -0
  28. data/roda-3.106.0/lib/roda/plugins/common_logger.rb +82 -0
  29. data/roda-3.106.0/lib/roda/plugins/conditional_sessions.rb +67 -0
  30. data/roda-3.106.0/lib/roda/plugins/content_for.rb +105 -0
  31. data/roda-3.106.0/lib/roda/plugins/content_security_policy.rb +333 -0
  32. data/roda-3.106.0/lib/roda/plugins/cookie_flags.rb +157 -0
  33. data/roda-3.106.0/lib/roda/plugins/cookies.rb +51 -0
  34. data/roda-3.106.0/lib/roda/plugins/csrf.rb +81 -0
  35. data/roda-3.106.0/lib/roda/plugins/custom_block_results.rb +85 -0
  36. data/roda-3.106.0/lib/roda/plugins/custom_matchers.rb +89 -0
  37. data/roda-3.106.0/lib/roda/plugins/default_headers.rb +64 -0
  38. data/roda-3.106.0/lib/roda/plugins/default_status.rb +35 -0
  39. data/roda-3.106.0/lib/roda/plugins/delay_build.rb +18 -0
  40. data/roda-3.106.0/lib/roda/plugins/delegate.rb +77 -0
  41. data/roda-3.106.0/lib/roda/plugins/delete_empty_headers.rb +36 -0
  42. data/roda-3.106.0/lib/roda/plugins/direct_call.rb +38 -0
  43. data/roda-3.106.0/lib/roda/plugins/disallow_file_uploads.rb +38 -0
  44. data/roda-3.106.0/lib/roda/plugins/drop_body.rb +43 -0
  45. data/roda-3.106.0/lib/roda/plugins/each_part.rb +87 -0
  46. data/roda-3.106.0/lib/roda/plugins/early_hints.rb +25 -0
  47. data/roda-3.106.0/lib/roda/plugins/empty_root.rb +49 -0
  48. data/roda-3.106.0/lib/roda/plugins/environments.rb +79 -0
  49. data/roda-3.106.0/lib/roda/plugins/erb_h.rb +43 -0
  50. data/roda-3.106.0/lib/roda/plugins/error_email.rb +152 -0
  51. data/roda-3.106.0/lib/roda/plugins/error_handler.rb +133 -0
  52. data/roda-3.106.0/lib/roda/plugins/error_mail.rb +149 -0
  53. data/roda-3.106.0/lib/roda/plugins/exception_page.rb +445 -0
  54. data/roda-3.106.0/lib/roda/plugins/filter_common_logger.rb +46 -0
  55. data/roda-3.106.0/lib/roda/plugins/flash.rb +119 -0
  56. data/roda-3.106.0/lib/roda/plugins/h.rb +59 -0
  57. data/roda-3.106.0/lib/roda/plugins/halt.rb +108 -0
  58. data/roda-3.106.0/lib/roda/plugins/hash_branch_view_subdir.rb +76 -0
  59. data/roda-3.106.0/lib/roda/plugins/hash_branches.rb +145 -0
  60. data/roda-3.106.0/lib/roda/plugins/hash_matcher.rb +39 -0
  61. data/roda-3.106.0/lib/roda/plugins/hash_paths.rb +128 -0
  62. data/roda-3.106.0/lib/roda/plugins/hash_public.rb +125 -0
  63. data/roda-3.106.0/lib/roda/plugins/hash_public_cache.rb +91 -0
  64. data/roda-3.106.0/lib/roda/plugins/hash_routes.rb +304 -0
  65. data/roda-3.106.0/lib/roda/plugins/head.rb +99 -0
  66. data/roda-3.106.0/lib/roda/plugins/header_matchers.rb +88 -0
  67. data/roda-3.106.0/lib/roda/plugins/heartbeat.rb +41 -0
  68. data/roda-3.106.0/lib/roda/plugins/hmac_paths.rb +413 -0
  69. data/roda-3.106.0/lib/roda/plugins/hooks.rb +92 -0
  70. data/roda-3.106.0/lib/roda/plugins/host_authorization.rb +155 -0
  71. data/roda-3.106.0/lib/roda/plugins/host_routing.rb +194 -0
  72. data/roda-3.106.0/lib/roda/plugins/hsts.rb +35 -0
  73. data/roda-3.106.0/lib/roda/plugins/indifferent_params.rb +126 -0
  74. data/roda-3.106.0/lib/roda/plugins/inject_erb.rb +33 -0
  75. data/roda-3.106.0/lib/roda/plugins/invalid_request_body.rb +107 -0
  76. data/roda-3.106.0/lib/roda/plugins/ip_from_header.rb +34 -0
  77. data/roda-3.106.0/lib/roda/plugins/json.rb +119 -0
  78. data/roda-3.106.0/lib/roda/plugins/json_parser.rb +107 -0
  79. data/roda-3.106.0/lib/roda/plugins/link_to.rb +83 -0
  80. data/roda-3.106.0/lib/roda/plugins/mail_processor.rb +629 -0
  81. data/roda-3.106.0/lib/roda/plugins/mailer.rb +285 -0
  82. data/roda-3.106.0/lib/roda/plugins/map_matcher.rb +48 -0
  83. data/roda-3.106.0/lib/roda/plugins/match_affix.rb +64 -0
  84. data/roda-3.106.0/lib/roda/plugins/match_hook.rb +34 -0
  85. data/roda-3.106.0/lib/roda/plugins/match_hook_args.rb +93 -0
  86. data/roda-3.106.0/lib/roda/plugins/middleware.rb +225 -0
  87. data/roda-3.106.0/lib/roda/plugins/middleware_stack.rb +101 -0
  88. data/roda-3.106.0/lib/roda/plugins/module_include.rb +96 -0
  89. data/roda-3.106.0/lib/roda/plugins/multi_public.rb +95 -0
  90. data/roda-3.106.0/lib/roda/plugins/multi_route.rb +156 -0
  91. data/roda-3.106.0/lib/roda/plugins/multi_run.rb +144 -0
  92. data/roda-3.106.0/lib/roda/plugins/multi_view.rb +83 -0
  93. data/roda-3.106.0/lib/roda/plugins/multibyte_string_matcher.rb +57 -0
  94. data/roda-3.106.0/lib/roda/plugins/named_routes.rb +167 -0
  95. data/roda-3.106.0/lib/roda/plugins/named_templates.rb +102 -0
  96. data/roda-3.106.0/lib/roda/plugins/not_allowed.rb +151 -0
  97. data/roda-3.106.0/lib/roda/plugins/not_found.rb +55 -0
  98. data/roda-3.106.0/lib/roda/plugins/optimized_segment_matchers.rb +53 -0
  99. data/roda-3.106.0/lib/roda/plugins/optimized_string_matchers.rb +50 -0
  100. data/roda-3.106.0/lib/roda/plugins/padrino_render.rb +42 -0
  101. data/roda-3.106.0/lib/roda/plugins/param_matchers.rb +83 -0
  102. data/roda-3.106.0/lib/roda/plugins/params_capturing.rb +118 -0
  103. data/roda-3.106.0/lib/roda/plugins/part.rb +76 -0
  104. data/roda-3.106.0/lib/roda/plugins/partials.rb +74 -0
  105. data/roda-3.106.0/lib/roda/plugins/pass.rb +47 -0
  106. data/roda-3.106.0/lib/roda/plugins/path.rb +255 -0
  107. data/roda-3.106.0/lib/roda/plugins/path_matchers.rb +54 -0
  108. data/roda-3.106.0/lib/roda/plugins/path_rewriter.rb +111 -0
  109. data/roda-3.106.0/lib/roda/plugins/permissions_policy.rb +340 -0
  110. data/roda-3.106.0/lib/roda/plugins/placeholder_string_matchers.rb +48 -0
  111. data/roda-3.106.0/lib/roda/plugins/plain_hash_response_headers.rb +32 -0
  112. data/roda-3.106.0/lib/roda/plugins/precompile_templates.rb +156 -0
  113. data/roda-3.106.0/lib/roda/plugins/public.rb +182 -0
  114. data/roda-3.106.0/lib/roda/plugins/r.rb +35 -0
  115. data/roda-3.106.0/lib/roda/plugins/recheck_precompiled_assets.rb +107 -0
  116. data/roda-3.106.0/lib/roda/plugins/redirect_http_to_https.rb +99 -0
  117. data/roda-3.106.0/lib/roda/plugins/redirect_path.rb +59 -0
  118. data/roda-3.106.0/lib/roda/plugins/relative_path.rb +76 -0
  119. data/roda-3.106.0/lib/roda/plugins/render.rb +998 -0
  120. data/roda-3.106.0/lib/roda/plugins/render_coverage.rb +105 -0
  121. data/roda-3.106.0/lib/roda/plugins/render_each.rb +224 -0
  122. data/roda-3.106.0/lib/roda/plugins/render_locals.rb +102 -0
  123. data/roda-3.106.0/lib/roda/plugins/request_aref.rb +77 -0
  124. data/roda-3.106.0/lib/roda/plugins/request_headers.rb +83 -0
  125. data/roda-3.106.0/lib/roda/plugins/response_attachment.rb +94 -0
  126. data/roda-3.106.0/lib/roda/plugins/response_content_type.rb +79 -0
  127. data/roda-3.106.0/lib/roda/plugins/response_request.rb +36 -0
  128. data/roda-3.106.0/lib/roda/plugins/route_block_args.rb +50 -0
  129. data/roda-3.106.0/lib/roda/plugins/route_csrf.rb +388 -0
  130. data/roda-3.106.0/lib/roda/plugins/run_append_slash.rb +51 -0
  131. data/roda-3.106.0/lib/roda/plugins/run_handler.rb +51 -0
  132. data/roda-3.106.0/lib/roda/plugins/run_require_slash.rb +46 -0
  133. data/roda-3.106.0/lib/roda/plugins/sec_fetch_site_csrf.rb +131 -0
  134. data/roda-3.106.0/lib/roda/plugins/send_file.rb +127 -0
  135. data/roda-3.106.0/lib/roda/plugins/sessions.rb +537 -0
  136. data/roda-3.106.0/lib/roda/plugins/shape_friendly.rb +232 -0
  137. data/roda-3.106.0/lib/roda/plugins/shared_vars.rb +86 -0
  138. data/roda-3.106.0/lib/roda/plugins/sinatra_helpers.rb +444 -0
  139. data/roda-3.106.0/lib/roda/plugins/slash_path_empty.rb +26 -0
  140. data/roda-3.106.0/lib/roda/plugins/static.rb +42 -0
  141. data/roda-3.106.0/lib/roda/plugins/static_routing.rb +89 -0
  142. data/roda-3.106.0/lib/roda/plugins/status_303.rb +34 -0
  143. data/roda-3.106.0/lib/roda/plugins/status_handler.rb +86 -0
  144. data/roda-3.106.0/lib/roda/plugins/streaming.rb +170 -0
  145. data/roda-3.106.0/lib/roda/plugins/strip_path_prefix.rb +33 -0
  146. data/roda-3.106.0/lib/roda/plugins/symbol_matchers.rb +188 -0
  147. data/roda-3.106.0/lib/roda/plugins/symbol_status.rb +32 -0
  148. data/roda-3.106.0/lib/roda/plugins/symbol_views.rb +37 -0
  149. data/roda-3.106.0/lib/roda/plugins/timestamp_public.rb +78 -0
  150. data/roda-3.106.0/lib/roda/plugins/type_routing.rb +217 -0
  151. data/roda-3.106.0/lib/roda/plugins/typecast_params.rb +1185 -0
  152. data/roda-3.106.0/lib/roda/plugins/typecast_params_sized_integers.rb +108 -0
  153. data/roda-3.106.0/lib/roda/plugins/unescape_path.rb +41 -0
  154. data/roda-3.106.0/lib/roda/plugins/view_options.rb +203 -0
  155. data/roda-3.106.0/lib/roda/plugins/view_subdir_leading_slash.rb +49 -0
  156. data/roda-3.106.0/lib/roda/plugins.rb +68 -0
  157. data/roda-3.106.0/lib/roda/request.rb +722 -0
  158. data/roda-3.106.0/lib/roda/response.rb +221 -0
  159. data/roda-3.106.0/lib/roda/session_middleware.rb +184 -0
  160. data/roda-3.106.0/lib/roda/version.rb +18 -0
  161. data/roda-3.106.0/lib/roda.rb +741 -0
  162. data/ultra-max-sys.gemspec +12 -0
  163. 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