Creating and installing a brand new gem - Creating a gem from scratch Part 1

This is the first in a series of posts where I build a basic gem using bundler and then post it to rubygems. Written as a way to keep all the details together for creating a gem whilst working on Faker-RPG.

You can find the other parts of the series here: Part 2, Part 3
I'll start from scratch with creating an empty gem. The final code is available on Github.

Creating an empty gem

First we need a clean rvm environment

$ rvm --create use 2.0.0@dicebag_wrapper_example
ruby-2.0.0-p451 - #gemset created /Users/Lockyy/.rvm/gems/ruby-2.0.0-p451@dicebag_wrapper_example
ruby-2.0.0-p451 - #generating dicebag_wrapper_example wrappers..................
Using /Users/Lockyy/.rvm/gems/ruby-2.0.0-p451 with gemset dicebag_wrapper_example

We'll be using bundler to create the basic framework of the gem and installing it so we will install that first.

$ gem install bundler
Fetching: bundler-1.6.3.gem (100%)
Successfully installed bundler-1.6.3
Parsing documentation for bundler-1.6.3
Installing ri documentation for bundler-1.6.3
1 gem installed

Now to create an empty gem we'll use the 'bundle gem ' command. This creates a new directory named after your gem and generates the files for an empty gem inside the new directory. The new files are listed below.

As well as creating the new files it instantiates a new git repository for the gem.

$ bundle gem dicebag_wrapper_example
      create  dicebag_wrapper_example/Gemfile
      create  dicebag_wrapper_example/Rakefile
      create  dicebag_wrapper_example/LICENSE.txt
      create  dicebag_wrapper_example/README.md
      create  dicebag_wrapper_example/.gitignore
      create  dicebag_wrapper_example/dicebag_wrapper_example.gemspec
      create  dicebag_wrapper_example/lib/dicebag_wrapper_example.rb
      create  dicebag_wrapper_example/lib/dicebag_wrapper_example/version.rb
Initializing git repo in /Users/Lockyy/Documents/Programming/Ruby/dicebag_wrapper_example

The Gemspec file

This file includes the configuration data for the gem, the file created by bundler by default is below

dicebag_wrapper_example.gemspec

# coding: utf-8
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'dicebag_wrapper_example/version'

Gem::Specification.new do |spec|
  spec.name          = "dicebag_wrapper_example"
  spec.version       = DicebagWrapperExample::VERSION
  spec.authors       = ["Daniel Lockhart"]
  spec.email         = ["daniel@lockyy.com"]
  spec.summary       = %q{TODO: Write a short summary. Required.}
  spec.description   = %q{TODO: Write a longer description. Optional.}
  spec.homepage      = ""
  spec.license       = "MIT"

  spec.files         = `git ls-files -z`.split("\x0")
  spec.executables   = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
  spec.test_files    = spec.files.grep(%r{^(test|spec|features)/})
  spec.require_paths = ["lib"]

  spec.add_development_dependency "bundler", "~> 1.6"
  spec.add_development_dependency "rake"
end

The file by default has a lot of information already filled in.

It has filled in the name, version, author name, email, license, and has filled in the files to be included in the gem when packaged.

spec.name reflects the gems name and will be what is used for requiring and installing the gem. It matches the name given in the 'bundler gem' command.

spec.version is your gem's version number, typically ruby gems follow the Semantic Versioning pattern for version numbers, you can read more about this here
DicebagWrapperExample::VERSION is set in version.rb, you should update your gem's version number in there.

lib/dicebag_wrapper_example/version.rb

module Dicer
  VERSION = "0.0.1"
end

spec.authors and spec.email are pulled from your system's git configuration. You can change them to whatever you want, they do not need to match your git configuration.

spec.license is by default the MIT license, this license is available for viewing in the LICENSE.txt file created by bundler.

spec.summary, spec.description, and spec.homepage are all unset by default. Summary is required, description and homepage are optional. I've set the three as shown below. You won't be able to package the gem until you change summary and description, the todo blocks it.

spec.summary       = "Wrapper for Dicebag"
spec.description   = "A wrapper for dicebag to provide several premade functions, i.e. DicebagWrapperExample::Roll.d6"
spec.homepage      = "https://github.com/Lockyy/dicebag_wrapper_example"

Below that the gemspec includes the gem's files.

spec.files         = `git ls-files -z`.split("\x0")
spec.executables   = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
spec.test_files    = spec.files.grep(%r{^(test|spec|features)/})
spec.require_paths = ["lib"]

spec.files includes the gem's files, these are obtained by default using git's ls-files command. This means if you created a file that it must be staged or committed for it to be included in the gem you create.

Below the file inclusion are the gem's dependencies. Development dependencies are only installed in development, runtime dependencies are installed in production, and regular dependencies in both.

By default the gem has two development dependencies:

spec.add_development_dependency "bundler", "~> 1.6"
spec.add_development_dependency "rake"

Currently it has no dependencies for production. We'll be adding one, dicebag, the gem we're creating a wrapper for.
Add the following line below the development dependencies:

spec.add_dependency 'dicebag', '~> 3.1.0'

The final gemspec file:

dicer.gemspec

# coding: utf-8
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'dicer/version'

Gem::Specification.new do |spec|
  spec.name          = "dicer"
  spec.version       = Dicer::VERSION
  spec.authors       = ["Daniel Lockhart"]
  spec.email         = ["daniel@lockyy.com"]
  spec.summary       = "Wrapper for Dicebag"
  spec.description   = "A wrapper for dicebag to provide several premade functions, i.e. Dicer.d6"
  spec.homepage      = "https://github.com/Lockyy/dicebag_wrapper_example"
  spec.license       = "MIT"

  spec.files         = `git ls-files -z`.split("\x0")
  spec.executables   = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
  spec.test_files    = spec.files.grep(%r{^(test|spec|features)/})
  spec.require_paths = ["lib"]

  spec.add_development_dependency "bundler", "~> 1.6"
  spec.add_development_dependency "rake"

  spec.add_dependency 'dicebag', '~> 3.1.0'
end

Installing and running the gem

Now at this stage the gem can already be packaged, installed, and required. Let's do that and see what we have.

So to create the gem and automatically install it we can use the 'rake install' command.

$ rake install
dicebag_wrapper_example 0.0.1 built to pkg/dicebag_wrapper_example-0.0.1.gem.
dicebag_wrapper_example (0.0.1) installed.

$ gem list dicebag_wrapper_example

*** LOCAL GEMS ***

dicebag_wrapper_example (0.0.1)

I've run 'gem list dicebag_wrapper_example' to show that our newly minted gem is correctly installed.

Now let's open a ruby console and see if our currently limited functionality is working correctly.

$ irb
2.0.0-p451 :001 > require 'dicebag_wrapper_example'
 => true
2.0.0-p451 :002 > DicebagWrapperExample::VERSION
 => "0.0.1"

Everything is working perfectly. We have an empty gem installed correctly and we can ask for its version number.

Part two will involve adding basic tests and then the logic in the gem.

Thanks for subscribing!
Like what I'm doing? Subscribe and I'll let you know when I write new stuff.
Want to unsubscribe?
Subscribe