Catching up with Rake

Kayode Adeniyi
7 min readMar 21, 2017

--

First up — I’m just getting to learn more about the magic named Rake. As such, most of this might probably be common knowledge to most people.

Recently, I decided to do some tasks in rails which made me go a bit deep into checking out Rake. It was hard finding quick answers to my questions so I decided to write this piece as a handy guide for rails developers when dealing with rake.

Assumption

You are already a Ruby on Rails developer

What is rake?

According to wikipedia, Rake is a software task management tool. It allows the user to specify tasks and describe dependencies as well as to group tasks in a namespace.

It is similar to SCons and Make, but it has a number of differences. The tool is written in the Ruby programming language and the Rakefiles (equivalent of Makefiles in Make) use Ruby syntax. It was originated by Jim Weirich.

Rake is Ruby Make, a standalone Ruby utility that replaces the Unix utility ‘make’, and uses a ‘Rakefile’ and .rake files to build up a list of tasks. In Rails, Rake is used for common administration tasks, especially sophisticated ones that build off of each other.

It is most often used for administration level tasks that can be scripted. The advantage of using Rake over Make is that it is a Ruby tool and can interface with your Rails app natively. Models, data constraints and business rules are all available for use. In rails, rake tasks are written under root/lib/tasks

Inbuilt tasks

Out of the box, Rails comes with a set of predefined Rake tasks that allow you to perform some common tasks. I’ve listed some of the tasks that I’ve found very useful below:

rake about: lists versions of all Rails frameworks and the environment

rake assets:clean[keep]: removes old compiled assets

rake assets:clobber: removes compiled assets

rake assets:precompile : compiles all the assets named in config.assets.precompile

rake db:create : creates the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:create:all to create all databases in the config). Without RAILS_ENV or when RAILS_ENV is development, it defaults to creating the development and test databases.

rake db:drop : drops the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:drop:all to drop all databases in the config). Without RAILS_ENV or when RAILS_ENV is development, it defaults to dropping the development and test databases.

rake db:migrate : migrates the database (options: VERSION=x, VERBOSE=false, SCOPE=blog)

rake db:migrate:status : display status of migrations

rake db:rollback : rolls the schema back to the previous version (specify steps w/ STEP=n)

rake db:schema:cache:clear : clears a db/schema_cache.dump file

rake db:schema:cache:dump : creates a db/schema_cache.dump file

rake db:schema:dump : creates a db/schema.rb file that is portable against any DB supported by Active Record

rake db:schema:load : loads a schema.rb file into the database

rake db:seed : loads the seed data from db/seeds.rb

rake db:setup : creates the database, loads the schema, and initialises with the seed data (use db:reset to also drop the database first)

rake db:structure:dump : dumps the database structure to db/structure.sql

rake db:structure:load : recreates the databases from the structure.sql file

rake db:version : retrieves the current schema version number

rake dev:cache : toggles development mode caching on/off

rake initializers : prints out all defined initializers in the order they are invoked by Rails

rake log:clear : truncates all/specified *.log files in log/ to zero bytes (specify which logs with LOGS=test,development)

rake middleware : prints out your Rack middleware stack

rake notes : enumerates all annotations (use notes:optimize, :fixme, :todo for focus)

rake notes:custom : enumerates a custom annotation, specify with ANNOTATION=CUSTOM

rake restart : restarts app by touching tmp/restart.txt

rake routes : prints out all defined routes in match order, with names

rake secret : generates a cryptographically secure secret key (this is typically used to generate a secret for cookie sessions)

rake stats : reports code statistics (KLOCs, etc) from the application or engine

rake test : runs all tests in test folder

rake test:db : runs tests quickly, but also reset db

rake time:zones[country_or_offset]: lists all time zones, list by two-letter country code (`rails time:zones[US]`), or list by UTC offset (`rails time:zones[-8]`)

rake tmp:clear : clears cache and socket files from tmp/ (narrow w/ tmp:cache:clear, tmp:sockets:clear)

rake tmp:create : creates tmp directories for cache, sockets, and pids

Rake options

To see all available options for rake, run rails -h

Some common options are:

-A, — all : shows all tasks, even uncommented ones (in combination with -T or -D)

-j, — jobs [NUMBER] : specifies the maximum number of tasks to execute in parallel. (default is number of CPU cores + 4)

