In the second and last part of this mini series, we’ll finish this introduction with sections about outputting Ruby code, interpolation, plain text and how to customize Slim to your needs. After that article you should be ready for some Slim action.
Outputting Code
You have already seen a bit about how to use Ruby in your templates. This section gives you all you need to make use of this. In the first article, we have been using Ruby already in our templates. Let me remind you what I mean:
Slim
html head title = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true = javascript_include_tag 'application', 'data-turbolinks-track' => true = csrf_meta_tags
As you can see, inside this head tag, we already used a couple of methods from Rails to deal with styles and JavaScript stuff—nothing major. All you need to do to execute Ruby code is prepend it with an equals =
sign. If your code needs to spread over multiple lines, just append a backslash \
at the end of each line and keep going onto the next one. Should you end the line in a comma ,
, then you don’t need the backslash. Nice little touch if you ask me.
Let’s have a look at another, more concrete example. Writing forms is often a pain—lots of boilerplate code, lots of repetition, and all these dreaded <%= %>
signs in ERB. This can get messy in no time. Could be nicer, huh?
ERB
<%= form_for @agent do |f| %> <%= f.label :name %> <%= f.text_field :name %> <%= f.label :number %> <%= f.text_field :number %> <%= f.label :licence_to_kill %> <%= f.check_box :licence_to_kill %> <%= f.label :gambler %> <%= f.check_box :gambler %> <%= f.label :womanizer %> <%= f.check_box :womanizer %> <%= f.submit %> <% end %>
A lot of stuff to write for creating a new @agent
object, no? Slim lets you handle this a lot more succinctly. We just keep the equals sign and get rid of most other stuff. Tadaa!
Slim
= form_for @agent do |f| = f.label :name = f.text_field :name = f.label :number = f.text_field :number = f.label :licence_to_kill = f.check_box :licence_to_kill = f.label :gambler = f.check_box :gambler = f.label :womanizer = f.check_box :womanizer = f.submit
You can clearly see why this project is called Slim. So much excess fat is gone. Don’t tell me you don’t like what you see—I know you’re digging it! Just an =
sign and you can populate your markup with Ruby code—in this case from Rails, of course. And when you compare it to the HTML rendered on the final page, it’s hard to ignore how compact Slim really is.
HTML Output
<form action="/" accept-charset="UTF-8" method="post"><input name="utf8" type="hidden" value="✓" /> <input type="hidden" name="authenticity_token" value="+P2I801EkEVBlsMgDo9g9/XgwwQfCBd1eoOBkFmgAHE4bxYi9HGUjEjsNwNMnEadV2tbDtYvQhFb4s/SNMXYtw==" /> <label for="agent_name">Name</label> <input type="text" name="agent[name]" id="agent_name" /> <label for="agent_number">Number</label> <input type="text" name="agent[number]" id="agent_number" /> <label for="agent_licence_to_kill">Licence to kill</label> <input name="agent[licence_to_kill]" type="hidden" value="0" /> <input type="checkbox" value="1" name="agent[licence_to_kill]" id="agent_licence_to_kill" /> <label for="agent_gambler">Gambler</label> <input name="agent[gambler]" type="hidden" value="0" /> <input type="checkbox" value="1" name="agent[gambler]" id="agent_gambler" /> <label for="agent_womanizer">Womanizer</label> <input name="agent[womanizer]" type="hidden" value="0" /> <input type="checkbox" value="1" name="agent[womanizer]" id="agent_womanizer" /> <input type="submit" name="commit" value="Save Agent" /> </form>
Remember the initial question that the Slim core team is guided by: “What's the minimum required to make this work?” When you look at the final HTML output, I guess it’s fair to say that Slim has answered that question quite successfully—no complaints on my end. I want to throw in a couple more small examples to give you more opportunity to get used to how this looks in Slim.
This ERB snippet …
<%= render "shared/agents", collection: @agents %>
… becomes this in Slim:
= render "shared/agents", collection: @agents
ERB
<h2>Agents</h2> <ul> <% @agents.each do |agent| %> <li class='agent'> <div>Name: <%= agent.name %></div> <div>Number: <%= agent.number %></div> <div>Licence to kill: <%= agent.licence_to_kill %></div> </li> <% end %> </ul>
Slim
h2 Agents ul - @agents.each do |agent| li.agent div | Name: = agent.name div | Number: = agent.number div | Licence to kill: = agent.licence_to_kill
You could also write this in a more streamlined way via interpolation. You don’t want to go too crazy with that one, though. This would look like this then:
Slim
h2 Agents ul - @agents.each do |agent| li.agent div Name: #{agent.name} div Number: #{agent.number} div Licence to kill: #{agent.licence_to_kill}
Text Interpolation
I mentioned this before briefly, but since it is a form of outputting Ruby code it belongs to this section as well. You can use standard text interpolation from Ruby in your Slim templates as well, of course.
Slim
h2 Welcome Mr. #{misix_agent.surname}! I expect you to die! h2 Welcome Mr. \#{misix_agent.surname}! I expect you to die!
HTML
<h2> Welcome Mr. Bond! I expect you to die! </h2> <h2> Welcome Mr. \#{misix_agent.surname}! I expect you to die! </h2>
As seen above, a simple leading backslash \
escapes the interpolation.
Control Code
One more for the road. Let’s say you want to use a couple of conditionals in your view. Similar to Haml, you signify Ruby code that is not supposed to be outputted onto the page by a simple dash -
. You have seen this in the example above where we used that to iterate over @agents
without displaying that particular part of the code.
Although you should try to stay away from all kinds of conditionals in your views where possible and try to find better OOP solutions for such cases—which is a story for another time—they would look something like this:
Slim
- if current_user.role == "admin" p#admintxt | Welcome back my master! = link_to "Edit Profile", edit_user_path(:current) = link_to "Logout", logout_path - elsif current_user = link_to "Edit Profile", edit_user_path(:current) = link_to "Logout", logout_path - else = link_to "Register", new_user_path = link_to "Login", login_path
ERB
<% if current_user.role == "admin" %> <p id="admintxt">Welcome back my master!</p> <%= link_to "Edit Profile", edit_user_path(:current) %> <%= link_to "Logout", logout_path %> <% elsif current_user %> <%= link_to "Edit Profile", edit_user_path(:current) %> <%= link_to "Logout", logout_path %> <% else %> <%= link_to "Register", new_user_path %> <%= link_to "Login", login_path %> <% end %>
If you want to output code without HTML escaping in place, just use two equals signs ==
. That’s it!
Before we move on, I definitely should take the time to mention this: As you are hopefully aware, tons of view code—aka tons of Ruby code in our context—is a serious smell and should be minimized at all times. Just because Slim makes it maybe even more enticing to plaster your templates with tons of logic, does not mean that you should. Practice constraint in that department! Done right, on the other hand, Slim makes it really elegant to inject Ruby where needed.
Inline HTML
If you feel the need to write HTML in your Slim templates, you have the option to. I haven’t used that feature, nor would I care to use it, but maybe during a transitional phase this could be helpful for newcomers. Let’s have a super quick look.
Slim
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 <body> header.navbar .logo = link_to "sample app", 'root_path', id: "logo" <nav> ul.navbar-right li = link_to "Home", 'root_path' li = link_to "Help", 'help_path' li = link_to "Log in", 'login_path' </nav> .main = yield </body> </html>
When Slim encounters the left angle bracket <
, it knows that you want to mix in some HTML.
Verbatim Text (Word for Word)
The pipe character |
signals to Slim that you want to have plain text—word for word—and just copies the line. In effect, this lets you avoid any kind of processing. The documentation says that if you want to write verbatim text over multiple lines, you have to indent the text with each line break.
Slim
body p | Slim is my new best friend. Slim is my new best friend.
HTML Output
<body> <p> Slim is my new best friend. Slim is my new best friend. </p> </body>
Screenshot
If you put the text on the same line as the pipe character, you can set the left margin after the pipe plus one single space. Out of curiosity, I fooled around with this a bit and found the following results. Only the last example variation has a little obvious hiccup you should be aware of—it swallows the first word of the sentence.
Slim
body p | This line is on the left margin. This line will have one space in front of it. This line will have two spaces in front of it. And so on... p | This line is on the left margin. This line will have one space in front of it. This line will have two spaces in front of it. And so on... p This line is on the left margin. This line will have one space in front of it. This line will have two spaces in front of it. And so on... p This line is on the left margin. This line will have one space in front of it. This line will have two spaces in front of it. And so on... p This line is on the left margin. This line will have one space in front of it. This line will have two spaces in front of it. And so on...
Screenshot
How the output is rendered into your HTML markup differs a bit.
<body> <p> This line is on the left margin. This line will have one space in front of it. This line will have two spaces in front of it. And so on... </p> <p> This line is on the left margin. This line will have one space in front of it. This line will have two spaces in front of it. And so on... </p> <p> This line is on the left margin. This line will have one space in front of it. This line will have two spaces in front of it. And so on... </p> <p> This line is on the left margin. This line will have one space in front of it. This line will have two spaces in front of it. And so on... </p> <p> <This>line is on the left margin.</This><This>line will have one space in front of it.</This><This>line will have two spaces in front of it.</This><And>so on...</And> </p> </body>
Comments
Of course it’s necessary to comment out your code every once in a while. Don’t forget, though, that too many comments are a smell as well. Just try to keep it to an absolute minimum!
A forward slash /
is all you need to comment out any code.
Slim
body /p | This line is on the left margin. This line will have one space in front of it. This line will have two spaces in front of it. And so on...
Boom! And now this paragraph is gone from the page. This comment leaves no trace in the final HTML markup. You just need to apply it to the parent selector and all its children will be commented out as well. So, even comments are slim and minimal.
If, on the other hand, you want some HTML comment <!-- -->
that shows up in the final output rendered, you just need to add an exclamation mark !
after the slash.
Slim
body /!p | This line is on the left margin. This line will have one space in front of it. This line will have two spaces in front of it. And so on...
HTML Output
<body> <!--p | This line is on the left margin. This line will have one space in front of it. This line will have two spaces in front of it. And so on...-->
Neat!
Custom Shortcuts
We have been using shortcuts all along. When you type a dot .
or a hash symbol #
you tell Slim that you want to use predefined shortcuts for classes and ids. That’s certainly a very nice default, but what can you do to expand on that and create your own little snippety snippets? We can do this for tags and attributes alike. Welcome to the awesomeness of Slim!
In Rails, we just need to set up an initializer with the following pattern:
config/initializers/slim.rb
Slim::Engine.set_options shortcut: {'c' => {tag: 'container'}, '#' => {attr: 'id'}, '.' => {attr: 'class'} }
In Sinatra apps, you simply add the same configuration anywhere below the line where you require 'slim'
.
your_sinatra_app.rb
require 'sinatra' require 'slim' Slim::Engine.set_options shortcut: {'c' => {tag: 'container'}, '#' => {attr: 'id'}, '.' => {attr: 'class'} } get('/') { slim :index } __END__ @@ index doctype html html head title Slim Templates body h1 Boss Level Templates With Slim
You can set options on the Slim::Engine by providing a hash with the shortcut you need. In the example above, we instructed Slim to use c
as a shortcut for a container
tag. You would use it like this in your Slim files:
Slim
c.content Now you have a container tag with a .content class.
And the rendered HTML would look like this, of course:
HTML
<container class="content"> Now you have a container tag with a .content class. </container>
Pretty nice, huh? But you didn’t think that’s where the music stops, did you? We can take it further than that. Let me give you an example that is a bit more involved:
config/initializers/slim.rb
Slim::Engine.set_options shortcut: { '#' => {attr: 'id'}, '.' => {attr: 'class'}, 'c' => {tag: 'container'}, '&' => {tag: 'input', attr: 'type'}, '@' => {attr: 'role'}, '^' => {attr: %w(data-role role)} }
In this example, I not only created custom tags but also provided handy custom attributes. Let’s dissect this step by step. By the way, I broke the options hash over several lines to keep it readable and to avoid having a long line of code that nobody likes to stumble upon. Reads much nicer, don’t you think?
Via the ampersand symbol &
, we can now create an input tag, and we just need to feed it a type—which immediately follows the ampersand. We can use any symbol that makes sense to use; there's no need to use the same one that I did. Be careful, though, and try to make uncompromising decisions in that department.
Slim
&text name="user" &password name="pw" &submit
HTML Output
<input name="user" type="text"> <input name="pw" type="password"> <input type="submit">
When you make changes to this initializer with your custom shortcuts, you shouldn’t forget to restart your local server. Without that, your changes won’t be reflected during preprocessing.
Next, if I need a role
attribute, I can now just prefix it with an @
symbol.
Slim
.person@admin Daniel Mendler
HTML Output
<div class="person" role="admin"> Daniel Mendler </div>
Refresher: The role attribute is a semantic approach to describe the role of the element in question—if you need to determine the purpose of the element.
See, via the dot we get a class="person"
class and the @admin
gave us a role="admin"
. Pretty handy dandy, but we can take this one little step further and use an array to specify multiple attributes that should get created via a single shortcut.
Slim
.nifty^hacker CrackDoctor
HTML Output
<div class="nifty" data-role="hacker" role="hacker"> CrackDoctor </div>
Because we associated an array of attributes for our ^
shortcut, Slim creates data-role
and role
attributes simultaneously via a single symbol. That can come in pretty handy. Imagine if you want to output an element similar to the following and can do it concisely with a shortcut and some Ruby code.
HTML
<source src="track1.mp3" type="audio/mpeg" data-duration="1min5secs" data-tempo="125bpm" data-artist="The Beatles" />
Writing all of this by hand seems not to be the best use of your time—we have code to do that job for us. Well, there you have it, that’s all you need to know to create your own set of awesome shortcuts—or to create a big mess when you don’t practice a bit of constraint. I would recommend not going overboard with this—especially try to stay away from defining shortcuts that use symbols that Slim is already attached to.
Refresher: The data- attributes are used to have some private data on your page or application. Stuff that helps you filter content, for example. They are custom attributes which can be used on all HTML elements. Using them for JavaScript purposes is another common practice. It is also very handy for testing elements on a page if you want to make sure that particular elements show up and you want to avoid having designers mess with your styles.
Configuring Slim
Before you leave, I wanted to show you a little sneak peek into the vast configuration options and how you apply them. For Rails, you’d create an environment file like config/environments/development.rb
and specify the options you need. You simply place your configuration some place inside the Rails.application.configure
block.
Rails.application.configure do Slim::Engine.set_options default_tag: 'p', tabsize: 2, attr_list_delims: {'(' => ')', '[' => ']', '{' => '}', '«' => '»', '‹' => '›' } end
In this configuration I made sure the default tag that gets created if a tag name is omitted is a <p>
tag—not a div
tag, which is the standard setting. Also, I adjusted the tabsize to use two white spaces and finally added two more delimiters for wrapping attributes of tags. Now I can use ‹ ›
and « »
as well for that. Not terribly useful but good for demonstration purposes.
In the example below, you can see that all delimiters for attribute wrappers create the same output—also that .some-class
or #some-id
creates <p>
tags per default.
Slim
body #zeroth a{href="http://slim-lang.com" title='Home page'} Goto the home page .first a[href="http://slim-lang.com" title='Home page'] Goto the home page .second a(href="http://slim-lang.com" title='Home page') Goto the home page .third a‹href="http://slim-lang.com" title='Home page'› Goto the home page .fourth a«href="http://slim-lang.com" title='Home page'» Goto the home page
HTML Output
<body> <p id="zeroth"></p> <a href="http://slim-lang.com" title="Home page">Goto the home page</a> <p class="first"></p> <a href="http://slim-lang.com" title="Home page">Goto the home page</a> <p class="second"></p> <a href="http://slim-lang.com" title="Home page">Goto the home page</a> <p class="third"></p> <a href="http://slim-lang.com" title="Home page">Goto the home page</a> <p class="fourth"></p> <a href="http://slim-lang.com" title="Home page">Goto the home page</a> </body>
Alternatively, you can also set this stuff in config/initializers/slim.rb
as I showed you in the section about custom shortcuts.
For Sinatra, it’s the same drill as discussed in the shortcuts section as well. Just set your options somewhere below your require 'slim'
statement and you are good to go.
Take a look at the documentation under “Available options” to read more about what is available for configuration. Slim gives you many options to play with.
Final Thoughts
That’s basically it. There are one or two more advanced topics that you should dig into if needed, but I thought they are mostly not beginner-friendly nor heavily used on a day-to-day basis. I wanted to keep things simple and show you all you need for quickly switching to this awesome, awesome templating engine. Have fun, and I hope Slim is now one of your favorite new toys!
Comments