Using Ruby Libraries

David Morales David Morales
/
Three library sections with wooden bookshelves, filled with books

Ruby’s libraries ecosystem, mainly supported by the community, is one of the language’s most powerful features. These libraries allow developers to reuse and build upon pre-existing, well-tested solutions. For many common functionalities, there is already a library available. This can save you significant time and effort.

Ruby’s ecosystem is structured into three components:

  1. Ruby Core Library: Built-in functionality that is always available.
  2. Ruby Standard Library: Built-in functionality that must be explicitly required.
  3. RubyGems: External, “pluggable” functionality.

To manage RubyGems dependencies effectively, Bundler is a crucial tool. While optional, it simplifies maintenance and ensures consistency across projects.

We will also see how libraries are loaded using the Ruby load path.


The Ruby Core Library: The Foundation of Ruby

The Ruby Core Library provides fundamental features that are always loaded and ready to use. It is available as soon as your Ruby program starts.

Examples of Core Library Features

name = "Ruby"
puts name.upcase # Outputs "RUBY"
numbers = [1, 2, 3, 4]
puts numbers.sum # Outputs 10
scores = [80, 90, 100]
average = scores.reduce(:+) / scores.size
puts average # Outputs 90

The Ruby Standard Library: Batteries Included

The Ruby Standard Library extends the Core Library with a wide range of optional tools. These libraries cover many areas, such as file handling, data serialization, and network communication. However, they must be explicitly “required” to use.

Examples of Standard Libraries

require 'date'
puts Date.today # Outputs the current date
puts Date.parse('2025-01-01') + 7 # Adds 7 days to a date

This library simplifies working with dates, including parsing, formatting, and calculations like adding days or finding differences between dates.

require 'digest'
password_digest = Digest::SHA256.hexdigest('mysecretpassword')

This library is useful for securing sensitive data, such as passwords or API tokens.

require 'json'
data = { name: 'John', age: 30 }
json_data = JSON.generate(data) # Converts a Ruby hash to JSON
parsed_data = JSON.parse(json_data) # Parses JSON back into a Ruby hash

Commonly used to handle APIs and data serialization.

require 'net/http'
uri = URI('https://example.com')
response = Net::HTTP.get(uri)
puts response # prints the HTML code

Essential for working with REST APIs or web scraping.

Try it yourself - Use the JSON gem

Write a Ruby script that uses the JSON gem to parse the following JSON data and display the hex value for the yellow color.

{
"colors": {
"red": "#f00",
"green": "#0f0",
"blue": "#00f",
"cyan": "#0ff",
"magenta": "#f0f",
"yellow": "#ff0",
"black": "#000"
}
}
Show me the steps.
  1. Create a new file, for example json_example.rb.

  2. Require the JSON gem.

    require 'json'
  3. Store the JSON in a variable.

    data = '{
    "colors": {
    "red": "#f00",
    "green": "#0f0",
    "blue": "#00f",
    "cyan": "#0ff",
    "magenta": "#f0f",
    "yellow": "#ff0",
    "black": "#000"
    }
    }'
  4. Parse the JSON and display the hex value for the yellow color.

    parsed_data = JSON.parse(data)
    puts parsed_data['colors']['yellow']
  5. Run the script:

    Terminal window
    ruby json_example.rb

You should see the "#ff0" hex value for the yellow color.

RubyGems: Ruby’s Package Manager

A Ruby gem is a library, packaged and ready to be distributed and installed easily. These gems are hosted on RubyGems. Think of it as Ruby’s “app store”, hosting thousands of gems for various purposes—from small utilities to full-fledged frameworks like Rails.

A gem page in RubyGems has useful metadata, such as version history, dependencies, and links to the documentation.

Once installed and required, you can use the gem.

How to Use RubyGems

To search, install and manage gems you can use the gem package manager, which communicates directly with the RubyGems repository and takes care of dependencies. This command is preinstalled with Ruby.

  1. List installed gems:

    Terminal window
    gem list

    This list applies to the current Ruby version, in case you use a version manager such as rbenv.

  2. Install a Gem:

    Terminal window
    gem install [gem_name]

    This command installs the dependencies first, and lastly the intended gem.

    For example, installing the Sinatra gem (a lightweight framework):

    Terminal window
    gem install sinatra
  3. Require the gem in your code:

    require 'sinatra'
    get '/' do
    'Hello world!'
    end

