What's the proper way to set up a Ruby development environment?

I’m trying to set up a Ruby development environment with nix on macOS. We don’t use gemset.nix or bundix, so I just need Ruby 3.2.6 and Bundler 2.5.17 available in the shell. Nixos 24.11 has Ruby 3.2.8.


First approach (with overrideAttrs)

with import (fetchTarball {
  url = "https://github.com/nixos/nixpkgs/archive/nixos-24.11.tar.gz";
  sha256 = "16pw0f94nr3j91z0wm4ndjm44xfd238vcdkg07s2l74znkaavnwk";
}) { };

let
  ruby = ruby_3_2.overrideAttrs (old: {
    version = "3.2.6";
    src = pkgs.fetchurl {
      url = "https://cache.ruby-lang.org/pub/ruby/3.2/ruby-3.2.6.tar.gz";
      sha256 = "sha256-2ctl7N8/GGaWOfJji2M3ntb7sX2Trk5ybU6yv2ikg3A=";
    };
  });
in

mkShell {
  buildInputs = [ ruby.devEnv ];
}

But then I noticed something strange:

$ ruby -v
ruby 3.2.6 (2024-10-30 revision 63aeb018eb) [arm64-darwin24]
$ which ruby
/nix/store/8df7ax670j43pa9y6wr5lm848wcrw1al-ruby-dev-3.2.8/bin/ruby

The derivation name still shows ruby-dev-3.2.8, even though the actual Ruby version is 3.2.6. ChatGPT suggested to use mkRuby.


Second approach (with mkRuby)

with import (fetchTarball {
  url = "https://github.com/nixos/nixpkgs/archive/nixos-24.11.tar.gz";
  sha256 = "16pw0f94nr3j91z0wm4ndjm44xfd238vcdkg07s2l74znkaavnwk";
}) { };

let
  ruby = mkRuby {
    version = mkRubyVersion "3" "2" "6" "";
    hash = "sha256-2ctl7N8/GGaWOfJji2M3ntb7sX2Trk5ybU6yv2ikg3A=";
    cargoHash = "sha256-6du7RJo0DH+eYMOoh3L31F3aqfR5+iG1iKauSV1uNcQ=";
  };
in

mkShell {
  buildInputs = [ ruby.devEnv ];
}

Now the derivation name looks correct:

$ ruby -v
ruby 3.2.6 (2024-10-30 revision 63aeb018eb) [arm64-darwin24]
$ which ruby
/nix/store/fsy72y9kx7bmkhj5rww00krv81aybp16-ruby-dev-3.2.6/bin/ruby

Bundler question

I also found the source of the bundler package here:
:arrow_right: nixpkgs/pkgs/by-name/bu/bundler/package.nix at 5d736263df906c5da72ab0f372427814de2f52f8 · NixOS/nixpkgs · GitHub

However, I don’t understand how to build Bundler 2.5.17 specifically, and how to include it properly in ruby.devEnv. Any tips or examples would be helpful!

Naive approach doesn’t work:

bundler = bundler.overrideAttrs(old: {
  version = "2.5.17";
});

Questions

  1. Is mkRuby the recommended way to get a specific Ruby version?
  2. Why did overriding ruby_3_2 not result in a correctly versioned derivation name?
  3. What’s the correct way to build and use Bundler 2.5.17 with a custom Ruby version in a dev shell?
  4. Is there a simpler or better way to do all this if I just want Ruby and Bundler, without gemset or Gemfile support?

Any guidance would be greatly appreciated!

If you really just want this particular Ruby version (3.2.6) and bundler in 2.5.17 you could write such a flake.nix:

{
  description = "Ruby 3.2 development environment with Bundler 2.5.17";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/63158b9cbb6ec93d26255871c447b0f01da81619";
    flake-parts.url = "github:hercules-ci/flake-parts";
  };

  outputs = inputs@{ self, flake-parts, ... }:
    flake-parts.lib.mkFlake { inherit inputs; } {
      systems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ];

      perSystem = { pkgs, ... }: {
        devShells.default = pkgs.mkShell {
          buildInputs = [
            pkgs.ruby_3_2
          ];

          shellHook = ''
            export GEM_HOME=$PWD/.gem
            export GEM_PATH=$GEM_HOME
            export PATH=$GEM_HOME/bin:$PATH

            # Install specific bundler version
            gem install bundler -v 2.5.17

            ruby -v
            bundler -v
          '';
        };
      };
    };
}

After a nix develop it should give you:

Successfully installed bundler-2.5.17
Parsing documentation for bundler-2.5.17
Done installing documentation for bundler after 0 seconds
1 gem installed
ruby 3.2.6 (2024-10-30 revision 63aeb018eb) [x86_64-linux]
Bundler version 2.5.17