In Sinatra, what does erb :receipt render, and how do the layout, locals and instance variables reach that template?
answer
- a views folder beside app_file
- Symbol names a file, String is source
- layout.erb wraps it through yield
- self is the app instance
- output is not HTML-escaped
basics
~10 serb :receipt renders views/receipt.erb, wrapped in views/layout.erb when that file exists. The template runs with the request's app instance as self, so it sees instance variables and helpers; the locals option adds local variables.
solid answer
~40 s`erb :receipt` looks for `receipt.erb` in the `views` setting, which defaults to a `views` folder next to `app_file`, and returns the rendered String for the route to return as the body. A Symbol names a template; a String is template source, so `erb 'receipt'` renders the word receipt. If `views/layout.erb` exists it wraps the output at its `<%= yield %>`; a missing default layout is silently skipped, `layout: false` turns it off and `layout: :admin` picks another. The template is evaluated with the app instance as `self`, so `@receipt` and helper methods are visible, while `locals: { title: 'x' }` defines local variables. `<%= %>` output is not HTML-escaped by default, so wrap untrusted text in `escape_html`.
code
ruby · 15 lines# app.rb
require 'sinatra'
helpers do
def money(cents) = format('%.2f', cents / 100.0)
end
get '/receipts/:id' do
@receipt = { id: params['id'], cents: 1999, note: '<b>thanks</b>' }
erb :receipt, locals: { title: "Receipt #{params['id']}" }
end
get '/receipts/:id/line' do
erb :line_item, layout: false # fragment without views/layout.erb
endgo deeper
Recall that erb :name renders views/name.erb, that layout.erb wraps it through yield, and that instance variables from the route are visible in the template.
Explain Symbol versus String arguments, how layouts are applied or skipped, locals versus instance variables, and why output needs escape_html.
Show the production view: template caching per environment, escaping untrusted data, and keeping partials and layouts predictable as views multiply.
Decide whether a service should render HTML at all, or stay a JSON API with templates handled elsewhere, given who maintains the views.
## Rendering methods and where they look Sinatra exposes one method per template engine (`erb`, `haml`, `builder`, `markdown`, `slim` and others), each a thin call to a private `render` method that compiles the template through the **Tilt** gem. For `erb`, Tilt picks an ERB implementation: the Erubi gem or Ruby's own ERB library. `erb :receipt` looks in the **`views`** setting. By default that is `File.join(root, 'views')`, where `root` is the directory of `app_file`, the file that defined the app. So a route in `app.rb` renders `views/receipt.erb`. If the file does not exist, rendering raises `Errno::ENOENT`. The call **returns a String**. Returning it from the route makes it the body; you can also post-process it or pass it to `halt`. ## What the first argument means | Argument | Meaning | |---|---| | `:receipt` | a named template defined with `template :receipt do ... end`, else the file `views/receipt.erb` | | `'receipt'` | template **source**: the output is the literal word `receipt` | | `'<%= Time.now %>'` | template source, compiled and cached like a file | The String form is the classic surprise. `erb 'receipt'` is not a file lookup, and nothing warns you. ## Named templates and a different views folder Small apps sometimes skip files entirely. `template :receipt do '<h1><%= title %></h1>' end` registers a named template in the class, and `layout do '<html><%= yield %></html>' end` does the same for the layout; `erb :receipt` then finds the named template before it looks on disk. To keep files elsewhere, change the setting once, for example `set :views, File.join(__dir__, 'templates')`, or pass `views:` for a single call. Options Sinatra does not recognise are handed on to the template engine. ## Layouts Rendering with a layout happens in this order: 1. Sinatra renders the inner template first. 2. It then renders the layout, by default `:layout`, i.e. `views/layout.erb`, passing the inner output as a block, which the layout emits with `<%= yield %>`. 3. If the **default** layout file is missing, the layout step is silently skipped. 4. `layout: false` skips it on purpose; `layout: :admin` uses `views/admin.erb`, and a layout you name explicitly that does not exist raises `Errno::ENOENT`. A partial rendered from inside a template (`<%= erb :line_item %>`) is not wrapped in the layout again, because Sinatra switches the default layout off while it renders a template. ## What the template can see The template is evaluated with the **request's application instance as `self`**, the same object the route and filters ran on: - instance variables set in filters or the route, such as `@receipt`; - helper methods, and built-ins such as `params`, `request`, `session` and `settings`; - local variables passed with `locals: { title: 'Receipt' }`, or as the third positional argument; - `escape_html`, available because `Sinatra::Base` includes `Rack::Utils`. ```erb <!-- views/layout.erb --> <html><body><%= yield %></body></html> <!-- views/receipt.erb --> <h1><%= title %></h1> <p>Total: <%= money(@receipt[:cents]) %></p> <p><%= escape_html(@receipt[:note]) %></p> ``` `<%= %>` inserts its value as-is, with no HTML escaping, under Sinatra's default ERB setup. Anything that came from a request or a third party goes through `escape_html`. ## Content type and the response Rendering does not touch headers. When a route finishes without setting a content type, Sinatra applies the `default_content_type` setting, `text/html`, which is what an ERB page wants. A route that renders something else says so first, for example `content_type :txt` before `erb :receipt_text`, and the symbol is resolved to a MIME type through Rack's registry. Because `erb` returns a plain String, the same rendered text can also be used as an email body, written to a file, or passed to `halt` with a status of its own, such as `halt 404, erb(:not_found)`. ## Caching The `reload_templates` setting defaults to on in development only. In development Sinatra clears its template cache on every request, so edits show up at once; in other environments each compiled template is cached for the life of the process, and a changed template needs a restart or deploy. ## Common mistakes - Passing a String name and getting the name printed back. - Expecting `locals` to be the only way in, when instance variables already reach the template. - Assuming a missing `layout.erb` raises; only an explicitly named layout does. - Printing untrusted text with `<%= %>` and no `escape_html`. - Editing a template in production and wondering why nothing changed.
- A route returns erb 'receipt' and the page shows only the word receipt. Why?A String argument is template source, not a file name. Sinatra compiles the text 'receipt' as a template, which renders to itself. Use the Symbol `erb :receipt` to load `views/receipt.erb`.
- Why does a partial rendered from inside a template not get wrapped in the layout a second time?While rendering a template Sinatra sets its default layout to false, so any `erb` call made from inside that template renders without a layout unless you pass one explicitly. Only the outermost call from the route gets `views/layout.erb`.
- A template edit shows up at once in development but not in production. Why?`reload_templates` defaults to on only in development, where Sinatra clears its template cache on each request. Elsewhere compiled templates stay cached for the process lifetime, so the change appears after a restart.
A layout is a picture frame: the inner template is the painting, finished first, and <%= yield %> is the opening the frame leaves for it. layout: false hangs the painting without a frame.
saying these in an interview costs you the question
- erb 'receipt' and erb :receipt both load views/receipt.erb.
- Instance variables must be passed through locals to reach a template.
- Sinatra HTML-escapes every <%= %> output by default.
- Without a views/layout.erb file every erb call raises an error.
- Templates are re-read from disk on every request in production.