Roda – Part 2
In a previous episode, guest chef Federico Iachetti showed us how to get up and running with the lightweight Roda web toolkit. Today, he’s going to demonstrate how to work with HTML views in this deceptively powerful little library. Enjoy!
Video transcript & code
In the previous episode, we wrote a Roda application that captures emails from the user. We ended up with this code.
EMAILS = [
'fede@example.com',
'avdi@example.com'
]
class App < Roda
route do |r|
r.on "emails" do
r.get do
r.root do
[
"<ul>",
EMAILS.map {|email| " <li>#{email}</li>"},
"</ul>",
].join("\n")
end
r.is "form" do
<<~EOF
<form action="/emails" method="post">
<label for="email">Email</label>
<input name="email" type="text" value=""/>
</form>
EOF
end
end
r.post do
EMAILS << r.params["email"]
r.redirect "/emails/"
end
end
end
end
Which works fine. But we barely scratched the surface of what Roda can provide.
So far, we've been creating HTML strings by hand. But this is far from ideal.
In order to help us avoid this (and many other) annoyances, Roda ships with an extensive plugins library. Including plugins for working with HTML views!
Lets get rid of the ugly inline HTML strings. To do so, we add the render
plugin to the app. The default templating language it uses is ERB, but it supports many different template engines through the Tilt gem. Say we want to use the slim
syntax.
class App < Roda
plugin :render, engine: "slim"
# ...
end
Now, we can replace the inline HTML with a call to the view
method. Lets give this view the emails/list
name, and pass the emails as local variables (just the way we do with Rails).
# ...
r.root do
view "emails/list", locals: {emails: EMAILS}
end
# ...
When we try to load the page, we see that there's an error: the template file is missing. And the error shows us the file we need to create.
GET "/emails/"
# => "STATUS: 500 Internal Server Error"
error
# => "Errno::ENOENT: No such file or directory @ rb_sysopen - /home/fedex/Dropbox/rubytapas-guest/apricot_cocktail_ii_with_federico_iachetti/examples/episode2/email_capture1/views/emails/list.slim"
Lets provide it
views/emails/list.slim
ul
- emails.each do |email|
li= email
Now when we reload, we get a second error.
This is telling us Roda looked for a layout file, but couldn't find it.
GET "/emails/"
# => "STATUS: 500 Internal Server Error"
error
# => "Errno::ENOENT: No such file or directory @ rb_sysopen - /home/fedex/Dropbox/rubytapas-guest/apricot_cocktail_ii_with_federico_iachetti/examples/episode2/email_capture1/views/layout.slim"
We create it, and we add yield
where we want our page-specific content to be added.
views/layout.slim
html
head
body
.pageWrapper
.content
== yield
We also change our /emails/form
route to use a view.
And put our template in place.
And now when we reload again, they work.
Since we have 2 pages we can access in our app, some navigation would be nice.
We can add it to the layout template but, for organization purposes we want to create a partial for it.
To do this, we use the partials
plugin.
class App < Roda
plugin :render, engine: "slim"
plugin :partials
# ...
end
And, with that in place, we can use the partial
method to add our partial to the layout passing the partial name as an argument.
We'll pass the current path as a local variable in order to \"highlight\" the current page.
views/layout.slim
html
head
body
== partial "nav", locals: { current_path: request.path }
.pageWrapper
.content
== yield
And now we create the template for the partial. The convention for partials is the same Rails uses: adding a leading underscore to the partial file name.
In the template we add a nav
element with a nested unordered list. Then we add a list item for each path.
If the current element on the iteration matches the current path, we add the .currentPath
class. Then a link to our current path. We also add current in parens for now.
views/~nav~.slim
nav.navbar
ul
- %w[/emails/ /emails/form].each do |path|
li[class=(:currentPath if path == current_path)]
a[href=path]
= path
= " (current)" if path == current_path
And now we can see that everything works. We can navigate to both our views using the navigation bar.
But it looks pretty ugly. Lets add some style to the mix. For this we can use the assets
plugin to include a stylesheet.
We'll give it a list of stylesheet files to be included under the key :css
. We could also add JavaScript files this way, if we wanted to.
We also need to add the r.assets
route to make these asset files available to clients.
class App < Roda
plugin :render, engine: "slim"
plugin :partials
plugin :assets, css: %w[main.scss]
route do |r|
r.assets
# ...
end
end
Then, we add the assets
method in our layout, specifying the asset type :css
in order for the plugin to add the appropriate link
tags.
html
head
== assets(:css)
body
== partial "nav", locals: { current_path: request.path }
.pageWrapper
.content
== yield
And now we can add our style and script files to the assets/css
directory.
$text-color: #282828;
$background-color: #eeeeee;
body {
color: $text-color;
background-color: $background-color;
margin: 0px;
font-size: large;
}
a {
color: $text-color;
text-decoration: none;
&:visited, &:hover {
color: $text-color;
text-decoration: none;
}
&:hover {
font-weight: bold;
}
}
.pageWrapper {
display: flex;
justify-content: center;
}
.content {
width: 80%;
padding: 50px;
background-color: lighten($background-color, 40%);
border-radius: 10px;
/* offset-x | offset-y | blur-radius | color */
box-shadow: 3px 3px 5px 1px rgba(0, 0, 0, 0.3);
}
nav.navbar {
top: 0;
left: 0;
width: 100%;
height: 50px;
margin-bottom: 25px;
ul {
& * {
color: white;
}
$nav-color: #787878;
display: flex;
height: 100%;
padding: 0;
background-color: $nav-color;
justify-content: space-around;
li {
display: flex;
justify-content: center;
align-items: center;
list-style: none;
width: 100%;
&.currentPath {
border:5px solid $nav-color;
background-color: darken($nav-color, 8%);
font-weight: bold;
}
a {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
}
}
}
Now, when we go to http://localhost:9292/emails we see our new style applied to both pages of our site.
Roda ships with a lot more plugins worth taking a look at that will make your life easier. To learn more, you can visit Roda's site.
Happy hacking!
Responses