Hi, I'm Nicholas Johnson!

software engineer / trainer / AI enthusiast

Creating a Gem

A gem is a zip file containing code, typically, though not always Ruby code, plus a little metadata to help RubyGems and Bundler to manage it nicely. Gems contain a gemspec manifest file, which contains metadata, and usually a lib directory containing the code itself.

Because a gem contains Ruby code, it can do whatever you like. It can monkeypatch Rails. It can delare new modules and classes. It can extend existing objects, etc.

A gem can be stored locally, or on a gem host like rubygems.org. If you publish your gem to rubygems it will be available for anyone to download and use. If you keep it in your project, you can still use bundler, you just provide a path.

http://asciicasts.com/episodes/245-new-gem-with-bundler

In this section we’re going to create a simple gem using Bundler.

Creating a gem using Bundler

We are going to create a summarise gem which will extend the string class with methods to create a summary, and to check whether the string can be summarised, like so:

  class String
    def summarise(l=200)
      i = 0
      self.split.map{ |word| word if (i += word.length) < l}.compact.join(' ')
    end

    def summarisable?(length=200)
      return self.summarise(length) != self
    end
  end

first of all create the gem:

  bundle gem summarise

We now have a new summarize directory containing a lib directory for code, a gemspec file for metadata, and a few other files.

We also have an empty git repository initialised for us.

Gemspec

The gemspec file is the heart of your gem, it contains all the metadata about your gem. Your generated gemspec will look something like this:

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

  Gem::Specification.new do |spec|
    spec.name          = "summarise"
    spec.version       = Summarise::VERSION
    spec.authors       = ["Nicholas Johnson"]
    spec.email         = ["email@domain.com"]
    spec.description   = %q{TODO: Write a gem description}
    spec.summary       = %q{TODO: Write a gem summary}
    spec.homepage      = ""
    spec.license       = "MIT"

    spec.files         = `git ls-files`.split($/)
    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.3"
    spec.add_development_dependency "rake"
  end

Notice how spec.files is set by getting the files currently included in git.

Also notice how the version is taken from the Summarize::VERSION constant. This is defined in lib/summarise/version.rb. The version can be updated by editing this file.

Gem Contents

We now define the gem in the lib directory.

lib/sumarise.rb

This file simply imports the rest of the code.

  require "summarise/version"
  require "summarise/string_extensions"
  require "summarise/string"

  module Summarise
  end

lib/summarise/string_extensions.rb

This declares the methods we want to add.

  module Summarise
    module StringExtensions
      def summarise(l=200)
        i = 0
        self.split.map{ |word| word if (i += word.length) < l}.compact.join(' ')
      end

      def summarisable?(length=200)
        return self.summarise(length) != self
      end
    end
  end

lib/summarise/string.rb

This extends the String class.

  class String
    include Summarise::StringExtensions
  end

Building the gem

Because the gemspec uses Git to discover which files to include, we must first commit your updated files.

  git add .
  git commit -a -m "first commit"

Build the gem using the gem build command:

  gem build summarise.gemspec

You will create a file called something like: summarise-0.0.1.gem

Local Private Gems

If you want to keep your gem private, you can deploy it directly into your vendor/gems directory, like so:

First unpack it into vendor/gems:

  gem unpack summarise-0.0.1.gem --target /path_to_rails_app/vendor/gems/.

Now declare it in your Gemfile:

  gem 'summarise', :path => "#\{File.expand_path(__FILE__)}/../vendor/gems/summarise-0.0.1"

Finally install it into Gemfile.lock

  bundle install

This is a good way to develop a gem, as you can deploy it locally and work on it in situ.

Uploading your gem to RubyGems.org

If you’d like to share your gem with the community, you can also push it to RubyGems.org

You’ll need a RubyGems account:

https://rubygems.org/users/new

Now simply push the packaged gem:

  gem push summarise-0.0.1.gem

Exercise - creating a gem

Create a random string gem. I want to be able to call something like:

String.random(6)

to get back a random alphanumeric string.