I'm used to defining gemfiles with simple commands, like gem 'devise'. I'm now putting my App in production through a dockerized container on AWS, together with a pipeline that pulls and builds new containers based on pushes to my master branch on Git. This means that every time I push a new version, AWS will rebuild the entire app from scratch, also installing all the latest versions of the gems.
I'm having some concerns what will happen when there are new versions of gems. This could of course mean that my code could stop working, if the included gems change their behaviour or methods.
What is the best practice is this case? Should I start explicitly defining what versions of the Gems I want? And if so, how do I make sure that I do profit from the fixes that are provided?
I'm considering defining all my gems like this:
gem 'devise', '~> 4.6.0'
# Or
gem 'devise', '~> 4.6'
What would be the most flexible, but "ensuring" I don't run into any issues in the long run?
Unexpected version changes should usually be prevented by Gemfile.lock, that contains exact versions that were last installed/upgraded and is committed alongside gemfile and can be used to ensure production can be built each time exactly the same. First I'd look into why your build pipeline does not use it.
Even if you have CI and good tests coverage that will prevent production from breaking, it's much better to be sure that something broke not because of your changes, but a gem update. Thus gem updates, even to minor and patch versions, should come in separate commits.
As for versions in gemfile itself - you should specify version dependencies according to version policy of that specific gem (even when relying on a lock-file). Most gems use semantic versioning, so that gem should not break anything within one major version (~>4.6 option) and updates should not require major changes to your app (but anyway there may be deprecations that flood your log or some regressions, so in any case it's better to have a version tested before production).
To stay updated about new versions of dependencies - periodically run bundle outdated to see which gems updates are there.
Related
I'm looking for simple, but good way to cleanup gemfile and make rails startup faster.
How can I get a list of all required gems vs all loaded gems.
bundle clean --force will remove old gems (or older versions of currently-used gems) previously installed, but not currently being used in your current Gemfile.lock manifest.
First, if you want to check what are the gems used by your project, I invite you to run gem server in your project folder root, then go to http://0.0.0.0:8808/
You will be able to understand the dependencies of all the gems your project is using. It will also show you all versions of the same gem.
To remove old versions of gems you can run as #changingrainbows mention
bundle clean --force
After this step run your gem server again and watch the result, a clean and understandable gem list with all dependencies.
It depends what you're after here.
If you're looking to remove old, unused gem versions, then bundle clean.
If you've been adding gems as you develop and have lost track of the ones you actually use, and have good test coverage, then try this answer.
If you want to reduce the number of gems rails pulls in at startup to the bare minimum, try gem_bench.
I think it is impossible. When your APP starts it loads gems from Gemfile.lock but it does not know if they (gems) are needed in your code or not. The APP inform you by raising an exception When something calls a class or method that is undefined if some needed gem is missed (if you remove it from Gemfile), but this can happen at any moment (not during starting your APP).
So if you are looking the way to clean up your gem list I think the best way to do it manually (I know it is not easy way). Analyse each gem to find out what functionality it provides and decide (or find in your code) if it is needed or not. Additionally tests (if you have them) should help you a lot.
I like to vendor as many gems as possible, except those that must be built on each platform (libxml, etc.) but sometimes I like to use some bleeding-edge code rather than the gems that are out there on the gem servers.
Can I clone a github gem directly into vendor/gems. I guess I could, but will it affect my app code since it is already a git repository? I would like to just do periodic git pulls for these couple of gems so that I don't have to update every gem and maybe break something.
Use of vendor/gems has been deprecated in favor of using Bundler and Gemfile instead. The vendor system had a number of flaws including a lack of support for compiled extensions, so it was never a complete solution.
You're better off locking your versions in the Gemfile as required. If you want to use bleeding edge versions, comment out the version declaration, remove Gemfile.lock and do a bundle install again.
It's often the case that the published version of the gem has a flaw you need to repair by forking and fixing, so it's easy to track this:
gem 'broken-gem', :git => 'git://github.com/myname/broken-gem.git'
The advantage here is that the Gemfile serves as documentation of what versions of the gems you require, and where to get them. vendor/gems leaves people in the dark as to where that version came from.
I want to know should I specify gem version for each gem I add to Gemfile or not. Earlier with few of my projects I didn't specified any versions for all the gems and bundler took care of it, which worked quite well as well.
But recently I got to work on few project which were under development for last 6 month. In that project, many of gem versions were specified in Gemfile only and Gemfile.lock was ignored. That caused a lot headache to finally resolve version conflicts and upgrade few gems.
Also got to know that it's bad practice to remove Gemfile.lock from application version control - nice article by yehuda - http://yehudakatz.com/2010/12/16/clarifying-the-roles-of-the-gemspec-and-gemfile/
So, my question is should I specify version for each and every gem I specifiy in Gemfile or just specify name and bundler will take care of version ? What is best practice to handle this ?
UPDATE -
Updating this question to correctly specify the problem, as a Gemfile sovles many problems :)
Prob 1 - Every developer should have same version of gems.
Actually adding Gemfile.lock into version control solved this problem. Developers just have to take care that they run 'bundle install'/'bundle' rather than 'bundle update' as this will update versions as well.
Prob 2 - Some gems version, if changed, brakes application code.
Actually with omniauth,there are this type of issues, as API are changed from one version to another. And yes, to keep application working, versions will need to be specified for this gems.
My Prob. -
So, in my gemfile, as versions for both A and B are strictly specified, and as they both depend on different versions of Z, which is there dependency, I even can't run the bundle install or bundle update. The only solution was to remove versions and let bundler to take the call. That's why I had question like -
Gemfile.version_specification_mandatory? #=> true/false
I think it's best to not specify gem versions in the Gemfile. On rare occasions, it may be necessary to specify a version--e.g., when a newer version breaks your app. But specifying versions for all of your gems is usually overkill. The Gemfile.lock file (which you don't edit, but you do check into version control) will keep newer releases of gems from being used in your app, until you explicitly upgrade to them.
If you are using the gem for something that is available only in a specific version, you need to specify the version.
Bundler installs the latest version or uses the available version on the system if no version is specified. This works for the developer because the latest version has the feature she needs. But if the feature gets lost in the future versions of the same gem and the version is not specified in the Gemfile, all subsequent installations of the gem for different people or different machines will produce undesired effects.
I have faced these problems particularly for will paginate 3's release candidate versions.
I don't recall how Bundler worked back in 2011, but in 2021, if you don't specify a version in your Gemfile, you can't assume that Bundler will always automatically install the latest version. Instead, Bundler will try to find the right combination of versions to make all your gems compatible with one another. This could lead to some gems being downgraded, which is probably not what you want or expect. Bundler will do this silently, without a warning message, which you could argue is a broken user experience.
On the other hand, when you specify a version, if there is a conflict, Bundler will let you know and you can then decide how you want to proceed.
I recorded a screencast recently to show a real example of an older version of a gem being installed when the Gemfile didn't specify a version number.
It causes so many deployment issues it's ridiculous. Most of the time I don't care what version of gems are used, just want to use the latest one.
UPDATE in response to comments:
Here are a couple of examples off the top of my head:
developer A is using a pre-release of a gem so when he runs 'bundle update', the Gemfile.lock is messed up for everyone else and if you deploy it, there goes your site.
A bug in a gem gets fixed so we run gem update across our servers, restart rails and yay, bug fixed! Oh, but wait, it's not fixed? Thanks bundler. What should have been an easy fix is now a full code deploy across our servers.
That's just a couple off the top of my head. At least let us decide if we want to lock in gem versions or perhaps at least allow a range of versions for instance any 2.X version.
UPDATE 2: And yet another issue when there are windows developers on the team
Here is what's showing up in a windows Gemfile.lock:
nokogiri (1.4.4)
nokogiri (1.4.4-x86-mingw32)
Wow, this is just awesome. Sure makes for easy teamwork and deployment.
I recommend starting to use two techniques with your development and deployment:
Specify version number of gems in your gemfile.
For example:
gem "rails", "3.0.1"
gem "will_paginate", "~> 3.0.pre2"
This way, when you decide you want to update rails, or will_paginate, change the version numbers in your gemfile.
Only update certain gems
Rather than the generic bundler update command, run
bundler update rails
This will only update the rails gem to the newest version, rather than get the latest of all gems.
If you use both 1 & 2, you'll have a happier experience.
Then, simply, don't check your Gemfile.lock into source control. All of the specific problems you listed are solved.
Of course, you are sacrificing the enormous advantage that Bundler gives you over any other dependencies management system.
Just a bit of background, I come from a strong C#/staticly typed background. Therefore I tend to think in terms of .dlls. So if I was working in a project, I'd reference my required dlls and that would be that.
Being new to Ruby and Rails I find I might be doing something wrong. For example, I create a Rails app at home using the gems I have locally. Using a different computer (say a work computer) I attempt to work on the project only to find I'm using different versions of the gems. After carrying out a bundle install I'm back to a working project.
The issue I have with this is that my gem library becomes 'messy'. I end up with several versions of the same gem. Is this the way others work? When using a gem (from a require) will it default to the latest version? I feel as if I'm not managing the dependencies correctly, though as I've mentioned I'm new to the world of Ruby.
Should I just include my dependencies, then perform a bundle install each time I have different/missing dependencies? What happens if I wish to upgrade to a newer version of a gem? Would it be a case of updating the gemfile that bundle uses and getting on with it?
Yes, bundler is the way to go to work with dependancies with Ruby on Rails. Why ? First, because it's shipped with it (at least for version 3.0). Second, because it's simple as hell (unlike maven with Java).
A non exhaustive list of feature :
it lets you declare one or many gem repository to fetch gems from
to group your dependencies by environment (development, production...)
to specify version you'd like
and so on. For more on this, check this http://gembundler.com/rationale.html
Regarding your question : yes, bundler will take the latest version available is none is specified.
Also, I would add a disavantage : you cannot specify a gem version depending on the OS. For example, nokogiri has a linux version AND a win version.
The default behaviour when requiring a module in a gem is to assume you want the latest version of the gem if you have more than one installed. You can change this by specifying which version you want in a specific application like this:
gem "rails", "2.3.8"
Before you require anything from the gem. This ensures that this application will use the specified version of the gem, even if a newer one is installed.
You can of course clean out obsolete versions of your installed gems whenever you don't need them again, or if you use bundler consistently: just wipe everything and run bundler again to get just your required gems installed.
Another useful tool is the Ruby Version Manager (RVM), in addition to handling different versions of ruby it provides a feature called gemsets which allows you to isolate different applications or environments from eachother. That is App A can have its separate gemset with all its required gems, and App B have another gemset with only its required gems. This will reduce the clutter in your dependencies quite a bit.