Ruby

Introducing Rebundler: make your Gemfile look great with a single command


Today I’m introducing Rebundler. It’s a little tool to automatically re-organize and annotate your Gemfile. I wrote it because I like my Gemfiles to be structured neatly, but in reality I always just put gems near vaguely adjacent gems or end up chucking them at the end of the file. Eventually my Gemfiles turn out to be an incoherent mess.

Rebundler groups and sorts your gems and adds a description to each, which gives you more context on what a gem does. Especially with some of the funky names in the Ruby ecosystem, it’s not always clear from the name alone.

Here’s a small example (full example below):

Before
source "https://rubygems.org"

gem "rails"
gem "puma"
gem "avo"

group :development do
  gem "herb"
end
After
source "https://rubygems.org"

gem "avo" # Admin panel framework and Content Management System for Ruby on Rails.
gem "puma" # A Ruby/Rack web server built for parallelism.
gem "rails" # Full-stack web application framework.

group :development do
  gem "herb" # The modern HTML+ERB Toolchain
end

Installation

You can either add it to your Gemfile or run it standalone.

bundle add rebundler or gem install rebundler.

❯ bundle exec rebundle
Reordering and annotating Gemfile...
✓ Gemfile has been reordered and annotated

There’s a ‘CI mode’, where it checks if the formatting is still correct, without writing to the file. Useful if you want to add it as a step in your linting pipeline.

❯ bundle exec rebundle --ci
Checking if Gemfile is properly formatted...
✓ Gemfile is properly formatted

Try it online

You can also play around with it on the online Rebundler Playground. Just paste your Gemfile and hit Rebundle. It encodes the file in a hash in the URL, so you can easily share the URL around.

How it works

Since Gemfiles are just regular Ruby files, they can be parsed with Prism. Rebundler uses Prism’s visitor pattern to group gems that it thinks belong together. It also looks out for generic Gemfile setup methods, like ruby and source and puts them at the top.

Gems defined at the ‘top level’ are grouped together. Gems inside blocks (like group :development do .. end) stay together, and duplicate groups are automatically merged.

Prism is only a parser: it can’t format the parsed code back into Ruby1. To work around this, Rebundler does some classic string manipulation. Prism saves the exact line and column coordinates of each parsed piece of code, so it uses those to extract the original code as a string. This has the added benefit that it keeps your code style intact, as the code is just copied.

When reconstructing the Gemfile, it sorts each group’s gems alphabetically. It then loads each gem’s description from the local bundle (fetching it from RubyGems if it’s missing) and appends it to the line as a comment. To do this it needs to take some additional steps to find the trailing comment of the original line2.

By default it keeps existing trailing comments intact. Other comments are currently discarded.

The current version should work with most Gemfiles, but there are probably esoteric ones out there which it doesn’t support (they are Ruby files, after all, so you can do anything you want). If you have a straightforward Gemfile with only ‘Gemfile syntax’ it should work.

Full example

Here’s a full example using RubyEvents’ Gemfile (view in playground). It’s a good example of a ‘messy’ Gemfile, with occasional comments, commented-out gems, and no real grouping or structure — exactly the kind Rebundler benefits the most.

Before
source "https://rubygems.org"
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby file: ".ruby-version"

# Use Rails edge
gem "rails", github: "rails/rails"

# The modern asset pipeline for Rails [https://github.com/rails/propshaft]
gem "propshaft"

# Use sqlite3 as the database for Active Record
gem "sqlite3", ">= 2.1.0"

# Use the Puma web server [https://github.com/puma/puma]
gem "puma"

# use jbuilder for the api
gem "jbuilder"

# Bundle and transpile JavaScript [https://github.com/rails/jsbundling-rails]
# gem "jsbundling-rails"

# Hotwire's SPA-like page accelerator [https://turbo.hotwired.dev]
gem "turbo-rails"

# Hotwire's modest JavaScript framework [https://stimulus.hotwired.dev]
# gem "stimulus-rails"

# Bundle and process CSS [https://github.com/rails/cssbundling-rails]
# gem "cssbundling-rails"

