The asset pipeline, introduced with Rails 3.1, makes it simple to include versioned external assets as application dependencies. Provided those assets are packaged as Ruby gems, the process is as simple as adding the gem to your
External assets are made available in Rails via Rails engines. When the engine is loaded into your Rails application, the engine’s asset paths are added to your application’s load paths. This makes them available for require in your manifest files. An asset gem is just an absurdly simple engine. As an example, I’ll walk you through the process I recently took for momentjs-rails. The process is wordy, but it’s really pretty simple.
Create Gem Framework
Bundler makes it simple to create the files and folders necessary for creating a gem. Run the following command to create and initialize a Git repository along with several template files for the gem.
lib/momentjs-rails/version.rb and set the version like so:
1 2 3 4 5
In the event that you need to push an update to your gem that does not change the version of the assets it wraps, I would investigate adding a fourth value to the version string (
Start Your Engine
Bundler created the gem as a standard Ruby module, but we want it to be a Rails engine. Edit
lib/momentjs-rails.rb to subclass
Rails::Engine like so:
1 2 3 4 5 6 7 8
Yes, the class is empty on purpose. All we’re doing here is declaring the gem as an engine. This will cause rails to add its directories to the load path when the gem is required.
Add The Assets
Download the asset files you want to include in the gem. If availble, I prefer to use the uncompressed, non-minified versions. They’ll actually be readable if you need to dig into them and you can rely on the asset pipeline to minify them in production or on precompilation. In the case of the Moment.js library, I downloaded the release version to my desktop and ran the following commands:
I used the
vendor/assets directory here because these aren’t assets I’m maintaining directly. If they were assets I was responsible for maintaining, I would put them in
app/assets though the difference is purely semantic.
Complete The Gemspec
momentjs-rails.gemspec file contains all of the details that describe the gem. Open it up and complete any pending
TODOs left by Bundler. I usually set the homepage to the GitHub repository for the gem. Additionally, I removed the
gem.test_files lines, as this gem has neither. I also prefer to change the
gem.files setting to avoid shelling out to git. I used the following pure-ruby implementation instead:
Railties needs to be declared as a dependency of the gem. Our gem needs Rails 3.1 or greater, but we’ll stop short of 4.0 for now. Use the following setting:
Build and Test
Bundler set up some default rake tasks for us that save a few characters when building. Simply run
rake build to build the gem.
I haven’t included any specs with this gem. Full-featured engines usually have a dummy Rails application in the test directory that loads the engine and can be used to run tests. While this would work to test the asset gem, I haven’t set this up yet. Instead, I generally do the following:
1 2 3 4 5 6 7 8 9
That’s quite the little dance, but the
curl command should return the contents of the moment.js file if everything is wired up correctly.
I included a very simple readme file with the Gem as its sole documentation. I brifely described Moment.js, supplied simple usage instructions, and a word about versioning. See the momentjs-rails readme.
Push To GitHub and RubyGems
Create a GitHub repository for your project, stage all of your commits, commit, and push the code to GitHub.
If you’ve never published a gem on RubyGems before, you’ll need to sign up for an account there. Your account settings will contain an API key that should be copied to
~/.gem/credentials. Once that’s done, publishing your gem is as simple as:
This will create a Git tag for this version, and push the built gem to RubyGems.