How to Use Brunch for Sinatra Projects

For months, I have been doing more Elixir and Phoenix work. I learned a lot and that’s worth writing about later. Currently, Phoenix uses Brunch by default for asset management. In recent days, I needed to update some old Sinatra projects and I found Sprockets is outdated and a bit challenging to work with considering the speed. The Ruby community should probably accept that for most projects Sprockets is more of a problem than a solution. Why?

  1. Build time would be slow even if you are only precompiling assets that are changed or added up to the project.
  2. If you have a lot of assets and most would need to be compressed with UglifyJS or YUI compressor, it might not work as well as it does for JavaScript options.
  3. Sprockets simply does not work great if you have a React.js, Riot.js or Vue.js project. It would work fine if you are using Angular.js and it offers some conveniences. But based on my experience, Brunch and Webpack make sense over Sprockets if you have a lot of components or your entire app is a single-page application.

I have made some effort to update a 3-year-old Sinatra template which some people might have used in the past. I just want to give a walkthrough on how to use the template and update brunch-config.js as needed.

Using Simple Sinatra MVC

The objective of the template is to show people how to follow the MVC pattern for Sinatra projects. The directory structure is very close to Ruby on Rails. In fact, I use the same dependencies like ActiveRecord and ActiveSupport. What we do not need: ActionView. Why? I think it offers no advantages. There are very few useful helpers which you would usually never need in a small project. Sinatra supports Slim and Haml like a pro.

I will make an effort on teaching Sinatra sometime. This guide is focused on the alternative Asset Pipeline.

To create a new Sinatra project, try:

git clone --depth 1 git://github.com/kathgironpe/simple-sinatra-mvc.git myapp
rm -r myapp/.git && rm myapp/README.md

Understanding the directory structure

Brunch allows you to specifiy which directory would be used for non-JS and non-CSS files. Check brunch-config.js and you should see:

  conventions: {
    // This option sets where we should place non-css and non-js assets in.
    // By default, we set this to '/app/assets/files'. Files in this directory
    assets: /^(app\/assets\/files)/
  }

My assets/files directory usually contains subdirectories called fonts and images. A typical project would need such structure.

├── assets
│   ├── files
│   │   ├── fonts
│   │   └── images
│   ├── javascripts
│   │   └── app.js
│   └── stylesheets
│       ├── app.scss
│       ├── base
│       │   ├── _base.scss
│       │   ├── _buttons.scss
│       │   ├── _forms.scss
│       │   ├── _layout.scss
│       │   ├── _lists.scss
│       │   ├── _media.scss
│       │   ├── _tables.scss
│       │   ├── _typography.scss
│       │   └── _variables.scss
│       └── layouts
│           ├── _grid-settings.scss
│           └── _layout.scss

Everything on app/assets will be watched and compiled using the commands npm run watch or npm run deploy which is meant for production.

  paths: {
    // Dependencies and current project directories to watch
    watched: [
      'app/assets'
    ],

    // Where to compile files to
    public: 'public/assets'
  }

You have an option to add public/assets on your .gitignore or keep it. It depends on your needs. The convention is to ignore the directory and assets will be built for production after dependencies are installed. Review package.json and check what else you might need for postinstall.

Brunch Magic

From a user perspective, Brunch is better than Webpack because apart from installing JS dependencies for compression of assets, you need a few lines of code to make it work for Webpack:

if (process.env.NODE_ENV === 'production') {
  config.plugins.push(
    new webpack.optimize.DedupePlugin(),
    new webpack.optimize.UglifyJsPlugin({ minimize: true })
  );
}

There isn’t an equivalent of that code for Brunch. Everything just works if you install the right dependencies like uglify-js-brunch.

Module Wrapper

If you have assets which would not need AMD or RequireJS, simply add the following configuration:

  modules: {
    wrapper: false
  }

Fingerprinting or Asset name digests

Have you noticed how precompiling assets on Ruby on Rails appends a hash on the names of the files, while this isn’t exactly a default you get with Phoenix or any project that uses brunch? To support this, it took a few minutes of research and writing two methods. Not rocket science.

You have to install fingerprint-brunch and add some helpers on your Sinatra application. I will make efforts to improve the template for this.

npm install fingerprint-brunch --save-dev

Update your config file and specifiy the name for the assets manifest.


  plugins: {
   ...
    fingerprint: {
      manifest: 'public/assets/assets.json'
    }
  }

The helpers I wrote are two methods, which you could use the get the correct asset path.

  # require 'json'  needed on top of the file
  def parse_manifest_file(file)
    manifest_file = File.read(file)
    JSON.parse(manifest_file)
  end

  def link_to_asset(file)
    path = %(public#{file})
    data = parse_manifest_file('./public/assets/assets.json')
    data[path].gsub('public', '') if data[path]
  end

You would use it on a layout file like so:

link rel="stylesheet" href='#{link_to_asset('/assets/stylesheets/app.css')}' media='all'

This is sufficient for most apps and I discovered you can use sw-precache for Brunch projects. That’s worth fiddling with later.

What about Webpack?

I have plans on creating more templates, and one which uses Webpack. But these days, I think the community has a lot for React.js and Redux.

What about Sprockets 4?

Like Webpack, I think it still makes sense. Probably would work on another repository or branch that would use it. Of the few advantages would be the ability to support scss.erb files.

Further Reading

  1. Simple Sinatra MVC documentation
  2. Brunch documentation
  3. Getting Started with Brunch