Class: Stipa::ViewContext
- Inherits:
-
Object
- Object
- Stipa::ViewContext
- Defined in:
- lib/stipa/template.rb
Overview
The context object that becomes ‘self` inside every template. Includes helpers for rendering, HTML escaping, and Vue integration.
Instance Method Summary collapse
-
#_binding ⇒ Object
Internal: expose binding for ERB.
-
#_set_content(html) ⇒ Object
Internal: store the inner-page HTML for the layout’s ‘yield`.
-
#_set_local(name, value) ⇒ Object
Internal: inject a local variable as a method on this context.
-
#content ⇒ Object
(also: #yield_content)
Called inside a layout template to render the inner page content.
-
#content_for(section = nil, &block) ⇒ Object
Named content blocks — store content from a page, render in the layout.
-
#h(value) ⇒ Object
(also: #escape_html)
HTML-escape a value.
-
#initialize(engine) ⇒ ViewContext
constructor
A new instance of ViewContext.
-
#javascript_tag(*srcs, type: nil, **attrs) ⇒ Object
Include one or more JavaScript files.
-
#render(partial, locals: {}) ⇒ Object
Render a partial from within a template.
-
#render_template(name, locals: {}) ⇒ Object
Render a template (same engine, layout: false by default).
-
#stipa_vue_bootstrap(src: '/stipa-vue.js') ⇒ Object
Include the Stīpa Vue bootstrapper.
-
#stylesheet_tag(*hrefs, **attrs) ⇒ Object
Include one or more stylesheets.
-
#vue_component(name, props: {}, tag: 'div', **html_attrs) ⇒ Object
Emit a Vue 3 component mount point.
-
#vue_script(src: nil, version: '3', dev: false) ⇒ Object
Include Vue 3 from a CDN (or a local path you serve).
Constructor Details
#initialize(engine) ⇒ ViewContext
Returns a new instance of ViewContext.
83 84 85 86 |
# File 'lib/stipa/template.rb', line 83 def initialize(engine) @engine = engine @_blocks = {} end |
Instance Method Details
#_binding ⇒ Object
Internal: expose binding for ERB.
194 195 196 |
# File 'lib/stipa/template.rb', line 194 def _binding binding end |
#_set_content(html) ⇒ Object
Internal: store the inner-page HTML for the layout’s ‘yield`.
189 190 191 |
# File 'lib/stipa/template.rb', line 189 def _set_content(html) @_layout_content = html end |
#_set_local(name, value) ⇒ Object
Internal: inject a local variable as a method on this context.
184 185 186 |
# File 'lib/stipa/template.rb', line 184 def _set_local(name, value) define_singleton_method(name) { value } end |
#content ⇒ Object Also known as: yield_content
Called inside a layout template to render the inner page content.
Use one of these in your layout:
<body><%= content %></body>
<body><%= yield_content %></body>
(Plain ERB ‘yield` is a Ruby keyword and cannot be used here.)
205 206 207 |
# File 'lib/stipa/template.rb', line 205 def content @_layout_content end |
#content_for(section = nil, &block) ⇒ Object
Named content blocks — store content from a page, render in the layout.
Page: <% content_for :title do %>Home<% end %> Layout: <title><%= content_for(:title) %></title>
214 215 216 217 |
# File 'lib/stipa/template.rb', line 214 def content_for(section = nil, &block) return @_blocks[section] if section && !block @_blocks[section] = block.call if section && block end |
#h(value) ⇒ Object Also known as: escape_html
HTML-escape a value. Use this in attributes or when interpolating untrusted user input inside text content.
<div class="<%= escape_html(user.css_class) %>">
Or use the alias:
<div class="<%= h(user.css_class) %>">
106 107 108 |
# File 'lib/stipa/template.rb', line 106 def h(value) ERB::Util.html_escape(value.to_s) end |
#javascript_tag(*srcs, type: nil, **attrs) ⇒ Object
Include one or more JavaScript files.
<%= javascript_tag '/app.js' %>
<%= javascript_tag '/a.js', '/b.js', type: 'module' %>
165 166 167 168 169 |
# File 'lib/stipa/template.rb', line 165 def javascript_tag(*srcs, type: nil, **attrs) extra = attrs.map { |k, v| %( #{k}="#{h(v)}") }.join type_attr = type ? %( type="#{h(type)}") : '' srcs.map { |src| %(<script src="#{h(src)}"#{type_attr}#{extra}></script>) }.join("\n") end |
#render(partial, locals: {}) ⇒ Object
Render a partial from within a template. Partials follow the Rails convention of a leading underscore on disk, but you refer to them without it.
<%= render 'sidebar' %> → views/_sidebar.html.erb
<%= render 'users/row', locals: { u: user } %> → views/users/_row.html.erb
225 226 227 228 229 230 231 232 |
# File 'lib/stipa/template.rb', line 225 def render(partial, locals: {}) name = if partial.include?('/') partial.sub(%r{([^/]+)\z}, '_\1') else "_#{partial}" end @engine.render(name, locals: locals, layout: false) end |
#render_template(name, locals: {}) ⇒ Object
Render a template (same engine, layout: false by default). Useful for partials; pass layout: :default if you want to wrap it.
94 95 96 |
# File 'lib/stipa/template.rb', line 94 def render_template(name, locals: {}) @engine.render(name, locals: locals, layout: false) end |
#stipa_vue_bootstrap(src: '/stipa-vue.js') ⇒ Object
Include the Stīpa Vue bootstrapper. This script auto-discovers vue_component mount points and mounts them. Must appear AFTER vue_script and AFTER any component registrations.
<%= stipa_vue_bootstrap %>
158 159 160 |
# File 'lib/stipa/template.rb', line 158 def stipa_vue_bootstrap(src: '/stipa-vue.js') %(<script type="module" src="#{src}"></script>) end |
#stylesheet_tag(*hrefs, **attrs) ⇒ Object
Include one or more stylesheets.
<%= stylesheet_tag '/app.css' %>
<%= stylesheet_tag '/reset.css', '/app.css' %>
174 175 176 177 |
# File 'lib/stipa/template.rb', line 174 def stylesheet_tag(*hrefs, **attrs) extra = attrs.map { |k, v| %( #{k}="#{h(v)}") }.join hrefs.map { |href| %(<link rel="stylesheet" href="#{h(href)}"#{extra}>) }.join("\n") end |
#vue_component(name, props: {}, tag: 'div', **html_attrs) ⇒ Object
Emit a Vue 3 component mount point.
The Stīpa Vue bootstrapper (stipa-vue.js) picks up all elements with data-vue-component and mounts the corresponding registered component, passing data-props as the component’s initial props.
ERB:
<%= vue_component("Counter", props: { initial: 5 }) %>
<%= vue_component("SearchBox", props: { q: params[:q] }, class: "mt-4") %>
Rendered HTML:
<div data-vue-component="Counter" data-props="{"initial":5}"></div>
Options:
props: Hash passed as JSON to the component (default {})
tag: HTML wrapper element (default 'div')
Any other keyword args become HTML attributes on the wrapper element.
132 133 134 135 136 137 |
# File 'lib/stipa/template.rb', line 132 def vue_component(name, props: {}, tag: 'div', **html_attrs) attr_parts = html_attrs.map { |k, v| %(#{k}="#{h(v)}") } attr_str = attr_parts.empty? ? '' : " #{attr_parts.join(' ')}" props_json = h(props.to_json) %(<#{tag} data-vue-component="#{h(name)}" data-props="#{props_json}"#{attr_str}></#{tag}>) end |
#vue_script(src: nil, version: '3', dev: false) ⇒ Object
Include Vue 3 from a CDN (or a local path you serve).
<%= vue_script %> → unpkg, production build
<%= vue_script(version: '3.4.21') %> → pin a specific version
<%= vue_script(dev: true) %> → development build (warnings)
<%= vue_script(src: '/vendor/vue.js') %> → self-hosted
145 146 147 148 149 150 151 |
# File 'lib/stipa/template.rb', line 145 def vue_script(src: nil, version: '3', dev: false) unless src build = dev ? 'vue.global.js' : 'vue.global.prod.js' src = "https://unpkg.com/vue@#{version}/dist/#{build}" end %(<script src="#{src}"></script>) end |