rbenv: Surviving without gemsets - ruby-on-rails

TL;DR
Don't bother with gemsets; multiple versions of a gem may be installed concurrently.
When necessary, specify which version to execute using $ gem-based-binary _version_ args notation.
Use bundle exec when you have a Gemfile specifying the version.
gem install rails -v 3.2.13
rails _3.2.13_ new Project2
cd Project2
bundle exec rails server
UPDATE: 2015-06-04
I wrote this question three years ago. Partly, it was based on a false assumption, and partly the situation has changed since then. With appreciation to #indirect for his original answer, I want to call attention to #kelvin's newer (less upvoted) answer, summarized above.
My false assumption: Only a single version of a gem could be installed at a time, hence the need for gemsets to isolate the namespace. Not true. Multiple versions of a gem may be installed concurrently. The most recent one will be used when invoked from a command line, unless you have a Gemfile specifying the version constraints and invoke the command via bundle exec, or specify the version as its first argument.
See also How can I call an older version of a gem from the commandline? re: the underscore-version notation.
Original question:
I have multiple projects going on using different versions of Rails. I have a workflow (described below) for creating projects using specific versions of rails, and keeping the projects isolated from each other. I'd like to experiment with other workflows, in particular, using rbenv instead of RVM, but it's not clear how to do so.
QUESTION: What is the best current practice for creating multiple rails projects, each using a different version of rails, when making use of rbenv and bundler, as opposed to rbenv-gemset or rvm?
USE CASE: I have two rails projects, called ProjectA and ProjectB. ProjectA is developed using one version of rails ("RailsA"), whereas ProjectB uses a different version ("RailsB"). How do I manage having both versions installed?
THE GEMSETS APPROACH: When I first started with Rails development, I used RVM. In addition to supporting multiple, concurrent installations of ruby, RVM supports having multiple Named Gem Sets. Each project has its own independent collection of gems (including rails itself) called a gemset:
rvm gemset create RailsA
rvm gemset use RailsA
# RailsA. Note: My question is not version-specific.
gem install rails --version 3.0
rails new ProjectA
cd ProjectA
rvm --rvmrc use `rvm current`
vi Gemfile
bundle install
cd ..
## Now do the same for ProjectB
rvm gemset create RailsB
rvm gemset use RailsB
gem install rails --version 3.2
rails new ProjectB
cd ProjectB
rvm --rvmrc use `rvm current`
vi Gemfile
bundle install
Note: The very creation of the project folders should be done (IMHO) by a rails new command using the desired version of rails, since the skeleton files change from version to version. (Perhaps I should revisit this premise?)
THE BUNDLER APPROACH: I've been playing with using rbenv instead of RVM, but I don't understand the workflow as clearly. In the README.md, Sam Stephenson writes that "rbenv does not ... manage gemsets. Bundler is a better way to manage application dependencies." There is a plugin (rbenv-gemset) for getting the same results as rvm's gemsets, but Sam clearly favors using Bundler instead. Unfortunately, he doesn't elaborate on what the workflow would look like. Even the Bundler website doesn't explicitly connect all the dots of how to isolate one project from another. Several blogs and gists come to the rescue, suggesting the following ~/.bundle/config file:
---
BUNDLE_PATH: vendor/bundle
(BTW, I'm not sure what the "---" is about. The docs make no mention of it and it doesn't seem to make a difference.)
This effectively gives each rails project its own gemset, storing the gems in ProjectX/vendor/bundle/. In fact, rails itself will be (re-)installed there, making the project completely independent of the rest of my environment, once I run bundle install.
But the elephant in the room is the chicken-and-egg problem of creating the rails project folder in the first place!! In order to create the ProjectA folder using RailsA, I need to install rails (and its numerous dependencies) first. But when I want to create ProjectB, I must then switch to using RailsB. Without gemsets, I must do some serious upgrading/downgrading. Not cool.
A possible solution is simply not to worry about what version of rails I use to create the ProjectX folder. If I then use rails 3.0 to create a 3.2 project, I could just manually create the app/assets tree. But that just irks me. Ain't there a better way?

