RubyGems #
RubyGems is the package distribution infrastructure for the Ruby ecosystem — the equivalent of npm for Node.js, pip for Python, or Maven for Java. Almost every Ruby library, framework, or tool you can install is distributed as a gem through rubygems.org. But RubyGems isn’t just about installation — it shapes how you define project dependencies, lock versions for reproducibility, isolate environments between projects, and even distribute your own code. Understanding RubyGems and Bundler deeply is the skill that separates developers who can work professionally in Ruby from those who can only write simple scripts.
What Is a Gem? #
A gem is a Ruby package containing source code, documentation, and metadata. Every gem has a unique name, a version number, and a list of dependencies on other gems it needs.
A typical gem structure:
gem-name/
├── lib/
│ └── gem_name.rb ← the main entry point that gets required
│ └── gem_name/
│ └── ... ← implementation files
├── test/ or spec/ ← test suite
├── bin/ ← executables if any
├── README.md
├── CHANGELOG.md
├── LICENSE.txt
└── gem-name.gemspec ← gem metadata and configuration
Each gem is identified by the combination of name and version:
# Gem name: rails, version: 7.1.2
# Gem name: nokogiri, version: 1.15.4
# List all gems installed on the system
gem list
# List a specific version
gem list rails
# Detailed info about a gem
gem info rails
gem info rails --remote # search on rubygems.org
gem CLI Commands #
gem is the main command-line tool for interacting with RubyGems:
# Updating RubyGems itself
gem update --system
# Searching gems on rubygems.org
gem search nokogiri
gem search "^rails$" # only gems exactly named "rails"
# Installing gems
gem install nokogiri
gem install rails --version "~> 7.1" # specific version
gem install puma --no-document # without documentation (faster)
# Installing several gems at once
gem install rspec rubocop pry
# Uninstalling gems
gem uninstall nokogiri
gem uninstall nokogiri --version 1.14.0 # only a specific version
# Updating gems
gem update rails # one gem
gem update # all gems (be careful!)
# Listing a gem's dependencies
gem dependency rails
# Gem file locations on the system
gem contents nokogiri
gem environment # complete RubyGems environment info
# Check whether any gems are outdated
gem outdated
flowchart TD
A[gem install name] --> B[Contact rubygems.org]
B --> C[Download the .gem file]
C --> D[Extract to GEM_HOME]
D --> E[Run extconf.rb\nif there's a native extension]
E --> F[Update the gem index]
F --> G[Gem ready to use\nwith require]Semantic Versioning #
Before discussing Bundler, it’s important to understand semantic versioning (SemVer) — the version numbering standard used by almost all Ruby gems.
Format: MAJOR.MINOR.PATCH — example: 2.4.1
MAJOR — changes when there are NON-backward-compatible changes
developers must adjust their code when upgrading
MINOR — changes when there are BACKWARD-COMPATIBLE new features
safe to upgrade, no code changes needed
PATCH — changes when there are backward-compatible bug fixes
always safe to upgrade
Examples:
1.0.0 → 2.0.0 Breaking change — be careful!
1.0.0 → 1.1.0 New features, safe
1.0.0 → 1.0.1 Bug fix, safe
Version constraints in the Gemfile use these symbols:
# Exactly this version
gem 'rails', '7.1.2'
# Greater than or equal to
gem 'rails', '>= 7.0'
# Less than
gem 'rails', '< 8.0'
# Combination — between two versions
gem 'rails', '>= 7.0', '< 8.0'
# Pessimistic constraint (~>) — the most commonly used
gem 'rails', '~> 7.1' # >= 7.1 AND < 8.0 (MINOR updates allowed)
gem 'rails', '~> 7.1.2' # >= 7.1.2 AND < 7.2 (PATCH updates only)
gem 'nokogiri', '~> 1.15' # >= 1.15 AND < 2.0
Guide to choosing a version constraint:
'~> X.Y' → most common for dependencies — allow minor updates
'~> X.Y.Z' → conservative — only allow patch updates
'>= X.Y' → flexible — usable for tools like RSpec
'= X.Y.Z' → very strict — usually not recommended unless there's a reason
no version → avoid in production — no compatibility guarantee
Bundler — Project Dependency Management #
Bundler is a tool that works on top of RubyGems to manage dependencies at the project level — ensuring all developers and environments use exactly the same gem versions.
# Install Bundler (usually already present with modern Ruby)
gem install bundler
# Check the Bundler version
bundle --version
Gemfile — Defining Dependencies #
The Gemfile declares all the gems a project needs:
# Gemfile
source "https://rubygems.org" # gem source (required)
# The Ruby version the project uses — highly recommended
ruby "3.3.0"
# Gem without a version — not recommended for production
gem "json"
# Gems with pessimistic constraints
gem "sinatra", "~> 3.0"
gem "puma", "~> 6.3"
gem "nokogiri", "~> 1.15"
# Gems only for specific environments
group :development do
gem "pry", "~> 0.14" # debugging
gem "rubocop", "~> 1.57" # linter
gem "solargraph" # language server for IDEs
end
group :test do
gem "rspec", "~> 3.12"
gem "factory_bot", "~> 6.3"
gem "faker", "~> 3.2"
gem "simplecov", "~> 0.22", require: false
end
group :development, :test do
gem "dotenv", "~> 2.8" # env variables from a .env file
gem "byebug", "~> 11.1" # debugger
end
group :production do
gem "rack-timeout", "~> 0.7"
end
# Gems from other sources
gem "my_private_gem", git: "https://github.com/account/private-gem.git"
gem "my_private_gem", git: "https://github.com/account/private-gem.git", branch: "main"
gem "local_gem", path: "../local-gem" # local gem on disk
bundle install — Install All Dependencies #
# Install all gems from the Gemfile
bundle install
# Install without certain groups
bundle install --without production
# Install to a specific directory (useful for deployment)
bundle install --path vendor/bundle
# Update Gemfile.lock after changing the Gemfile
bundle install
After bundle install, Bundler creates or updates the Gemfile.lock file:
# Gemfile.lock (generated automatically — don't edit manually)
GEM
remote: https://rubygems.org/
specs:
mustermann (3.0.0)
rack (3.0.8)
rack-protection (3.1.0)
rack (>= 3.0.0, < 4)
sinatra (3.1.0)
mustermann (~> 3.0)
rack (~> 3.0)
rack-protection (= 3.1.0)
tilt (~> 2.0)
tilt (2.3.0)
PLATFORMS
x86_64-linux
DEPENDENCIES
sinatra (~> 3.0)
RUBY VERSION
ruby 3.3.0p0
BUNDLED WITH
2.4.22
Gemfile.lockmust be committed to version control for applications. It guarantees all developers and production servers use identical gem versions — preventing the “works on my machine” bug caused by gem version differences. For gem libraries (not applications),Gemfile.lockis usually not committed so users can use versions compatible with their own projects.
bundle update — Updating Gem Versions #
# Update all gems (within the constraints in the Gemfile)
bundle update
# Update only one gem — safer
bundle update nokogiri
# Update several gems
bundle update rails puma
# See which gems are outdated
bundle outdated
# See the difference before and after updating
bundle update nokogiri --conservative # only patch/minor updates
bundle exec — Run with the Right Gems #
bundle exec ensures commands run using the gems listed in the project’s Gemfile, rather than globally installed gems:
# Without bundle exec — could use a different version!
ruby script.rb
rspec
rake db:migrate
# With bundle exec — always uses gems from the project Gemfile
bundle exec ruby script.rb
bundle exec rspec
bundle exec rake db:migrate
# Shortcut — add to .zshrc or .bashrc
alias be="bundle exec"
be rspec
be rake
When bundle exec is required:
✓ Running rspec, rake, or other gem executables
✓ In CI/CD pipelines
✓ When working with many projects with different gem versions
✓ On production servers
✗ Not needed if using RVM or rbenv configured to use
bundle exec automatically (depending on configuration)
Other Bundler Commands #
# List all gems used by the project
bundle list
# Gem file location on the system
bundle show nokogiri
# Open a gem's source code in the editor
bundle open nokogiri
# Information about the gems being used
bundle info rails
# Run IRB with all project gems loaded
bundle console # (or: irb -r bundler/setup)
# Generate binstubs — executables in ./bin/ that automatically use bundle exec
bundle binstubs rspec-core
bundle binstubs rake
# After this: ./bin/rspec and ./bin/rake automatically use bundle exec
# Check whether any used gems have vulnerabilities
bundle audit # needs the gem 'bundler-audit'
Creating Your Own Gem #
Creating your own gem is useful for sharing code between projects or distributing a library to the Ruby community.
Initializing the Gem Structure #
# Bundler provides a generator for creating gem structures
bundle gem my_gem
# With additional options
bundle gem my_gem --test=rspec --ci=github --mit
This command creates a complete directory structure:
my_gem/
├── lib/
│ ├── my_gem.rb ← entry point
│ └── my_gem/
│ └── version.rb ← version constant
├── spec/
│ ├── spec_helper.rb
│ └── my_gem_spec.rb
├── .github/
│ └── workflows/main.yml ← CI with GitHub Actions
├── Gemfile
├── Rakefile
├── README.md
├── CHANGELOG.md
├── LICENSE.txt
└── my_gem.gemspec ← gem metadata
The Gemspec — Gem Metadata Configuration #
# my_gem.gemspec
require_relative "lib/my_gem/version"
Gem::Specification.new do |spec|
spec.name = "my_gem"
spec.version = MyGem::VERSION
spec.authors = ["Your Name"]
spec.email = ["[email protected]"]
spec.summary = "A short summary of what this gem does"
spec.description = "A longer description of this gem"
spec.homepage = "https://github.com/account/my_gem"
spec.license = "MIT"
# Minimum required Ruby version
spec.required_ruby_version = ">= 3.0.0"
# Files included in the gem
spec.files = Dir.glob("{lib,bin}/**/*") + %w[README.md LICENSE.txt CHANGELOG.md]
spec.bindir = "bin"
spec.executables = spec.files.grep(%r{\Abin/}) { |f| File.basename(f) }
spec.require_paths = ["lib"]
# Runtime dependencies — needed when the gem is used
spec.add_dependency "nokogiri", "~> 1.15"
spec.add_dependency "faraday", "~> 2.7"
# Development dependencies — only needed when developing the gem
spec.add_development_dependency "rspec", "~> 3.12"
spec.add_development_dependency "rubocop", "~> 1.57"
end
Building and Publishing a Gem #
# Build the gem into a .gem file
gem build my_gem.gemspec
# => my_gem-0.1.0.gem
# Install the built gem locally for testing
gem install my_gem-0.1.0.gem
# Push to rubygems.org (needs an account and API key)
gem push my_gem-0.1.0.gem
# Or use rake (already configured in the Rakefile by bundler gem)
bundle exec rake release
# → automatically: bump version, create git tag, build, push to rubygems.org
Security and Gem Auditing #
Dependency security is a responsibility that’s often overlooked:
# Install bundler-audit for vulnerability checks
gem install bundler-audit
# Update the advisory database
bundle audit update
# Check whether any gems have known vulnerabilities
bundle audit check
# Check and show details
bundle audit check --verbose
# Example output if there's a problem:
# Vulnerabilities found!
# Name: rack
# Version: 2.2.6
# Advisory: CVE-2023-27539
# Criticality: Medium
# URL: https://github.com/advisories/GHSA-hxqx-xwvh-44m2
# Title: Denial of Service Vulnerability in Rack Content-Disposition parsing
# Solution: upgrade to ~> 2.0.9.4, ~> 2.1.4.4, ~> 2.2.6.4, >= 3.0.8
Gem security practices:
✓ Run bundle audit regularly (and in the CI pipeline)
✓ Commit Gemfile.lock — makes tracking version changes easy
✓ Inspect new gems before adding them — download counts, active maintainers
✓ Use Dependabot or Renovate for automatic updates
✓ Avoid gems not updated in >2 years unless truly stable
✗ Don't install gems from unclear sources
✗ Don't use git: constraints in a production Gemfile from repos you don't control
Essential Gems Every Developer Should Know #
The Ruby ecosystem has thousands of gems, but a few are de-facto standards that appear in almost every professional project:
Web Frameworks and Servers
rails is Ruby’s most complete web framework — MVC, ORM (ActiveRecord), mailer, job queue, and much more are all integrated. sinatra is a minimalist alternative for APIs or small applications. puma is the multi-threaded web server that’s the default in modern Rails.
Testing
rspec is the most popular BDD (Behavior-Driven Development) framework in Ruby — its syntax is expressive and reads like sentences. minitest is a lighter alternative built into Ruby. factory_bot makes creating test data easy, faker generates realistic fake data, and capybara handles browser integration testing.
Databases and ORMs
activerecord (part of Rails but usable standalone) is the most popular ORM in Ruby. sequel is a more flexible alternative. pg is the PostgreSQL adapter, mysql2 for MySQL, and sqlite3 for SQLite.
HTTP Clients
faraday is an HTTP client with a flexible middleware system. httparty is simpler and suitable for cases that don’t need much customization.
General Utilities
dotenv loads environment variables from a .env file — the standard for local configuration. rubocop is an automatic linter and formatter that enforces the Ruby Style Guide. pry is a REPL far more powerful than the built-in IRB — it can be used as a debugger. sidekiq handles background jobs efficiently using Redis.
Parsing and Serialization
nokogiri for HTML and XML parsing — essential for web scraping. oj is the fastest JSON parser for Ruby. psych (built-in) for YAML.
A Typical Workflow in a Real Project #
sequenceDiagram
participant Dev as Developer
participant Bundler
participant RubyGems as rubygems.org
participant Git
Dev->>Bundler: bundle install
Bundler->>RubyGems: Download gems per the Gemfile
RubyGems-->>Bundler: .gem files
Bundler->>Dev: Create/update Gemfile.lock
Dev->>Git: commit Gemfile.lock
note over Dev: Adding a new gem
Dev->>Dev: Edit Gemfile
Dev->>Bundler: bundle install
Bundler->>RubyGems: Download the new gem
Bundler->>Dev: Update Gemfile.lock
Dev->>Git: commit Gemfile + Gemfile.lock
note over Dev: Running tests
Dev->>Bundler: bundle exec rspec
Bundler->>Dev: Run rspec with the right gems# A typical daily workflow
# 1. Clone the project from Git
git clone https://github.com/team/project.git
cd project
# 2. Install all dependencies per Gemfile.lock
bundle install
# 3. Build a new feature — add a gem if needed
echo "gem 'faraday', '~> 2.7'" >> Gemfile
bundle install
# 4. Run tests
bundle exec rspec
# 5. Run the linter
bundle exec rubocop
# 6. Commit the changes
git add Gemfile Gemfile.lock
git commit -m "Add faraday for HTTP client"
# 7. Deploy to production
bundle install --deployment # install to vendor/bundle
bundle exec puma -C config/puma.rb
Summary #
- Every project needs a Gemfile — define all dependencies with appropriate version constraints. Avoid adding gems without version constraints in production environments.
Gemfile.lockmust be committed for applications — it guarantees reproducibility across all environments. For libraries/gems, it doesn’t need to be committed.- The pessimistic constraint
~>is the standard —~> 2.1means>= 2.1and< 3.0, letting minor updates in while blocking breaking changes.- Always use
bundle exec— ensures the right gem versions are used, especially in CI and production.- Run
bundle outdatedandbundle auditregularly — keep dependencies current and free of known vulnerabilities.bundle open gem_nameto read a gem’s source code straight from the terminal — the best way to understand how the gems you use work.bundle binstubsfor shortcuts — generate executables in./bin/that automatically use bundle exec.- Group gems by environment —
group :developmentandgroup :testprevent debugging and testing gems from being installed in production.- Semantic versioning determines your upgrade strategy — PATCH is safe to update anytime, MINOR needs a review of changes, MAJOR needs migration.
bundle gemfor creating new gems — this generator creates a complete, ready-to-use directory structure, gemspec, and CI configuration.