When running this code snippet, the Puma web server starts up and keeps listening at port 4567. When visiting localhost at that port, it returns “Hello world!”.

Bundler: Managing Dependencies

Bundler is a tool that can install gems and annotate the version in a file named Gemfile. Then it resolves the dependencies and installs specific versions that play well together, keeping them all under control using a second file named Gemfile.lock

In other words, you have a list of the gem versions you need for your project in Gemfile, and Bundler manages Gemfile.lock automatically with the technical details on the exact required dependencies.

These files can be shared with a team so everyone uses the same versions and can collaborate effectively.

How is it Different from RubyGems?

The gem command installs the gem so it is available in general from Ruby. Bundler creates a sandbox for your application, and only uses the specific versions described in Gemfile. That way you can work on several projects, each with their gem versions.

It uses two key files:

source 'https://rubygems.org'
gem 'sinatra'
gem 'nokogiri'

The source line specifies the repository where the gems are located. RubyGems is the standard. You can also use a local repository or a private one.

Specifying Gem Versions

If you want to use a specific version of a gem, you can specify it like this:

gem 'sinatra', '4.1.1'

This is good to lock a version to the last known compatible one with your app, not allowing any version different than 4.1.1. If you want to allow any higher version, starting from 4.1.1, you can use the “optimistic version constraint”:

gem 'sinatra', '>= 4.1.1'

There is a “pessimistic version constriant” too:

gem 'sinatra', '~> 4'

This means “greater than or equal to version 4.x.x”, so it allows 4.0.0, 4.0.1, 4.1.0, but not 5.0.0 or higher.

Similarly you can do the same for the minor version:

gem 'sinatra', '~> 4.1.0'

This allows 4.1.x versions, for example 4.1.0 or 4.1.1, but not 4.2.0 or higher.

Another way to specify versions is using a range:

gem 'sinatra', '>= 4.1.1', '< 5.0'

This allows any version between 4.1.1 and 5.0, but not 5.0.1 or higher.

Using Bundler

  1. Initialize a new project:

    Terminal window
    bundle init

    This command creates a Gemfile in your project directory.

  2. Add dependencies to Gemfile. For example let’s add the Chronic gem for natural language date parsing:

    Terminal window
    bundle add chronic

    Bundler gets the gem metadata from RubyGems, then resolves the dependencies. As it has no dependencies, it just downloads chronic and installs it. The gem is added to Gemfile and the dependencies to Gemfile.lock.

    If you need to reinstall everything in a different computer, after upgrading Ruby, or another developer collaborates in the project, you can use this command:

    Terminal window
    bundle install

    This uses Gemfile.lock to download and install the same versions.

  3. Create a file with some sample code to use the gem:

    example.rb
    require 'chronic'
    puts Chronic.parse('tomorrow')
  4. Run your application:

    Terminal window
    bundle exec ruby example.rb

    Prepending bundle exec ensures that only the gems specified in your Gemfile.lock are used, avoiding potential conflicts with other installed versions.

Ruby Load Path: Where Ruby Looks for Code

Ruby’s $LOAD_PATH determines where the require method searches for files. You can inspect it by running this from your program:

puts $LOAD_PATH

This prints an array of directories where Ruby gems are stored.

If you require devise, Ruby will look for a file with the name devise.rb in each directory listed in the load path until it finds it. Then it will load it.

If Ruby can’t find a required file, you might need to add the enclosing directory to the load path:

$LOAD_PATH << './lib'
require 'my_library'

As you see, the $LOAD_PATH is dynamic, meaning you can modify it at runtime to include custom directories.


Conclusion

Ruby libraries significantly enhance productivity by providing reusable, tested components for common tasks. Ruby’s ecosystem is mature and ever-evolving, with updated and new tools constantly emerging.

Test your knowledge

  1. What is the Ruby Core Library?

  1. How do you use a library from the Ruby Standard Library?

  1. What does Bundler do?

  1. How can you inspect Ruby’s $LOAD_PATH?

  1. What is a Gemfile used for?