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,998 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
require "tilt"
|
|
4
|
+
|
|
5
|
+
class Roda
|
|
6
|
+
module RodaPlugins
|
|
7
|
+
# The render plugin adds support for template rendering using the tilt
|
|
8
|
+
# library. Two methods are provided for template rendering, +view+
|
|
9
|
+
# (which uses the layout) and +render+ (which does not).
|
|
10
|
+
#
|
|
11
|
+
# plugin :render
|
|
12
|
+
#
|
|
13
|
+
# route do |r|
|
|
14
|
+
# r.is 'foo' do
|
|
15
|
+
# view('foo') # renders views/foo.erb inside views/layout.erb
|
|
16
|
+
# end
|
|
17
|
+
#
|
|
18
|
+
# r.is 'bar' do
|
|
19
|
+
# render('bar') # renders views/bar.erb
|
|
20
|
+
# end
|
|
21
|
+
# end
|
|
22
|
+
#
|
|
23
|
+
# The +render+ and +view+ methods just return strings, they do not have
|
|
24
|
+
# side effects (unless the templates themselves have side effects).
|
|
25
|
+
# As Roda uses the routing block return value as the body of the response,
|
|
26
|
+
# in most cases you will call these methods as the last expression in a
|
|
27
|
+
# routing block to have the response body be the result of the template
|
|
28
|
+
# rendering.
|
|
29
|
+
#
|
|
30
|
+
# Because +render+ and +view+ just return strings, you can call them inside
|
|
31
|
+
# templates (i.e. for subtemplates/partials), or multiple times in the
|
|
32
|
+
# same route and combine the results together:
|
|
33
|
+
#
|
|
34
|
+
# route do |r|
|
|
35
|
+
# r.is 'foo-bars' do
|
|
36
|
+
# @bars = Bar.where(:foo).map{|b| render(:bar, locals: {bar: b})}.join
|
|
37
|
+
# view('foo')
|
|
38
|
+
# end
|
|
39
|
+
# end
|
|
40
|
+
#
|
|
41
|
+
# You can provide options to the plugin method:
|
|
42
|
+
#
|
|
43
|
+
# plugin :render, engine: 'haml', views: 'admin_views'
|
|
44
|
+
#
|
|
45
|
+
# = Plugin Options
|
|
46
|
+
#
|
|
47
|
+
# The following plugin options are supported:
|
|
48
|
+
#
|
|
49
|
+
# :allowed_paths :: Set the template paths to allow. Attempts to render paths outside
|
|
50
|
+
# of these paths will raise an error. Defaults to the +:views+ directory.
|
|
51
|
+
# :assume_fixed_locals :: Set if you are sure all templates in your application use fixed locals
|
|
52
|
+
# to allow for additional optimization. This is ignored unless both
|
|
53
|
+
# compiled methods and fixed locals are not supported.
|
|
54
|
+
# :cache :: nil/false to explicitly disable permanent template caching. By default, permanent
|
|
55
|
+
# template caching is disabled by default if RACK_ENV is development. When permanent
|
|
56
|
+
# template caching is disabled, for templates with paths in the file system, the
|
|
57
|
+
# modification time of the file will be checked on every render, and if it has changed,
|
|
58
|
+
# a new template will be created for the current content of the file.
|
|
59
|
+
# :cache_class :: A class to use as the template cache instead of the default.
|
|
60
|
+
# :check_paths :: Can be set to false to turn off template path checking.
|
|
61
|
+
# :engine :: The tilt engine to use for rendering, also the default file extension for
|
|
62
|
+
# templates, defaults to 'erb'.
|
|
63
|
+
# :escape :: Use Erubi as the ERB template engine, and enable escaping by default,
|
|
64
|
+
# which makes <tt><%= %></tt> escape output and <tt><%== %></tt> not escape output.
|
|
65
|
+
# If given, sets the <tt>escape: true</tt> option for all template engines, which
|
|
66
|
+
# can break some non-ERB template engines. You can use a string or array of strings
|
|
67
|
+
# as the value for this option to only set the <tt>escape: true</tt> option for those
|
|
68
|
+
# specific template engines.
|
|
69
|
+
# :layout :: The base name of the layout file, defaults to 'layout'. This can be provided as a hash
|
|
70
|
+
# with the :template or :inline options.
|
|
71
|
+
# :layout_opts :: The options to use when rendering the layout, if different from the default options.
|
|
72
|
+
# :template_opts :: The tilt options used when rendering all templates. defaults to:
|
|
73
|
+
# <tt>{outvar: '@_out_buf', default_encoding: Encoding.default_external}</tt>.
|
|
74
|
+
# :engine_opts :: The tilt options to use per template engine. Keys are
|
|
75
|
+
# engine strings, values are hashes of template options.
|
|
76
|
+
# :views :: The directory holding the view files, defaults to the 'views' subdirectory of the
|
|
77
|
+
# application's :root option (the process's working directory by default).
|
|
78
|
+
#
|
|
79
|
+
# = Render/View Method Options
|
|
80
|
+
#
|
|
81
|
+
# Most of these options can be overridden at runtime by passing options
|
|
82
|
+
# to the +view+ or +render+ methods:
|
|
83
|
+
#
|
|
84
|
+
# view('foo', engine: 'html.erb')
|
|
85
|
+
# render('foo', views: 'admin_views')
|
|
86
|
+
#
|
|
87
|
+
# There are additional options to +view+ and +render+ that are
|
|
88
|
+
# available at runtime:
|
|
89
|
+
#
|
|
90
|
+
# :cache :: Set to false to not cache this template, even when
|
|
91
|
+
# caching is on by default. Set to true to force caching for
|
|
92
|
+
# this template, even when the default is to not permantently cache (e.g.
|
|
93
|
+
# when using the :template_block option).
|
|
94
|
+
# :cache_key :: Explicitly set the hash key to use when caching.
|
|
95
|
+
# :content :: Only respected by +view+, provides the content to render
|
|
96
|
+
# inside the layout, instead of rendering a template to get
|
|
97
|
+
# the content.
|
|
98
|
+
# :inline :: Use the value given as the template code, instead of looking
|
|
99
|
+
# for template code in a file.
|
|
100
|
+
# :locals :: Hash of local variables to make available inside the template.
|
|
101
|
+
# :path :: Use the value given as the full pathname for the file, instead
|
|
102
|
+
# of using the :views and :engine option in combination with the
|
|
103
|
+
# template name.
|
|
104
|
+
# :scope :: The object in which context to evaluate the template. By
|
|
105
|
+
# default, this is the Roda instance.
|
|
106
|
+
# :template :: Provides the name of the template to use. This allows you
|
|
107
|
+
# pass a single options hash to the render/view method, while
|
|
108
|
+
# still allowing you to specify the template name.
|
|
109
|
+
# :template_block :: Pass this block when creating the underlying template,
|
|
110
|
+
# ignored when using :inline. Disables caching of the
|
|
111
|
+
# template by default.
|
|
112
|
+
# :template_class :: Provides the template class to use, instead of using
|
|
113
|
+
# Tilt or <tt>Tilt[:engine]</tt>.
|
|
114
|
+
#
|
|
115
|
+
# Here's an example of using these options:
|
|
116
|
+
#
|
|
117
|
+
# view(inline: '<%= @foo %>')
|
|
118
|
+
# render(path: '/path/to/template.erb')
|
|
119
|
+
#
|
|
120
|
+
# If you pass a hash as the first argument to +view+ or +render+, it should
|
|
121
|
+
# have either +:template+, +:inline+, +:path+, or +:content+ (for +view+) as
|
|
122
|
+
# one of the keys.
|
|
123
|
+
#
|
|
124
|
+
# = Fixed Locals in Templates
|
|
125
|
+
#
|
|
126
|
+
# By default, you can pass any local variables to any templates. A separate
|
|
127
|
+
# template method is compiled for each combination of locals. This causes
|
|
128
|
+
# multiple issues:
|
|
129
|
+
#
|
|
130
|
+
# * It is inefficient, especially for large templates that are called with
|
|
131
|
+
# many combinations of locals.
|
|
132
|
+
# * It hides issues if unused local variable names are passed to the template
|
|
133
|
+
# * It does not support default values for local variables
|
|
134
|
+
# * It does not support required local variables
|
|
135
|
+
# * It does not support cases where you want to pass values via a keyword splat
|
|
136
|
+
# * It does not support named blocks
|
|
137
|
+
#
|
|
138
|
+
# If you are using Tilt 2.6+, you can used fixed locals in templates, by
|
|
139
|
+
# passing the appropriate options in :template_opts. For example, if you
|
|
140
|
+
# are using ERB templates, the recommended way to use the render plugin is to
|
|
141
|
+
# use the +:extract_fixed_locals+ and +:default_fixed_locals+ template options:
|
|
142
|
+
#
|
|
143
|
+
# plugin :render, template_opts: {extract_fixed_locals: true, default_fixed_locals: '()'}
|
|
144
|
+
#
|
|
145
|
+
# This will default templates to not allowing any local variables to be passed.
|
|
146
|
+
# If the template requires local variables, you can specify them using a magic
|
|
147
|
+
# comment in the template, such as:
|
|
148
|
+
#
|
|
149
|
+
# <%# locals(required_local:, optional_local: nil) %>
|
|
150
|
+
#
|
|
151
|
+
# The magic comment is used as method parameters when defining the compiled template
|
|
152
|
+
# method.
|
|
153
|
+
#
|
|
154
|
+
# For better debugging of issues with invalid keywords being passed to templates that
|
|
155
|
+
# have not been updated to support fixed locals, it can be helpful to set
|
|
156
|
+
# +:default_fixed_locals+ to use a single optional keyword argument
|
|
157
|
+
# <tt>'(_no_kw: nil)'</tt>. This makes the error message show which keywords
|
|
158
|
+
# were passed, instead of showing that the takes no arguments (if you use <tt>'()'</tt>),
|
|
159
|
+
# or that no keywords are accepted (if you pass <tt>(**nil)</tt>).
|
|
160
|
+
#
|
|
161
|
+
# If you are sure your application works with all templates using fixed locals,
|
|
162
|
+
# set the :assume_fixed_locals render plugin option, which will allow the plugin
|
|
163
|
+
# to optimize cache lookup for renders with locals, and avoid duplicate compiled
|
|
164
|
+
# methods for templates rendered both with and without locals.
|
|
165
|
+
#
|
|
166
|
+
# See Tilt's documentation for more information regarding fixed locals.
|
|
167
|
+
#
|
|
168
|
+
# = Speeding Up Template Rendering
|
|
169
|
+
#
|
|
170
|
+
# The render/view method calls are optimized for usage with a single symbol/string
|
|
171
|
+
# argument specifying the template name. So for fastest rendering, pass only a
|
|
172
|
+
# symbol/string to render/view. Next best optimized are template calls with a
|
|
173
|
+
# single :locals option. Use of other options disables the compiled template
|
|
174
|
+
# method optimizations and can be significantly slower.
|
|
175
|
+
#
|
|
176
|
+
# If you must pass a hash to render/view, either as a second argument or as the
|
|
177
|
+
# only argument, you can speed things up by specifying a +:cache_key+ option in
|
|
178
|
+
# the hash, making sure the +:cache_key+ is unique to the template you are
|
|
179
|
+
# rendering.
|
|
180
|
+
#
|
|
181
|
+
# = Recommended +template_opts+
|
|
182
|
+
#
|
|
183
|
+
# Here are the recommended values of :template_opts for new applications (a couple
|
|
184
|
+
# are Erubi-specific and can be ignored if you are using other templates engines):
|
|
185
|
+
#
|
|
186
|
+
# plugin :render,
|
|
187
|
+
# assume_fixed_locals: true, # Optimize plugin by assuming all templates use fixed locals
|
|
188
|
+
# template_opts: {
|
|
189
|
+
# scope_class: self, # Always uses current class as scope class for compiled templates
|
|
190
|
+
# freeze: true, # Freeze string literals in templates
|
|
191
|
+
# extract_fixed_locals: true, # Support fixed locals in templates
|
|
192
|
+
# default_fixed_locals: '()', # Default to templates not supporting local variables
|
|
193
|
+
# escape: true, # For Erubi templates, escapes <%= by default (use <%== for unescaped
|
|
194
|
+
# chain_appends: true, # For Erubi templates, improves performance
|
|
195
|
+
# skip_compiled_encoding_detection: true, # Unless you need encodings explicitly specified
|
|
196
|
+
# }
|
|
197
|
+
#
|
|
198
|
+
# = Accepting Template Blocks in Methods
|
|
199
|
+
#
|
|
200
|
+
# If you are used to Rails, you may be surprised that this type of template code
|
|
201
|
+
# doesn't work in Roda:
|
|
202
|
+
#
|
|
203
|
+
# <%= some_method do %>
|
|
204
|
+
# Some HTML
|
|
205
|
+
# <% end %>
|
|
206
|
+
#
|
|
207
|
+
# The reason this doesn't work is that this is not valid ERB syntax, it is Rails syntax,
|
|
208
|
+
# and requires attempting to parse the <tt>some_method do</tt> Ruby code with a regular
|
|
209
|
+
# expression. Since Roda uses ERB syntax, it does not support this.
|
|
210
|
+
#
|
|
211
|
+
# In general, these methods are used to wrap the content of the block and
|
|
212
|
+
# inject the content into the output. To get similar behavior with Roda, you have
|
|
213
|
+
# a few different options you can use.
|
|
214
|
+
#
|
|
215
|
+
# == Use Erubi::CaptureBlockEngine
|
|
216
|
+
#
|
|
217
|
+
# Roda defaults to using Erubi for erb template rendering. Erubi 1.13.0+ includes
|
|
218
|
+
# support for an erb variant that supports blocks in <tt><%=</tt> and <tt><%==</tt>
|
|
219
|
+
# tags. To use it:
|
|
220
|
+
#
|
|
221
|
+
# require 'erubi/capture_block'
|
|
222
|
+
# plugin :render, template_opts: {engine_class: Erubi::CaptureBlockEngine}
|
|
223
|
+
#
|
|
224
|
+
# See the Erubi documentation for how to capture data inside the block. Make sure
|
|
225
|
+
# the method call (+some_method+ in the example) returns the output you want added
|
|
226
|
+
# to the rendered body.
|
|
227
|
+
#
|
|
228
|
+
# == Directly Inject Template Output
|
|
229
|
+
#
|
|
230
|
+
# You can switch from a <tt><%=</tt> tag to using a <tt><%</tt> tag:
|
|
231
|
+
#
|
|
232
|
+
# <% some_method do %>
|
|
233
|
+
# Some HTML
|
|
234
|
+
# <% end %>
|
|
235
|
+
#
|
|
236
|
+
# While this would output <tt>Some HTML</tt> into the template, it would not be able
|
|
237
|
+
# to inject content before or after the block. However, you can use the inject_erb_plugin
|
|
238
|
+
# to handle the injection:
|
|
239
|
+
#
|
|
240
|
+
# def some_method
|
|
241
|
+
# inject_erb "content before block"
|
|
242
|
+
# yield
|
|
243
|
+
# inject_erb "content after block"
|
|
244
|
+
# end
|
|
245
|
+
#
|
|
246
|
+
# If you need to modify the captured block before injecting it, you can use the
|
|
247
|
+
# capture_erb plugin to capture content from the template block, and modify that content,
|
|
248
|
+
# then use inject_erb to inject it into the template output:
|
|
249
|
+
#
|
|
250
|
+
# def some_method(&block)
|
|
251
|
+
# inject_erb "content before block"
|
|
252
|
+
# inject_erb capture_erb(&block).upcase
|
|
253
|
+
# inject_erb "content after block"
|
|
254
|
+
# end
|
|
255
|
+
#
|
|
256
|
+
# This is the recommended approach for handling this type of method, if you want to keep
|
|
257
|
+
# the template block in the same template.
|
|
258
|
+
#
|
|
259
|
+
# == Separate Block Output Into Separate Template
|
|
260
|
+
#
|
|
261
|
+
# By moving the <tt>Some HTML</tt> into a separate template, you can render that
|
|
262
|
+
# template inside the block:
|
|
263
|
+
#
|
|
264
|
+
# <%= some_method{render('template_name')} %>
|
|
265
|
+
#
|
|
266
|
+
# It's also possible to use an inline template:
|
|
267
|
+
#
|
|
268
|
+
# <%= some_method do render(:inline=><<-END)
|
|
269
|
+
# Some HTML
|
|
270
|
+
# END
|
|
271
|
+
# end %>
|
|
272
|
+
#
|
|
273
|
+
# This approach is useful if it makes sense to separate the template block into its
|
|
274
|
+
# own template. You lose the ability to use local variable from outside the
|
|
275
|
+
# template block inside the template block with this approach.
|
|
276
|
+
#
|
|
277
|
+
# == Separate Header and Footer
|
|
278
|
+
#
|
|
279
|
+
# You can define two separate methods, one that outputs the content before the block,
|
|
280
|
+
# and one that outputs the content after the block, and use those instead of a single
|
|
281
|
+
# call:
|
|
282
|
+
#
|
|
283
|
+
# <%= some_method_before %>
|
|
284
|
+
# Some HTML
|
|
285
|
+
# <%= some_method_after %>
|
|
286
|
+
#
|
|
287
|
+
# This is the simplest option to setup, but it is fairly tedious to use.
|
|
288
|
+
module Render
|
|
289
|
+
# Support for using compiled methods directly requires Ruby 2.3 for the
|
|
290
|
+
# method binding to work, and Tilt 1.2 for Tilt::Template#compiled_method.
|
|
291
|
+
tilt_compiled_method_support = defined?(Tilt::VERSION) && Tilt::VERSION >= '1.2' &&
|
|
292
|
+
([1, -2].include?(((compiled_method_arity = Tilt::Template.instance_method(:compiled_method).arity) rescue false)))
|
|
293
|
+
NO_CACHE = {:cache=>false}.freeze
|
|
294
|
+
COMPILED_METHOD_SUPPORT = RUBY_VERSION >= '2.3' && tilt_compiled_method_support && ENV['RODA_RENDER_COMPILED_METHOD_SUPPORT'] != 'no'
|
|
295
|
+
FIXED_LOCALS_COMPILED_METHOD_SUPPORT = COMPILED_METHOD_SUPPORT && Tilt::Template.method_defined?(:fixed_locals?)
|
|
296
|
+
|
|
297
|
+
if FIXED_LOCALS_COMPILED_METHOD_SUPPORT
|
|
298
|
+
def self.tilt_template_fixed_locals?(template)
|
|
299
|
+
template.fixed_locals?
|
|
300
|
+
end
|
|
301
|
+
# :nocov:
|
|
302
|
+
else
|
|
303
|
+
def self.tilt_template_fixed_locals?(template)
|
|
304
|
+
false
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
# :nocov:
|
|
308
|
+
|
|
309
|
+
if compiled_method_arity == -2
|
|
310
|
+
def self.tilt_template_compiled_method(template, locals_keys, scope_class)
|
|
311
|
+
template.send(:compiled_method, locals_keys, scope_class)
|
|
312
|
+
end
|
|
313
|
+
# :nocov:
|
|
314
|
+
else
|
|
315
|
+
def self.tilt_template_compiled_method(template, locals_keys, scope_class)
|
|
316
|
+
template.send(:compiled_method, locals_keys)
|
|
317
|
+
end
|
|
318
|
+
# :nocov:
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
# Setup default rendering options. See Render for details.
|
|
322
|
+
def self.configure(app, opts=OPTS)
|
|
323
|
+
if app.opts[:render]
|
|
324
|
+
orig_cache = app.opts[:render][:cache]
|
|
325
|
+
orig_method_cache = app.opts[:render][:template_method_cache]
|
|
326
|
+
opts = app.opts[:render][:orig_opts].merge(opts)
|
|
327
|
+
end
|
|
328
|
+
app.opts[:render] = opts.dup
|
|
329
|
+
app.opts[:render][:orig_opts] = opts
|
|
330
|
+
|
|
331
|
+
opts = app.opts[:render]
|
|
332
|
+
opts[:engine] = (opts[:engine] || "erb").dup.freeze
|
|
333
|
+
opts[:views] = app.expand_path(opts[:views]||"views").freeze
|
|
334
|
+
opts[:allowed_paths] ||= [opts[:views]].freeze
|
|
335
|
+
opts[:allowed_paths] = opts[:allowed_paths].map{|f| app.expand_path(f, nil)}.uniq.freeze
|
|
336
|
+
opts[:check_paths] = true unless opts.has_key?(:check_paths)
|
|
337
|
+
opts[:assume_fixed_locals] &&= FIXED_LOCALS_COMPILED_METHOD_SUPPORT
|
|
338
|
+
|
|
339
|
+
unless opts.has_key?(:check_template_mtime)
|
|
340
|
+
opts[:check_template_mtime] = if opts[:cache] == false || opts[:explicit_cache]
|
|
341
|
+
true
|
|
342
|
+
else
|
|
343
|
+
ENV['RACK_ENV'] == 'development'
|
|
344
|
+
end
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
begin
|
|
348
|
+
app.const_get(:RodaCompiledTemplates, false)
|
|
349
|
+
rescue NameError
|
|
350
|
+
compiled_templates_module = Module.new
|
|
351
|
+
app.send(:include, compiled_templates_module)
|
|
352
|
+
app.const_set(:RodaCompiledTemplates, compiled_templates_module)
|
|
353
|
+
end
|
|
354
|
+
opts[:template_method_cache] = orig_method_cache || (opts[:cache_class] || RodaCache).new
|
|
355
|
+
opts[:template_method_cache][:_roda_layout] = nil if opts[:template_method_cache][:_roda_layout]
|
|
356
|
+
opts[:cache] = orig_cache || (opts[:cache_class] || RodaCache).new
|
|
357
|
+
|
|
358
|
+
opts[:layout_opts] = (opts[:layout_opts] || {}).dup
|
|
359
|
+
opts[:layout_opts][:_is_layout] = true
|
|
360
|
+
if opts[:layout_opts][:views]
|
|
361
|
+
opts[:layout_opts][:views] = app.expand_path(opts[:layout_opts][:views]).freeze
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
if layout = opts.fetch(:layout, true)
|
|
365
|
+
opts[:layout] = true
|
|
366
|
+
|
|
367
|
+
case layout
|
|
368
|
+
when Hash
|
|
369
|
+
opts[:layout_opts].merge!(layout)
|
|
370
|
+
when true
|
|
371
|
+
opts[:layout_opts][:template] ||= 'layout'
|
|
372
|
+
else
|
|
373
|
+
opts[:layout_opts][:template] = layout
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
opts[:optimize_layout] = (opts[:layout_opts][:template] if opts[:layout_opts].keys.sort == [:_is_layout, :template])
|
|
377
|
+
end
|
|
378
|
+
opts[:layout_opts].freeze
|
|
379
|
+
|
|
380
|
+
template_opts = opts[:template_opts] = (opts[:template_opts] || {}).dup
|
|
381
|
+
template_opts[:outvar] ||= '@_out_buf'
|
|
382
|
+
unless template_opts.has_key?(:default_encoding)
|
|
383
|
+
template_opts[:default_encoding] = Encoding.default_external
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
engine_opts = opts[:engine_opts] = (opts[:engine_opts] || {}).dup
|
|
387
|
+
engine_opts.to_a.each do |k,v|
|
|
388
|
+
engine_opts[k] = v.dup.freeze
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
if escape = opts[:escape]
|
|
392
|
+
require 'tilt/erubi'
|
|
393
|
+
|
|
394
|
+
case escape
|
|
395
|
+
when String, Array
|
|
396
|
+
Array(escape).each do |engine|
|
|
397
|
+
engine_opts[engine] = (engine_opts[engine] || {}).merge(:escape => true).freeze
|
|
398
|
+
end
|
|
399
|
+
else
|
|
400
|
+
template_opts[:escape] = true
|
|
401
|
+
end
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
template_opts.freeze
|
|
405
|
+
engine_opts.freeze
|
|
406
|
+
opts.freeze
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
# Wrapper object for the Tilt template, that checks the modified
|
|
410
|
+
# time of the template file, and rebuilds the template if the
|
|
411
|
+
# template file has been modified. This is an internal class and
|
|
412
|
+
# the API is subject to change at any time.
|
|
413
|
+
class TemplateMtimeWrapper
|
|
414
|
+
def initialize(roda_class, opts, template_opts)
|
|
415
|
+
@roda_class = roda_class
|
|
416
|
+
@opts = opts
|
|
417
|
+
@template_opts = template_opts
|
|
418
|
+
reset_template
|
|
419
|
+
|
|
420
|
+
@path = opts[:path]
|
|
421
|
+
deps = opts[:dependencies]
|
|
422
|
+
@dependencies = ([@path] + Array(deps)) if deps
|
|
423
|
+
@mtime = template_last_modified
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
# If the template file exists and the modification time has
|
|
427
|
+
# changed, rebuild the template file, then call render on it.
|
|
428
|
+
def render(*args, &block)
|
|
429
|
+
res = nil
|
|
430
|
+
modified = false
|
|
431
|
+
if_modified do
|
|
432
|
+
res = @template.render(*args, &block)
|
|
433
|
+
modified = true
|
|
434
|
+
end
|
|
435
|
+
modified ? res : @template.render(*args, &block)
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
# Return when the template was last modified. If the template depends on any
|
|
439
|
+
# other files, check the modification times of all dependencies and
|
|
440
|
+
# return the maximum.
|
|
441
|
+
def template_last_modified
|
|
442
|
+
if deps = @dependencies
|
|
443
|
+
deps.map{|f| File.mtime(f)}.max
|
|
444
|
+
else
|
|
445
|
+
File.mtime(@path)
|
|
446
|
+
end
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
# If the template file has been updated, return true and update
|
|
450
|
+
# the template object and the modification time. Other return false.
|
|
451
|
+
def if_modified
|
|
452
|
+
begin
|
|
453
|
+
mtime = template_last_modified
|
|
454
|
+
rescue
|
|
455
|
+
# ignore errors
|
|
456
|
+
else
|
|
457
|
+
if mtime != @mtime
|
|
458
|
+
reset_template
|
|
459
|
+
yield
|
|
460
|
+
@mtime = mtime
|
|
461
|
+
end
|
|
462
|
+
end
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
if COMPILED_METHOD_SUPPORT
|
|
466
|
+
# Whether the underlying template uses fixed locals.
|
|
467
|
+
def fixed_locals?
|
|
468
|
+
Render.tilt_template_fixed_locals?(@template)
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
# Compile a method in the given module with the given name that will
|
|
472
|
+
# call the compiled template method, updating the compiled template method
|
|
473
|
+
def define_compiled_method(roda_class, method_name, locals_keys=EMPTY_ARRAY)
|
|
474
|
+
mod = roda_class::RodaCompiledTemplates
|
|
475
|
+
internal_method_name = :"_#{method_name}"
|
|
476
|
+
begin
|
|
477
|
+
mod.send(:define_method, internal_method_name, compiled_method(locals_keys, roda_class))
|
|
478
|
+
rescue ::NotImplementedError
|
|
479
|
+
return false
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
mod.send(:private, internal_method_name)
|
|
483
|
+
mod.send(:define_method, method_name, &compiled_method_lambda(roda_class, internal_method_name, locals_keys))
|
|
484
|
+
mod.send(:private, method_name)
|
|
485
|
+
|
|
486
|
+
method_name
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
# Returns an appropriate value for the template method cache.
|
|
490
|
+
def define_compiled_method_cache_value(roda_class, method_name, locals_keys=EMPTY_ARRAY)
|
|
491
|
+
if compiled_method = define_compiled_method(roda_class, method_name, locals_keys)
|
|
492
|
+
[compiled_method, false].freeze
|
|
493
|
+
else
|
|
494
|
+
compiled_method
|
|
495
|
+
end
|
|
496
|
+
end
|
|
497
|
+
|
|
498
|
+
private
|
|
499
|
+
|
|
500
|
+
# Return the compiled method for the current template object.
|
|
501
|
+
def compiled_method(locals_keys=EMPTY_ARRAY, roda_class=nil)
|
|
502
|
+
Render.tilt_template_compiled_method(@template, locals_keys, roda_class)
|
|
503
|
+
end
|
|
504
|
+
|
|
505
|
+
# Return the lambda used to define the compiled template method. This
|
|
506
|
+
# is separated into its own method so the lambda does not capture any
|
|
507
|
+
# unnecessary local variables
|
|
508
|
+
def compiled_method_lambda(roda_class, method_name, locals_keys=EMPTY_ARRAY)
|
|
509
|
+
mod = roda_class::RodaCompiledTemplates
|
|
510
|
+
template = self
|
|
511
|
+
lambda do |locals, &block|
|
|
512
|
+
template.if_modified do
|
|
513
|
+
mod.send(:define_method, method_name, Render.tilt_template_compiled_method(template, locals_keys, roda_class))
|
|
514
|
+
mod.send(:private, method_name)
|
|
515
|
+
end
|
|
516
|
+
|
|
517
|
+
_call_optimized_template_method([method_name, Render.tilt_template_fixed_locals?(template)], locals, &block)
|
|
518
|
+
end
|
|
519
|
+
end
|
|
520
|
+
end
|
|
521
|
+
|
|
522
|
+
private
|
|
523
|
+
|
|
524
|
+
# Reset the template, done every time the template or one of its
|
|
525
|
+
# dependencies is modified.
|
|
526
|
+
def reset_template
|
|
527
|
+
@template = @roda_class.create_template(@opts, @template_opts)
|
|
528
|
+
end
|
|
529
|
+
end
|
|
530
|
+
|
|
531
|
+
module ClassMethods
|
|
532
|
+
if COMPILED_METHOD_SUPPORT
|
|
533
|
+
# If using compiled methods and there is an optimized layout, speed up
|
|
534
|
+
# access to the layout method to improve the performance of view.
|
|
535
|
+
def freeze
|
|
536
|
+
begin
|
|
537
|
+
_freeze_layout_method
|
|
538
|
+
rescue
|
|
539
|
+
# This is only for optimization, if any errors occur, they can be ignored.
|
|
540
|
+
# One possibility for error is the app doesn't use a layout, but doesn't
|
|
541
|
+
# specifically set the :layout=>false plugin option.
|
|
542
|
+
nil
|
|
543
|
+
end
|
|
544
|
+
|
|
545
|
+
# Optimize _call_optimized_template_method if you know all templates
|
|
546
|
+
# are going to be using fixed locals.
|
|
547
|
+
if render_opts[:assume_fixed_locals] && !render_opts[:check_template_mtime]
|
|
548
|
+
include AssumeFixedLocalsInstanceMethods
|
|
549
|
+
end
|
|
550
|
+
|
|
551
|
+
super
|
|
552
|
+
end
|
|
553
|
+
end
|
|
554
|
+
|
|
555
|
+
# Return an Tilt::Template object based on the given opts and template_opts.
|
|
556
|
+
def create_template(opts, template_opts)
|
|
557
|
+
opts[:template_class].new(opts[:path], 1, template_opts, &opts[:template_block])
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
# A proc that returns content, used for inline templates, so that the template
|
|
561
|
+
# doesn't hold a reference to the instance of the class
|
|
562
|
+
def inline_template_block(content)
|
|
563
|
+
Proc.new{content}
|
|
564
|
+
end
|
|
565
|
+
|
|
566
|
+
# Copy the rendering options into the subclass, duping
|
|
567
|
+
# them as necessary to prevent changes in the subclass
|
|
568
|
+
# affecting the parent class.
|
|
569
|
+
def inherited(subclass)
|
|
570
|
+
super
|
|
571
|
+
opts = subclass.opts[:render] = subclass.opts[:render].dup
|
|
572
|
+
if COMPILED_METHOD_SUPPORT
|
|
573
|
+
opts[:template_method_cache] = (opts[:cache_class] || RodaCache).new
|
|
574
|
+
end
|
|
575
|
+
opts[:cache] = opts[:cache].dup
|
|
576
|
+
opts.freeze
|
|
577
|
+
end
|
|
578
|
+
|
|
579
|
+
# Return the render options for this class.
|
|
580
|
+
def render_opts
|
|
581
|
+
opts[:render]
|
|
582
|
+
end
|
|
583
|
+
|
|
584
|
+
private
|
|
585
|
+
|
|
586
|
+
# Precompile the layout method, to reduce method calls to look it up at runtime.
|
|
587
|
+
def _freeze_layout_method
|
|
588
|
+
if render_opts[:layout]
|
|
589
|
+
instance = allocate
|
|
590
|
+
# This needs to be called even if COMPILED_METHOD_SUPPORT is not set,
|
|
591
|
+
# in order for the precompile_templates plugin to work correctly.
|
|
592
|
+
instance.send(:retrieve_template, instance.send(:view_layout_opts, OPTS))
|
|
593
|
+
|
|
594
|
+
if COMPILED_METHOD_SUPPORT && (layout_template = render_opts[:optimize_layout]) && !opts[:render][:optimized_layout_method_created]
|
|
595
|
+
instance.send(:retrieve_template, :template=>layout_template, :cache_key=>nil, :template_method_cache_key => :_roda_layout)
|
|
596
|
+
layout_method = opts[:render][:template_method_cache][:_roda_layout]
|
|
597
|
+
define_method(:_layout_method){layout_method}
|
|
598
|
+
private :_layout_method
|
|
599
|
+
alias_method(:_layout_method, :_layout_method)
|
|
600
|
+
opts[:render] = opts[:render].merge(:optimized_layout_method_created=>true)
|
|
601
|
+
end
|
|
602
|
+
end
|
|
603
|
+
end
|
|
604
|
+
end
|
|
605
|
+
|
|
606
|
+
module InstanceMethods
|
|
607
|
+
# Render the given template. See Render for details.
|
|
608
|
+
def render(template, opts = (no_opts = true; optimized_template = _cached_template_method(template); OPTS), &block)
|
|
609
|
+
if optimized_template
|
|
610
|
+
_call_optimized_template_method(optimized_template, OPTS, &block)
|
|
611
|
+
elsif !no_opts && opts.length == 1 && (locals = opts[:locals]) && (optimized_template = _optimized_render_method_for_locals(template, locals))
|
|
612
|
+
_call_optimized_template_method(optimized_template, locals, &block)
|
|
613
|
+
else
|
|
614
|
+
opts = render_template_opts(template, opts)
|
|
615
|
+
retrieve_template(opts).render((opts[:scope]||self), (opts[:locals]||OPTS), &block)
|
|
616
|
+
end
|
|
617
|
+
end
|
|
618
|
+
|
|
619
|
+
# Return the render options for the instance's class.
|
|
620
|
+
def render_opts
|
|
621
|
+
self.class.render_opts
|
|
622
|
+
end
|
|
623
|
+
|
|
624
|
+
# Render the given template. If there is a default layout
|
|
625
|
+
# for the class, take the result of the template rendering
|
|
626
|
+
# and render it inside the layout. Blocks passed to view
|
|
627
|
+
# are passed to render when rendering the template.
|
|
628
|
+
# See Render for details.
|
|
629
|
+
def view(template, opts = (content = _optimized_view_content(template) unless defined?(yield); OPTS), &block)
|
|
630
|
+
if content
|
|
631
|
+
# First, check if the optimized layout method has already been created,
|
|
632
|
+
# and use it if so. This way avoids the extra conditional and local variable
|
|
633
|
+
# assignments in the next section.
|
|
634
|
+
if layout_method = _layout_method
|
|
635
|
+
return _call_optimized_template_method(layout_method, OPTS){content}
|
|
636
|
+
end
|
|
637
|
+
|
|
638
|
+
# If we have an optimized template method but no optimized layout method, create the
|
|
639
|
+
# optimized layout method if possible and use it. If you can't create the optimized
|
|
640
|
+
# layout method, fall through to the slower approach.
|
|
641
|
+
if layout_template = self.class.opts[:render][:optimize_layout]
|
|
642
|
+
retrieve_template(:template=>layout_template, :cache_key=>nil, :template_method_cache_key => :_roda_layout)
|
|
643
|
+
if layout_method = _layout_method
|
|
644
|
+
return _call_optimized_template_method(layout_method, OPTS){content}
|
|
645
|
+
end
|
|
646
|
+
end
|
|
647
|
+
else
|
|
648
|
+
opts = parse_template_opts(template, opts)
|
|
649
|
+
content = opts[:content] || render_template(opts, &block)
|
|
650
|
+
end
|
|
651
|
+
|
|
652
|
+
if layout_opts = view_layout_opts(opts)
|
|
653
|
+
content = render_template(layout_opts){content}
|
|
654
|
+
end
|
|
655
|
+
|
|
656
|
+
content
|
|
657
|
+
end
|
|
658
|
+
|
|
659
|
+
private
|
|
660
|
+
|
|
661
|
+
if COMPILED_METHOD_SUPPORT
|
|
662
|
+
# If there is an instance method for the template, return the instance
|
|
663
|
+
# method symbol. This optimization is only used for render/view calls
|
|
664
|
+
# with a single string or symbol argument.
|
|
665
|
+
def _cached_template_method(template)
|
|
666
|
+
case template
|
|
667
|
+
when String, Symbol
|
|
668
|
+
if (method_cache = render_opts[:template_method_cache])
|
|
669
|
+
_cached_template_method_lookup(method_cache, template)
|
|
670
|
+
end
|
|
671
|
+
end
|
|
672
|
+
end
|
|
673
|
+
|
|
674
|
+
# The key to use in the template method cache for the given template.
|
|
675
|
+
def _cached_template_method_key(template)
|
|
676
|
+
template
|
|
677
|
+
end
|
|
678
|
+
|
|
679
|
+
# Return the instance method symbol for the template in the method cache.
|
|
680
|
+
def _cached_template_method_lookup(method_cache, template)
|
|
681
|
+
method_cache[template]
|
|
682
|
+
end
|
|
683
|
+
|
|
684
|
+
# Return a symbol containing the optimized layout method
|
|
685
|
+
def _layout_method
|
|
686
|
+
self.class.opts[:render][:template_method_cache][:_roda_layout]
|
|
687
|
+
end
|
|
688
|
+
|
|
689
|
+
# Use an optimized render path for templates with a hash of locals. Returns the result
|
|
690
|
+
# of the template render if the optimized path is used, or nil if the optimized
|
|
691
|
+
# path is not used and the long method needs to be used.
|
|
692
|
+
def _optimized_render_method_for_locals(template, locals)
|
|
693
|
+
render_opts = self.render_opts
|
|
694
|
+
return unless method_cache = render_opts[:template_method_cache]
|
|
695
|
+
|
|
696
|
+
case template
|
|
697
|
+
when String, Symbol
|
|
698
|
+
if fixed_locals = render_opts[:assume_fixed_locals]
|
|
699
|
+
key = template
|
|
700
|
+
if optimized_template = _cached_template_method_lookup(method_cache, key)
|
|
701
|
+
return optimized_template
|
|
702
|
+
end
|
|
703
|
+
else
|
|
704
|
+
key = [:_render_locals, template]
|
|
705
|
+
if optimized_template = _cached_template_method_lookup(method_cache, key)
|
|
706
|
+
# Fixed locals case
|
|
707
|
+
return optimized_template
|
|
708
|
+
end
|
|
709
|
+
|
|
710
|
+
locals_keys = locals.keys.sort
|
|
711
|
+
key << locals_keys
|
|
712
|
+
if optimized_template = _cached_template_method_lookup(method_cache, key)
|
|
713
|
+
# Regular locals case
|
|
714
|
+
return optimized_template
|
|
715
|
+
end
|
|
716
|
+
end
|
|
717
|
+
else
|
|
718
|
+
return
|
|
719
|
+
end
|
|
720
|
+
|
|
721
|
+
if method_cache_key = _cached_template_method_key(key)
|
|
722
|
+
template_obj = retrieve_template(render_template_opts(template, NO_CACHE))
|
|
723
|
+
unless fixed_locals
|
|
724
|
+
key.pop if fixed_locals = Render.tilt_template_fixed_locals?(template_obj)
|
|
725
|
+
key.freeze
|
|
726
|
+
end
|
|
727
|
+
method_name = :"_roda_template_locals_#{self.class.object_id}_#{method_cache_key}"
|
|
728
|
+
|
|
729
|
+
method_cache[method_cache_key] = case template_obj
|
|
730
|
+
when Render::TemplateMtimeWrapper
|
|
731
|
+
template_obj.define_compiled_method_cache_value(self.class, method_name, locals_keys)
|
|
732
|
+
else
|
|
733
|
+
begin
|
|
734
|
+
unbound_method = Render.tilt_template_compiled_method(template_obj, locals_keys, self.class)
|
|
735
|
+
rescue ::NotImplementedError
|
|
736
|
+
false
|
|
737
|
+
else
|
|
738
|
+
self.class::RodaCompiledTemplates.send(:define_method, method_name, unbound_method)
|
|
739
|
+
self.class::RodaCompiledTemplates.send(:private, method_name)
|
|
740
|
+
[method_name, fixed_locals].freeze
|
|
741
|
+
end
|
|
742
|
+
end
|
|
743
|
+
end
|
|
744
|
+
end
|
|
745
|
+
|
|
746
|
+
# Get the content for #view, or return nil to use the unoptimized approach. Only called if
|
|
747
|
+
# a single argument is passed to view.
|
|
748
|
+
def _optimized_view_content(template)
|
|
749
|
+
if optimized_template = _cached_template_method(template)
|
|
750
|
+
_call_optimized_template_method(optimized_template, OPTS)
|
|
751
|
+
elsif template.is_a?(Hash) && template.length == 1
|
|
752
|
+
template[:content]
|
|
753
|
+
end
|
|
754
|
+
end
|
|
755
|
+
|
|
756
|
+
if RUBY_VERSION >= '3'
|
|
757
|
+
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
|
758
|
+
def _call_optimized_template_method((meth, fixed_locals), locals, &block)
|
|
759
|
+
if fixed_locals
|
|
760
|
+
send(meth, **locals, &block)
|
|
761
|
+
else
|
|
762
|
+
send(meth, locals, &block)
|
|
763
|
+
end
|
|
764
|
+
end
|
|
765
|
+
RUBY
|
|
766
|
+
# :nocov:
|
|
767
|
+
elsif RUBY_VERSION >= '2'
|
|
768
|
+
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
|
769
|
+
def _call_optimized_template_method((meth, fixed_locals), locals, &block)
|
|
770
|
+
if fixed_locals
|
|
771
|
+
if locals.empty?
|
|
772
|
+
send(meth, &block)
|
|
773
|
+
else
|
|
774
|
+
send(meth, **locals, &block)
|
|
775
|
+
end
|
|
776
|
+
else
|
|
777
|
+
send(meth, locals, &block)
|
|
778
|
+
end
|
|
779
|
+
end
|
|
780
|
+
RUBY
|
|
781
|
+
else
|
|
782
|
+
# Call the optimized template method. This is designed to be used with the
|
|
783
|
+
# method cache, which caches the method name and whether the method uses
|
|
784
|
+
# fixed locals. Methods with fixed locals need to be called with a keyword
|
|
785
|
+
# splat.
|
|
786
|
+
def _call_optimized_template_method((meth, fixed_locals), locals, &block)
|
|
787
|
+
send(meth, locals, &block)
|
|
788
|
+
end
|
|
789
|
+
end
|
|
790
|
+
# :nocov:
|
|
791
|
+
else
|
|
792
|
+
def _cached_template_method(_)
|
|
793
|
+
nil
|
|
794
|
+
end
|
|
795
|
+
|
|
796
|
+
def _cached_template_method_key(_)
|
|
797
|
+
nil
|
|
798
|
+
end
|
|
799
|
+
|
|
800
|
+
def _optimized_render_method_for_locals(_, _)
|
|
801
|
+
nil
|
|
802
|
+
end
|
|
803
|
+
|
|
804
|
+
def _optimized_view_content(template)
|
|
805
|
+
nil
|
|
806
|
+
end
|
|
807
|
+
end
|
|
808
|
+
|
|
809
|
+
# Convert template options to single hash when rendering templates using render.
|
|
810
|
+
def render_template_opts(template, opts)
|
|
811
|
+
parse_template_opts(template, opts)
|
|
812
|
+
end
|
|
813
|
+
|
|
814
|
+
# Private alias for render. Should be used by other plugins when they want to render a template
|
|
815
|
+
# without a layout, as plugins can override render to use a layout.
|
|
816
|
+
alias render_template render
|
|
817
|
+
|
|
818
|
+
# If caching templates, attempt to retrieve the template from the cache. Otherwise, just yield
|
|
819
|
+
# to get the template.
|
|
820
|
+
def cached_template(opts, &block)
|
|
821
|
+
if key = opts[:cache_key]
|
|
822
|
+
cache = render_opts[:cache]
|
|
823
|
+
unless template = cache[key]
|
|
824
|
+
template = cache[key] = yield
|
|
825
|
+
end
|
|
826
|
+
template
|
|
827
|
+
else
|
|
828
|
+
yield
|
|
829
|
+
end
|
|
830
|
+
end
|
|
831
|
+
|
|
832
|
+
# Given the template name and options, set the template class, template path/content,
|
|
833
|
+
# template block, and locals to use for the render in the passed options.
|
|
834
|
+
def find_template(opts)
|
|
835
|
+
render_opts = self.class.opts[:render]
|
|
836
|
+
engine_override = opts[:engine]
|
|
837
|
+
engine = opts[:engine] ||= render_opts[:engine]
|
|
838
|
+
if content = opts[:inline]
|
|
839
|
+
path = opts[:path] = content
|
|
840
|
+
template_class = opts[:template_class] ||= ::Tilt[engine]
|
|
841
|
+
opts[:template_block] = self.class.inline_template_block(content)
|
|
842
|
+
else
|
|
843
|
+
opts[:views] ||= render_opts[:views]
|
|
844
|
+
path = opts[:path] ||= template_path(opts)
|
|
845
|
+
template_class = opts[:template_class]
|
|
846
|
+
opts[:template_class] ||= ::Tilt
|
|
847
|
+
end
|
|
848
|
+
|
|
849
|
+
if (cache = opts[:cache]).nil?
|
|
850
|
+
cache = content || !opts[:template_block]
|
|
851
|
+
end
|
|
852
|
+
|
|
853
|
+
if cache
|
|
854
|
+
unless opts.has_key?(:cache_key)
|
|
855
|
+
template_block = opts[:template_block] unless content
|
|
856
|
+
template_opts = opts[:template_opts]
|
|
857
|
+
|
|
858
|
+
opts[:cache_key] = if template_class || engine_override || template_opts || template_block
|
|
859
|
+
[path, template_class, engine_override, template_opts, template_block]
|
|
860
|
+
else
|
|
861
|
+
path
|
|
862
|
+
end
|
|
863
|
+
end
|
|
864
|
+
else
|
|
865
|
+
opts.delete(:cache_key)
|
|
866
|
+
end
|
|
867
|
+
|
|
868
|
+
opts
|
|
869
|
+
end
|
|
870
|
+
|
|
871
|
+
# Return a single hash combining the template and opts arguments.
|
|
872
|
+
def parse_template_opts(template, opts)
|
|
873
|
+
opts = Hash[opts]
|
|
874
|
+
if template.is_a?(Hash)
|
|
875
|
+
opts.merge!(template)
|
|
876
|
+
else
|
|
877
|
+
if opts.empty? && (key = _cached_template_method_key(template))
|
|
878
|
+
opts[:template_method_cache_key] = key
|
|
879
|
+
end
|
|
880
|
+
opts[:template] = template
|
|
881
|
+
opts
|
|
882
|
+
end
|
|
883
|
+
end
|
|
884
|
+
|
|
885
|
+
# The default render options to use. These set defaults that can be overridden by
|
|
886
|
+
# providing a :layout_opts option to the view/render method.
|
|
887
|
+
def render_layout_opts
|
|
888
|
+
Hash[render_opts[:layout_opts]]
|
|
889
|
+
end
|
|
890
|
+
|
|
891
|
+
# Retrieve the Tilt::Template object for the given template and opts.
|
|
892
|
+
def retrieve_template(opts)
|
|
893
|
+
cache = opts[:cache]
|
|
894
|
+
if !opts[:cache_key] || cache == false
|
|
895
|
+
found_template_opts = opts = find_template(opts)
|
|
896
|
+
end
|
|
897
|
+
cached_template(opts) do
|
|
898
|
+
opts = found_template_opts || find_template(opts)
|
|
899
|
+
render_opts = self.class.opts[:render]
|
|
900
|
+
template_opts = render_opts[:template_opts]
|
|
901
|
+
if engine_opts = render_opts[:engine_opts][opts[:engine]]
|
|
902
|
+
template_opts = template_opts.merge(engine_opts)
|
|
903
|
+
end
|
|
904
|
+
if current_template_opts = opts[:template_opts]
|
|
905
|
+
template_opts = template_opts.merge(current_template_opts)
|
|
906
|
+
end
|
|
907
|
+
|
|
908
|
+
define_compiled_method = COMPILED_METHOD_SUPPORT &&
|
|
909
|
+
(method_cache_key = opts[:template_method_cache_key]) &&
|
|
910
|
+
(method_cache = render_opts[:template_method_cache]) &&
|
|
911
|
+
(method_cache[method_cache_key] != false) &&
|
|
912
|
+
!opts[:inline]
|
|
913
|
+
|
|
914
|
+
if render_opts[:check_template_mtime] && !opts[:template_block] && !cache
|
|
915
|
+
template = TemplateMtimeWrapper.new(self.class, opts, template_opts)
|
|
916
|
+
|
|
917
|
+
if define_compiled_method
|
|
918
|
+
method_name = :"_roda_template_#{self.class.object_id}_#{method_cache_key}"
|
|
919
|
+
method_cache[method_cache_key] = template.define_compiled_method_cache_value(self.class, method_name)
|
|
920
|
+
end
|
|
921
|
+
else
|
|
922
|
+
template = self.class.create_template(opts, template_opts)
|
|
923
|
+
|
|
924
|
+
if define_compiled_method && cache != false
|
|
925
|
+
begin
|
|
926
|
+
unbound_method = Render.tilt_template_compiled_method(template, EMPTY_ARRAY, self.class)
|
|
927
|
+
rescue ::NotImplementedError
|
|
928
|
+
method_cache[method_cache_key] = false
|
|
929
|
+
else
|
|
930
|
+
method_name = :"_roda_template_#{self.class.object_id}_#{method_cache_key}"
|
|
931
|
+
self.class::RodaCompiledTemplates.send(:define_method, method_name, unbound_method)
|
|
932
|
+
self.class::RodaCompiledTemplates.send(:private, method_name)
|
|
933
|
+
method_cache[method_cache_key] = [method_name, Render.tilt_template_fixed_locals?(template)].freeze
|
|
934
|
+
end
|
|
935
|
+
end
|
|
936
|
+
end
|
|
937
|
+
|
|
938
|
+
template
|
|
939
|
+
end
|
|
940
|
+
end
|
|
941
|
+
|
|
942
|
+
# The name to use for the template. By default, just converts the :template option to a string.
|
|
943
|
+
def template_name(opts)
|
|
944
|
+
opts[:template].to_s
|
|
945
|
+
end
|
|
946
|
+
|
|
947
|
+
# The template path for the given options.
|
|
948
|
+
def template_path(opts)
|
|
949
|
+
path = "#{opts[:views]}/#{template_name(opts)}.#{opts[:engine]}"
|
|
950
|
+
if opts.fetch(:check_paths){render_opts[:check_paths]}
|
|
951
|
+
full_path = self.class.expand_path(path)
|
|
952
|
+
unless render_opts[:allowed_paths].any?{|f| full_path.start_with?(f)}
|
|
953
|
+
raise RodaError, "attempt to render path not in allowed_paths: #{full_path} (allowed: #{render_opts[:allowed_paths].join(', ')})"
|
|
954
|
+
end
|
|
955
|
+
end
|
|
956
|
+
path
|
|
957
|
+
end
|
|
958
|
+
|
|
959
|
+
# If a layout should be used, return a hash of options for
|
|
960
|
+
# rendering the layout template. If a layout should not be
|
|
961
|
+
# used, return nil.
|
|
962
|
+
def view_layout_opts(opts)
|
|
963
|
+
if layout = opts.fetch(:layout, render_opts[:layout])
|
|
964
|
+
layout_opts = render_layout_opts
|
|
965
|
+
|
|
966
|
+
method_layout_opts = opts[:layout_opts]
|
|
967
|
+
layout_opts.merge!(method_layout_opts) if method_layout_opts
|
|
968
|
+
|
|
969
|
+
case layout
|
|
970
|
+
when Hash
|
|
971
|
+
layout_opts.merge!(layout)
|
|
972
|
+
when true
|
|
973
|
+
# use default layout
|
|
974
|
+
else
|
|
975
|
+
layout_opts[:template] = layout
|
|
976
|
+
end
|
|
977
|
+
|
|
978
|
+
layout_opts
|
|
979
|
+
end
|
|
980
|
+
end
|
|
981
|
+
end
|
|
982
|
+
|
|
983
|
+
module AssumeFixedLocalsInstanceMethods
|
|
984
|
+
# :nocov:
|
|
985
|
+
if RUBY_VERSION >= '3.0'
|
|
986
|
+
# :nocov:
|
|
987
|
+
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
|
988
|
+
def _call_optimized_template_method((meth,_), locals, &block)
|
|
989
|
+
send(meth, **locals, &block)
|
|
990
|
+
end
|
|
991
|
+
RUBY
|
|
992
|
+
end
|
|
993
|
+
end
|
|
994
|
+
end
|
|
995
|
+
|
|
996
|
+
register_plugin(:render, Render)
|
|
997
|
+
end
|
|
998
|
+
end
|