cippaciong

Home | Blog | About

Ruby in 2024

2024-12-23

It's funny how things come full circle. The last time I wrote Ruby code was back in 2014, while still at the university. Now, after spending almost 10 years working with other languages and technologies, I find myself drawn back to it for a side project I've been working on in my spare time. There's something comforting about returning to an old language you liked - it's like catching up with an old friend you haven't seen in years.

Before diving back into my project, I needed to figure out how to set up a proper Ruby development environment again. What surprised (and honestly, delighted) me was discovering that the setup process hasn't changed much since 2014. In a world where development tools and best practices seem to change every few months, this stability is refreshing.

Ruby Version Managers

The first thing you need when setting up a programming language on your machine is a version manager for that language (though newer languages tend to include this as part of their core tooling). Once you start juggling multiple projects, you'll likely need to run different versions of the language depending on the project, and you want to be able to switch between them seamlessly.

The Ruby ecosystem offers several version managers. While rvm is the oldest and still maintained, the community has largely moved on to newer alternatives:

All these tools focus solely on switching between Ruby versions - they don't handle the actual compilation and installation of Ruby itself. For that part, they all depend on ruby-build, a tool developed by the rbenv team that handles the heavy lifting of compiling and installing Ruby from source.

After trying out the different options, I settled on rbenv for a few reasons:

Setting Up rbenv

Getting started with rbenv is straightforward. Depending on your system, install it with:

Then initialize it by running rbenv init once. This will add the following line at the end of your .profile or .bash_profile file:

eval "$(rbenv init - --no-rehash bash)"

Here's a gotcha I ran into: the line needs to appear before your .bashrc loading or bash autocompletion won't work (at least in my case):

eval "$(rbenv init - --no-rehash bash)"

[[ -f ~/.bashrc ]] && . ~/.bashrc

Once everything's set up, source your .bash_profile with . .bash_profile or simply log out and then log in to activate rbenv. Then run which ruby to check that it points to rbenv's path now:

$ which ruby
$HOME/.rbenv/shims/ruby

Managing Ruby Versions

Now that you have rbenv installed and initialized, you can use it to manage your ruby versions. Here are some useful commands to manage ruby versions:

Global and Local Versions

One of the best things about version managers is how easily you can specify different versions in different contexts. When using rbenv, you have two scopes:

My preferred setup is to use the system version (the one shipped by your OS or installed using the OS package manager) globally, while keeping project-specific versions local:

# Set the machine-wide default
rbenv global system

# Set project-specific version
cd myproject && rbenv local 3.3.5

Managing Gems

Most of the languages I usually work with use the same tool to install global development tools or to manage project dependencies (e.g. go install/go get or npm install -g/npm install). Ruby, however, maintains a clearer separation of concerns, with the two main actors being RubyGems (gem) and Bundler (bundle).

RubyGems

RubyGems is Ruby's built-in package manager that lets you download and install gems, which are packaged Ruby applications or libraries. When you run gem install rails, you're telling RubyGems to fetch the Rails framework from rubygems.org (the central repository) and install it on your system. These gems become available globally on your machine, similar to installing it from your OS package manager. RubyGems manages individual gems in isolation, which works well for simple use cases. However, as applications grew more complex, different projects needed different versions of the same gems. This is where Bundler enters the picture.

Bundler

Bundler is the project-level dependencies manager. Imagine you're working on two Ruby projects: one uses Rails 6.1 and another needs Rails 7.0. Without Bundler, this would be nearly impossible to manage with RubyGems alone. Bundler lets each project specify exactly which gem versions it needs in a file called Gemfile. When you run bundle install, Bundler reads this file and creates a precise snapshot of all dependencies (including dependencies of dependencies) in Gemfile.lock.

Using Them Together in 2024

In modern Ruby development, you'll use both tools, but in different ways. RubyGems is typically used to install development tools you want available system-wide, like:

gem install rails         # Installing the Rails CLI
gem install ruby-lsp      # Installing a Ruby language server
gem install foreman       # Installing a process manager

Meanwhile, Bundler manages the gems that your actual Ruby applications depend on. You'll primarily interact with it through:

bundle install    # Install all dependencies specified in Gemfile
bundle update     # Update dependencies to their latest allowed versions
bundle exec       # Run commands in the context of your project's gems

Both gem and bundler are shipped together with Ruby and you should not be required to do anything else to have them on your system.

RubyGems Cheatsheet

Now that you have a version manager, a recent version of Ruby, and gem/bundle to manage your tools and dependencies, you should be ready to start developing with Ruby.

As a final note, here's a list of useful gem commands that I need often but always forget:

Install Gems

Gems can be installed with gem install <name> that will also pull in the gem dependencies

gem install bundler
gem install rails

When using gem, remember that gems are installed only for the currently active Ruby version, typically located at ~/.gem/ruby/${ruby_version}$/gems/.

Update Gems

You can get a list of gems that have updates available with:

gem outdated

To update a gem, run:

gem update <name>

This will take care of updating the gem and all its dependencies.

Warning: gem outdated and gem update will include also the system gems installed externally (e.g. via pacman). Make sure to always specify the name of the gem you want to update to avoid undesired effects on your system gems.

Cleanup Gems

When you update a gem, the old version of the gem is not automatically removed, both versions are kept installed. If you want to clean it up, run:

gem cleanup <name>

to remove all old versions of that gem, or:

gem uninstall <name> --version <version>

to remove just a specific version.

Gem Dependencies

You can list all the dependencies of a gem with:

gem dependency <name>

Similarly, you can list all the gems that depend on a specific gem with:

gem dependency <name> --reverse-dependencies