Assets

Hanami supports powerful features for web assets.

Configuration

Each application can have its own separated assets settings in application configuration.

Compile Mode

Toggle this value, to determine if the application must preprocess or copy assets from sources to public directory. It's turned on by default in development and test environments, but turned off for production.

# apps/web/application.rb
module Web
  class Application < Hanami::Application
    configure do
      # ...
      assets do
        # compile true, enabled by default
      end
    end

    configure :production do
      assets do
        compile false
      end
    end
  end
end

Fingerprint Mode

In order to force browsers to cache the right copy of an asset, during the deploy, Hanami creates a copy of each file by appending its checksum to the file name.

We can control this feature via application configuration. It's turned on by default only in production environment.

# apps/web/application.rb
module Web
  class Application < Hanami::Application
    configure do
      # ...
      assets do
        # fingerprint false, disabled by default
      end
    end

    configure :production do
      assets do
        fingerprint true
      end
    end
  end
end

If enabled, assets helpers will generate checksum relative URLs.

<%= javascript 'application' %>
<script src="/assets/application-d1829dc353b734e3adc24855693b70f9.js" type="text/javascript"></script>

Serve Static Assets

It can dynamically serve them during development. It mounts Hanami::Static middleware in project Rack stack. This component is conditionally activated, if the environment variable SERVE_STATIC_ASSETS equals to true.

By default, new projects are generated with this feature enabled in development and test mode, via their corresponding .env.* files.

# .env.development
# ...
SERVE_STATIC_ASSETS="true"

Hanami assumes that projects in production mode are deployed using a web server like Nginx that is responsible to serve them without even hitting the Ruby code.

Static assets serving is enabled by default in development and test environments, but turned off for production.

There are cases where this assumption isn't true. For instance, Heroku requires Ruby web apps to serve static assets. To enable this feature in production, just make sure that this special environment variable is set to true (in .env or .env.production).

What Does It Mean To Serve Static Assets With Hanami?

As mentioned above, when this feature is enabled, a special middleware is added in front of the project Rack stack: Hanami::Static.

Incoming requests can generate the following use cases

Fresh Asset

GET /assets/application.js

It copies the apps/web/assets/javascripts/application.js to public/assets/application.js and then serves it.

Assets are copied only if the destination path is NOT existing (eg. public/assets/application.js). If it DOES exist, the asset is only served, without copying it.

When an application has turned OFF assets compilation (Compile mode), Hanami won't copy the file.

Stale Asset

This could happen in development mode, when we require an asset the first time, it's get copied over public/, then we edit it, so the destination file is stale.

GET /assets/application.js
# edit the original file: apps/web/assets/javascripts/application.js
# then require it again
GET /assets/application.js

It copies again the source into the destination file (public/assets/application.js) and then serves it.

Precompiled Asset

Let's say we use Sass to write our stylesheets.

GET /assets/application.css

It preprocess the apps/web/assets/stylesheet/application.css.sass to public/assets/application.css and then serves it.

Dynamic Resource

GET /books/23

This isn't a static file available under public/, so the control passes to the backend that hits the appropriate action.

Missing Resource

GET /unknown

This isn't a static file or a dynamic resource, the project returns a 404 (Not Found).

Sources

Each application has a separated set of directories where to look up for files. Assets are recursively searched under these paths.

New projects have a default directory where to put application assets:

% tree apps/web/assets
apps/web/assets
├── favicon.ico
├── images
├── javascripts
└── stylesheets

3 directories, 1 file

We can add as many directories we want under it (eg. apps/web/assets/fonts).

For a given application named Web, the default assets source is apps/web/assets

Adding Sources

If we want add other sources for a given application, we can specify them in the configuration.

# apps/web/application.rb
module Web
  class Application < Hanami::Application
    configure do
      # ...
      assets do
        # apps/web/assets is added by default
        sources << [
          'vendor/assets'
        ]
      end
    end
  end
end

This will add apps/web/vendor/assets and all its subdirectories.

Hanami looks recursively to the assets sources. In order to NOT accidentally disclose sensitive files like secrets or source code, please make sure that these sources directories ONLY contain web assets.

Third Party Gems

Hanami allows to use Rubygems as a way to distribute web assets and make them available to Hanami applications.

Third party gems can be maintained by developers who want to bring frontend frameworks support to Hanami. Let's say we want to build an hanami-emberjs gem.

% tree .
# ...
├── lib
│   └── hanami
│       ├── emberjs
│       │   ├── dist
│       │   │   ├── ember.js
│       │   │   └── ember.min.js
│       │   └── version.rb
│       └── emberjs.rb
├── hanami-emberjs.gemspec
# ...

We put in an arbitrary directory only the assets that we want to serve. Then we add it to Hanami::Assets.sources.

# lib/hanami/emberjs.rb
require 'hanami/assets'

module Hanami
  module Emberjs
    require 'hanami/emberjs/version'
  end
end

Hanami::Assets.sources << __dir__ + '/emberjs/source'

When an application will do require 'hanami/emberjs', that directory will be added to the sources where Hanami will be able to lookup for assets.

<%= javascript 'ember' %>

We can use the javascript helper to include ember.js in our application.


Prev: Mailers - Testing
Next: Assets - Preprocessors
Looking for Lotus? We renamed the project and it's now called Hanami. Read the announcement.