Multiple Bootstrap4 Themes with Webpacker

The goal of this post is to share a flexible way of supporting multiple Bootstrap4 themes, using webpacker. If you hate reading blog posts or just want to jump straight in to the code, I’ve created a demo app you can pull from Github here.

From Sprockets to Webpacker

Recently I began the process of migrating a Rails app from sprockets to webpacker. I hit many snags along the way, but one of the most awkward ones was supporting multiple themes. The Rails application I was working on had a “default” theme and a “dark” theme. Both were compiled and served with some “Rails magic” and I did’nt really have to worry too much about how sprockets worked behind the scenes. As the application grew, our dev team decided to move to webpacker, so we could take advantage of JS libraries like React. I figured that since Bootstrap 4 uses SCSS and variable overriding, it’d be a pretty simple aspect of the migration. Needless to say I was wrong.

If you’re a Rails developer who’s grown very used to the “Rails way” of doing things, webpacker and it’s configuration can seem a little daunting. Sprockets handles the asset pipeline with little to no configuration. Webpacker does require some basic configuration and understanding of javascript importing, but it’s worth it in the long haul.

Getting started

The first thing you’ll need to do is change how your application loads in javascript and stylesheets. So here we’re telling our application to use the webpacker helpers: javascript_pack_tag and stylesheet_pack_tag to load our assets or ‘packs’.

The current_theme variable needs to return a theme string, for example: 'cerulean' or 'darkly'. This will be the name of our theme’s pack. A good idea for current_theme is to add a string column to your uses table called ‘theme’, where users theme selection can be saved. Then write a UserHelper method to access this in the view for the current_user. In the demo app I’ve just stored the variable as a simple cookie.

The next place we need to look is app/javascript/packs/application.js. This is the default entry point for webpacker when it is compiling your assets.

So what does that mean? Basically everything that you want webpacker to compile must be imported here, this is fairly new to sprockets veterans.

In my application.js I’m importing some standard libraries like bootstrap, then I’m importing all of my themes.

Structure is Key

An important thing to note here is my directory structure, it’s very important to keep your packs clearly separated from an early stage. This will allow us to dynamically request a specific theme in our view code. Lets focus on the theme app/javascript/packs/cerulean, this theme contains a index.js file that you can use to import scss files. Then in our cerulean.scss file we can import our custom theme variables, bootstrap and any other styles needed for our theme. The most important thing to note here is the order of the imports. Here we take advantage of Bootstrap4 using SCSS, by importing our variables first to overwrite the bootstrap default variables. This is what allows us to have custom colors, fonts etc.

Creating your own theme

As you can see the rest of our theme’s packs have exactly the same structure and exactly the same importing strategy. This gives us a consistent and predictable folder structure for our code, but also allows fully flexible theming. With this approach you can add a new theme in minutes!

So now all you have to do is create a _variables.scss and override bootstrap to your liking. If you’re like me and don’t have time to come up with a totally unique theme by yourself there’s lots of places online where you can buy themes, or just download free ones. For my demo app, I used BootSwatch themes, you should check them out for ideas and color schemes.

Single Table Inheritance with Rails

Recently my team was tasked with building an in house Kanban Ticketing system, similar to JIRA. At it’s core the system would create and manage tickets, which are assigned to people to work on.

We needed to support several different types of tickets, using the JIRA example you could have: story, defect, request or epic tickets. It became apparent to the team early on that all these tickets would have the exact same database structure, but would need to behave differently.

When to use Single Table Inheritance

Before diving into how to implement STI, I want to point out that this design choice only makes sense in certain circumstances. You should ask yourself the following questions:

  • Does your application have several sub-models that inherit from a parent model class?
  • Do the sub-models all have the same attributes and database columns as each other?
  • Do the sub-models need to behave differently from the parent model class?

If you answered yes to all of the above, then you’ve come to the right place. If not you may need to consider another design like polymorphism, or even separate tables for each model. Having attributes that are not used by all sub-models can lead to a tonne of nulls in your database.

