Software localization

Sharing Assets Between Rails and Other Apps

Stop copying and pasting stylesheets, JavaScripts, and logos—and learn how to share your assets between Rails and other applications.
Software localization blog category featured image | Phrase

While this works great at first, you will notice that it will become harder and harder to maintain updates of your assets across your projects. Every time someone decides to use a different font or to adjust some frequently used colors from your palette you will find yourself grepping through your entire codebase, hoping you at least named your color variables consistently so you won’t miss any of them.

And although you think you worked extra carefully, you will eventually find something like this:

[caption id="attachment_2556" align="alignnone" width="300"]colors.scss from Project A | Phrase colors.scss from Project A[/caption]

[caption id="attachment_2557" align="alignnone" width="300"]colors.scss from Project B | Phrase colors.scss from Project B[/caption]

At Phrase, we found ourselves in exactly that position. Our documentation and landing pages are separated from our main application. But of course, they all share at least some base assets:

  • Brand colors
  • Fonts and sizes
  • Logos
  • JavaScript libraries
  • Bootstrap Overrides
  • and even whole UI components

After looking at the slight chaos creeping in over the past years, we evaluated possible solutions to clean things up.

Rails engines

Enter the world of Rails engines. Rails engines have been around for a long time and are the best way to provide reusable components for Rails applications as they can provide shared models, controllers, business logic - and assets.

You can consume assets found in a Rails engine seamlessly in your main application via the asset pipeline. This makes Rails engines the perfect utility to share assets across multiple Rails projects. You can organize a Rails engine just like any other gem. This will from now on be the single source of truth for all important assets.

Getting started with Rails engines is easy:

Create your engine

First, create a new Rails engine:

$ rails plugin new mycompany-ui -T

You are now the proud creator of a new gem named mycompany-ui, which contains a Rails engine (that is not very useful, yet).

Move assets to their new home

Now the important work starts. Put all relevant assets into the engine, simply by moving them from your main application to your gem and into mycompany-ui/lib/assets. We found it to be a good first step to start with the main color definition file and take it from there.

It is also a good idea to define an entry point for importing the assets into your app. So if your color definition file is located at /mycompany/ui/assets/stylesheets/mycompany/_colors.scss, you might want to also add an entry point like this:

@import "mycompany/_colors";

@import "mycompany/_reset";

@import "mycompany/_typo";

// etc.

Connecting the engine

To now add the gem to your main app, simply add it to your Gemfile:

gem "mycompany-ui", path: "/path/to/engine"

and install it via bundler

$ bundle install

We’ll talk about deployment strategies later, but when starting out you might want to reference it from your local workspace in order to iterate quickly.

Now, simply reference the stylesheet entry point in your main application stylesheet file as you would with any other gem:

@import "mycompany";

// your remaining styles go here

You can share JavaScript and all other assets that are handled via the Asset pipeline the same way.

Deployment

To make the gem available for deployment, you basically have these options:

  1. Publish the gem via GitHub or RubyGems publicly (as you would with any other gem)
  2. Host the gem in private via your own gem server
  3. Keep everything in a private GitHub repository

While our asset library does not contain any sensitive information, we still decided not to make it public, simply because it is irrelevant for other developers. That’s why we went with option three and host it in a private repository on GitHub.

Voilá - you can now share your most important assets across multiple Rails applications!

Hello, webpack!

However, most larger teams do not only use Rails and the asset pipeline but also other technologies such as Node.js with various frontend architectures and frameworks.

For example, although our documentation center is built with nanoc (a static site generator written in Ruby), its JavaScripts and stylesheets are managed via webpack so it first seemed we won’t be able to benefit from having our base assets in a Rails engine.

Luckily, we can make the Rails engine also available as an npm module to make it consumable via webpack:

Transform the engine

First, add a package.json to the root of the Rails engine in order to declare the gem as an npm module:

{

  "name": "mycompany-ui",

  "version": "1.0.0",

  "description": "My Company UI & Styles",

  "main": "index.js",

  "private": true,

  "scripts": {},

  "repository": {

    "type": "git",

    "url": "git+https://github.com/mycompany/mycompany-ui.git"

  },

  "author": "Manuel Boy",

  "license": "ISC",

  "bugs": {

    "url": "https://github.com/mycompany/mycompany-ui/issues"

  },

  "homepage": "https://github.com/mycompany/mycompany-ui#readme",

  "dependencies": {}

}

The Rails engine that contains your base assets, is now also installable via npm (respectively yarn).

Consume the module

To add the Rails engine (which is now also an NPM module) to your application, simply add it to the package.json of your project:

{

  "name": "mycompany-some-project",

  ...

  "dependencies": {

    "phraseapp-ui": "file:/path/to/mycompany-ui"

  }

}

And run:

$ npm install

Again, we found it useful to start with local file references, since your new asset library is likely to grow.

You can now simply reference the assets from the Rails engine slash npm module like this:

@import "~mycompany-ui/lib/assets/stylesheets/mycompany/_colors";

@import "~mycompany-ui/lib/assets/stylesheets/mycompany/_reset";

@import "~mycompany-ui/lib/assets/stylesheets/mycompany/_typo";

// etc.

Deployment

Just like the Ruby gem, you must make the module accessible in order to deploy your application. While you can use private modules via npm registry, we found it sufficient to reference it directly from GitHub:

{

  "dependencies": {

    ...

    "phraseapp-ui": "git+https://github.com/phrase/phraseapp-ui.git",

    ...

  }

}

Working with the asset library

Once you have started sharing assets between projects, you can start refactoring some frontend code like your stylesheets and move base resources like colors, typography declarations, and many more to the central asset library. You will find it deliberating to remove duplication between different projects.

Whenever you need to change things like a font or your color palette, you simply need to update it in your new central asset library, re-deploy every app and you’re done. Having this library will also improve the overall quality of your stylesheets since you are now forced to think twice about which resources need to go into the core library and which are just project-specific.

Summary

We found this way of sharing assets across applications a great way to reuse frontend code and ensure consistent looks when working with separate projects. But although it is fairly easy to set up, don’t fool yourself: In a large application landscape, finding and extracting the right code and restructuring it properly, takes time. On the other hand: if you want to stay productive in an ever-growing code base, steps like these are inevitable.