rvm gemsets with Bundler - ruby-on-rails

Using Rails:
If Bundler retrieves the proper gems (and dependencies) and locks them in the Gemfile.lock for a given project, isn't using a gemset for this same project overkill? I've been told that using gemsets is still a good practice because merely having 2 versions of the same gem in your current PATH can cause conflicts. Is this right, or do you only need one or the other: Bundler or RVM?

It's redundant to use RVM's gemsets if you're using bundler.
Conflicts when using Bundler arise primarily for two reason:
Using gems that require other gems without precise version specifications.
Executable conflicts: you have both rails v3 and v4 installed, so where do we go to when calling rails g migration or calling rake?
The first issue can be resolved if you're careful about specifying your gem versions more explicitly in your Gemfile.
When working within a project with a Gemfile, the second issue can be resolved by prefixing executable calls with bundle exec, which will run the command within the context of the current bundle (e.g. bundle exec rake db:migrate).
When you want to specify a gem version outside of a Gemfile's context (e.g. rails new fancy_app), you can specify any gem's version by providing it as the first argument surrounded by underscores.
rake --version
rake _10.3.1_ --version
rails new rails_latest_app
rails _3.2.12_ new rails_3_app
rails _4.0.4_ new rails_4_app
RubyGems handles all of this for you by creating version-aware wrappers for any gem's executables. Just run cat `which gem_executable` (with gem_executable being something like rake, rails, foreman, pry, etc.) and have a look.
Stephen Ball has a good blog post about how to use Bundler instead of RVM gemsets, which explores the overlaps in further detail.
While RVM's gemsets are not necessary, RVM provides other conveniences:
Automatically adding bundler binstubs to the PATH, so you can avoid typing bundle exec. Note the bundler plugin for oh-my-zsh provides the same feature
Managing multiple Ruby versions
The ruby version manager rbenv provides similar features as well.

Yes, gemsets are overkill. Just use bundler.
RVM is still usefull for managing versions of Ruby itself - but don't use it for gemsets. Just use bundler for gem version management.
Regarding conflicts between gem versions, if you use bundle exec before each command you shouldn't have a problem - eg. bundle exec rake db:migrate or whatever.

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.

rbenv: Surviving without gemsets

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.

How can I keep my Ruby Gems the same on several servers?

I have several servers for development, staging, testing and production and want to keep my Ruby version and Gem versions in sync between the different machines -- what's the best way to do this?
If you're using Rails 3, you can just use the Gemfile, and bundler, and when you do a "bundle update" on whatever machine you're on, it will automatically install the correct versions of Gems for that user account.
You should use RVM to manage your Ruby versions and to keep the Gems for different Ruby versions separate from each other.
You should also create a "gemset" for your Rails application, to make sure that the Gems installed there are not modified from another Rails application you might use or develop on the same machine.
http://beginrescueend.com/
http://railscasts.com/episodes/200-rails-3-beta-and-rvm
rvm is the best option to manage ruby versions
However I am not sure if you really want to change ruby version often.
bundler & Gemfile is great way to handle gem versions
Track you Gemfile && Gemfile.lock files in git(or other cvs) tree, and run bundle install on change
You can also, in the event you have more gems in a gemset than just what's in your Gemfile, do a
rvm gemset export gemsetname.gems
And then on the other machine you would do a
rvm gemset import gemsetname.gems
You can even do it as
rvm x.x.x#mygemset exec rvm gemset export mygemset.gems
so you don't have to go directly into your gemset. And then on the new machine,
rvm --create use x.x.x#mygemset && rvm gemset import mygemset.gems
which will create and then populate your mygemset gem set.

What are the benefits & drawbacks of installing things into the #global gemset in RVM?

What are the benefits & drawbacks of installing things into the #global gemset in RVM?
Let's say I want to install different versions of rails on the same server. I then want the ability to install multiple ruby apps on the same server, with the least duplication of files to save on disk space. However, I still want to avoid dependency problems, gem conflict issues and other problems.
Let's also assume that each app has extra gems it needs that I only want in it's local project gemset.
Would I be better off:
Installing both Rails 3 and Rails 2 gems into the #global gemset
...And use project-local gemsets for their gems...
Installing Rails 3 into a #rails3 gemset, and Rails 2 into a #rails2 set... then cloning for each project I need?
For example:
rvm use ree#rails3 && rvm gemset export rails3.gems
rvm use ree#rails2 && rvm gemset export rails2.gems
rvm use --create ree#project1-on-rails3 && rvm gemset import rails3.gems
Install more project-local gems here...
rvm use --create ree#project2-on-rails2 && rvm gemset import rails2.gems
Install more project-local gems here...
Something else entirely...
NOTE: I wrote this whole response assuming that you are using Bundler to manage your gem dependencies. I realize that some people don't, and you didn't mention Bundler in your question. If you aren't using Bundler, I would point out that it probably is the best way to conserve disk space (only if you bundle install --system, though!). If you are using exported gemsets to manage dependencies, I think your scheme sounds reasonable, but I have no experience with it.
Both Rails 3, and Rails 2 with Bundler will set their load path appropriately such that they will not load any gems (or any versions of any gems) that are not in the Gemfile.lock. There's not really any way that I've experienced to have a "gem dependency problem" on the server. It is important that you run bundle install on your development machine whenever you modify the Gemfile, and that you check your Gemfile.lock into source control, as described on the Bundler homepage.
I spent some time digging into the use-cases of gemsets back in January. The reasons I found to use separate gemsets for each project were:
Your shell environment is the same as your application environment (scripts run correctly without bundle exec).
You can easily browse and grep through the source code of all your dependencies, by navigating to the gemset install directory.
It prevents some reported ‘heisenbugs’, according to the author of RVM. I have experienced something like this where a gem executable wasn't available and bundle exec didn't seem to help.
I don't think any of these benefits are very compelling on the server, so if you are aiming to conserve disk space, I'm not sure why you would use gemsets at all.
Actually, the only reason I've used rvm at all on the server was because it was a convenient way to build ruby from source (we needed a version that wasn't available in the native package manager).

Rails 3.0 bundle

"rails new app" command is generating two rails applications namely "new" and "app" but both of them doesn't contains Gemfile, due to which I am not being able to run "bundle" command by changing current directory to either of the applications. I am using "rvm use 1.9.2" command to have ruby 1.9.2 and the more "gem list" command also shows bundle and bundler.
so any help .
You're probably using a old Rails2 "rails" binary.
Give rails --version a try.
If you have multiple versions of Rails installed you'll have trouble with which binary is being used.
Check if you have both 2.3.x and 3.0.x installed by running gem list.
If you need both versions, you'll want to create gemsets for each rails version in rvm. Then you do something like rvm use 1.9.2#rails2 or rvm use 1.9.2#rails3 to specify which gemset you want to use.

Resources