There are a few ways to ensure that your Ruby application/ script has all its dependencies available when you set it up or deploy. The most common way is to use Bundler so that you can specify your Gemfile and Bundler can help set up the gems on a remote server when you deploy. Rails lets you “freeze” your gems to the vendor directory which is convenient so that you can package everything together and distribute it. I wanted to do the same for a non-Rails Ruby application but finding material on this is quite difficult since everything leads back to Rails and finding recent material is quite difficult.
At that time, I posted this on Twitter about the trouble in finding information!
Since
rubylangorg</a> & <a href="https://twitter.com/rails?ref_src=twsrc%5Etfw">
rails are so tightly conflated (even in Google) means that finding #Ruby solutions can be difficult, and the fact that Rails integrates popular tools well means u need to peel back layers of magic :) when u want to do seemingly obvious things on your side!— Mohit Sindhwani (@onghu) July 10, 2020
This post collects up all the information that I collected to handle this.
This is what I want to do: in my project directory, I want to have all the gems that the Ruby application uses. This means:
- Having a simple way to have all related gems/ dependencies deployed to the project directory
- Getting the Ruby application to load from this directory rather than expecting gems to be installed on the system
The Simple Solution
After a lot of searching, it turns out that the simplest approach is to use Bundler:
- Bundler has a command called
package
that will install the gems to the project’s./vendor/cache
directory – this meets our first requirement. - Our second requirement is then met by ensuring that we use Bundler in our application. To do this, start your application with the line below so that Bundler will adjust the load paths. to ensure that if you
require
a gem, it will be loaded from the directory in your project directory.
require bundler/setup
When you run Bundler, you should use bundle install --local
if all the gems are already in the directory.
You probably want to call bundle package
with --all
so that it also packages :git, :path, and .gem dependencies. So, you might end up doing this:
> bundle package --all
Advantages of the Approach
In my mind, the main advantage is that everything that you need for your application to run is already packaged with your application/ script. You just need Ruby with Bundler installed to be available on the target system.
This method also works well with private gems – the gem code is installed to your ./vendor/cache
directory so that you don’t have to worry about connecting to the private repository from the server where you are deploying your application/ script.
[On a related note, check my other post on accessing a gem from a private BitBucket repository, if that is relevant to your case.]
Disadvantages of this Approach
- The main disadvantages that I see are related to duplication in the following ways:
- System-installed gems – even if the gems are installed within the system, this does mean that another copy is present within the application directory
- Multiple applications – in the same vein, if multiple applications package their gems together and are deployed to the same machine, there are multiple copies of the gems across the applications
- Repository – if you are saving all the gems in your repository, then that’s additional storage that you don’t need to use
- The other important note is that when you package the gems, you need to ensure that all gems are correctly platformed. This means that binary gems that are saved to the project directory are for the platform on which you run the command.
Eventually, it comes down to you to decide what makes most sense in your case, depending on how much control you have and how much you can expect will be done to set up things when the application is deployed. One of the other reasons is also that it is possible that if you’re setting up using Bundler and it tries to get the gems from Rubygems, if Rubygems is down, then the entire process might get stopped since that’s one more thing outside of your control.
Deeper Reading
This main reason for having this section here is for me to remember where to look for additional information that I found while searching up the final simple solution. If you’re interested in going deeper, read here – also, if you have some inputs, please add to the comments so that it can all be consolidated together.
Documentation about bundle package
is available at https://bundler.io/v1.6/bundle_package.html (or similar for later versions) – read this for more information on what the package command does.
You might also be interested in details on how to use Bundler without a Gemfile in a single-file script
Also, if you feel like reading very old conversations that will give you some more background, take a look at these:
- https://stackoverflow.com/questions/14607636/is-it-advisable-to-include-the-contents-of-vendor-cache-in-git-in-a-rails-3-2-ap
- https://makandracards.com/makandra/538-freeze-vendor-unpack-a-single-ruby-gem-with-and-without-bundler
- https://stackoverflow.com/questions/3393690/how-to-freeze-gems-in-a-ruby-application