How-to: Develop a RubyGem Using Bundler

I am by no means a Ruby expert. This is my humble attempt of sharing what I have learned and serves as a note to future me. It should be helpful to whoever is new to Ruby and would like to have a jump start on developing a Ruby gem.

Prerequisites

Ruby

Ruby is a powerful programming language which gains popularity with the rise of many active open source projects and communities built around it. Before thinking build some library from scratch, there might already be a free open source library which you can easily leverage. Therefore saving tons of development time.

gem

A gem is a Ruby library packaged in a standard format. You can easily download and install gems to help your development through RubyGems, which will be discussed later in the article. In a nutshell, RubyGems works a lot like apt or yum.

rvm

rvm stands for Ruby enVironment Manager. rvm allows you to switch to a specific Ruby version and a specific set of gems for different projects. It is highly recommended that you install rvm to manage different versions of Ruby and gemsets for your projects.

To install rvm, do.

1
curl -L https://get.rvm.io | bash -s stable –ruby

To install Ruby 1.9.3, do.

1
2
3
rvm install 1.9.3
rvm use 1.9.3
rvm rubygems latest

Use rvm list to verify your Ruby installation. Use rvm list known to see a list of available Ruby versions that rvm is capable of installing.

Notice the last command rvm rubygems lastest will install latest version of rubygems, which is a Ruby library packaging and distribution manager.

RubyGems

As mentioned above, with RubyGems, installing libraries published by others is made very easy.

1
gem install [gem_name]

Substitute [gem_name] in above command to install the actual gem you want.

  • Note: A gem may depend on other gems. Therefore installing one gem may result in installing multiple other gems.

  • Tip: You can specify which version of the gem you want to install by gem install [gem_name] -v ‘1.0.0’, gem install [gem_name] -v ‘>=1.0.0’, or gem install [gem_name] -v ‘~>1.0.0’

  • Important: If you are using rvm like many people recommended, DO NOT use sudo when installing gems. You may come across many tutorials telling you to do things like sudo gem install devise. DON’T do that. Because sudo will mess up your rvm. See more details on why not using sudo here if you are interested.

Git

Install git, do.

1
sudo apt-get install git

Building your first gem using Bundler

Bundler is a great gem which can do a lot of things. Scaffolding a new gem is one of its many features.

I will be using colorfulMD as an example gem for this walk through. ColorfulMD colorfies your markdown text by adding <font color=”a_color”></color> tag to certain text defined in a config.yaml file.

There is a very good guide here. I will be explaining things that might not be too obvious. At least not so obvious to me.

Getting started

To install Bundler, do.

1
gem install bundler

To create a new gem, do.

1
bundle gem colorfulMD

This should create a new folder called colorfulMD. Some skeleton files and directories will be created under ./colorfulMD.

If you have Git installed, bundle gem colorfulMD also initializes a Git repository in ./colorfulMD so you can start committing right away.

  • Tip: You can add .*~ and *~ to .gitignore file if your text editor creates tmp file ending with ~. This will prevent unnecessary files get pushed into your repo.

  • Tip: If you are using IDEs like RubyMine. You may also want to add .idea to your .gitignore file.

  • Tip: Usually you can define all your gem dependencies in your Gemfile. But Bundler creates [gem_name].gemspec file by default and defines dependencies there.

  • Tip: It is a good practice that you put a .rvmrc like this one in colorfulMD folder. cd out of colorfulMD folder and cd back in, you should see that rvm will initialize a independent Ruby environment for this project. This means when working in colorfulMD folder, you are using different Ruby version or gemsets than the global ones.

Following BDD

I didn’t find BDD particularly intriguing. But as for this walk through, we shall start writing our test cases even before any actual coding following BDD discipline.

Open colorfulMD.gemspec file and add the following. (example here)

1
2
s.add_development_dependency "cucumber"
s.add_development_dependency "aruba"

(add_development_dependency means those gems will only be installed for development use.)

cucumber gem and aruba gem are great testing tools help us generate test code following BDD discipline. (here is a pretty good introductive tutorial for cucumber) bundle install is a command that goes through gemspec file, figure out the dependency and installs gems needed.

Run bundle install to install dependencies.

