Ruby n00b learns about: Bundler vs Rake vs Guard
# 17 Mar 2017 by SeanAs I was starting out with Ruby, I quickly encountered three… things that popped up everywhere: Bundler, Rake, and Guard.
I learned pretty quickly (because all of the really nice Rubyists on the internet are very willing to tell you this) that they are three Ruby tools for making Ruby development easier, more consistent, and better.
They’re all clearly different (else why the heck would a community that tries to be as expressive as possible duplicate its tools for no reason)… but it isn’t immediately clear to the n00b how they’re different. The first time you’re likely to use them, it’s often going to be to prefix your commands when running your code.
Which makes them all look like taskrunners, and at first blush, and I was confused about when to use which.
So, they’re not all task runners.
- Bundler is a configurator for your Ruby project, and allows you to specify which gems, and which versions of those gems, should be used in your project (aka ‘dependencies’).
- Rake is a taskrunner. It runs discrete, core (the important) development tasks.
- Guard is also a taskrunner, but unlike Rake, it’s an always on kinda thing. It automates the running of other tasks, like Rake tasks.
Let’s get a little more detail.
Bundler
Bundler is great, because at this point, there a quite a few different versions of Ruby, and the gems we use, that it can be hard to make sure that two people working on the same project (or, one person working across multiple computers) are actually using the same underlying code. Things can change from one version to another.
When you use Bundler, you create a file, called your Gemfile
in the root of your project, and in it you specify which gems you need, and even which versions (either specific, or newer than a certain version, or older… you’ve got options). Then run bundle install
, and Bundler reads the Gemfile
and installs those specific versions of the gems. It also creates a Gemfile.lock
for you. If the Gemfile
is your shopping list, the Gemfile.lock
is your receipt from the market–it tells you exactly what you’ve got after running Bundler (don’t edit this guy yourself. That’s like tax fraud).
# Gemfile
source 'https://rubygems.org'
gem 'rails', '5.0.1'
gem 'puma', '3.4.0'
# Gemfile.lock; don't edit this yourself
GEM
remote: https://rubygems.org/
specs:
actioncable (5.0.1)
actionpack (= 5.0.1)
nio4r (~> 1.2)
websocket-driver (~> 0.6.1)
...
PLATFORMS
ruby
DEPENDENCIES
byebug (= 9.0.0)
...
BUNDLED WITH
1.14.6
Bundler also allows you to run commands on a project where things are in different versions from your system defaults. Instead of running ruby awesome-thing
, you run bundle exec ruby awesome-thing
, and Bundler makes sure that everything is done with what you’ve told it to use for this project. If you’re using Bundler, it’s a good idea to prepend all your commands with bundle exec
.
I’ve recently been working through the Rails Tutorial, and kept getting FIXNUM is deprecated...
warnings. Turns out, it is deprecated in Ruby 2.4.0. So I switched to Ruby 2.2.5, and the warnings have disappeared (related: check out RBenv for installing and switching between different versions of Ruby–super easy to use, and very useful on a Mac where the system version of Ruby, 2.0.0-p648, is past End of Life).
Changing the Ruby version I’ve been using could have caused a bunch of issues for my gem dependencies, because they wouldn’t have been installed properly for that new version of Ruby. Bundler makes this pretty easy though–run bundle install
again, and Bundler makes sure that you have the right gems, in the right place, for your project. Helps avoid those weird errors that happen just because you’ve got some idiosyncratic setup.
Rake
The name is a cute portmanteau of Ruby make, named for the Unix tool Make, and performing similar functions: it won’t compile1 your code (because Ruby isn’t compiled in the same way as lower-level languages). But it does run the important tasks like testing your code, or maybe deployment. You set up individual, discrete tasks in your Rakefile
:
# Rakefile
desc "One line task description"
task :name_of_task do
# Your code goes here
end
When you call up Rake tasks, you use the syntax rake name_of_task
(ommitting the ‘:’ off the beginning because rake will figure that out for you). Rake will then do whatever was in that task.
One of the most frequent uses of Rake is to run code tests (rake test
). If you’re doing this, you’ll need two things in your Rakefile - tell it how to run tests (by requiring the rake/testtask
classes included in Rake), and where your test files (that you’ll need to have written, beyond the scope of this post) are located:
# Rakefile
require 'rake/testtask' # This is where you tell Rake to use the testtask class
Rake::TestTask.new do |t| # This is where you tell Rake where to find your test files
t.libs << "tests"
t.test_files = FileList['tests/test*.rb']
t.verbose = true
end
You can do pretty much anything with Rake, but in general Rake tasks should automate single, but core functions you would otherwise have to perform manually (deploying to a server: good; running some file in a loop: bad).
Also, if you’re using Bundler, don’t forget bundle exec rake <task name>
.
Guard
Where Rake should be used for single operations, Guard is here to automate things that can/should be on loops. The name is also good, once you get it: it guards (read: watches) the files you tell it to for changes, and then initiates other tasks when it sees changes, like running rake test
when it sees you update one of your test files. Nice, because it means you don’t have to explicitly run the task yourself.
Much like everything else in this post, it’s got its own file (Guardfile
) where you configure it, and then set it off on the commandline: [bundle exec] guard <task name>
.
Guardfiles look a lot like Rakefiles in the way they’re set up:
# Guardfile
guard 'test/*' do
bundle exec rake test
end
You ‘guard’ a file or files, and ‘do’ something when they change. The obvious example (as above) is watching your test files for changes, and running rake test
if you update or save a new one, though you can get fancier too.
To start it run [bundle exec] guard
and then ignore it until you see an error. You should probably do this in a new terminal because guard just loops until it hits an error, so you won’t be getting command back (unless you force it to stop with Ctrl-C).
You can also set up different Guard groups–different sets of tasks in you Guardfile
that you run with guard group <group name>
. Check out Guard’s docs for more examples.
With our powers combined…
Use them all together!
Bundler does your installation with bundle install
and makes sure your commands run with the right versions and gems with bundle exec <command>
.
And use Guard to automatically fire you Rake tasks so you don’t have to (using bundle exec
to start Guard)!