# Use Redis adapter to run Action Cable in production
# gem "redis", ">= 4.0.1"

# Use Kredis to get higher-level data types in Redis [https://github.com/rails/kredis]
# gem "kredis"

# Use Active Model has_secure_password [https://guides.rubyonrails.org/active_model_basics.html#securepassword]
gem "bcrypt", "~> 3.1.7"

# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem "tzinfo-data", platforms: %i[windows jruby]

# Reduces boot times through caching; required in config/boot.rb
gem "bootsnap", require: false

# Deploy this application anywhere as a Docker container [https://kamal-deploy.org]
gem "kamal", "2.7.0", require: false

# Add HTTP asset caching/compression and X-Sendfile acceleration to Puma [https://github.com/basecamp/thruster/]
gem "thruster", require: false

# Use Active Storage variants [https://guides.rubyonrails.org/active_storage_overview.html#transforming-images]
gem "image_processing", "~> 1.2"

# Image processing for event asset generation
gem "mini_magick"

# All sorts of useful information about every country packaged as convenient little country objects
gem "countries"

# ISO 639-1 and ISO 639-2 language code entries and convenience methods
gem "iso-639"

# A minimal client of Bluesky/ATProto API
gem "minisky", "~> 0.4.0"

# Extract Collaborator Objects from your Active Records, a new concept called Associated Objects
gem "active_record-associated_object"

# Headless Chrome driver for Capybara
gem "cuprite"

# A single delightful Ruby way to work with AI.
gem "ruby_llm", "~> 1.15.0"

# A simple and clean Ruby DSL for creating JSON schemas.
gem "ruby_llm-schema", github: "marcoroth/ruby_llm-schema", branch: "conditionals"

# JSON Schema validator
gem "json_schemer"

# YouTube V3 API client.
gem "yt"

# Family of libraries that support various formats of XML "feeds".
gem "rss", "~> 0.3.1"

# YAML Editing and Refactoring with Better Accuracy
gem "yerba", "~> 0.5.1"

# Powerful and seamless HTML-aware ERB parsing and tooling.
gem "herb", "~> 0.9"

# An ActionView-compatible ERB engine with modern DX - re-imagined with Herb.
gem "reactionview", "~> 0.3"

# Agnostic pagination in plain ruby.
gem "pagy"

# gem "activerecord-enhancedsqlite3-adapter"
gem "solid_cache"

# Database-backed Active Job backend.
gem "solid_queue", github: "joshleblanc/solid_queue", branch: "async-mode"

# Operational controls for Active Job
gem "mission_control-jobs"

# Simple, powerful, first-party analytics for Rails
gem "ahoy_matey"

# The simplest way to group temporal data
gem "groupdate"

# Create beautiful JavaScript charts with one line of Ruby
gem "chartkick", "~> 5.0"

# Use Vite in Rails and bring joy to your JavaScript experience
gem "vite_rails"

# Collection of SEO helpers for Ruby on Rails.
gem "meta-tags"

# Logs performance and exception data from your app to appsignal.com
gem "appsignal"

# Autoload dotenv in Rails.
gem "dotenv-rails"

# Automatic generation of html links in texts
gem "rails_autolink", "~> 1.1"

# Easily generate XML Sitemaps
gem "sitemap_generator", "~> 6.3"

# A framework for building reusable, testable & encapsulated view components
gem "view_component"

# Adds ActiveRecord-specific methods to Dry::Initializer
gem "dry-initializer-rails"

# Type system for Ruby supporting coercions, constraints and complex types
gem "dry-types", "~> 1.7"

# Protocol Buffers are Google's data interchange format.
gem "google-protobuf", require: false

# ActiveJob::Performs adds the `performs` macro to set up jobs by convention.
gem "active_job-performs"

# Use the OpenAI API with Ruby!
gem "ruby-openai"

# Repairs broken JSON strings.
gem "json-repair", "~> 0.2.0"

# Markdown that smells nice
gem "redcarpet", "~> 3.6"

