Categories
Technical

Creating a Gem in Ruby: a Jewelcrafting guide for Developers

What is a Gem?

Gems are small chunks of code used to perform specific duties without interfering with, or becoming a direct part of our code. The code required for a gem will still be a part of our project’s ecosystem, but we will ultimately remove some overhead, and avoid unnecessary duplication by using a gem instead of coding it ourselves.


Why create a Gem?

Gems are a helpful byproduct created when you either have a piece of self contained code, or else when a developer realizes the code might benefit others. If we want to use that code elsewhere or want to share it, that’s when it’s time to make a gem.


How to create a Gem

OK, so we have some standalone code that could help others and that we think works well as a RubyGem. We want to package it up so that it’s easy to distribute and install. To do this we need to do three things:

  • Put our classes into a namespace to avoid clashing with similarly named classes in other Gems
  • Organise our files into directories following the RubyGems conventions
  • Create the RubyGem and publish it to a public server

Namespacing the code

Right now, all of our classes and modules would be defined at the top level. As our goal is to distribute this, and the people who install our gem might use it as a library rather than a standalone program, we need to namespace our classes. This prevents naming collisions. For example, if someone sees our gem and likes our User class and wants to use it in their code, they would require it. The problem comes if they already have a User class in their code, it would require both, merging it into a Frankenstein class, and nobody wants that.

To avoid this, we need to wrap our classes and specs in a unique namespace / module. As an example of how that would look:

class User
  …
end

Would turn into:

module MyAmazingGem
  class User
    ...
  end
end

This would turn the User class into the MyAmazingGem::User class, thus making it different and avoiding confusion. I have used MyAmazingGem as the module name here, but you could use anything.

It is always a good idea to run any tests before making any changes. As a note, this blog will assume we are using RSpec to test but some programmers may be using other gems.


Organising our files

Our next task is to organise our files into directories according to the RubyGems conventions. This way, anyone who installs our gem has a good idea of where to find everything. To do this we create three subdirectories beneath our top level directory:

  • bin: Contains the main program file, and any related files
  • lib: Contains all the code for the application (sometimes called ‘library files’)
  • spec: Contains all the RSpec files

It is also convention for a gem to contain two top-level plain text documentation files: a README (a short description of the gem and how to use it) and a LICENSE (the license under which you distribute your code – see the MIT license for reference. It doesn’t have to be long but try and have one or else people won’t know if they can use it).
Remember, after moving all those files around we may have to change our require_relative statements to require. Require then checks for a load path. So the statements may move from: require_relative ‘user’ to require ‘my_amazing_gem/user’.


Building the gem

Now we have everything we need to package up our RubyGem, we do that by writing a Gem specification file, or gemspec. The gemspec defines what’s in the gem, who made it, the version of the gem, etc. The gemspec will be a file called gem_name.gemspec, the .gemspec part being important, and will be located in the top-level application directory. It will look something like this:

Gem::Specification.new do |s|
  s.name         = "INSERT GEM NAME HERE"
  s.version      = "INSERT VERSION HERE"
  s.author       = "INSERT YOUR NAME HERE"
  s.email        = "INSERT YOUR EMAIL HERE"
  s.homepage     = "INSERT HOMEPAGE URL HERE"
  s.summary      = "INSERT SUMMARY HERE"
  s.description  = File.read(File.join(File.dirname(__FILE__), 'README'))
  s.licenses     = ['MIT']

  s.files         = Dir["{bin,lib,spec}/**/*"] + %w(LICENSE README)
  s.test_files    = Dir["spec/**/*"]
  s.executables   = [ 'An array of command-line scripts that RubyGems should install when folks install the gem' ]

  s.required_ruby_version = '>=1.9'
  s.add_development_dependency 'rspec', '~> 2.8', '>= 2.8.0'
end

  • name: This field contains the gem name, which should be all lowercase letters with underscores or hyphens separating words.
  • version: This field contains the gem version, which generally follows the major.minor.release numbering notation. For example, “1.0.0” would indicate the first major release of this gem.
  • author: Your name.
  • email: Your email.
  • homepage: A URL to the gem’s homepage.
  • summary: A really short description of the gem.
  • description: A longer description of the gem.
  • licenses: An array of license abbreviations for the gem.
  • files: An array of files to include in the gem package.
  • test_files: An array of just the test (spec) files.
  • executables: An array of command-line scripts that RubyGems should install when someone installs the gem.
  • required_ruby_version: This is really important. Since we used require_relative, which is only supported in Ruby 1.9 or newer, this ensures that the gem can only be run with that version of Ruby.
  • add_development_dependency: Since we use RSpec, it’s good to list it as a development dependency.

Once the gemspec is all filled in correctly it’s time to build the gem. To do this we need to run: gem build gem_name.gemspec. The build command reads and executes the gemspec, then generates a packaged .gem file. E.g if you named the gem ‘gem_name’ and set the version to 1.0.0, then you’ll end up with a file called: gem_name-1.0.0.gem.


Publishing the Gem

At this point, we could share our packaged .gem file with people by sending it to them or putting it on a shared server.  However we could share it with the world on RubyGems.org. First of all, we need to make an account on the site. From here, we need to check there isn’t already a gem with the same name (Since RubyGems.org is the main repository for most open-source Ruby code, it’s a possibility). Once we are sure the gem name is available we can push the .gem file to the public server by typing: gem push gem_name-1.0.0.gem. If this is the first time you’re publishing a gem, you’ll be prompted for your username and password. These credentials are stored for future pushes. 

Once your gem is pushed it will be available for installation by anyone in the world in just a few seconds. To check and search for it on the public server we can use: gem search -r gem_name. You should see a positive response. To install the gem from the remote server we can use: gem install gem_name.

We are done! Congratulations and happy gem building!

By Alex Clifford

Software Developer at Resolver