Sharing forms a bridge between everyone involved and it makes things grow. That’s the basis for the open-source movement, which gave way and allowed so many great things to happen – especially in the recent years.
This principal applies for Ruby and Ruby based applications. That’s why - and how - a developer can get started working on making their idea a reality so rapidly, thanks to all the available tools, libraries, and frameworks that they can take advantage of.
In this DigitalOcean article, we aim to help those trying to find ways to give back to the community by sharing their own Ruby-based or Ruby-related creations. We are going to shed some light behind the mystery of packaging code for others to be able to easy download it as a Gem using the RubyGems package manager, which makes the whole process a breeze.
One of the ways of distributing applications, libraries, or other programming related code bundles is to put them in archives called packages. Application packages contain already compiled and ready-to-use software in an easy-to-keep-track-of and easy-to-use way. They usually come with additional files that contain information about the package and sometimes documentation as well.
Packaging applications, therefore, consists of following a set format defined by package management tools (i.e. the RubyGems) and using these tools to share them with others in an easily accessible way.
In this tutorial, we will begin with understanding RubyGems, the Gem package format, and then learn how to package a Ruby application from start to finish, beginning with creating the package structure to contain the code (and other related material).
Note: The subject matter of this article is to package applications. This section contains a summary of related tools and materials. To learn more about them, you can read the introductory first part of our RubyGems series.
RubyGems is the default package manager for Ruby. It helps with all application package lifecycle from downloading to distributing Ruby applications and relevant binaries or libraries. RubyGems is a powerful package management tool which provides the developers a standardised structure for packing application in archives called Ruby Gems.
A Gem is a Ruby application package which can contain anything from a collection of code to libraries, and/or list of dependencies that the packaged code actually needs to run.
Gem packages contain different sets of components. Each component gets placed inside a dedicated location within the gem bundle.
All the below elements (and more) can go inside Gems:
Application code;
Tests;
Description of dependencies;
Binaries;
Relevant Documentation;
Information regarding the package (gemspec).
Gems are formed of a structure similar to the following:
/[package_name] # 1
|__ /bin # 2
|__ /lib # 3
|__ /test # 4
|__ README # 5
|__ Rakefile # 6
|__ [name].gemspec # 7
[package_name]:
The main root directory of the Gem package.
/bin:
Location of the executable binaries if the package has any.
/lib:
Directory containing the main Ruby application code (inc. modules).
/test:
Location of test files.
Rakefile:
The Rake-file for libraries which use Rake for builds.
[packagename].gemspec:
*.gemspec file, which has the name of the main directory, contains all package meta-data, e.g. name, version, directories etc.
In case you don’t have Ruby installed already, you can follow one of the two links below to get it properly set up on your platform of choice.
CentOS / Rhel:
How To Install Ruby 2.1.0 On CentOS 6.5 Using RVM
Ubuntu / Debian:
How To Install Ruby 2.1.0 On Ubuntu 13 With RVM
One of the tools we will be using for creating Gems is Bundler. Once Ruby and thus RubyGems are installed on your system, you can use the ``gem` command to get bundler.
Run the following to install bundler
using gem
:
gem install bundler
# Successfully installed bundler-1.5.3
# Parsing documentation for bundler-1.5.3
# Done installing documentation for bundler after 3 seconds
# 1 gem installed
There are several ways to start creating a Gem package. One the methods is to use the popular Bundler, a Ruby environment and dependency manager that helps with an application’s requirements and maintenance of the code. This tool can also be used to scaffold a Gem distribution directory to kick-start the packaging process.
Gem packages are kept in package directories which should be named after your package as we have discussed in the previous structuring section. Since these are simple locations found on the file system, you can use the Unix mkdir
command to create them one by one… or get Bundler to do the job.
Run the following command to scaffold all the necessary directories inside a folder, named after your Gem’s desired name:
# Usage: [sudo] bundle gem [your chosen gem name]
# Example:
bundle gem my_gem
Note: Gem names need to be unique. Therefore, you should search and make sure that the name you would like to use for your Gem is not already chosen by someone else’s project. In order to verify, you can visit and search on RubyGems.org.
The above command will run a series of commands to create our package structure, e.g.:
# bundle gem my_gem
# create my_gem/Gemfile
# create my_gem/Rakefile
# create my_gem/LICENSE.txt
# create my_gem/README.md
# create my_gem/.gitignore
# create my_gem/my_gem.gemspec
# create my_gem/lib/my_gem.rb
# create my_gem/lib/my_gem/version.rb
# Initializing git repo in .../my_gem
As you will see, Bundler has also created a brand-new Git repository which comes in handy with various versioning operations.
Note: If you would like to learn more about how to work with Git, check out the DigitalOcean community articles on the subject.
The .gemspec
file contains some absolutely vital information regarding Gem packages. Ever Gem must be shipped with one where meta-data from Gem name to version and description to folders to be included by Gem are found.
Tip: .gemspec
files are regular Ruby scripts – which means that they are programmable.
To see the contents of the generic gemspec created by Bundler, use the following command:
cat my_gem/my_gem.gemspec
# # coding: utf-8
# lib = File.expand_path('../lib', __FILE__)
# $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
# require 'my_gem/version'
# Gem::Specification.new do |spec|
# spec.name = "my_gem"
# spec.version = MyGem::VERSION
# spec.authors = ["Maintainer Name"]
# spec.email = ["maintainer@email.address"]
# 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.5"
# spec.add_development_dependency "rake"
# end
# mba:Git
You can either edit this file now, or before each time you package and publish.
In order to modify this file, you can use the following command to edit it using nano:
nano my_gem/my_gem.gemspec
This will open up the nano text editor.
One of the recommended additional information you might want to declare here is the minimum Ruby interpreter version required to run your code.
You can do this with the required_ruby_version
declaration, which can be added towards the bottom end of the file for consistency.
For example:
# ..
# Declare that the Gem is compatible with
# version 2.0 or greater
spec.required_ruby_version = ">= 2.0"
spec.add_development_dependency "bundler", "~> 1.5"
spec.add_development_dependency "rake"
end
Once you are done editing your file, press CTRL+X and confirm with Y to save and exit.
Note: Do not forget to modify the declarations that contain a “to-do” placeholder (i.e. %q{TODO:
) such as the spec.description
.
Your library (or application, framework, etc.) shall always go inside the /lib
directory. Inside this directory, there shall be a Ruby script named exactly the same way as your Gem. This file is the main one that gets imported when another application depends on your Gem.
The recommended and tidiest way to place your application code is to divide it into bits and place them in a directory, inside /lib
, where they are used and made available by my_gem.rb
to the public.
When you look at the contents of the /lib
directory, you will see that the main Ruby script and a directory to contain your code is ready:
ls -l my_gem/lib
# drwxr-xr-x 3 user staff 102 dd Mmm hh:mm my_gem
# -rw-r--r-- 1 user staff 70 dd Mmm hh:mm my_gem.rb
And the my_gem
directory inside /lib
comes with a version file:
cat my_gem/lib/my_gem/version.rb
module MyGem
VERSION = "0.0.1"
end
This VERSION
number is set to be automatically imported and used inside the *.gemspec
file by Bundler. You can modify it here and change it to match your Gem’s current version.
You should move all your code here to be used by your application.
As an example, let’s create a Hello [name]!
module.
nano my_gem/lib/my_gem/hail.rb
Place the below example inside:
class Hail
def self.name(n = "Dalek")
n
end
end
Press CTRL+X and confirm with Y to save and exit.
In the previous step, we have learned that for the purposes of keeping everything in order, applications with especially a lot of classes should be separated into pieces with all elements placed inside the /lib/[gem-name]
directory.
Let’s see how we can modify the main Ruby script that gets imported when somebody uses your Gem.
Run the following command to edit the imported Ruby file inside /lib
using nano:
# Usage: [sudo] nano my_gem/lib/[gem name].rb
nano my_gem/lib/my_gem.rb
You will see a very short script similar to the one below:
require "my_gem/version"
module MyGem
# Your code goes here...
end
Here, you should import all your classes and code from the /lib/[gem name]
directory and use them.
In our case, let’s see how to use Hail
class which we created in the previous step.
Modify your code similarly to below example:
require "my_gem/version"
require "my_gem/hail"
module MyGem
def self.hi(n = "Default Name")
hail = Hail
Hail.name(n)
end
end
Press CTRL+X and confirm with Y to save and exit.
Note: Although there is no need to make Hail
an instantiable class, for the purposes of demonstration, we have made it so and left MyGem as a module to use its methods directly.
Once you move your code inside and modify your main imported script, you will want to make sure everything works, naturally. The simplest way to go about doing this is to use Bundler again – and not by installing the Gem.
First, let’s enter the Gem directory and then use Bundler’s console feature:
cd my_gem
bundler console
This will load your Gem using the information from the *.gemspec
and let you get to work, e.g.:
bundler console
# irb(main):001:0> MyGem.hi("Hello world!")
# => "Hello world!"
In a real world scenario, it is highly likely that your Gem itself will be dependent on others.
These dependencies are also listed in the *.gemspec
file.
Run the following command to edit the file using nano:
nano my_gem/my_gem.gemspec
Add the following instructions at an appropriate block to list dependencies:
# Usage: spec.add_runtime_dependency "[gem name]", [[version]]
spec.add_runtime_dependency "activesupport", [">= 4.0"]
Press CTRL+X and confirm with Y to save and exit.
Note: You can list all necessary dependencies by repeating the instructions successively on the *.gemspec
file.
Once your Gem is ready to be shipped, you should commit the Git repository for versioning.
Use the following command to commit with Git:
git commit -m "Version 0.1.0"
Git, then, is going to commit all your code and give you the results:
# [master (root-commit) d4640b8] Version 0.1.0
# 8 files changed, 104 insertions(+)
# create mode 100644 .gitignore
# create mode 100644 Gemfile
# create mode 100644 LICENSE.txt
# create mode 100644 README.md
# create mode 100644 Rakefile
# create mode 100644 lib/my_gem.rb
# create mode 100644 lib/my_gem/version.rb
# create mode 100644 my_gem.gemspec
Once you are happy with your Gem, you can release it to the world on RubyGems.org.
Note: In order to release your code, you will need to have an account at https://rubygems.org/sign_up
.
Once all is set, you can create the package using the gem
tool.
Run the following to create your package:
# Usage: [sudo] gem build [gem name].gemspec
# Example:
gem build mygem.gemspec
# Successfully built RubyGem
# Name: my_gem
# Version: 0.1.0
# File: my_gem-0.1.0.gem
There are a couple of ways to publish newly minted Gem. Either way, you will need to log-in with a RubyGems.org account so let’s start with doing that first.
Run the following command to log-in using gem
:
gem push
Enter your email address, and then your password to sign in.
Note: We specifically refrain from giving a Gem-name to push so that we only log-in without performing further action.
In order to simply push your Gem, run the following:
# Usage: [sudo] gem push [gem file]
# Example:
gem push my_gem-0.1.0.gem
Alternatively, you can benefit from Bundler’s Rake tasks. You can see a full list with the following:
rake -T
rake build:
Build my_gem-0.0.1.gem into the pkg directory
rake install:
Build and install my_gem-0.0.1.gem into system gems
rake release:
Create tag v0.0.1 and build and push my_gem-0.0.1.gem to Rubygems
If you call rake release
, your package will be pushed to your set Git account and then to RubyGems.org, e.g.:
rake build
# my_gem 0.1.0 built to pkg/my_gem-0.1.0.gem.
rake release
# rake aborted!
# Couldn't git push. `git push 2>&1' failed with the following output:
# fatal: No configured push destination.
# Either specify the URL from the command-line or configure a remote repository using
# git remote add <name> <url>
To continue, add a remote Git account:
# Usage: git remote add origin git@github.com:[user name]/[repository].git
# Example:
git remote add origin git@github.com:maintainer1/my_gem.git
Then simultaniously release your code using rake
:
rake release
# ..
# Pushed MyGem
And that’s it! You can now go to RubyGems.org and check out your Gem:
http://www.rubygems.org/gems/[your gem name]
<div class=“author”>Submitted by: <a href=“https://twitter.com/ostezer”>O.S. Tezer</a></div>
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
You confused “principle” with “principal”.