# Syntax highlighting
gem "rouge", "~> 4.4"

# Country Select Plugin
gem "country_select"

# Admin panel framework and Content Management System for Ruby on Rails.
gem "avo"

group :avo, optional: true do
  gem "avo-pro", source: "https://packager.dev/avo-hq/"
end

# Marksmith is a GitHub-style markdown editor for Ruby on Rails applications.
gem "marksmith"

# A fast, safe, extensible parser for CommonMark. This wraps the comrak Rust crate.
gem "commonmarker", ">= 2.6.1"

# ActiveRecord like interface to read only access and query static YAML files
gem "frozen_record", "~> 0.27.2"

# A convenient way to diff string in ruby
gem "diffy"

# ActiveRecord soft-deletes done right
gem "discard"

# Makes consuming restful web services dead easy.
gem "httparty"

# Use OmniAuth to support multi-provider authentication [https://github.com/omniauth/omniauth]
gem "omniauth"

# Official OmniAuth strategy for GitHub.
gem "omniauth-github"

# Provides a mitigation against CVE-2015-9284 [https://github.com/cookpad/omniauth-rails_csrf_protection]
gem "omniauth-rails_csrf_protection"

# An accessible autocomplete for Ruby on Rails apps using Hotwire.
gem "hotwire_combobox", "~> 0.4.0"

# Common locale data and translations for Rails i18n.
gem "rails-i18n", "~> 8.0"

# Ruby standards gems
gem "openssl" # https://github.com/ruby/openssl/issues/949

# Class to build custom data structures, similar to a Hash.
gem "ostruct"

# Complete geocoding solution for Ruby.
gem "geocoder", github: "alexreisner/geocoder" # Use latest geocoder with Nominatim improvements until next release

# RubyGems.org API wrapper for gem information
gem "gems"

# A glamorous CLI toolkit for Ruby
gem "gum", "~> 0.3.1"

# Regex pattern searching in files
gem "grepfruit"

# Create beautiful JavaScript maps with one line of Ruby
gem "mapkick-rb", "~> 0.2.0"

# Typesense Ruby client
gem "typesense", "~> 4.1"

# Typesense integration to your favorite ORM
gem "typesense-rails", "~> 1.0.0.rc4"

group :development, :test do
  # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem
  gem "bundler-audit", require: false
  gem "debug", platforms: %i[mri windows]
  gem "byebug"
  gem "minitest-difftastic", "~> 0.2"
end

group :development do
  # A gem for generating annotations for Rails projects.
  gem "annotaterb"

  # Use console on exceptions pages [https://github.com/rails/web-console]
  gem "web-console"

  # Add speed badges [https://github.com/MiniProfiler/rack-mini-profiler]
  gem "rack-mini-profiler"

  # For call-stack profiling flamegraphs
  gem "stackprof"

  # Speed up commands on slow machines / big apps [https://github.com/rails/spring]
  # gem "spring"

  # Use listen to watch files for changes [https://github.com/guard/listen]
  gem "listen", "~> 3.5"

  # Guard for watching file changes and auto-importing [https://github.com/guard/guard]
  gem "guard"

  # Benchmark is no longer included in the standard lib - used for benchmarking
  gem "benchmark"

  gem "ruby-lsp-rails", require: false
  gem "standardrb", "~> 1.0", require: false
  gem "erb_lint", require: false
  gem "authentication-zero", require: false
end

group :test do
  # Use system testing [https://guides.rubyonrails.org/testing.html#system-testing]
  gem "capybara"
  gem "rails-controller-testing"
  gem "selenium-webdriver"
  gem "vcr", "~> 6.1"
  gem "webmock"
end

gem "icalendar", "~> 2.12"
After
source "https://rubygems.org"

git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby file: ".ruby-version"

