193

Is there something similar to the Python utility virtualenv?

Basically it allows you to install Python packages into a sandboxed environment, so easy_install django doesn't go in your system-wide site-packages directory, it would go in the virtualenv-created directory.

For example:

$ virtualenv test
New python executable in test/bin/python
Installing setuptools...cd .........done.
$ cd test/
$ source bin/activate
(test)$ easy_install tvnamer
Searching for tvnamer
Best match: tvnamer 0.5.1
Processing tvnamer-0.5.1-py2.5.egg
Adding tvnamer 0.5.1 to easy-install.pth file
Installing tvnamer script to /Users/dbr/test/bin

Using /Library/Python/2.5/site-packages/tvnamer-0.5.1-py2.5.egg
Processing dependencies for tvnamer
Finished processing dependencies for tvnamer
(test)$ which tvnamer 
/Users/dbr/test/bin/tvnamer

Is there something like this for RubyGems?

dbr
  • 165,801
  • 69
  • 278
  • 343

8 Answers8

99

RVM works closer to how virtualenv works since it lets you sandbox different ruby versions and their gems, etc.

Ryan
  • 1,372
  • 14
  • 20
Van Nguyen
  • 4,001
  • 1
  • 25
  • 17
83

Neither sandbox, RVM, nor rbenv manage the versions of your app's gem dependencies. The tool for that is bundler.

  • use a Gemfile as your application's dependency declaration
  • use bundle install to install explicit versions of these dependencies into an isolated location
  • use bundle exec to run your application
michaeljoseph
  • 7,023
  • 5
  • 26
  • 27
pje
  • 21,801
  • 10
  • 54
  • 70
  • 7
    Also, personally I think people overuse rbenv/rvm. If you don't absolutely need to have multiple, isolated versions of ruby on the same machine—and you probably don't—don't use rbenv/rvm. Their "abstraction" doesn't come for free; I guarantee you will have to spend time debugging them at some point. My advice: just install ruby with your operating system's package manager. Latest is greatest. – pje Oct 26 '14 at 20:45
  • 17
    Am I missing something? Bundler still tries to install packages system wide by default. – detly Feb 21 '19 at 09:27
  • 12
    Isolated environments are pretty much non-optional in a modern work flow. If you rely on system ruby and system package manager you have *NO* guarantees your installs will be repeatable and thats going to bite you in the face come deploy time. Of *course* you'll occasionally get bogged down fighting the install monster. Thats also a good thing. Because you solve the problems in your dev environment so you never have to solve them in your live environment. To repeat, if your getting paid to code *never* use the system environment. Use an isolated environment, for sanity sake. – Shayne Mar 30 '19 at 05:06
  • OK, but what if the Gemfile requires a specific version of Bundler? I can't use Bundler to install Bundler. – Resigned June 2023 May 09 '21 at 20:46
  • 3
    Yes, this answer is currently misleading. From the perspective of someone coming from the Python land, Ruby's bundler gem works exactly like doing `sudo pip install` unless you run verbose commands. `bundle install` does **not** use an isolated location, instead it tries to install gems from the Gemfile wherever it can, by all means necessary. This includes breaking the system ruby. Progress is being made on https://github.com/rubygems/rubygems/pull/5327 – Iuri Guilherme Jun 16 '22 at 15:16
  • 2
    The current recommended way to use bundler as the OP of this answer intends requires an extra step after creating the Gemfile and before running `bundle install` which is setting the path which bundler will use when invoking the gem command: `bundle config set --local path 'vendor/bundle'`. This will ensure that gems are installed in a local directory and not mess with the system or even user environment, behaving pretty much like python's venv, pipenv, virtualenvwrapper, poetry, etc. – Iuri Guilherme Jun 16 '22 at 15:25
  • @IuriGuilherme Perhaps you mean `bundle config --local path 'vendor/bundle'`? – Alex Willmer Nov 13 '22 at 14:30
  • `bundle config set ` was added in bundler 2.1 (released Dec 2019). It's an alias for `bundle config `. – Alex Willmer Nov 13 '22 at 14:41
  • @luri Guilherme "Isolated environments are pretty much non-optional in a modern work flow." then "To repeat, if your getting paid to code never use the system environment. Use an isolated environment, for sanity sake." I must of missed something in what was getting conveyed but these seem opposed to each other! – NDEthos May 27 '23 at 06:30
25

I'll mention the way I do this with Bundler (which I use with RVM - RVM to manage the rubies and a default set of global gems, Bundler to handle project specific gems)

bundler install --binstubs --path vendor

Running this command in the root of a project will install the gems listed from your Gemfile, put the libs in ./vendor, and any executables in ./bin and all requires (if you use bundle console or the Bundler requires) will reference these exes and libs.

GEOCHET
  • 21,119
  • 15
  • 74
  • 98
