relationship bundler and gem - ruby-on-rails

Coming from the java world if
rake == ant
gem == maven #at least the dependency part
then what the heck is bundler?
It says "managing your application's dependencies", but isn't that what gem is doing by fetching them for me?

Gem fetches a library and installs it in one of the predefined places, like vendor gem, system or user gem directory at that point. That installed version will then be used by your program. So if you have two programs requiring two different versions of a gem you may be out of luck, since when you install the more current one the apps that require the older can have problems. You can specify version numbers for the gems, but those will need to be available on the target server(s) going forward.
There is where bundler comes - it manages exact dependencies including gem versions. So when you deploy an application and have specified exact gem versions that are required, bundler takes care of getting and installing those exact versions at that point. Your app will now have the specified versions 'bundled in' so that you it won't break, when, on the target system, gems have different, incompatible versions.

Rake = ant
gem = jar
Bundler = maven

Related

Manage gem dependencies based on Ruby version

I'm working on a gem that works on Ruby 1.9.3, but installing an up-to-date Gemfile works only on Ruby 2.2. Is there a way of separating dependencies based on Ruby versions?
I've seen this approach in the past:
pry_debugger = RUBY_VERSION < '2.0.0' ? 'pry-debugger' : 'pry-byebug'
spec.add_development_dependency pry_debugger
Or should I just consider supporting one Ruby version (let's say 2.0) and find the supported gems? What's the easiest way of finding what gems work on my local Ruby version?
Logic like you show will work fine for development dependencies since it will be evaluated at bundle install time.
It will not work for runtime dependencies since the logic will be evaluated at gem-build time and not when the gem runs in another environment.
RubyGems does not provide a way for you to specify different gems based on the runtime environment, so the only way you could support that would be to release two differently named versions of your gem with different gemspecs.
Personally, I don't see the point of putting development dependencies in the .gemspec, so I always just add these to my Gemfile and reserve the gemspec for runtime dependencies only.
This separates the concerns better and makes it clear that gem selection logic can be used in the Gemfile, but not in the gemspec.

How can I find out what gem is dependent on termios in my Gemfile?

I have updated all of my gems, including to Rails 3.2.8, prior to a new deployment. However, my application is now broken because something is trying to install gem "termios" version 0.9.4.
Apparently, 0.9.4 does not work on any computer or server I own. There are some newer versions, 0.9.6 specifically, but they are not posted in wherever bundler looks for gems.
There are some version on Github, but they have been mysteriously renamed "ruby-termios". Well, some gem in my Gemfile is not looking for ruby-termios. It's looking for termios. Failure.
How can I find out which gem is trying to install this so I can see if it can be whacked?
Check your Gemfile.lock - it has all the gems and their dependencies listed in it. As long as you've been able to install these gems in the past, you'll be able to tell where that dependency is coming from.
The gem command will dump out the tree of dependencies for you.
$ gem dependency
Or if you want to check just a specific gem.
$ gem dependency foo

Does Bundler Gem take in consideration your Ruby environment?

My question is simple one, does gem bundler considers your ruby environment (e.g. 1.8.7 | 1.9.2) before deciding which gem to take based on gem file?
Let's say your gemfile contains
gem 'thor'
gem 'json'
gem 'grit'
When you run bundle install will take versions of the gem that are compatible with your current ruby environment or just latest gems?
It depends! Bundler relies on the configuration of the Gemspecs that each Gem provides.
Gemspecs offer the posibility to provide different or additional dependencies based on the runtime environment. IE you can change the dependencies for JRuby or provide different binaries for i386 architectures.
As far as i know, it's not possible to declare a gem as 1.9 or 1.8 compatible (which would have made sense to me). I think it's partly so, because 1.9 is 99% downward compatible.
You are always forced to have a look at the gems themselves. Because of this, there are sites like http://isitruby19.com/
As you might see, it's not an issue of Bundler, but RubyGems.

Beginning with Ruby on Rails. How do Gemfile and how gems work when specifying versions to download?

I tried reading the documentation but this still wasn't clear to me:
Suppose my I have the following statements in my Gemfile:
source 'https://rubygems.org'
gem 'rails', '3.2.2'
# Bundle edge Rails instead:
# gem 'rails', :git => 'git://github.com/rails/rails.git'
gem 'sqlite3'
gem 'json'
...
I'm trying to learn how to use Ruby on Rails this website: http://ruby.railstutorial.org/ and it says that I need to have the sqlite3 gem for this version of rails (3.2.2).
My question is: if I don't specify the exact version of the sqlite3 gem for the system to download when I run bundle install then will it install the latest version that works with the version of rails and ruby that I'm working with (suppose it's 3.2.2 and 1.8.7 p-352 respectively) or will it just download the latest version of the sqlite3 gem available whether it works with what I have installed or not?
I'm not really sure how this aspect of the Gemfile works and the documentation hasn't been clear or I haven't found where it explicitly says this or otherwise.
So, it will install the latest version of sqlite3 that is compatible with the dependencies specified by the other gems you've listed.
Say, rails said it only worked with sqlite3 that was greater than 1.0 but less than 4.0, it might install 3.9.5. I'm making all these numbers up, just an example.
In fact, I don't know if rails does specify any particular versions of sqlite3 it requires. Presumably 'json' doesn't either, since it's got nothing to do with databases. If none of the other gems you list specify any requirements for sqlite3, it'll just install the latest version available.
'bundle install' is using bundler, some more about how it resolves dependencies is here.
So bundler will download the latest gem that meets the requirements of all the other gems you list. Say you listed a gem A which required sqlite3 earlier than 2.0 and a gem B that required sqlite3 later than 2.1 -- 'bundle install' would complain and say it can't satisfy them all.
This relies on the gems own advertisements of each of their own requirements for the other gems they rely on though. Sometimes they can be wrong, or missing. It may be that rails doesn't actually specify what version of sqlite3 it requires -- since it doesn't actually require sqlite3 at all (you don't have to use sqlite with rails), this may very well be. In which case bundler can't do much but get the latest version of sqlite3. Which doesn't guarantee that version will work. But it can only do so much.
You also mention the issue of compatibility with specific versions of ruby, like 1.8.7-p352. Bundler is less capable there, you can't generally count on it knowing what version is compatible with a particular version of ruby (because there's not a great way for gems to express this in a way that bundler can use, alas).
But in general, things work out. If you don't know or care what version of sqlite3 (or any other gem) you want, but know you want one -- do what you did, run 'bundle install', see if it works. It probably will. If it doesn't, google the error message and you'll probably find someone explaining why it didn't and what you need to do instead.
PS: If that wasn't enough. sqlite3 is sort of an odd case, because while Rails is designed to work with it, it's not required. But Rails by default, when creating a new app with latest version of rails rails new my_app, will already put sqlite3 in your Gemfile for you. And it'll do it just like you did, without a version constraint. That's reason to feel pretty confident it will work fine. Note that in rails 3.2.2, rails new my_app will put some other things in your Gemfile that do have more specific versions listed, like gem 'sass-rails', '~> 3.2.3', because while those things aren't absolutely required for Rails (or else they'd be expressed as requirements and not need to be explicitly in your app's Gemfile), the rails installer knows that only certain versions will work.
Fairly new to Rails as well so correct me if I am wrong.
The Gemfile looks to the gems directory for the gems listed and has no impact on actually "installing" them. Performing a "gem install" command without a version will grab the most recent version listed no matter what your Gemfile says. When you "bundle install", that is when version-specific lookups happen in your gems directory.
Bundler always looks at your Gemfile.lock file first when you're doing bundle install. If you open that file, you'll see the gems as well as the version used by your application. This is convenient for existing projects because you're always sure that every other developer involved with the project will be working with the same gem versions.
If it doesn't find that file (i.e. doing rails new projectname), or the specified gem or version in Gemfile isn't consistent with Gemfile.lock, it will download the latest or specified version of the gem.

What is the difference between Gemfile and Gemfile.lock in Ruby on Rails

I am a beginner to Ruby on Rails and I am using Rails 3.0.9.
What is the difference between Gemfile and Gemfile.lock in Rails?
The Gemfile is where you specify which gems you want to use, and lets you specify which versions.
The Gemfile.lock file is where Bundler records the exact versions that were installed. This way, when the same library/project is loaded on another machine, running bundle install will look at the Gemfile.lock and install the exact same versions, rather than just using the Gemfile and installing the most recent versions. (Running different versions on different machines could lead to broken tests, etc.) You shouldn't ever have to directly edit the lock file.
Check out Bundler's Purpose and Rationale, specifically the Checking Your Code into Version Control section.
Usually we write dependencies in Gemfile as:
gem "nokogiri", "~> 1.4.4"
gem 'bcrypt-ruby', '~> 3.0.0'
gem 'uglifier', '>= 1.2.3'
..
Here you basically say: "I want nokogiri as long as it’s greater than version 1.4.4", etc. Now suppose that I have set up my Gemfile 8 months ago and I successful setup my app with this requirement. 8 months ago nokogiri version was 1.4.4. My rails apps was running perfectly without any problems with this version.
Now think I'm trying to build with the same Gemfile. But if we look at nokogiri versions we see that the current stable version has changed to 1.4.9. That means if we try to build, bundler will install version 1.4.9 of nokogiri (suppose we don't have Gemfile.lock).
What does it mean ?
As you see if you don't have any Gemfile.lock and run:
bundle install
then the currently used gems can be different at any time. Your app used the version 1.4.4 and it works 8 months ago without any problems, but if you try to build it now you get the version 1.4.9. Maybe it's broken with the latest version of nokogiri, the awesome feature you used with 1.4.4 is not more available, etc..
To prevent this kind of problem Gemfile.lock is used. In Gemfile.lock only the exact versions are written and thus only these will be installed. That means if you distribute your app with a Gemfile.lock, every machine will have the same gems installed and most important they all get the same version. This will give you a stable and common deployment stack.
How is Gemfile.lock created?
It is automatically created with the first:
bundle install
command. After that everytime you run bundle install, bundle will first look up Gemfile.lock and install the gems specified there. It's an habit to distribute this file among your projects to provide consistently and stability.
How to update Gemfile.lock?
If you're happy with the the latest version of your apps than you can update Gemfile.lock. Just reflect your changes to Gemfile. That means change the dependencies to the new exact versions in Gemfile. After that run:
bundle install
This will update you Gemfile.lock with your newest version of apps.
The Gemfile.lock
When you run bundle install, Bundler will persist the full names and versions of all gems that you used (including dependencies of the gems specified in the Gemfile(5)) into a file called Gemfile.lock.
Bundler uses this file in all subsequent calls to bundle install, which guarantees that you always use the same exact code, even as your application moves across machines.
Because of the way dependency resolution works, even a seemingly small change (for instance, an update to a point-release of a dependency of a gem in your Gemfile(5)) can result in radically different gems being needed to satisfy all dependencies.
As a result, you SHOULD check your Gemfile.lock into version control. If you do not, every machine that checks out your repository (including your production server) will resolve all dependencies again, which will result in different versions of third-party code being used if any of the gems in the Gemfile(5) or any of their dependencies have been updated.

Resources