tutorial-layout
Basics Learn about Layouts Renderer fun More about Dynasnips Cleaning up
Layouts
Since you almost certainly want your site to look good, one of the first things you’ll want to change in your vanilla site is the layout.
When the browser requests a snip, normally vanilla will present it within a layout template. This would typically include a header, a footer, and any other peripheral markup that shouldn’t be within the content of the snip itself. If you’re familiar with the construction of web applications, this will be exactly as you expect.
Layouts are just like any other snip - they can be sent through a renderer, and include other snips. The default layout snip is called, predictable, layout.snip
, and here’s the content:
<!DOCTYPE html>
<html lang="en">
<head>
{head}
</head>
<body>
<nav>
{navigation}
</nav>
<article class="h-entry">
<header>
<span class="p-name">{title-for}</span>
<p>{snip-details}</p>
</header>
<section class="e-content">
{current_snip}
</section>
</article>
<footer>
{footer}
</footer>
</body>
</html>
When you request /start
, this is the snip that’s actually rendered first. If this snip was just text, that’s all that would be returned; however, there are some dynasnip calls in here which help us actually return the content that the user requested.
current_snip
The most significant is the call to current_snip
. This figures out what snip was actually requested (e.g. if the url is /start
, it’s the start snip), and renders it in place.
Here’s the source of current_snip
:
class CurrentSnip < Vanilla::Dynasnip
usage %|
The current_snip dyna normally returns the result of rendering the snip named by the
'snip' value in the parameters. This way, it can be used in templates to place the currently
requested snip, in its rendered form, within the page.
It can also be used to determine an attribute of the current snip in a consistent way:
{current_snip name}
will output the name of the current snip.
|
def handle(attribute=nil)
return usage if app.request.snip_name == snip_name
if attribute ||= app.request.part
%|{"#{app.request.snip_name}"."#{attribute}"}|
else
%|{"#{app.request.snip_name}"}|
end
end
self
end
The default case just renders a string corresponding to snip inclusion of whichever snip was actually requested. In the case of A lot of my work at the moment involves chasing down bugs in an incredibly complicated publishing system which has multiple triggers (both internal and external) that make films available to watch on a variety of platforms at specific times in specific countries. When bugs creep in, films either stay up or don’t go up, and sometimes that annoys the wrong people. This week was one of those weeks, but involving a bug that was introduced about two years ago, and then fixed 8 months later, so calculating the true impact of the issue is pretty difficult. This is definitely why I got into software development. Does this site now run on Ruby 3.1? I hope it does. I’ve been slowly working through a bunch of my other small applications, trying to update them to modern versions of Ruby (and even Rails), so that at the very least they remain deployable. This site is one of those small applications./start
, this will generate the string “
Most recently
Weeknotes 2041
James Adam
Work
Not work
Previously
Other dynas
Of course, you can put other plain content in your layout, and other dynasnips too. In the provided layout there are calls to three other dynasnips.
The first is page_title
, which simply places a (hopefully) meaningful string in the title element of the page. Snips can set the title to be used by defining a :page_title
attribute. As usual, the source explains it more clearly:
class PageTitle < Vanilla::Dynasnip
def handle
if app.request.snip
app.request.snip.page_title || app.request.snip.title || app.request.snip.name
else
"Not found"
end
end
self
end
The second is link_to
, which is used to create links to other snips by name; although of course you can still just write HTML, this is often more convenient and wiki-esque. Here’s the source:
class LinkTo < Vanilla::Dynasnip
usage %|
The link_to dyna lets you create links between snips:
{link_to blah}
would insert a link to the blah snip.|
def handle(name=nil, link_text=name, part=nil)
return usage if requesting_this_snip?
return "You must provide a snip name" unless name
if app.soup[name]
%{<a href="#{url_to(name, part)}">#{link_text}</a>}
else
%{<a class="missing" href="#{url_to(name, part)}">#{link_text}</a>}
end
end
self
end
The third dynasnip used is link_to_current_snip
, which returns an HTML link to the snip that’s currently being rendered. I’ll let you figure out how to view the source yourself.
Other layouts
Vanilla looks for a snip called layout
by default, but this can be changed by setting the default_layout
configuration option in your application, e.g.
class Application < Vanilla::App; end
Application.configure do |config|
# other stuff..
config.default_layout = "my_layout"
end
You can also override the layout on a per-snip basis, simply by setting the :layout
attribute of the snip to the name of the layout snip to use instead.
Finally, if you implement a custom renderer class (see the renderers tutorial), you can also specify a layout to be used when the requested snip invokes that renderer. This can be useful if you have a particular kind of content that requires a different layout entirely.
Basics Learn about Layouts Renderer fun More about Dynasnips Cleaning up