Magicians never share their secrets. But we do. Sign up for our Ruby Magic email series and receive deep insights about garbage collection, memory allocation, concurrency and much more.
Gather ’round children, and let grandpa recount the ways of the old days when life was hard, and installing gems was a headache-inducing, hair-pulling, teeth-gritting ordeal.
Back when I was just starting in Ruby, there was no Bundler and gems had to be installed the hard way. In Rails, this meant running
rake gems:install a million times, fixing occurring bugs along the way, until the command passed with no errors. Today, we’re going to create a gem the old school way, after looking into what gems are and how they work.
RubyGems are an easy way to extend your own code with functionality written by other people. For example, instead of writing your own authentication/authorization code, you can use Devise, or if you want to re-size uploaded images you can use CarrierWave. This allows you to write reusable code that you can share with other people.
In its most basic form, a gem is nothing more than a zipped-up directory containing code and a
<name>.gemspec file. This
.gemspec file contains metadata about the gem such as its name, what files to load and its dependencies.
gem install or
bundle command downloads the zip file from the source and extracts it to your hard drive. You can find out where a gem is located by running
bundle info <gem name> or by directly opening the gem directory by running
bundle open <gem name>.
To load the gem into your application, Rubygems monkey-patches the
require function in the
Kernel class. It first tries to read the file from disk and if that doesn’t work, it then tries to resolve the file in each of the gems on your system. Once it finds the file in a gem it “activates” the gem by adding it to the load path.
If you use Bundler, it adds each specific gem to the load path during the
setup call. This saves Rubygems the hassle of trying to resolve the paths. It also prevents Ruby from loading a different version of the gem than is selected in the Gemfile(.lock).
The easiest way to create your own gem is to use Bundler to generate a gem scaffold. This includes a proper directory structure, license, code of conduct and a test environment for the gem.
However, today we’re going to create our own minimalistic gem with just two files, one containing the code and a
gemspec file that contains the metadata. Our gem will greet the user when called. Let’s start by creating a directory for our gem.
mkdir howdy cd howdy
In this directory, we’ll create a
lib folder that will contain the code and a
howdy.gemspec file that will contain the metadata. It should look something like this:
1 2 3 4 5
tree . ├── howdy.gemspec └── lib └── howdy.rb
Our howdy gem has the following code:
1 2 3 4 5
class Howdy def greet "howdy!" end end
howdy.gemspec file contains information about the version, author, etc. It also specifies the files to keep when building a gem. This prevents the users of the gem from having to download unnecessary files such as tests and other files that aren't needed to run the gem code.
1 2 3 4 5 6 7 8 9 10 11 12
Gem::Specification.new do |spec| spec.name = "howdy" spec.version = "0.0.1" spec.authors = ["Robert Beekman"] spec.email = ["firstname.lastname@example.org"] spec.summary = %(Greets the user) spec.description = %(Howdy is a gem that greets the user when called) spec.license = "MIT" spec.files = ["lib/howdy.rb"] end
To build the gem we can use the
gem build howdy.gemspec command. It generates a
howdy-0.0.1.gem file containing your code. To make the gem available to other people, you can publish it to rubygems.org with the
gem publish command.
These are the steps needed to create and publish a very basic gem. We hope you enjoyed us diving into the archeology of gems, and the old school way of making them. As mentioned before this was for educational purposes; we recommend using Bundler to generate a gem scaffold in today's world.
Peace out, youngsters! If you have any ideas, questions or comments, please don't hesitate to let us know.