Ruby
Article

Volt: Assets, Components, and Routes

By Dhaivat Pandya

volt

Volt is an awesome new Ruby web framework that aims to reduce the barrier between the client and server (a bit like Meteor, but in Ruby). Last time around, we uncovered some of the basics of Volt. You can check out the code right here. Here, we’ll build on the bookmarking app and go through asset management, components and routing, the Volt way. Even if you haven’t read the last article in its entirety, it should be easy to catch on.

Let’s start off with the simple bookmarking page we built last time with Volt. Here’s what it looks like right now:

zZJrlIG

That doesn’t really appeal to my eyes and it’s a bit hard to use. To make it look a bit better, we should be able to link in some CSS.

Assets

Volt’s asset management is a bit primitive and the terse-at-best documentation page on the topic notes that “asset management is still early, and likely will change quite a bit.” Fortunately, we can still do the basic stuff we need to, e.g. add some CSS. Before we get to that, let’s change the markup so styling the page is a bit easier. Open up config/base/index.html and change it to the following:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8" />
    <% javascript_files.each do |javascript_file| %>
      <script src="<%= javascript_file %>"></script>
    <% end %>

    <% css_files.each do |css_file| %>
      <link href="<%= css_file %>" media="all" rel="stylesheet" type="text/css" />
    <% end %>

    <link href='http://fonts.googleapis.com/css?family=Oswald' rel='stylesheet' type='text/css'>
    </head>
    <body>

    </body>
</html>

Really, the only thing we’ve added is a Google Web Font with this line:

<link href='http://fonts.googleapis.com/css?family=Oswald' rel='stylesheet' type='text/css'>

That’ll just let us use the font from our CSS to make the page look a little bit better. We need to make a few more changes in app/main/views/main/bookmarks.html in order to get a hold of the page elements using CSS selectors:

<:Title>
  Bookmarks

<:Body>
    <form e-submit="add_bookmark" role="form">
        <div class="form-group">
            <label class="muted">New Bookmark</label>

          <div class="form-element">
            <span class="input_label">Description</span><input class="form-control" type='text' value="{{ page._new_bookmark._description }}" />
          </div>

          <div class="form-element">
            <span class="input_label">URL</span><input class="form-control" type="text" value="{{ page._new_bookmark._url  }}" />
            <input type="submit" value="Add Bookmark" />
          </div>
        </div>
    </form>

  <div id="bookmarks">
    <h1 id="bookmark_header">Bookmarks</h1>

      {{ _bookmarks.each do |bookmark| }}
          <a class="bookmark" href="{{ bookmark._url }}">{{ bookmark._description }}</a>
      {{ end }}
    </ul>
  </div>

A quick refresher about the meat of this code might be helpful. There are really only two parts that are special. First of all, we have the form input:

<input class="form-control" type="text" value="{{ page._new_bookmark._url  }}" />

Here, we’re defining a binding page._new_bookmark._url so that we can access that value in the controller and expect it to hold the value of the text field. The other important part is the bookmark display:

{{ _bookmarks.each do |bookmark| }}
      <a class="bookmark" href="{{ bookmark._url }}">{{ bookmark._description }}</a>
  {{ end }}

If you’ve used Rails before, this type of code shouldn’t come as a surprise. Basically, we’re looping over the bookmarks and then creating links for each of them. However, Volt makes this special, because we’re actually creating bindings for bookmark._url and bookmark._description which are from the bookmark object constructed from the database. Alright, so we have the basic display working, but it looks pretty bad. Let’s spruce it up with some CSS.

Asset management in Volt is still in its early stages but if you come from Rails, you’ll feel at home with it. Assets are divded up by components. Right now, we only have one component and it’s called “main”. Volt links in your assets for you. What exactly does it link in? As the documentation notes: “Anything placed inside of a components asset/js or assets/css folder is served at /assets/{js,css} (via Sprockets).” Volt, like Rails, also uses Sprockets in to pre-compile and manage assets.

We’ll do a simple dual-color (with a few shades) scheme for the bookmarks page. Let’s first get some colors down as SCSS variables so we don’t have to keep repeating them. Create the file app/main/assets/variables.css.scss and slam down the colors:

$background-color: #EFECCA;
$muted-color: #A7A37E;
$accent-color: #046380;
$muted-accent: #CDE0E5;
$text-field: #FAF8E4;

We have some CSS directives that can be applied across the entire component. Stuff those inside app/main/assets/css/app.css.scss:

@import 'variables';

body {
    background-color: $background-color;
}

.header {
    border-bottom: 5px solid $accent-color;
}

.header h3 {
    font-family: Oswald;
    color: $accent-color;
}

.header li a, .header li:hover a {
    background-color: $muted-color;
    border-radius: 0px;
    color: #fff;
    font-weight: bold;
    font-size: 12px;
    letter-spacing: 1px;
    text-transform: uppercase;
}

.header li:hover a {
    background-color: $accent-color;
}

.muted {
    color: $muted-color;
}

