Making a Command-line Ruby Gem - Write, Build, and Push

April 5, 2018

A shiny red gem

Anyone who has used Ruby before knows that gems are the spice of life. They are easy to install and distribute, even easier to use, and most importantly, they provide useful functionality. It's not uncommon to see entire software projects centered around a single well-made gem. Take any Ruby on Rails project, and you'll see this in practice.

Gems are, in a nutshell, Ruby code packaged for easy distribution and use. It's easy to make your first gem, and even easier to reuse someone else's! Thousands upon thousands of gems are available on RubyGems.org, ready to solve your problems.

In this article, we're going to do two things. First, we'll get started by creating our first gem and pushing it to RubyGems. This way, everyone in the world will be able to install and use it with the

gem install
command. Not bad!

After that, we're going to modify the gem so that you can use it anywhere on your computer, just by typing its name into the console. Sweet!

Requirements

In order to complete this tutorial, you'll need a version of Ruby installed on your computer. If you don't already have it, consider first installing RVM (Ruby Version Manager), and then selecting the version of Ruby you want via RVM. This will save you countless headaches in the future should you need to switch Ruby versions for any reason.

For the record, I used Ruby version

2.3.3p200
in the creation of this tutorial. As long what you use is at least somewhat recent, you should be fine.

Note that this tutorial was created using Ubuntu Linux (on Windows). I'm sure there's a way to accomplish this using only Windows, but I have to leave that for you to figure out.

Setting Up the Gem

Let's start by creating the initial file structure of our gem. Create an empty directory and

cd
to it. For this tutorial, I'll be creating the
pagekey
gem. When you make yours, be sure to use a unique name that's not already taken on RubyGems.org.

mkdir pagekey
cd pagekey

The skeleton of our gem will consist of a

.gemspec
file to specify the gem configuration, a
lib
folder to hold our source, and of course our first source file. I'll create these now:

touch pagekey.gemspec
mkdir lib
touch lib/pagekey.rb
mkdir bin

Our file structure should look like this:

pagekey
|-- pagekey.gemspec
|-- bin
|-- lib
|-- pagekey.rb

Now I'll edit

pagekey.gemspec
and include some information about our gem. Remember to update this with information specific to your own gem.

Gem::Specification.new do |s|
s.name = 'pagekey'
s.version = '0.1.0'
s.platform = Gem::Platform::RUBY
s.summary = 'PageKey Solutions tutorial gem'
s.description = "Created in a tutorial found on blog.pageKeySolutions.com. Doesn't do too much!"
s.authors = ['Steve G']
s.email = ['info@pagekeysolutions.com']
s.homepage = 'http://rubygems.org/gems/pagekey'
s.license = 'MIT'
s.files = Dir.glob("{lib,bin}/**/*") # This includes all files under the lib directory recursively, so we don't have to add each one individually.
s.require_path = 'lib'
end

Perfect. Now that the gem is configured, let's add some really basic code and test it out. To accomplish this, I'll edit

lib/pagekey.rb
:

module PageKey
def self.hello_world
"Good morning world and all who inhabit it!"
end
end

Excellent. We're all set up as far as code goes. Now we can leverage Ruby's wonderfully streamlined gem workflow to build and test our creation. Watch how easy it is.

Building and Testing

To package everything up, we will provide our

.gemspec
file as the only input:

gem build pagekey.gemspec

If all goes well, you'll see:

Successfully built RubyGem
Name: pagekey
Version: 0.0.0
File: pagekey-0.0.0.gem

Now we will install it so that it will be accessible from our code.

gem install ./pagekey-0.0.0.gem

For projects that use your gem, you may want to include it in your

Gemfile
and run
bundle install
:

# Inside Gemfile:
gem 'pagekey', '~> 0.0.0'

Time for the moment of truth. We can test it on

irb
, the interactive ruby console. Type
irb
and it will start the interpreter.

> require 'pagekey'
=> true
> PageKey::hello_world
=> "Good morning world and all who inhabit it!"

Wonderful. It works as expected.