ian
  • 12,003
  • 9
  • 51
  • 107
  • 2
    A wee tip for those on macOS, if you name the path `vendor.noindex` your Spotlight searches won't be cluttered up with data indexed from the vendored gems. – ian Aug 17 '19 at 14:03
  • 2
    This is what I do too (with `rbenv`, but that's another story), since it isolates the gemset and the ruby version. Nothing is installed systemwide and every project has all it's dependencies well declared. Tip: use `bundle config path vendor` not to ever forget that argument. – nandilugio Oct 29 '19 at 09:01
  • An optional addition to this would be to have scripts that expect to reference thus-installed libraries do something like: `ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __FILE__)` followed by `require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])`. This combination should self-configure the individual executable to find bundle-installed libraries according to a `Gemfile` that's in the same directory (adjust accordingly if you have subdirectories). Not 100% sure how this interacts with the `--path=vendor` piece, but it seems to work for me. – lindes Oct 21 '22 at 02:41
18

No one seems to have mentioned rbenv.

Xuan
  • 5,255
  • 1
  • 34
  • 30
  • 4
    rbenv is a ruby manager, but it at a package level (the equivalent of virtualenv) it doesn't natively offer a gemset manager to be able to offer a virtual env. – yekta Jun 23 '15 at 14:29
18

If you only need to install gems as non-root, try setting the GEM_HOME environment variable. Then just run gem.

For example:

$ export GEM_HOME=$HOME/local/gems
$ gem install rhc
mpb
  • 1,277
  • 15
  • 18
  • Awesome! On OSX that folder is hidden: `export GEM_HOME=$HOME/.local/gems` – Bruno Jun 01 '18 at 08:30
  • 1
    It might be easier to use `GEM_HOME=$HOME/.local` so it shares the same `.bin` folder. In that case we don't have to update our `$PATH` variable. – Bruno Jun 01 '18 at 08:50
  • nb: https://stackoverflow.com/questions/55074420/gem-home-is-ignored-by-gem-install – Jan Kyu Peblik Oct 05 '19 at 00:10
  • By far the cleanest no-dependencies approach I've seen yet for installing tools which don't break each other when updated (all that's left is adding an alias in your `~/.bashrc` and you're done). +1 – Paradoxis Oct 12 '19 at 21:55
4

I recommend direnv. It is an environment switcher for the shell.

Before each prompt it checks for the existence of an ".envrc" file in the current and parent directories. If the file exists (and authorized), it is loaded into a bash sub-shell and all exported variables are then captured by direnv and then made available the current shell.

Here is how to use direnv with ruby-install

+ ruby-install

Add this to the ~/.direnvrc

use_ruby() {
  local ruby_root=$HOME/.rubies/$1
  load_prefix "$ruby_root"
  layout_ruby
}

Install ruby-install (brew install ruby-install) and install a bunch of rubies.

ruby-install ruby 1.9.3
ruby-install ruby 2.0.0
ruby-install ruby 2.2.0

And then make a couple of symlinks for convenience:

ln -s .rubies/1.9 ruby-1.9.3-p*
ln -s .rubies/2.0 ruby-2.0.0
ln -s .rubies/2.2 ruby-2.2.0

And finally in any project's .envrc:

use ruby 2.0

This will put all gems under the project's .direnv/ruby directory (makes opening gems easier). bundler will put wrapper binaries in .direnv/bin (no more bundle exec!).

+ rbenv

It's also possible to use rbenv by adding the use rbenv command in any .envrc file. This will activate rbenv which in turn will put the ruby wrappers in the PATH.

Note that it's not necessary to install rbenv in the .bashrc or .zshrc for this to work.

+ RVM

Here is the most complicated .envrc that I use on ruby projects:

rvm use 1.8.7
layout ruby
PATH_add .direnv/bundler-bin

rvm is used to select the right ruby version for you

layout commands automatically set some of the usual environment variables. For now only the ruby layout exists. What it does is set the GEM_HOME environment variable and it's bin directory to your path. Because it depends on the ruby version, make sure to call it after "rvm". Since each ruby layout directories have their own GEM_HOME, you don't need to use rvm's gemsets.

PATH_add prepends and expands the given relative path. In that case, I use this to segregate the bundler binstubs from my own bin scripts with bundle install --binstubs .direnv/bundler-bin

If you want to find out what those commands exactly do, for now: cat direnv stdlib | less

Shin Kim
  • 4,911
  • 3
  • 29
  • 31
  • 2
    While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - [From Review](/review/low-quality-posts/10273901) – jezrael Nov 20 '15 at 10:20
  • @ jezrael Thank for your comment! – Shin Kim Apr 14 '16 at 15:08
  • 1
    Super, no problem. – jezrael Apr 14 '16 at 15:09
4

Here's two cents from an experienced Python developer currently learning Ruby. Using rbenv together with its rbenv-gemset plugin is the closest thing to Python Virtual Environments I have found so far.

rbenv is a tool to manage multiple Ruby versions against the same platform. If you are a Python developer you have probably stumbled upon pyenv. Well, they share the very same purpose and, as a matter of fact, pyenv was actually born as a fork of rbenv.

Without entering in to much technical details, rbenv intercepts Ruby commands using SHIM executables injected into your PATH, and determines which Ruby version has been specified by your application, and passes your commands along to the correct Ruby installation.

A Ruby version can be specified for a project in many different ways. One of the most common is to place a .ruby-version file in the project root directory containing the desired version.

However, whenever a version is used to install gems, those will be shared among all the projects using that version.

Here's where rbenv-gemset plugins comes in handy. In a similar fashion to the .ruby-version file you may initialize a .ruby-gemsets file containing the path of the directory where the gems should be installed for this project (by default a local .gems directory in the project root). Thus isolation between project is achieved.

Here's an oldie but good article about the topic.

  • 1
    While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - [From Review](/review/late-answers/30877031) – Flair Jan 25 '22 at 18:35
1

Mineshaft is a project that I've been working on for some time and am continuing development work on.

It offers the ability to both create virtual environments akin to how virtualenv works and can also install Ruby globally as well.

Nicholas.V
  • 1,926
  • 2
  • 15
  • 30
ctesterman
  • 11
  • 1