Inside colorfulMD, do mkdir features. cd into features and create directory structure and files like shown here.

  • /support: Ruby files in support folder will be executed before cucumber runs any actual tests, therefore it is good to put code that sets up environment here.

  • /step_definitions: Actual test code for a feature will be in this folder.

  • cli.feature: Write your feature in this file. You can name it differently.

I put down the following into my cli.feature file.

1
2
3
4
5
6
7
8
9
10
11
12
Feature: CLI
  In order to colorfy markdown
  As a CLI
  I want to add <font color> tag to some text

  Scenario: add <font color="given_color"> tag to "given_string"
    Given three arguments "given_color", "given_string" and "text"
    When I call colorfy of CLI with "red" "apple" in "I had an apple this afternoon"
    Then the output should equal
      """
      I had an <font color="red">apple</font> this afternoon
      """

It doesn’t seem to really matter what you put right after Feature: and Scenario:. The main thing you want to define here is what follows Given, When and Then keywords. It is relatively comprehensive. Given defines what argument(s) your method is taking. (you don’t need given if you are not going to do anything fancy with the arguments) I specified values of the three arguments with When keyword. Note how close the test case is to natural language. That is supposed to be the beauty of BDD. Given the arguments specified after When, I put down the expected output after Then keyword. Triple quotes was used for escaping the quotes in the expected output.

After all that, run bundle exec cucumber features/. Copy paste the output after You can implement step definitions for undefined steps with these snippets: to /step_definitions/colorfy_steps.rb. (you should copy paste something close to the below)

1
2
3
4
5
6
7
8
9
10
11
Given(/^three arguments "(.*?)", "(.*?)" and "(.*?)"$/) do |arg1, arg2, arg3|
  pending # express the regexp above with the code you wish you had
end

When(/^I call colorfy of CLI with "(.*?)" "(.*?)" in "(.*?)"$/) do |arg1, arg2, arg3|
  pending # express the regexp above with the code you wish you had
end

Then(/^the output should equal$/) do |string|
  pending # express the regexp above with the code you wish you had
end

Now add some actual testing code to colorfy_steps.rb like shown here.

Implementing CLI

Add spec.add_dependency "thor" to your gemspec file. And run bundle install.

At project root, do mkdir lib/colorfulMD, touch lib/colorfulMD/cli.rb and touch lib/colorfulMD/colorfulMD.rb. Paste the following to cli.rb and colorfulMD.rb respectively.

1
2
3
4
5
6
7
8
9
10
11
12
13
# $LOAD_PATH.unshift(File.dirname(__FILE__))
require 'thor'
require 'colorfulMD'

module ColorfulMD
  class CLI < Thor
    desc "colorfy GIVEN_COLOR GIVEN_STRING TEXT", "colorfy a given word by a given color defined in yaml"
    def colorfy(given_color, given_string, text)
        clrmd = ColorfulMD::Colorfulmd.new
        clrmd.colorfy(given_color, given_string, text)
    end
  end
end
1
2
3
4
5
6
7
8
module ColorfulMD
    class Colorfulmd
        # colorfy
        def colorfy(given_color, given_string, text)
          text.gsub(given_string, "<font color=\"" + given_color + "\">" + given_string + "</font>" )
        end
    end
end

At this point, if you run bundle exec cucumber features, you should see you have passed all cucumber test! Systems all green!

And now we just need to make a cli executable.

At project root, do mkdir bin. Do touch bin/colorfulMD and sudo chmod +x bin/colorfulMD. bin/colorfulMD will be your CLI executable for your gem. Paste the following code to bin/colorfulMD

1
2
3
4
5
#!/usr/bin/env ruby
$LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib/colorfulMD')
require 'colorfulMD/cli'
require 'colorfulMD'
ColorfulMD::CLI.start

Run your executable by bundle exec bin/colorfulMD colorfy red apple apple. You can modify the code to have colorfy method output string by replacing text.gsub(given_string, "<font color=\"" + given_color + "\">" + given_string + "</font>" ) with puts text.gsub(given_string, "<font color=\"" + given_color + "\">" + given_string + "</font>" ) in colorfulMD.rb.

This concludes the tutorial.

Comments