Should one bundle gems with the app? - ruby-on-rails

Having just got a new machine I have taken the opportunity to try something new.
RVM is great and I have been using gemsets but after reading a few blog posts I have decided use switch to rbenv and use bundler to manage my gems exclusively as outlined in Ruby Rogues episode 45.
I do not collaborate all that much and if I do it is usually with one or two other people.
The bundler documentation details the ability to package up gems in to the vendor/cache directory by running:
$ bundle package
$ bundle install --local
Great private gems that can be checked in to source control; I guess making deployment to a clean server or collaborating easier?
However if you check your Gemfile and Gemfile.lock into source control then what is the need for bundle package?
Ryan Mcgeary advocates for this approach in this blog post form early 2011 and in another blog post from 2010 Yehuda Katz says that:
You might want to install your bundled gems to a different location, such as a directory in the application itself. This will ensure that each application has its own copies of the gems, and provides an extra level of isolation.
This isolation is quite like gemsets I suppose and I can imagine when you have a huge list of system gems it would be difficult to know which ones are actually being used by your applications.
So packaging gems with the application?
Is anyone doing this? Is this practice obsolete?
What's the best practice and what are the advantages / disadvantages of bundling gems within the app?

I think bundle package has a narrow set of usage cases.
Gemfile and Gemfile.lock deal with version locking, but in the end you still go out to rubygems.org and download the gem when you do bundle install.
The three scenarios I can see bundle package being useful:
1) I have a clean production environment, and I don't want to touch rubygems.org at all. This might be due to a security protocol, limited internet access, etc. In the end I have a entirely self-contained application package with all the gems lined up and ready to go, without really touching the server environment, or the internet.
2) I want to download the gem, unpack it, screw around with it, and use that particular one. Especially if I don't wanna deal with things like git, or forking, or any of that stuff.
3) From a development team point of view, you can say that all gems being used must be packaged and local to the app. You wouldn't have to worry about developers installing different versions, touching their environments, etc. This reason is kinda weak in my eyes, but I've seen this logic once or twice.
In the end, unless you have a use case where you're actively hunting for this ability, I'd stay away from it.

Related

Selecting Ruby gems at run-time based on configuration

