Catching up with Rake
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"
endtask :task_1 do
puts "Running task 1"
endRake::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
endtask :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
endtask :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