-q, — quiet : do not log messages to standard output.

-T, — tasks [PATTERN] : display the tasks (matching optional PATTERN) with descriptions, then exit. -AT combination displays all of tasks contained no description.

Simple Rake task

All rake tasks follow a simple format. Below is a skeletal structure

desc "I am short, but comprehensive description for my cool task"
task task_name: [:prerequisite_task, :another_task_we_depend_on] do
# All your magic here
# Any valid Ruby code is allowed
end

To pass arguments to your custom rake task:

task :task_name, [:arg_1] => [:prerequisite_1, :prerequisite_2] do |task, args|
argument_1 = args.arg_1
end

This is then run as rake :task_name[arg_1] .

We can also group rake tasks by placing them in namespaces:

namespace :db do
desc "This task does nothing"
task :nothing do
# Seriously, nothing
end
end

The above task will be executed as rake db:nothing .

Let’s write a simple rake task that works almost like rake tmp:clear . rake tmp:clear only clears files in tmp/cache and tmp/socket but we want to make sure we remove every file and directory except sessions, cache, sockets and pids directories

# lib/tasks/tmp_task.rbdesc "Clears all files and directories in tmp/ except sessions, cache, sockets and pids directories"
task :clear do
FileUtils.rm_rf(Dir['tmp/*'])
Rake::Task["tmp:create"].invoke
end

desc “Clears all files and directories in tmp/...” : This is the description of what the rake task does.

task :clear doend

The block above houses the task to be executed whileclear is the name of the task.

FileUtils.rm_rf(Dir[‘tmp/*’]) : removes all files and folders from the tmp directory

Rake::Task[“tmp:create”].invoke : recreates sessions, cache, sockets and pids directories in tmp. The invoke method will be discussed in the next section.

To run this task on the terminal, run rake tmp_task:clear . This should clear everything in tmp except sessions, cache, sockets and pids directories.

Customising rake tasks

There are ways of extending and customising rake tasks. Below are some instance methods that can be run on any rake task.

  • Enhance: enhances a task with prerequisites or actions.
task :task_0 do
puts "Running task 0"
end
task :task_1 do
puts "Running task 1"
end
Rake::Task['task_0'].enhance do
# Put stuffs that want to do before 'answer_dump' runs
# Tasks can also be invoked or executed here
# e.g Rake:Task['task_1'].invoke
end

Whenever task_0 is called, it runs everything in the enhance block before it runs.

  • Invoke: invokes the task if it is needed. Prerequisites are invoked first. If a task has been called once, it doesn’t execute the tasks again. For example, consider the tasks below:
task :list_all_levels => [] do
Rake::Task[:list].invoke 1
Rake::Task[:list].invoke 2
Rake::Task[:list].invoke 3
end

task :list, [:level] => [] do |t, args|
puts "Hello level #{args.level}"
end

On running rake :list_all_levels , it only prints “Hello level 1” and stops. It doesn’t print “Hello level 2” because it sees that Rake::Task[:list] has been called once.

  • Execute: execute the actions associated with the task that it’s called on. It does not run the prerequisites.
task :list_all_levels => [] do
Rake::Task[:list].execute 1
Rake::Task[:list].execute 2
Rake::Task[:list].execute 3
end
task :list, [:level] => [] do |t, args|
puts "Hello level #{args}"
end

On running the task, it prints

Hello level 1
Hello level 2
Hello level 3

Note: Invoke takes in a splat argument while execute takes in just one. Caution must be taken with the way the argument is passed in. More details on this can be found here.

  • Reenable: reenables the task, allowing its tasks to be executed if the task is invoked again. This simply makes invoke work on a task that has already been invoked.
task :list_all_levels => [] do
Rake::Task[:list].invoke 1
Rake::Task[:list].reenable
Rake::Task[:list].invoke 2
Rake::Task[:list].reenable
Rake::Task[:list].invoke 3
Rake::Task[:list].reenable
end
task :list, [:level] => [] do |t, args|
puts "Hello level #{args[:level]}"
end

Using the sample code that we used earlier for invoke, by calling reenable before calling invoke subsequently makes the task to be invoked again.

Conclusion

Rake is indeed an interesting tool to learn and use to automate tasks in Ruby. I’ve listed a number of resources that I found useful below:

Thanks for reading.

#8

--

--