We're writing a Rails app that we want to be able to talk to any of several external data repositories via a uniform adapter interface, and we'd like to be able to add more later just by writing new implementations of that adapter interface (cf. ActiveRecord adapters).
Any one installation of the app will only need one adapter running, and we don't want to have to rev the code or even the Gemfile whenever we introduce a new adapter. Assuming we write each adapter as a standalone gem, what's the proper way to incorporate just one adapter gem at runtime, based on configuration?
Normally in Rails, you can only access gems that are listed in the Gemfile; Bundler enforces this. However that doesn't stop you from loading gems manually.
If your adapter gems are installed in a well-known location, then loading an adapter gem could be as simple as this:
$LOAD_PATH << "/path/to/adapters/my_adapter/lib"
require "my_adapter"
Where "my_adapter" is something you discover at runtime via configuration.
Put your configuration in the environment, as you're probably already doing.
For example, I use the dotenv gem.
Set an environment variable to your adapter gem name.
The result is an ENV var that Rails can access, such as:
ENV["adapter"] #=> "my_custom_gem_name"
Use the environment to pick the adapter, e.g. in your Rails config:
require ENV['adapter']
This presumes your gem name is the same as the require name. If your gem name is different, use the require name.
When you deploy a new adapter, you can put it anywhere on the Rails load path. Or you can adjust the Rails load path as you like e.g. adding a path to $LOAD_PATH.
An example of how to adjust the load path:
# /config/application.rb
module MyApp
class Application < Rails::Application
$LOAD_PATH << "/path/to/custom/gem"
require("my_custom_gem_name")
…
For example, put the gem code in ./lib, or install the gem into ./vendor or systemwide, using any tool you like such as scp or rsync or ansible, thus bypassing the typical bundle command.
You won't need to update the Gemfile.
I don't think there's a good way to do quite exactly what you ask. Part of the bundler system (that's what uses Gemfiles) is that only gems mentioned in your gemfile (and their dependencies) are available to your app, it is isolated to only them. If this sounds like a bad thing, we could have a long conversation about the pre-bundler dependency management problems that this system has successfully solved.
Option 1 -- what are you worried about anyway? There is probably no downside to including all the possible gems in your Gemfile, even if a given installation will only use one. If using Rails, you will want to make sure these gems are not require'd on launch. (If not using Rails, this might not be neccesary, as other environments don't necessarily ask bundler to require all gems on boot (which is wise), but won't hurt).
So all the gems will get installed in every installation, yes, but only the one you want to use will be loaded, when you issue the require at runtime -- you'll still want to do this on application load, perhaps in response to an ENV variable, to avoid any concurrency or load time weirdness. Anyway, the unused gems will just be sitting there on disk un-loaded. How big a downside is having them installed but not used? How much are you willing to increase the confusingness of your setup to get rid of this downside?
Option 1 is what I'd do.
Option 2 -- separate Gemfiles. Another option is preparing a separate Gemfile for each type of setup. Gemfiles are just ruby code, so you could have one 'base' gemfile including common gems, and then a separate Gemfile for each type of setup, which uses ruby to load/include the base gemfile and then adds the setup-specific gems. You'd give each Gemfile a separate name, Gemfile_adapter1 or whatever.
You're going to have to commit something to your source when you add support for a new adapter type, aren't you? Where does this adapter come from? I don't understand how you could do it without revising any code. Anyway, adding a new Gemfile for that adapter type when you add a new adapter doesn't seem like a huge barrier, but I dunno.
You can launch Rails specifying which of these gemfiles to use with the BUNDLE_GEMFILE env variable: BUNDLE_GEMFILE=./Gemfile_one rails server. And in every other command you do that will use the Gemfile. BUNDLE_GEMFILE=./SOME_GEMFILE bundle install. BUNDLE_GEMFILE=./SOME_GEMFILE RAILS_ENV=production bundle exec rake assets:precompile. If you use capistrano that's doing some of these things for you, figure out how to make sure cap uses the right BUNDLE_GEMFILE when it executes it. Figure out how to make your app server do that when it launches your app. Etc.
This will work out fine -- but is going to end up being a pain to keep track of and make sure it's working right in your entire devops stack.
I suppose you could even generate the Gemfiles at install time, instead of having them in your source repo, for even more confusing situation and another thing that can go wrong and be confusing to debug! (I wouldn't).
I'd consider this Option 2, but would prefer option 1 unless there were really good reasons it wasn't going to work.
Option 3 -- Don't use Bundler. You could abandon bundler and gemfiles entirely. Maybe, if you can convince Rails to do this somehow. It might be hard to convince Rails to do this.
If you could, you are in a situation where you have to install all your gems on your deploy system by hand, making sure they are the right versions and figuring out what versions are compatible with what other versions, and that no more recent versions you don't want are installed.
Then, at runtime, you just need to require all the gems you want, and the app will get the most recent version of that gem installed on the system.
I would never, ever, ever, do this. I don't know if you can get Rails to do it, but even if you could, I remember the dependency hell from before Bundler existed, and would never want to go back.

How to find unused gems and cleanup gemfile

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.

Should I remove all installed gems now that I'm planning on using RVM

I'm setting up RoR on my machine for the first time. I've gone through a couple of tutorials, and have installed some dependencies for each of them.
I haven't run into any issues yet, but having enjoyed using VirutalEnv for Python, I'm thinking that it'd be best to use RVM, based on my experience.
The question is as follows: will keeping these existing gems outside of their own unique environment cause problems for me in the long run, and thus I should remove all gems of this nature?
No it will not. RVM creates a isolated environment for each gemset so there will be no interference from old gems outside your gemsets. You don't even have to use RVM, bundler will work a long way alone.
Also, welcome to another Rails developer, we are glad to have you! :D

Gems in application build without bundler

I know that if application uses bundler, I can easily find all the gems installed by looking at the Gemfile.
Say, I am looking at the Rails 3 application that doesn't use bundler, how do I know what gems it uses?
Thanks
If it's not using Bundler, I don't know of a definitive way to identify every gem being used. You could search the entire app tree for require statements to start with, but that's not going to show most of them. Gems also require other gems internally, and will install their own dependencies, but those gems won't be referenced directly from your app's require statements.
If the app works and the tests pass (meaning you've at least got all the required gems installed), you could approach the problem by creating a Gemfile, listing the gems you know are needed, and then running your tests (or the app itself) via bundle exec, which will ensure that only the gems listed in the Gemfile are visible. Then you'll get failures related to missing gems, and can add them to the Gemfile until it all works. Once it's working via bundle exec, you'll know that you've captured all the requirements there.
If you're using RVM, you'll probably find it helpful to create a gemset for your app, along with a .rvmrc file in the app root, to take advantage of RVM's automatic gemset switching and Bundler integration. It'll make it easier to maintain the gem state going forward.
In any case, running gem list with the app in a working state will show you all the gems that it might be using, but without being scoped to a gemset or wrapped in bundle exec, you'll also see gems that were installed for other reasons that potentially have nothing to do with your app's dependencies.

Trouble understanding RVM, gems, and general Ruby on Rails environment setup

I've read through a few Q&A's here on this subject, but am still confused. I'm new to linux and new to programming, so please keep that in mind.
I understand that Ruby Gems is similar to apt-get. It's a package manager -- correct?
So if I want to install or remove gems, I can do it via a command like: sudo gems install {gem name}
So what then is RVM? Why would I want to use it? Doesn't Ruby Gems do what RVM does? Why then does Ruby Gems get installed with RVM?
Also, when specifying gems in a project's Gemfile, then using bundler to update, etc.. is this downloading the gems only to that project, or will they now be available across all projects?
Also, what is $PATH about? I don't know much about it, so when I read about it, I'm confused about what is the right $PATH, what if anything I should do to manage references in $PATH, etc. Can someone explain or point to any resources for beginners?
And finally, I'm using various tutorials, and they differ on versions for everything from Ruby to Rails to Gems. a) Should I be modifying my environment to match the version that they use? b) Once I'm done with a tutorial, should I leave all the versions alone, or should I try to upgrade everything up to the latest and greatest?
It's confusing because if I leave everything at the version levels in the tutorials, then I feel like I'm stuck in the past. While if I upgrade to the latest and greatest, I feel like things have all switched around on me and I'm not sure how to use all the tips and tricks I learned.
Thank you in advance for taking the time to help. Cheers.
This question is very broad so I chose to try to balance the explicitness with conciseness. If anyone finds anything wrong with the answer please tell and I'll straight up own up to it :)
RVM is a Ruby Version Manager. Hypothetically, some projects might require you to run ruby 1.9, another legacy project might require 1.8. RVM allows you to have both installations installed side-by-side, as opposed to having one authoritative system-level version of ruby. This facilitates installing later versions of ruby without fear of breaking anything, or of meddling with other user accounts' ruby version requirements (since usually one installs RVM at the user level, in your home directory). This even lets you try out the bleeding edge version of ruby without having anything to worry about, since you can always switch back easily.
When you install a gem, it generally becomes available to you everywhere that ruby installation is available to you, so in any project. When you specify gems in your Gemfile you're basically saying that independently of whatever gems you may have installed and their versions, that project requires gem x of version 2.2, y of version 3.1, and z of version 1.1. If you didn't already have those gems it installs them, if you did but not those versions, it installs them.
Path is an environment variable that allows operating systems to know where to look for programs when you invoke them. If you type someapp in the terminal, how can the operating system possibly know where someapp is? Well it searches for it in any of those directories supplied in $PATH. You can see what's in your path by doing echo $PATH in the shell.
As for varying versions of ruby, this brings me back to the reason for RVM. You can if you want install the version of ruby they use, and then in your Gemfile specifically state the version of the gems the tutorials use and you should be fine. You can have different versions of gems installed, and you can have different versions of ruby installed thanks to RVM.
Personally I would recommend working towards the latest version of everything so that it remains relevant. For example, it would be counterproductive to work on a tutorial that uses Rails 2 since it changed a lot when it went to 3, and somewhat from 3 to 3.1 and above etc. If possible use the latest versions, or at least be aware of the nuances (the base material tends to stay more or less the same), lest you work on a tutorial that is older only to get to work on your own project with the latest version of everything and not have it work.
Simple solution to your dilemma: ditch the tutorials that are too old. There are tons of resources out there that you're bound to find up-to-date material. Worst case, dated material typically has community support in form of comments which state the changes between the dated version of something and its corresponding recent version. E.g. "keep in mind that haha.what changed to lol.wut in version 3.1"
I can understand that this is confusing, RubyGems are as you write a package manager. RVM is a tool that makes it possible to have several versions of ruby installed on your system and easy swift between them.
If you using various tutorials, and they differ on versions for everything from Ruby to Rails to Gems you can (if you want to) create a RVM Gemset for the version you use. You will then create a sandbox for the Gems Bundler use in your project.
Bundler are as you write a tool for manage the Gems your application depends on. In the old days before Bundler it could be a hassle to figure out which gems your application depended on. Now Bundler do this for you.
Both Bundler and RVM are tools that is not absolutly necessary to use but they will help you. I personally do not use RVM anymore. It is to much of a monster in my taste so I use rbenv instead.
Regarding what versions of Rails to use I do agree that you should try to use 3.1 versions if possible but if you find some example application using Rails 3.0 you do not need to upgrade it. Also you do not need to run the absolute latest version of Rails. Rails 3.1 have a lot of bug fixes that the latest Rails 3.1.3 might not have.

Resources