Adding the CLI

Right now, if I type

pagekey
, I'll get an angry message from my console, like:
pagekey: command not found
. This isn't good - I want to use my gem just like
ls
,
cat
,
awk
, or any other useful program!

In order to make this gem available under a specific terminal command, we'll have to create an executable and link to it in our

.gemspec
file.

The executable will basically be a short Ruby script that accepts command line arguments and routes them to the gem's code in

./lib
.

Create a directory to hold the file with

mkdir bin
and edit the
bin/pagekey
file:

#!/usr/bin/env ruby
require 'pagekey'
puts PageKey::hello_world

Ensure that the file is executable by running

chmod +x bin/pagekey
. Our next step is to specify this executable in
pagekey.gemspec
so that it will be added to the system PATH variable when the gem is installed. Add the following line:

s.executables = ['pagekey']

Now the gem will look in the

bin
directory for the
pagekey
executable when you type
pagekey
on the command line.

Pushing and Publishing

After these efforts, our beautiful gem is ready to go. But, until it's in the open air, I'd it's nothing but a diamond in the rough (how pun-tastic!). Let's get this thing out there in the real world.

Make sure that you build your gem as described above with

gem build pagekey.gemspec
. Then, create an account at rubygems.org. Replace
USERNAME
with your RubyGems username in the following snippet, and run it:

curl -u USERNAME https://rubygems.org/api/v1/api_key.yaml >
~/.gem/credentials; chmod 0600 ~/.gem/credentials

This will set up your system with the proper credentials to publish gems to your RubyGems account. The final step is very simple: Just push it!

gem push pagekey-0.0.0.gem

The gem will upload, and it will become available for the world to see and download!

Bonus: Useful Rake Automation

One more tool that may be helpful as you get into the flow of gem development is

rake
, which allows you to automate processes using the Ruby programming language. In the base directory for your gem, add a
Rakefile
to hold your scripts. I've included an example that proved very helpful as I pursued my own little gem project:

GEM_NAME = "pagekey"
GEM_VERSION = "0.0.0"
task :default => :build
task :build do
system "gem build " + GEM_NAME + ".gemspec"
end
task :install => :build do
system "gem install " + GEM_NAME + "-" + GEM_VERSION + ".gem"
end
task :publish => :build do
system 'gem push ' + GEM_NAME + "-" + GEM_VERSION + ".gem"
end
task :clean do
system "rm *.gem"
end

To run these commands, just type

rake
,
rake build
,
rake install
,
rake publish
, or
rake clean
and see what happens.

The outcome of each task is fairly self-explanatory. The build command just builds the gem for you. The install command builds the gem and installs it, so that you can

require
it and try it out. The publish command also builds the gem, after which it takes care of pushing the gem for you.

The arrow

=>
indicates a dependency of tasks. For example,
task :install => :build
indicates that
:install
depends on
:build
, and so every time that
rake install
runs, the commands under the
:build
task run beforehand.

This

Rakefile
relies heavily on the
system
command, which utilizes the shell interpreter of the system you're runnning on. This means that the file is OS specific. All of these tasks can likely be performed in pure Ruby, but I found it much easier, especially for simple, small projects, to write everything as a
system
command. As you grow, however, it may be best to move away from OS-specific code.

As a challenge, I'll suggest to you one way to greatly improve this

Rakefile
.

Right now, you'd have to update the

.gemspec
file and your
Rakefile
if you wanted to change the version from
0.0.0
to
0.0.1
. This is not ideal. Modify your gem's code such that it references the
GEM_VERSION
in only one place within the application. Since everything is written in Ruby, you would be able to
require
a configuration file that contains the version as a variable and use that. Perhaps it would also be helpful to include a task in your
Rakefile
called
:increment
, which updates your gem version by incrementing the last number (moving
0.0.0
to
0.0.1
automatically).

Wrapping Up

Thanks for reading. I hope this article will help you get started writing your first Ruby gem, so that you can contribute to the large and impressive open-source Ruby community. Best of luck!