Most people solve this by installing the rails gem first via gem install rails. If you refuse to do that for some reason, you can opt out of the automatic bundling that Rails attempts to do for you. This will work completely regardless of your ruby management system.
mkdir myapp
cd myapp
echo "source :rubygems" > Gemfile
echo "gem 'rails', '3.2.2'" >> Gemfile
bundle install --path vendor/bundle
bundle exec rails new . --skip-bundle
When prompted, type "y" to replace your Gemfile with the default Rails one (or not, as you prefer). Then, once it's done:
bundle install
You're done, and you have boostrapped a new rails app with the version of your choice without installing the rails gem into rubygems.

Suppose you have rails 3.1.0 installed, but you want to create a new project using rails 3.2.13 which is not installed.
Let's say you want the new project to be in ~/projects/Project2.
gem install rails -v 3.2.13
cd ~/projects
rails _3.2.13_ new Project2
This will create the Gemfile for you, locked to the version of rails you specified on the command-line.
I deliberately omitted the idea of keeping a separate copy of gems for the new project, because that goes against the Bundler philosophy, which is to have all gems installed in one place. When you run rails, Bundler will pick the correct gem versions automatically from that central location. That means a project can share gems instead of installing a fresh copy for itself. (Note, however that each version of ruby you install will have its own gems. This is a good thing because native extensions likely won't work across ruby versions.)
You do have to be a bit more aware, because most commands, like rake, will load the newest version of rake that you have installed. You'll need to run bundle exec rake ... to make sure the correct version is loaded. Usually I'll run bundle exec for all commands except rails. You can create an alias to make it shorter (I use bex). To automate this with gem executables, you can use rbenv-binstubs, but you still have to be aware that running non-gem executables like ruby and irb won't automatically use the Gemfile.
Sidenote: rails new will run bundle install, which will check for the newest version of the dependencies. If you want bundler to try to use currently installed gems that satisfy the dependency requirements, you can skip the bundle install with rails new --skip-bundle, then run bundle check in the app dir.
Sidenote 2: suppose you want to use a ruby version for Project2 (e.g. 2.1.8) that's different from the default (e.g. 2.3.0). In that case, running gem install as specified above will install the gems under 2.3.0, which is a waste of time because you'll need to install the gems again under 2.1.8. To solve that problem, you can force the commands to use the preferred version via environment variable:
RBENV_VERSION=2.1.8 gem install rails -v 3.2.13
cd ~/projects
RBENV_VERSION=2.1.8 rails _3.2.13_ new Project2
echo 2.1.8 > Project2/.ruby-version
You could use rbenv shell to set the variable, but I only recommend that if you don't want rbenv to auto-switch based on .ruby-version files for the duration of that shell. It's very easy to forget that you have the variable set, and when you cd to a different project, it won't be using the version you expect.

There's a good recent post on exactly the topic of gemsets / bundler here http://rakeroutes.com/blog/how-to-use-bundler-instead-of-rvm-gemsets/ Good background you can apply to your rbenv setup.

Related

How to use rbenv and store gems locally - coming from rvm background

I used to use rvm and I want to start trying out rbenv.
From what I understand, rbenv does not have the same isolation built in when it comes to gems, it is only managing your ruby versions.
I know there is a rbenv addon that handles gems, but I dont' NEED to get it correct?
I can still download gems locally to my project and use bundle exec for each command?
Is there a short cut that I don't have to be so verbose when typing my commands?
Please explain the workflow as I dont' want to assume anything.
Update
I'm confused how to get the gems loaded into a separate folder.
Here's what I recommend:
Use rbenv for multiple Ruby version management, no customizations needed
a ruby installer plugin is now included with rbenv
it also handles ruby executable shims automatically, don't need to rbenv rehash anymore
it loads really fast (rvm has a noticable load time on shell startup)
Use bundler to dynamically resolve gems at runtime (options below)
it's fast enough anyways
don't need a special gem solution, bundler comes included /w Ruby now
Options to invoke bundler dynamically (I recommend the last one):
use bundle exec in front of every ruby executable
variant: create alias be='bundle exec'
create bundle binstubs <LIST GEM EXECUTABLES YOU WANT> for each project
use bin/ in front of every ruby executable to call the binstubs
do #2 and then set up .git/safe
lets you manually allow PATH lookups to the bin/ folder while in that project root
don't need to type bin/ anymore
Now multiple gem versions will all be installed into the same Ruby version bucket, and you let bundler dynamically add the right versions to the load path before every startup.

Does Rails need to be installed as a system gem?

In every setup describing a configuring an environment with rbenv and Bundler, the instructions are always to install bundle as a system gem, using gem install bundler. Often, they'll also recommend rbenv-bundler rbenv plugin, but the maintainers of rbenv discourage this.
What's not described is how to install Rails. Initializing a new Rails project creates a basic Gemfile for bundler. However, in order to initialize a Rails project, one needs to have Rails installed. It seems weird and even wrong to make a directory, write a basic Gemfile that includes Rails, run bundle install, and then initialize Rails to the current directory. In fact, I doubt that would even work well, if it worked at all.
So, does Rails need to be installed as a system gem with gem install rails? If so, how does one manage multiple versions of Rails, particularly with rbenv?
It totally makes sense to NOT install rails as system gem.
Without messing up rbenv or other ruby version manager you use, below are brief steps to create (initialize) a new Rails app from a directory with a Gemfile:
mkdir rails_app
cd rails_app
vi Gemfile # Edit it to include a rails version you need
bundle --path vendor # Wait for bundler to finish
bundle exec rails new ./
The last step would ask: Overwrite /path/to/rails_app/Gemfile? (enter "h" for help) [Ynaqdh]. Input y to get the default Rails Gemfile content.
Note: the above steps specify the local vendor directory (inside the rails app folder) to avoid installing gems to system global scope.
Answer is no, you don't install rails as system gem. Create a project folder, add .ruby-version file and add the ruby version you would like in this file i.e. 2.3.0. rbenv uses the version specified in this file and it won't be system's ruby.
Now you can do gem install bundler from this directory and create Gemfile and add your rails version. Now run bundle install and roll it on the tracks of RAILS.....
Force rails to vendor gems.
$ mkdir foo
$ cd foo
$ bundle config --local path vendor
$ rails new .

Having different rails and gems versions installed on dev environment

I'm quite newbie in ruby and ruby on rails and I'd like some clarification, if possible.
I'm currently having rails 4.2.6 installed on my development environment in which I have built a few projects. Now, for my new projects I'd like to use Rails 5, so I assume that if I type the gem install rails command will get me the latest rails verion and probably set it as default, so every time I want to create a new project by rails new my_new_project_name this project will have the latest version (currently v5).
My question is will my gem list contain both rails versions then or is it going to cause any conflicts and issues to my old porjects? so, if I want to work back on any of my projects which has the "old" version, won't affect of any changes, right? As far as I understand its the bundler who picks the version of each gem, right?
If thats the case, I assume same thing applies and for every other gem that I use for each project, right?
You will have all different versions. BUT you will need to add the version number for all gems to your gemfile
For example
and in the gemfile you state:
gem 'remotipart', '1.2.1'
Best thing to do when using different versions of a same gem is to use either rbenv or RVM to create different gemsets for each project. This way You can be sure that your project won't load by mistake another version .
I personally use RVM so i am going to let You know how to use it.
1) install RVM from here https://rvm.io
2 ) make sure You are loading RVM in your .bashrc or .bash_profile files from your home directory ( /home/your_username) . You can use this code:
export PATH="$PATH:$HOME/.rvm/bin"
[[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm"
3) take a look at this page https://rvm.io/workflow/projects and choose how You want to set RVM for your project . i personally use .rvmrc because i am old school...but those are not recommended anymore because they need trusting and are slower. As a alternative You can use .ruby-version and .ruby-gemset files. But You can use .versions.conf also If You want. Let's say for now we choose to use .ruby-version and .ruby-gemset files.
4) cd into your project and run this command `rvm --ruby-version use 2.3.3#project1 --create' . This will generate those two files în your project . And everytime You will cd into this project RVM will pick-up the gemset automatically.
5) Do the step 4) for the second project also but replace 'project1' with 'project2' .
6) Now You can edit the Gemfiles of these two project and put the version of rails that You desire.
7) to install each project You need to cd into that directory and run command 'gem install bundler' ( only first time) and then You can safely do 'bundle install'
8) repeat 7) for second project.
9) You are all Done. Now You have different version of same gem in two different gemsets .
To also answer the other questions:
1) having în the same gemset multiple versions of same gem can lead to conflicts especially when doing 'rails new project ' from terminal since this doesn't require a specific version . I suggest to create different gemset before installing a new version of a existing gem on your machine. For example for this particular case we can do this
rvm use 2.3.3#rails5 --create
gem install bundler
gem install rails -v 5.O.0.1
And now we can safely do 'rails new project' . This way the new version of rails is inside a new gemset .
I Hope this will help You :)
I solved this kind of dependence issues by moving all my developments to docker. So now I have a unique environment for each project trough use of Dockerfile.
I also employ Makefile and compose.yml to automatize by ci. Hope that help.

clarification on rvm and switching between rails

following http://railsapps.github.io/installrubyonrails-mac.html , I encounter the following commands
rvm use ruby-2.1.3#rails4.1 --create
gem install rails # installs the latest rails version
rails -v # returns 4.1.6
however, I can also do the following, which would add rails version to 4.0.8
rvm use ruby-2.1.3#rails4.0 --create
gem install rails --version=4.0.8 # installs the latest rails version
rails -v # returns 4.0.8
What is the point of this? Somewhere in the text it's said that this method is to prevent global gem-set and instead install rails based on project-specific gemsets? What does that even mean?
And this is the instructions on how to create a new rails project
$ mkdir myapp
$ cd myapp
$ rvm use ruby-2.1.3#myapp --ruby-version --create
$ gem install rails
$ rails new .
why not just call rails new myapp? The text says it's to "create a project-specific gemset", but I have no idea what that means. Wouldn't this just install rails 4.1.6 (newest version) ? why not just install rails 4.1.6 globally in the first place then?
Imagine you are a Rails developer in a company that has been doing Rails apps for the last 4 years. You have apps on Rails 2, Rails 3, Rails 4 - as new versions come out, you upgrade your toolset because, why not? Each new version is better.
However, they are not downward compatible. The Rails 2 app will not work with Rails 4.1. What if you are asked to urgently debug the Rails 2 app while hacking on Rails 4 one? Uninstall global Rails, install Rails 2, make the hack, then uninstall Rails 2 and reinstall the new one again, just so you can run the tests for your one-line bug fix?
That's where gemsets come in. You have environment per-application so that each application can be run self-sufficiently, with no versioning conflicts.
If you don't envision such scenarios of version conflict on your machine (i.e. if you can only imagine working on one project), gemsets are completely irrelevant.
EDIT after some confusion still in comments :) Let's go step by step, see what happens exactly.
$ mkdir myapp
$ cd myapp
You're now in an empty directory.
$ rvm use ruby-2.1.3#myapp --ruby-version --create
rvm creates a new gemset, named ruby-2.1.3#myapp, which will be run with Ruby 2.1.3. As a consequence, you have a new directory at ~/.rvm/gems/ruby-2.1.3#myapp, where your gemset will be. You also have two new files in your previously empty myapp directory: .ruby-version (which contains a single line saying ruby-2.1.3) and .ruby-version (containing the line myapp). These two lines are read by rvm every time you enter the myapp directory: it sets the current Ruby and gemset for you.
$ gem install rails
Having recognised that the current gemset is now ruby-2.1.3#myapp, the gem install command will download the newest rails gem, as well as all its dependencies, and put them in your gemset directory (~/.rvm/gems/ruby-2.1.3#myapp/).
$ get install rails --version=4.0.8
If you try this, it will dutifully install Rails 4.0.8, but since you have a newer version in your gemset and your application is not specifically having any requirements, the newer one will take precedence. This is normally not what you want; and anyway, there is rarely a reason to develop a project to comply with two different versions of a library (unless you're developing a library or a plugin; that's a different story).
$ rails new .
rails is actually executing ~/.rvm/gems/ruby-2.1.3#myapp/bin/rails. If you were not in myapp directory, linked to the gemset, this command would have failed (if you didn't have Rails installed in the global environment), or executed globally installed Rails (if you did).
So, it's not really designed to have two versions of Rails simultaneously in the same project. But when you make another project, with another gemset (say, ruby-2.1.3#myotherapp), you could have a different version of Rails while you're there. The version automagically changes just based on which directory you cd into.

How do you use multiple rails versions with rbenv?

Is it possible to use multiple versions of rails using rbenv (e.g. 2.3 and 3.1)? This was easy with gemsets in rvm, but I'm wondering what the best way is to do it now that I've switched to rbenv (also, I'm looking for a way to do it without rbenv-gemset).
not sure if you got an answer to this, but I thought I'd offer what I did and it seemed to work.
So once you get rbenv installed, and you use it to install a specific ruby version, you can install multiple versions of rails to for that ruby.
STEP 1. Install whatever version(s) of rails you want per ruby version
% RBENV_VERSION=1.9.2-p290 rbenv exec gem install rails --version 3.0.11
By using the "RBENV_VERSION=1.9.2-p290" prefix in your command line, you're specifying which ruby rbenv should be concerned with.
Then following that with the "rbenv exec" command, you can install rails. Just use the version flag as in the example to specify which version you want. Not sure if you can install multiple versions in one shot, but I just run this command as many times as needed to install each version I want.
Note: This will all be managed within your rbenv directory, so it's perfectly safe and contained.
STEP 2. Build a new rails project by specifying the rails version you want.
% RBENV_VERSION=1.9.2-p290 rbenv exec rails _3.0.11_ new my_project
STEP 3. Don't forget to go into that project and set the local rbenv ruby version.
% cd my_project
% rbenv local 1.9.2-p290
Now if you want to delete this project, just delete it as normal.
If you want to delete / manage a rails version from rbenv gems, you can use regular gem commands, just prefix your command line with:
% RBENV_VERSION=1.9.2-p290 rbenv exec gem {some command}
And of course, you can delete a complete ruby version and all its shims, etc that are managed within rbenv pretty easily. I like how self contained everything is.
Hope this helps.
For reference, this is a pretty good walk through of at least some of this stuff:
http://ascarter.net/2011/09/25/modern-ruby-development.html
There is a rbenv plugin called rbenv-gemset which should behave similar to the rvm gemset-command but since rbenv was never intended to work this way, I haven't tried it.
I usually manage Rails versions with Bundler as Nathan suggested in the comments of one of the other answers. I create a Gemfile with my desired Rails version, run bundle install, create the Rails application, let it replace the Gemfile and let Bundler take over:
mkdir my-rails-app
cd my-rails-app
echo "source 'https://rubygems.org'" > Gemfile
echo "gem 'rails', '3.2.17'" >> Gemfile
bundle install
bundle exec rails new . --force --skip-bundle
bundle update
If you want more detail, I wrote an article on my blog about it.
Hope it helps!
If you have setup ruby using rbenv the following will work.
Installing rails, the latest version (7.x as of Oct 2022)
gem install rails -v 7.0.2.4
# Find exe
rbenv rehash
To create a rails project with the latest rails version,
rails new project_1
This will create a rails application with the latest version, to verify we can see the rails version in the Gemspec file (or) see the logs during the installation,
Installing rails, 6.x.x.x version
Assuming we are going to install rails 6.0.4.8, then issue the following commands
gem install rails -v 6.0.4.8
rbenv rehash
Now, to create a rails project with 6.0.4.8 version (which is installed previously), specify the rails version along with the rails command.
rails _6.0.4.8_ new project_2
This will create a rails application with the 6.x version, to verify we can see the rails version in the Gemspec file (or) see the logs during the installation,
Other notes
Similarly, we can manage any no of rails versions in any number of
projects.
rbenv rehash Installs shims for all Ruby executables known to
rbenv
In this approach, you don't need to set or modify any ruby
environment variables.
You don't need to modify Gemspec file by yourself.
The instructions work as of Oct 2022.

Resources