Remember that STI is a great way of DRYing up models that share the same attributes, OO characteristics and database structure.

A real world example

I’m going to stick with the JIRA example given above, so the first thing we’ll do is generate a migration to create our ticket table.

Here’s an example migration for creating the ticket table.

The type column we’ve defined is the default column used by STI, to store the sub-model name. If you want to use a different column you just need to declare it your base model.

self.inheritance_column = :my_cool_column

Next we’ll need to define our Ticket model and it’s sub-models. A simple Ticket model will look like this.

So we’ve defined our base Ticket model with a few validations and an after_create action. Notice that we have not defined the method set_default_priority yet, this will be done in the sub-models.

All of the sub-models we’ve defined have a unique implementation of the set_default_priority function. This is to illustrate that by using STI we can have sub-models share the same database table, yet have behave differently.

Using the rails console we can test that STI is working as expected.

Now we know STI is working as expected, we can begin to build our ticketing system on our ticket model. We can add our routes and some controller actions.

Wrapping up

Through the use of Single Table Inheritance we’ve been able to design our sub-models to be flexible on a code level, while sharing the same database table. I hope this post has given you a good understanding of how and when to implement Single Table Inheritance. I’ve created a simple toy app with all the code shown above that you can find here on Github. Feel free to clone it and experiment yourself!

Optimizing Performance of Ordered Active Record Queries

Active Record enables Rails developers to model, store and query their application’s data, often without ever having to write a line of SQL.

While this is amazing for getting an app up and running quickly, once it’s up and running, you will undoubtedly experience performance issues and find yourself examining SQL queries. In this post I’ll share with you an example of a performance issue that can creep in when using the ActiveRecord::QueryMethods order function and how to fix it.

A Typical Rails Scenario

Lets take a blog app as an example and assume we have a Posts table and a Users table. The relationship between these tables is a User has many Posts. The Posts table is linked by the foreign key user_id to the Users table.

Posts

Posts table

Users

Users table

You’re bound to see an ActiveRecord statement that’s something like this:

With Active Record we can easily write a query to retrieve all the posts for a user and order them by when they were last updated. Active Record will go and generate the following SQL query:

However Active Record won’t check this query’s performance for you and when you go live with your blog you’ll see a big difference ordering 1 post vs ordering 1000 posts.

Copy this generated SQL query into your preferred SQL editor and prepend the EXPLAIN keyword to the start of the query.

EXPLAIN Yo Self

EXPLAIN is a must know for any serious Rails developer, it helps you understand your queries and identify possible performance issues.

There are already many good blog posts on EXPLAIN and it’s usage, such as this one here. Below is the output of running EXPLAIN on our query.

Explain Query 1

As you can see this is a terrible query, it is using no indexes and doing a full table scan, luckily there’s only 4 rows!

So the first thing most people will do in this case is add a foreign key constraint for user_id on the Posts table. Lets do that first and re-run the explained query.

Explain Query 2

Things are looking a lot better now with the addition of the foreign key constraint. If you look in the Extra column you’ll notice that there are two statements “Using index” (good) and “Using filesort” (bad).

What is Filesort then?

Using Filesort means you are performing a sort that can’t use an index. You might be thinking to yourself “I already added my foreign key so I do have an index”. While it’s true that you have an index, you haven’t created an index on the required columns for the sort.

This leads to:

  • A full scan of the result set
  • Using the filesystem to store chunks when the sort buffer in memory gets full
  • A decrease in query performance as the result set grows

How to fix it

Fortunately the hardest part of dealing with these types of performance issues is identifying them. To solve the Filesort issue we simply need to add an index to the appropriate columns. Going back to our Active Record generated SQL query, we can see that the columns we need to index are user_id and updated_at.

Lets add the index and EXPLAIN our query one last time.

Explain Query 3

Finally we have a query using the new index user_id_updated_at and “Using Filesort” is a thing of the past.