gem "active_job-performs" # ActiveJob::Performs adds the `performs` macro to set up jobs by convention.
gem "active_record-associated_object" # Associate a Ruby PORO with an Active Record class and have it quack like one.
gem "ahoy_matey" # Simple, powerful, first-party analytics for Rails
gem "appsignal" # Logs performance and exception data from your app to appsignal.com
gem "avo" # Admin panel framework and Content Management System for Ruby on Rails.
gem "bcrypt", "~> 3.1.7" # OpenBSD's bcrypt() password hashing algorithm.
gem "bootsnap", require: false # Boot large ruby/rails apps faster
gem "chartkick", "~> 5.0" # Create beautiful JavaScript charts with one line of Ruby
gem "commonmarker", ">= 2.6.1" # CommonMark parser and renderer. Written in Rust, wrapped in Ruby.
gem "countries" # Gives you a country object full of all sorts of useful information.
gem "country_select" # Country Select Plugin
gem "cuprite" # Headless Chrome driver for Capybara
gem "diffy" # A convenient way to diff string in ruby
gem "discard" # ActiveRecord soft-deletes done right
gem "dotenv-rails" # Autoload dotenv in Rails.
gem "dry-initializer-rails" # Adds ActiveRecord-specific methods to Dry::Initializer
gem "dry-types", "~> 1.7" # Type system for Ruby supporting coercions, constraints and complex types like structs, value objects, enums etc
gem "frozen_record", "~> 0.27.2" # ActiveRecord like interface to read only access and query static YAML files
gem "gems" # Ruby wrapper for the RubyGems.org API
gem "geocoder", github: "alexreisner/geocoder" # Complete geocoding solution for Ruby.
gem "google-protobuf", require: false # Protocol Buffers
gem "grepfruit" # Tool for searching regex patterns in files
gem "groupdate" # The simplest way to group temporal data
gem "gum", "~> 0.3.1" # Ruby wrapper for Charm's gum CLI tool.
gem "herb", "~> 0.9" # The modern HTML+ERB Toolchain
gem "hotwire_combobox", "~> 0.4.0" # Accessible Autocomplete for Rails apps
gem "httparty" # Makes http fun! Also, makes consuming restful web services dead easy.
gem "icalendar", "~> 2.12" # A ruby implementation of the iCalendar specification (RFC-5545).
gem "image_processing", "~> 1.2" # High-level wrapper for processing images for the web with ImageMagick or libvips.
gem "iso-639" # ISO 639-1 and ISO 639-2 language code entries and convenience methods.
gem "jbuilder" # Create JSON structures via a Builder-style DSL
gem "json-repair", "~> 0.2.0" # Repairs broken JSON strings.
gem "json_schemer" # JSON Schema validator. Supports drafts 4, 6, 7, 2019-09, 2020-12, OpenAPI 3.0, and OpenAPI 3.1.
gem "kamal", "2.7.0", require: false # Deploy web apps in containers to servers running Docker with zero downtime.
gem "mapkick-rb", "~> 0.2.0" # Create beautiful JavaScript maps with one line of Ruby
gem "marksmith" # Marksmith is a GitHub-style markdown editor for Ruby on Rails applications.
gem "meta-tags" # Collection of metadata helpers for Ruby on Rails.
gem "mini_magick" # Manipulate images with minimal use of memory via ImageMagick
gem "minisky", "~> 0.4.0" # A minimal client of Bluesky/ATProto API
gem "mission_control-jobs" # Operational controls for Active Job
gem "omniauth" # A generalized Rack framework for multiple-provider authentication.
gem "omniauth-github" # Official OmniAuth strategy for GitHub.
gem "omniauth-rails_csrf_protection" # Provides CSRF protection on OmniAuth request endpoint on Rails application.
gem "openssl" # SSL/TLS and general-purpose cryptography for Ruby
gem "ostruct" # Class to build custom data structures, similar to a Hash.
gem "pagy" # Pagy 🐸 The Leaping Gem!
gem "propshaft" # Deliver assets for Rails.
gem "puma" # A Ruby/Rack web server built for parallelism.
gem "rails", github: "rails/rails" # Full-stack web application framework.
gem "rails_autolink", "~> 1.1" # Automatic generation of html links in texts
gem "rails-i18n", "~> 8.0" # Common locale data and translations for Rails i18n.
gem "reactionview", "~> 0.3" # An ActionView-compatible ERB engine with modern DX - re-imagined with Herb.
gem "redcarpet", "~> 3.6" # Markdown that smells nice
gem "rouge", "~> 4.4" # A pure-ruby colorizer based on pygments
gem "rss", "~> 0.3.1" # Family of libraries that support various formats of XML "feeds".
gem "ruby_llm", "~> 1.15.0" # One beautiful Ruby API for GPT, Claude, Gemini, and more.
gem "ruby_llm-schema", github: "marcoroth/ruby_llm-schema", branch: "conditionals" # A simple Ruby DSL for creating JSON schemas.
gem "ruby-openai" # OpenAI API + Ruby! 🤖❤️
gem "sitemap_generator", "~> 6.3" # Easily generate XML Sitemaps
gem "solid_cache" # A database backed ActiveSupport::Cache::Store
gem "solid_queue", github: "joshleblanc/solid_queue", branch: "async-mode" # Database-backed Active Job backend.
gem "sqlite3", ">= 2.1.0" # Ruby library to interface with the SQLite3 database engine (http://www.sqlite.org).
gem "thruster", require: false # Zero-config HTTP/2 proxy
gem "turbo-rails" # The speed of a single-page web application without having to write any JavaScript.
gem "typesense", "~> 4.1" # Ruby Library for Typesense
gem "typesense-rails", "~> 1.0.0.rc4"
gem "tzinfo-data", platforms: %i[windows jruby] # Timezone Data for TZInfo
gem "view_component" # A framework for building reusable, testable & encapsulated view components in Ruby on Rails.
gem "vite_rails" # Use Vite in Rails and bring joy to your JavaScript experience
gem "yerba", "~> 0.5.1" # YAML Editing and Refactoring with Better Accuracy
gem "yt" # Yt makes it easy to interact with Youtube V3 API by providing a modular, intuitive and tested Ruby-style API.

