The blog ofJonathan Pepin

Know your tools

2018-10-23

Something I appreciate doing here and there is building smaller scale projects, focused on going back to basics on technologies I use.

I had a few months of frenzy at work where everything we worked on was so comlicated (not complex, complicated) it felt like we couldn't build anything. To cope with that, I had fun doing small projects in less than an hour, usually in bed when I woke up. My Sunday morning hacks.

I think this is an important exercise, because it forces you to think about the tools and the trade. Especially in today's environment, so much tools are abstracting the trade, that we not only take them for granted, we forget they are even here to make things easier. Take a dev server for example. Whether it is a Rails, create-react-app or Next.js project, you simply start your dev server and magically have your templates compiled, your javascript transpiled, your assets served and maybe your browser automatically reloaded.

Have you ever thought about this? How it works and what is happening? I don't do it often, and that's why I think those little projects are important.

I did just that recently, as I built the website for jon&jess.

Jess designed a website, and except for a contact form, everything was static. So I started to think about which solution would fit better.

I first thought about Jekyll. It's simple and easy. I like Markdown, but to me, it's great to write, not to structure webpages. I also really like the export feature of Nextjs. It exports your app in a static, browser-only package of html and javascript. I'm very quick with Rails, and could deploy something in minutes to Heroku. But for our simple website, all those solutions are an overkill.

So I decided to do as simple as I could. Some templates, some css, and maybe some javascript. It shouldn't be anything more.

When you do such small projects is when you finally see how much is done for you by tooling. When you go back to the roots per se.

Let's build some html

So let's say I want 4 pages. Index, About, Work and Contact. That's not a lot of pages and those pages are pretty basic. But how am I going to handle the layout? Do I copy/paste the header, nav, footer, meta tags, etc in every single page? No way, that would be too tedious, annoying and prone to errors.

That's when you start asking the right questions. How is html built? When using Rails, I usually use haml or erb. When using Express I use pug. With React we're writing jsx.

So here I am, even for the simplest of website, having to look into templating engines. I'll use haml, mostly because I like it.

So now that I want to use haml to generate some html, let's start with a simple example.

Let's generate the following index.html:

# index.html

<html>
  <body>
    <p>Welcome!</p>
  </body>
 </html>

First, we need to create the corresponding index.haml:

# index.haml

%html
  %body
    %p Welcome!

Then we can run haml index.haml index.html to generate the file.

Ok, so we are generating html from haml, but the CLI is not very useful since we can't do much more than what we just did. Let's write a ruby file to programatically generate our html. This will allow us to eventually add variables, etc.

# build.rb

require 'haml'

# read haml and generate html from it
src = Haml::Engine.new(File.open('index.haml')
out = src.render

# write html to file
File.open('index.html', 'w') do |f|
  f.write(out)
end

Ok, now we have a little bit of power in our hands. Let's create a common layout and update our index.haml to only have our page-related content.

# index.haml

%p Welcome!
# layout.haml

%html
  %head
     %title A great website!
   %body
     = yield

If you are familiar with Ruby's yield, the layout shouldn't bug your mind too much. If you are not, this will be clearer soon, but for now just know that in Ruby, methods can receive a block, and when they do, yield is where the block gets called.

So when we do src.render in our ruby file, we'll be able to add block as an argument, and its result will be inserted where yield is.

So here the idea is that we'll render the layout.haml file, and pass it what we want as content in the yield.

# compile.rb

require 'haml'

# read layout's haml
layout = Haml::Engine.new(File.open('layout.haml')

# read index's haml
index = Hamle::Engine.new(File.open('index.haml')

# generate html by rendering layout and index inside layout's yield
out = layout.render do
  index.render      # this is the block
end

# write html to file
File.open('index.html', 'w') do |f|
  f.write(out)
end

There. Now we can split our layout and our index, and when we run ruby compile.rb, the correct html will get rendered.

Make the build easier

Now that we are able to compile our haml files into html, we need to make it a bit nicer. First, we'll have multiple haml files, not only the index. So we'll eventually want to go over all files in a views directory and build them.

Rebuilding every page for any change (on a single file) would not be very efficient, so we want to add a way to check and only compile what changed. Rake, a ruby-based task runner is perfect for that.

# Rakefile

require 'haml'

def compile target, src
  layout = Haml::Engine.new(File.open('views/layout/layout.haml')
  page = Hamle::Engine.new(File.open(src)

  out = layout.render do
    page.render
  end

  File.open(target, 'w') do |f|
    f.write(out)
  end
end

FileList['views/*.haml'].each do |src|
  target = src.gsub('views/', '').gsub('haml', 'html')
  file target => src do |t|
    compile target, src
  end
  task :compile => target
end

Here I would recommend to check Rake's documentation if you are not familiar with it, but basically we are doing the following:

Doing it this way allows for each haml file to have its own task, allowing Rake to only re-compile the haml file that changed instead of re-compiling everything each time.

Now we can run rake compile and it will compile all files. When we update a file, we can run rake compile again, and only the changed file will get re-compiled.

Let's automate our flow

Now we have nice little compiler for our haml, but it gets pretty annoying to have to run it manually everytime we do a change in the haml. Would be nice if it could get compiled automatically when a change is done.

Rake can't do that by itself, but another ruby utility called guard does exactly that, and has a specific rake module, making it very easy to use both together.

Guard requires a Guardfile to know what to watch for and what to do when an update happens. Pretty straight forward.

# Guardfile

guard 'rake', :task => 'compile' do
  watch(%r{^views/.+\haml$})
end

This tells guard to run rake compile when a haml file is updated in views/. Now we can run guard and it will compile all our files, watch for changes, and recompile the correct file when one is updated.

And here we are, with a little, home made dev server. This is very similar (although simpler) than other dev servers coming with other framework.

The point of this is that knowing and mastering simple tools gives you a lot of power. We really want to build a simple website, and with the correct tools, we don't have to lean on bigger solutions that might be too big, or not perfectly adapted.

Knowing how the tools we use everyday work is very important exactly for this kind of reasons. Sometimes we need heavy lifting because we'll build a full web app, and a framework such as Rails is great. All the tooling and free stuff coming with it are really worth it.

But in this case, we just want to build a simple static website, with a few templates. No need for a big framework. Even a small framework (like jekyll etc) is too big. Being able to build our own mini-solutions is of great value!