Video

Want to see the full-length video right now for free?

Sign In with GitHub for Free Access

Notes

Why and When

Logic That Is Decoupled From Application Domain

When you find that you have code in your app that is not necessarily specific to your application, but instead is more general, you can consider extracting the logic to a gem. The act of extracting the code will help establish and maintain a clean line and prevent coupling of that logic to more application-specific concerns.

Note: this does not inherently require you to reuse this gem in more than one codebase or publish it to any gem server, public or private.

Logic Shared Between Apps

A more common use case for building or extracting a gem is to clean up cases where you have similar logic implemented across multiple applications. By extracting the code into a gem, and then using that across the multiple applications, you can ensure that the same code is used in all apps.

How to Extract a Gem

Stage It In lib

Often the best way to build a gem is to start with the code in your app. When you are looking to extract logic from an app into a gem, this may already be the case. But even new code can start in an app with the plan to extract it later.

The key aspect of working with gem code in an app is to keep it as isolated as possible. The code should collected in lib (with a matching spec/lib or test/lib directory) and should be as decoupled as possible. Try to avoid using application helpers or Rails helpers in your soon-to-be-gemified code.

Extracting Payload from Upcase Exercises

As an example of extracting a gem, check out this commit from the Upcase Exercises repo where Joe extracts the code for the Payload dependency injection framework, replacing it with the gemified version. The extraction in the commit is very clean as Joe had already done a great job of isolating and collecting the code in lib.

Note You'll need to have access to the Upcase Exercises repo, which all Professional subscribers can get by visiting the repositories page on Upcase.

Building the Gem

Use Bundler to Create the Gem

When you're ready to actually create the gem, you can use bundler to build out the skeleton and boiler plate files needed.

$ bundle gem foo
      create  foo/Gemfile
      create  foo/Rakefile
      create  foo/LICENSE.txt
      create  foo/README.md
      create  foo/.gitignore
      create  foo/foo.gemspec
      create  foo/lib/foo.rb
      create  foo/lib/foo/version.rb
Initializing git repo in ~/code/foo

From there, you can go to work in the lib/foo.rb (or equivalent for your gem name) file, building out your code.

Stephen Ball has a great two part series on writing a gem with tons of detail on the full process.

Bundle Console

You'll often want to poke around with your new gem code in the console while working, and luckily bundler makes this very easy. Run bundle console and you'll be dropped into an irb session with your gem's code loaded and ready for testing.

You can also use our old favorite pry in place of irb by running

$ bundle config console pry

and adding pry as a development dependency in your gemspec.

# foo.gemspec file
Gem::Specification.new do |spec|
  # additional gem config here...
  spec.add_development_dependency "pry"
end

Private Gems

The typical method for working with gems is to host them on Rubygems.org, but in some cases you may need to keep the gem code private and Rubygems does not support this.

One option for keeping your gem private is to use a service like Gemfury which acts as an additional source in your Gemfile and allows you to host and install your private gems via their infrastructure. This is likely the most direct route, but is a paid service.

There are other solutions including using a private Github repo as your gem's source and including an oauth token in the URL or vendoring your private gem code. These are a bit more work and are less clean in their implementation, but are free. Check out this gist which contains a summary and discussion of some of these approaches and the pros and cons of each.

Working From A Local Checkout

When working on the initial version or extraction of your gem, you'll often be in a period of high churn and iteration. For these times it can be useful to work against your local checkout of the gem, rather than against a published version of the gem.

Once again, bundler has our back and allows us to use the path option in our Gemfile to point at a local path:

# in Gemfile
gem "payload", path: "~/code/work/upcase/payload"

Note - While this saves you from having to download and build your gem for each change, you will still have to bundle and restart the server in the test app each time you make changes in the gem.

Testing Your Gem

A good test suite is critical to building a quality gem, but as more people use it, the configurations and environments you must support can grow quickly. The following tips can help you stay on top of this and keep your gem working across all supported configurations.

Appraisal

Appraisal is a gem we built here at thoughtbot to make it easier to test our gems in a variety of contexts. From it's Readme:

Appraisal integrates with bundler and rake to test your library against different versions of dependencies in repeatable scenarios called "appraisals."

As an example, check out FactoryGirl Rails' Appraisals file.

Travis CI's Build Matrix

In order to get full test coverage of dependencies / gems as well as Ruby versions, we use Travis CI's build matrix along with Appraisal to test across the full matrix of gem & ruby versions.

Check out Clearance's travis.yml config file for an example of how we configure this.

While you can use the Travis build matrix to fully cover the gem and ruby version configuration, we prefer to use Travis just to change ruby versions, and rely on Appraisal to manage the dependency sets as this allows us to reproduce these builds locally.

Rails Specific Gem Functionality

If you find yourself writing a gem for use within Rails applications, there are a few special features and configuration points that Rails exposes to allow your gem the access it needs.

Railties

Railties allow you to hook into Rails' internals in order to add configuration or provide generators and rake tasks. Each of the Rails gems like ActiveSupport, ActionView, and ActiveJob are implemented as Railties.

In addition, there are many community gems that act as Railties to provide more functionality. Some examples:

Rails Engines

Rails Engines are a subclass of Railties, so they inherit all of the configuration power of Railties, but they go further allowing you to include arbitrary app code. Engines are like including another entire Rails app with routes, controllers, models, and views.

Some examples of Engines being used to great effect:

  • Resque Web is a Rails engine that provides a full admin interface to your Resque setup.
  • Clearance is also an engine, providing routes, views, and controllers for user authentication.

Asset Gems

A subset of the functionality provided by acting as an Rails Engine is often used to provide assets for the asset pipeline. Like with your app, the assets paths {app,lib,vendor}/assets/{javascripts,stylesheets,images} will all be added to the asset pipeline load path, allowing you to reference a gem-provided asset as if it were in your application.

Some examples of asset gem usage:

  • jquery-rails is an example of an asset gem that provides the jQuery and jQuery UJS JavaScript files via the gem.
  • kalendae_assets is an asset gem that brings in JS, CSS, and images for a calendar widget.
  • momentjs-rails is a gemification of momentjs managed by none other than Derek Prior.

Derek also has a post on how to Gemify your assets that covers the process.

Rails-Assets.org As An Alternative

While it is great to know that we can use asset gems to provide asset files, the advent of Bower, a front end package manager, has to a certain degree superseded the asset gem technique.

Specifically Rails-assets.org is a free service that acts as an alternate gem source and provides an asset gem version of all packages on Bower allowing us to leverage those packages instead of duplicating them in the Rails world.

Conclusion

Now that you know all the tips and tools you can use to build and extract gems, why not try it out today and build your first gem!