The CSS itself is pretty simple, as it is just setting colors and fonts. What is important, however, is the fact that we can @import other SCSS files. If you’re from Rails-land, you might be expecting the app.css.scss to be converted into app.css and hold all of your CSS in one place. However, Volt doesn’t have asset bundling yet, so sit tight for that to come along.

We have some of bookmarks-specific CSS, which can go in app/main/assets/css/bookmarks.css.scss:

@import 'variables';

.form-group .muted {
    font-size: 14px;
    text-transform: uppercase;
    font-weight: normal;
    letter-spacing: 1px;
}

.form-group input[type="text"] {
    border: 1px solid $muted-color;
    border-radius: 0px;
    box-shadow: none;
    margin-bottom: 15px;
    background-color: $text-field;
}

.form-group input[type="submit"] {
    background-color: $accent-color;
    border: none;
    color: $muted-accent;
    font-size: 14px;
    padding: 15px;
    text-transform: uppercase;
    font-weight: bold;
    letter-spacing: 1px;

}

.form-group .input_label {
    background-color: $accent-color;
    color: #fff;
    padding: 5px;
}

.form-element {
    margin-top: 5px;
}

#bookmarks {
    margin-top: 55px;
}

#bookmark_header {
    text-align: center;
    font-weight: bold;
    text-transform: uppercase;
    font-size: 20px;
    color: $accent-color;
    margin-bottom: 35px;
}

#bookmarks ul li {
    list-style-type: none;
}

.bookmark {
    background-color: $accent-color;
    color: #fff;
    padding: 15px;
    margin-top: 3px;
    display: inline-block;
}

.bookmark:hover {
    color: #fff;
    text-decoration: none;
}

Here’s what our page should now look like:
Wbs1jKz

Remember that just naming the file bookmarks.css.scss doesn’t actually do anything. Volt would have included it even if we named it “treefrogs.css.scss”. Volt, frankly, doesn’t give two hoots about the name of the file. But, keeping your CSS somewhat separated might help in case you want to separate a part of your code into a different component. Another way to think about it is: If you have to really try to keep your CSS from getting all tangled, you should probably split into another component. By the way, what exactly is a component?

Components

Components aren’t just what you expect in Volt. First, they function as a way to divide up your application into little pieces. But, Volt also loads all of the files for a particular component as soon as you load up the first page within a component. The point of doing this is that if there is a component of your website called sales, we can say with reasonable confidence that once a user accesses a route within the sales component, that user is pretty likely to go to another route within the same component. So, the documentation advises us to think about components as “reload boundaries” within your app.

We can generate a component easily, as Volt gives us with a simple generator that will setup the directory structure and a few files.

volt generate component bookmarks

The output of the command gives you an overview of what we mean when we say “Volt component”:

create  app/bookmarks
  create  app/bookmarks/assets/css
  create  app/bookmarks/assets/js
  create  app/bookmarks/config/dependencies.rb
  create  app/bookmarks/config/routes.rb
  create  app/bookmarks/controllers/main_controller.rb
  create  app/bookmarks/models
  create  app/bookmarks/tasks
  create  app/bookmarks/views/index/index.html

Basically, the controllers, models, views, and assets are all separated by component. Since what the bookmarks app has right now is so simple, there isn’t much point moving it into a separate component. We’ll just stick with the main component (i.e. you can safely delete app/bookmarks).

We can separate our application by these “components” and each component has some routes associated with it. Although we’ve seen the routes in action before, we haven’t really understood how they work in Volt.

Routes

Routes are the lifeblood of most web frameworks because they tell us where we are in an application at any given point in time. But, because Volt does all this data syncing over Web sockets, the URL works in a somewhat “live” fashion. Say we load a page with the route “/people/dhaivat” where “dhaivat” is the name of one of the “people”. If, in the controller we end up changing the model value associated with “dhaivat”, then the URL will change on the client! Fundamentally, the URL doesn’t control the application’s state; the URL is just a way to refer to a specific state.

What can we actually do with routes then? We know that we have to set them inside app//config/routes.rb. The simplest of routes is something like this:

get '/bookmarks', _action: 'bookmarks'

This just maps the “/bookmarks” route to the “bookmarks” action in controllers/main_controller.rb. Turns out we can have bindings on routes just as we do for markup:

get "/bookmarks/{{ _bookmark_id }}", _action: 'show_bookmark'

With this route, if we visit the url “/bookmarks/16”, params._bookmark_id will contain 16. Then, we can change that value in the controller and the URL on the client will update to reflect that change. This, again, is one of the places where Volt tries to blur the distinction between client and server.

Wrapping It Up

If you’re coming from a traditional Rails background, Volt might seem a bit strange, at first. There are some rough edges within Volt still (e.g. no asset bundling), but as those continue to melt away, Volt is a fantastic framework for a web app where the client-side is just as (if not more) important than the server-side. Hopefully, this article will help you along your way to write your next app in Volt.

Recommended
Sponsors
Because We Like You
Free Ebooks!

Grab SitePoint's top 10 web dev and design ebooks, completely free!

Get the latest in Ruby, once a week, for free.