group :avo, optional: true do
  gem "avo-pro", source: "https://packager.dev/avo-hq/" # Visit https://avohq.io/ to get more information about this gem.
end

group :development do
  gem "annotaterb" # A gem for generating annotations for Rails projects.
  gem "authentication-zero", require: false # An authentication system generator for Rails applications
  gem "benchmark" # a performance benchmarking library
  gem "erb_lint", require: false # ERB lint tool
  gem "guard" # Guard keeps an eye on your file modifications
  gem "listen", "~> 3.5" # Listen to file modifications
  gem "rack-mini-profiler" # Profiles loading speed for rack applications.
  gem "ruby-lsp-rails", require: false # A Ruby LSP addon for Rails
  gem "stackprof" # sampling callstack-profiler for ruby 2.2+
  gem "standardrb", "~> 1.0", require: false # Alias for the standard gem, which has a standardrb binary
  gem "web-console" # A debugging tool for your Ruby on Rails applications.
end

group :development, :test do
  gem "bundler-audit", require: false # Patch-level verification for Bundler
  gem "byebug" # Ruby fast debugger - base + CLI
  gem "debug", platforms: %i[mri windows] # Debugging functionality for Ruby
  gem "minitest-difftastic", "~> 0.2" # Minitest Plugin to use difftastic for failed assertions
end

group :test do
  gem "capybara" # Capybara aims to simplify the process of integration testing Rack applications, such as Rails, Sinatra or Merb
  gem "rails-controller-testing" # Extracting `assigns` and `assert_template` from ActionDispatch.
  gem "selenium-webdriver" # Selenium is a browser automation tool for automated testing of webapps and more
  gem "vcr", "~> 6.1" # Record your test suite's HTTP interactions and replay them during future test runs for fast, deterministic, accurate tests.
  gem "webmock" # Library for stubbing HTTP requests in Ruby.
end

Give it a spin on your own Gemfile in the Rebundler Playground, or bundle add rebundler and run it locally. I’d love to hear what you think!

  1. It seems that might be coming via syntax_tree soon, though. 

  2. The next version of Prism will make that a lot easier.