The render plugin adds support for template rendering using the tilt library. Two methods are provided for template rendering, view
(which uses the layout) and render
(which does not).
plugin :render route do |r| r.is 'foo' do view('foo') # renders views/foo.erb inside views/layout.erb end r.is 'bar' do render('bar') # renders views/bar.erb end end
The render
and view
methods just return strings, they do not have side effects (unless the templates themselves have side effects). As Roda
uses the routing block return value as the body of the response, in most cases you will call these methods as the last expression in a routing block to have the response body be the result of the template rendering.
Because render
and view
just return strings, you can call them inside templates (i.e. for subtemplates/partials), or multiple times in the same route and combine the results together:
route do |r| r.is 'foo-bars' do @bars = Bar.where(:foo).map{|b| render(:bar, locals: {bar: b})}.join view('foo') end end
You can provide options to the plugin method:
plugin :render, engine: 'haml', views: 'admin_views'
Plugin Options¶ ↑
The following plugin options are supported:
:allowed_paths |
Set the template paths to allow. Attempts to render paths outside of these paths will raise an error. Defaults to the |
:cache |
nil/false to explicitly disable premanent template caching. By default, permanent template caching is disabled by default if RACK_ENV is development. When permanent template caching is disabled, for templates with paths in the file system, the modification time of the file will be checked on every render, and if it has changed, a new template will be created for the current content of the file. |
:cache_class |
A class to use as the template cache instead of the default. |
:check_paths |
Can be set to false to turn off template path checking. |
:engine |
The tilt engine to use for rendering, also the default file extension for templates, defaults to ‘erb’. |
:escape |
Use Erubi as the ERB template engine, and enable escaping by default, which makes |
:layout |
The base name of the layout file, defaults to ‘layout’. This can be provided as a hash with the :template or :inline options. |
:layout_opts |
The options to use when rendering the layout, if different from the default options. |
:template_opts |
The tilt options used when rendering all templates. defaults to: |
:engine_opts |
The tilt options to use per template engine. Keys are engine strings, values are hashes of template options. |
:views |
The directory holding the view files, defaults to the ‘views’ subdirectory of the application’s :root option (the process’s working directory by default). |
Render/View Method Options¶ ↑
Most of these options can be overridden at runtime by passing options to the view
or render
methods:
view('foo', engine: 'html.erb') render('foo', views: 'admin_views')
There are additional options to view
and render
that are available at runtime:
:cache |
Set to false to not cache this template, even when caching is on by default. Set to true to force caching for this template, even when the default is to not permantently cache (e.g. when using the :template_block option). |
:cache_key |
Explicitly set the hash key to use when caching. |
:content |
Only respected by |
:inline |
Use the value given as the template code, instead of looking for template code in a file. |
:locals |
Hash of local variables to make available inside the template. |
:path |
Use the value given as the full pathname for the file, instead of using the :views and :engine option in combination with the template name. |
:scope |
The object in which context to evaluate the template. By default, this is the |
:template |
Provides the name of the template to use. This allows you pass a single options hash to the render/view method, while still allowing you to specify the template name. |
:template_block |
|
:template_class |
Provides the template class to use, instead of using Tilt or |
Here’s an example of using these options:
view(inline: '<%= @foo %>') render(path: '/path/to/template.erb')
If you pass a hash as the first argument to view
or render
, it should have either :template
, :inline
, :path
, or :content
(for view
) as one of the keys.
Speeding Up Template Rendering¶ ↑
The render/view method calls are optimized for usage with a single symbol/string argument specifying the template name. So for fastest rendering, pass only a symbol/string to render/view. Next best optimized are template calls with a single :locals option. Use of other options disables the compiled template method optimizations and can be significantly slower.
If you must pass a hash to render/view, either as a second argument or as the only argument, you can speed things up by specifying a :cache_key
option in the hash, making sure the :cache_key
is unique to the template you are rendering.
Accepting Template Blocks in Methods¶ ↑
If you are used to Rails, you may be surprised that this type of template code doesn’t work in Roda:
<%= some_method do %> Some HTML <% end %>
The reason this doesn’t work is that this is not valid ERB syntax, it is Rails syntax, and requires attempting to parse the some_method do
Ruby code with a regular expression. Since Roda
uses ERB syntax, it does not support this.
In general, these methods are used to wrap the content of the block and inject the content into the output. To get similar behavior with Roda
, you have a few different options you can use.
Directly Inject Template Output¶ ↑
You can switch from a <%=
tag to using a <%
tag:
<% some_method do %> Some HTML <% end %>
While this would output Some HTML
into the template, it would not be able to inject content before or after the block. However, you can use the inject_erb_plugin to handle the injection:
def some_method inject_erb "content before block" yield inject_erb "content after block" end
If you need to modify the captured block before injecting it, you can use the capture_erb plugin to capture content from the template block, and modify that content, then use inject_erb to inject it into the template output:
def some_method(&block) inject_erb "content before block" inject_erb capture_erb(&block).upcase inject_erb "content after block" end
This is the recommended approach for handling this type of method, if you want to keep the template block in the same template.
Separate Block Output Into Separate Template¶ ↑
By moving the Some HTML
into a separate template, you can render that template inside the block:
<%= some_method{render('template_name')} %>
It’s also possible to use an inline template:
<%= some_method do render(:inline=><<-END) Some HTML END end %>
This approach is useful if it makes sense to separate the template block into its own template. You lose the ability to use local variable from outside the template block inside the template block with this approach.
Separate Header and Footer¶ ↑
You can define two separate methods, one that outputs the content before the block, and one that outputs the content after the block, and use those instead of a single call:
<%= some_method_before %> Some HTML <%= some_method_after %>
This is the simplest option to setup, but it is fairly tedious to use.
Classes and Modules
Constants
COMPILED_METHOD_SUPPORT | = | RUBY_VERSION >= '2.3' && tilt_compiled_method_support && ENV['RODA_RENDER_COMPILED_METHOD_SUPPORT'] != 'no' | ||
NO_CACHE | = | {:cache=>false}.freeze |
Public Class methods
Setup default rendering options. See Render
for details.
# File lib/roda/plugins/render.rb 232 def self.configure(app, opts=OPTS) 233 if app.opts[:render] 234 orig_cache = app.opts[:render][:cache] 235 orig_method_cache = app.opts[:render][:template_method_cache] 236 opts = app.opts[:render][:orig_opts].merge(opts) 237 end 238 app.opts[:render] = opts.dup 239 app.opts[:render][:orig_opts] = opts 240 241 opts = app.opts[:render] 242 opts[:engine] = (opts[:engine] || "erb").dup.freeze 243 opts[:views] = app.expand_path(opts[:views]||"views").freeze 244 opts[:allowed_paths] ||= [opts[:views]].freeze 245 opts[:allowed_paths] = opts[:allowed_paths].map{|f| app.expand_path(f, nil)}.uniq.freeze 246 opts[:check_paths] = true unless opts.has_key?(:check_paths) 247 248 unless opts.has_key?(:check_template_mtime) 249 opts[:check_template_mtime] = if opts[:cache] == false || opts[:explicit_cache] 250 true 251 else 252 ENV['RACK_ENV'] == 'development' 253 end 254 end 255 256 begin 257 app.const_get(:RodaCompiledTemplates, false) 258 rescue NameError 259 compiled_templates_module = Module.new 260 app.send(:include, compiled_templates_module) 261 app.const_set(:RodaCompiledTemplates, compiled_templates_module) 262 end 263 opts[:template_method_cache] = orig_method_cache || (opts[:cache_class] || RodaCache).new 264 opts[:template_method_cache][:_roda_layout] = nil if opts[:template_method_cache][:_roda_layout] 265 opts[:cache] = orig_cache || (opts[:cache_class] || RodaCache).new 266 267 opts[:layout_opts] = (opts[:layout_opts] || {}).dup 268 opts[:layout_opts][:_is_layout] = true 269 if opts[:layout_opts][:views] 270 opts[:layout_opts][:views] = app.expand_path(opts[:layout_opts][:views]).freeze 271 end 272 273 if layout = opts.fetch(:layout, true) 274 opts[:layout] = true 275 276 case layout 277 when Hash 278 opts[:layout_opts].merge!(layout) 279 when true 280 opts[:layout_opts][:template] ||= 'layout' 281 else 282 opts[:layout_opts][:template] = layout 283 end 284 285 opts[:optimize_layout] = (opts[:layout_opts][:template] if opts[:layout_opts].keys.sort == [:_is_layout, :template]) 286 end 287 opts[:layout_opts].freeze 288 289 template_opts = opts[:template_opts] = (opts[:template_opts] || {}).dup 290 template_opts[:outvar] ||= '@_out_buf' 291 unless template_opts.has_key?(:default_encoding) 292 template_opts[:default_encoding] = Encoding.default_external 293 end 294 295 engine_opts = opts[:engine_opts] = (opts[:engine_opts] || {}).dup 296 engine_opts.to_a.each do |k,v| 297 engine_opts[k] = v.dup.freeze 298 end 299 300 if escape = opts[:escape] 301 require 'tilt/erubi' 302 303 case escape 304 when String, Array 305 Array(escape).each do |engine| 306 engine_opts[engine] = (engine_opts[engine] || {}).merge(:escape => true).freeze 307 end 308 else 309 template_opts[:escape] = true 310 end 311 end 312 313 template_opts.freeze 314 engine_opts.freeze 315 opts.freeze 316 end
# File lib/roda/plugins/render.rb 220 def self.tilt_template_compiled_method(template, locals_keys, scope_class) 221 template.send(:compiled_method, locals_keys, scope_class) 222 end