0

Rails and Slim

I first encounter slim in my current project when I fixed a bug. It looks wierd and cryptic like a plain text to me without formatting whatsoever. But as I start to dig into it, it start to look more and more attractive.

What is Slim?

Slim is a template language whose goal is reduce the syntax to the essential parts without becoming cryptic.

The initial design of Slim is what you see below. It started as an exercise to see how much could be removed from a standard html template (<, >, closing tags, etc...). As more people took an interest in Slim, the functionality grew and so did the flexibility of the syntax.

What does it look like? Here it is:

doctype html
html
  head
    title Slim tutorial
  body
    h1 Heading

    #content
      p See what it look like, I don't know.

      == yield

      - unless books.empty?
        table
          - books.each do |book|
            tr
              td.title = book.title
              td.price = book.price
      - else
        p
         | No books found.  Please try something else.

    #footer
      = render 'footer'
      | Copyright © #{year} #{author}

It looks somewhat like ruby, is it not?

The code above has eliminated all closing tag, <, >, <%,div,... Make the code writing very concise but look a bit cryptic, or it is not.

2.Structure and syntax of Slim

First let install Slim. Add this to Gemfile:

gem 'slim'
gem 'slim-rails'

I think in order to make it easy for us to absorb the essence of the article, let's make a demo app. Suppose we have the following files in app/views,such as application.html.erb, _footer.html.erb, _header.html.erb,_shim.html.erb. We will go through each file one by one and convert it to slim.

Content of application.html.erb:

<!DOCTYPE html>
<html>
  <head>
    <title><%= full_title yield :title %></title>
    <%= stylesheet_link_tag    "application", media: "all", "data-turbolinks-track" => true %>
    <%= javascript_include_tag "application", "data-turbolinks-track" => true %>
    <%= csrf_meta_tags %>
    <%= render "layouts/shim" %>
  </head>
  <body>
    <%= render "layouts/header" %>
    <div class="container">
      <% if notice %>
        <p class="alert alert-success"><%= notice %></p>
      <% end %>
      <% if alert %>
        <p class="alert alert-danger"><%= alert %></p>
      <% end %>
      <%= yield %>
      <%= render "layouts/footer" %>
      <%= debug params if Rails.env.development? %>

Let's change it to slim now.

doctype html
html
  head
    title = full_title yield :title
    = stylesheet_link_tag    "application", media: "all", "data-turbolinks-track" => true
    = javascript_include_tag "application", "data-turbolinks-track" => true
    = csrf_meta_tags
    = render "layouts/shim"
  body
    = render "layouts/header"
    .container
      - if notice
        p.alert.alert-success = notice
      - if alert
        p.alert.alert-danger = alert
      = yield
      = render "layouts/footer"
      = debug params if Rails.env.development?

There are a lot going on here. So one thing obvious is that there is no closing tag.

doctype html --> DOCTYPE! html

html --> <html> </html>

head --> <head> </head>

title --> <title> </title>

body --> <body> </body>

Indentation is very important and is keeped the same as before converting. you may notice = sign. It equals to rails syntax <%= %>. So something like <%= render "layouts/header" %> is equivalent to = render "layouts/header".

You might also notice - sign. This denotes control code. Examples of control code are loops and conditionals. Blocks are defined only by indentation. So this does not display the result. It equivalent to rails <% %>.

So something like - if notice is equivalent to <% if notice %>. A trailing space behind - is necessary.

A nice short . is for class. So .container is equivalent to <div class="container">. and p.alert.alert-danger --> <p class="alert alert-danger">. Notice that there is no space between p and alert.

Let look at some more files: Content of header.html.erb

<header class="navbar navbar-fixed-top navbar-inverse">
  <div class="container">
    <%= link_to "test app", "#", id: "logo" %>
    <nav>
      <ul class="nav navbar-nav navbar-right">
        <li> <%= link_to "Help", help_path %></li>
        <li> <%= link_to "Home", root_path %></li>
        <li>
          <% if user_signed_in? %>
            <li><%= link_to "Tests", tests_path %></li>
            <li id="fat-menu" class="dropdown">
              <a href="#" class="dropdown-toggle" data-toggle="dropdown">
                <%= current_user.name %> <b class="caret"></b>
              </a>
              <ul class="dropdown-menu">
                <li><%= link_to "Profile", current_user %></li>
                <li><%= link_to "Settings", edit_user_registration_path(current_user) %></li>
                <li class="divider"></li>
                <li>
                  <%= link_to "Sign out", destroy_user_session_path, method: :delete %>
                </li>
              </ul>
            </li>
          <% else %>
            <li><%= link_to "Sign in", new_user_session_path %></li>
          <% end %>
        </li>
      </ul>
    </nav>
  </div>
</header>

is converted to header.slim with the following content:

header.navbar.navbar-fixed-top.navbar-inverse
  .container
    = link_to "test app", "#", id: "logo"
    nav
      ul.nav.navbar-nav.navbar-right
        li = link_to "Help", help_path
        li = link_to "Home", root_path
        li
          - if user_signed_in?
            = link_to "Tests", tests_path
            li#fat-menu.dropdown
              a.dropdown-toggle href="#" data-toggle="dropdown"
                = current_user.name
                b.caret
              ul.dropdown-menu
                li = link_to "Profile", current_user
                li = link_to "Settings", edit_user_registration_path(current_user)
                li.divider
                li = link_to "Sign out", destroy_user_session_path, method: :delete
          - else
            li = link_to "Sign in", new_user_session_path

li#fat-menu.dropdown in the above code is equivalent to <li id="fat-menu" class="dropdown"></li>. # is for id.

let see another file footer.html.erb and change it to footer.slim.

<footer class="footer">
  <small>
    <a href="http://railtutorial.org"> Rails Tutorial</a> by Michael Hartl
  </small>
  <nav>
    <ul>
      <li><%= link_to "About", about_path %></li>
      <li><%= link_to "Contact", contact_path %></li>
      <li><%= link_to "News", news_path %></li>
    </ul>
  </nav>
</footer>

is converted to:

footer.footer
  small
    a> href="http://railtutorial.org" Rails Tutorial
    ' by Michael Hartl

  nav
    ul
      li = link_to "About", about_path
      li = link_to "Contact", contact_path
      li = link_to "News", news_path

Do you notice a>? Why > there? > add a trailing space after the link, So that "Rails Tutorial" is not stuck with "by Michael Hartl".

And how about ' sign? It is verbatim text with trailing white space. The single quote tells Slim to copy the line, but makes sure that a single trailing white space is appended.

Last file shim.html.erb

<!--[if lt IE 9]>
  <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->

is converted to

/[if lt IE 9]
  script src="http://html5shim.googlecode.com/svn/trunk/html5.j"

This is called "IE conditional comment"

I hope you get a glimpse of slim. There are still more advanced features to explore such as helper, capturing, include and so on. You can start to implement it in your project.


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.