- Introduce Rake and Rake tasks.
- Understand what Rake is used for in our Ruby programs.
- Learn how to build a basic Rake task.
Rake is a tool that is available to us in Ruby that allows us to automate
certain jobs — anything from execute SQL to puts
-ing out a friendly message to
the terminal.
Rake allows us to define something called "Rake tasks" that execute these jobs. Once we define a task, that task can be executed from the command line.
Every program has some jobs that must be executed now and then. For example, the task of creating a database table, the task of making or maintaining certain files. Before Rake was invented, we would have to write scripts that accomplish these tasks in BASH, or we would have to make potentially confusing and arbitrary decisions about what segment of our Ruby program would be responsible for executing these tasks.
Writing scripts in BASH is tough, and BASH just isn't as powerful as Ruby. On the other hand, for each developer to make his or her own decisions about where to define and execute certain common tasks related to databases or file maintenance is confusing.
Rake provides us a standard, conventional way to define and execute such tasks using Ruby.
In fact, the C community was the first to implement the pattern of writing all their recurring system maintenance tasks in a separate file. They called this file the MakeFile because it was generally used to gather all of the source files and make it into one compiled executable file.
Rake was later developed by Jim Weirich as the task management tool for Ruby.
Building a Rake task is easy, since Rake is already available to us as a part of
Ruby. All we need to do is create a file in the top level of our directory
called Rakefile
. Here we define our task:
task :hello do
# the code we want to be executed by this task
end
We define tasks with task
+ name of task as a symbol
+ a block that contains
the code we want to execute.
If you open up the Rakefile
in this directory, you'll see our :hello
task:
task :hello do
puts "hello from Rake!"
end
Now, in your terminal in the directory of this project, type:
rake hello
You should see the following outputted to your terminal:
hello from Rake!
Rake comes with a handy command, rake -T
, that we can run in the terminal to
view a list of available Rake tasks and their descriptions. In order for rake -T
to work though, we need to give our Rake tasks descriptions. Let's give our
hello
task a description now:
desc 'outputs hello to the terminal'
task :hello do
puts "hello from Rake!"
end
Now, if we run rake -T
in the terminal, we should see the following:
rake hello # outputs hello to the terminal
So handy!
It is possible to namespace your Rake tasks. What does "namespace" mean? A
namespace is really just a way to group or contain something, in this case our
Rake tasks. So, we might namespace a series of greeting Rake tasks, like
hello
above, under the greeting
heading.
Let's take a look at namespacing now. Let's say we create another greeting-type
Rake task, hola
:
desc 'outputs hola to the terminal'
task :hola do
puts "hola de Rake!"
end
Now, let's namespace both hello
and hola
under the greeting
heading:
namespace :greeting do
desc 'outputs hello to the terminal'
task :hello do
puts "hello from Rake!"
end
desc 'outputs hola to the terminal'
task :hola do
puts "hola de Rake!"
end
end
Now, to use either of our Rake tasks, we use the following syntax:
rake greeting:hello
hello from Rake!
rake greeting:hola
hola de Rake!
As we move towards developing Sinatra and Rails web applications, you'll begin to use some common Rake tasks that handle certain database-related jobs.
One common pattern you'll soon become familiar with is the pattern of writing code that creates database tables and then "migrating" that code using a rake task.
Our Student
class currently has a #create_table
method, so let's use that
method to build out our own migrate
Rake task.
We'll namespace this task under the db
heading. This namespace will contain a
few common database-related tasks.
We'll call this task migrate
, because it is a convention to say we are
"migrating" our database by applying SQL statements that alter that database.
namespace :db do
desc 'migrate changes to your database'
task :migrate => :environment do
Student.create_table
end
end
But, if we run rake db:migrate
now, we're going to hit an error.
You might be wondering what is happening with this snippet:
task :migrate => :environment do
...
This creates a task dependency. Since our Student.create_table
code would
require access to the config/environment.rb
file (which is where the student
class and database are loaded), we need to give our task access to this file. In
order to do that, we need to define yet another Rake task that we can tell to
run before the migrate
task is run.
Let's check out that environment
task:
# in Rakefile
task :environment do
require_relative './config/environment'
end
After adding our environment task, running rake db:migrate
should create our
students table.
Another task you will become familiar with is the seed
task. This task is
responsible for "seeding" our database with some dummy data.
The conventional way to seed your database is to have a file in the db
directory, db/seeds.rb
, that contains some code to create instances of your
class.
If you open up db/seeds.rb
you'll see the following code to create a few
students:
require_relative "../lib/student.rb"
Student.create(name: "Melissa", grade: "10th")
Student.create(name: "April", grade: "10th")
Student.create(name: "Luke", grade: "9th")
Student.create(name: "Devon", grade: "11th")
Student.create(name: "Sarah", grade: "10th")
Then, we define a rake task that executes the code in this file. This task will
also be namespaced under db
:
namespace :db do
...
desc 'seed the database with some dummy data'
task :seed do
require_relative './db/seeds.rb'
end
end
Now, if we run rake db:seed
in our terminal (provided we have already run
rake db:migrate
to create the database table), we will insert five records
into the database.
If only there was some way to interact with our class and database without having to run our entire program...
Well, we can build a Rake task that will load up a Pry console for us.
We'll define a task that starts up the Pry console. We'll make this task
dependent on our environment
task so that the Student
class and the database
connection load first.
desc 'drop into the Pry console'
task :console => :environment do
Pry.start
end
Now, provided we ran rake db:migrate
and rake db:seed
, we can drop into our
console with the following:
rake console
This should bring up the following in your terminal:
[1] pry(main)>
Let's check to see that we did in fact successfully migrate and seed our database:
[1] pry(main)> Student.all
=> [[1, "Melissa", "10th"],
[2, "April", "10th"],
[3, "Luke", "9th"],
[4, "Devon", "11th"],
[5, "Sarah", "10th"]]
We did it!