To help out people understand rake I am including a Rakefile I use to build my Life Graph. I will walk through each section and then append the entire file at the end of this post.
First off, rake uses the idea of ‘tasks’ that each will do a job. Some tasks are Rake Tasks and others are File Tasks. An easy way to think of these is that file tasks depend on files and will update or run something on them. make running gcc
on some .c files is the classic example. Rake Tasks on the other hand are basically anything else. They can do just about anything you want them to do as long as your shell and/or Ruby can do it.
desc "Build life.png and ssh it to theadmin.org" task :default => [ :full ]
This is an example of a Rake Task. It is called default, which means I can run it by calling rake default
or just rake
. This task requires that the task :full is ran first as a dependency. So this is how you make the :full task be the default.
desc "Do the full build" task :full => [ :ask, 'life.png', :send ] do puts "Full Build" end
Here is another Rake Task, this time called :full. Notice this one depend on the tasks :ask, ‘life.png’, and :send. This means Rake would run each one of these tasks before it would run :full. To finish here this task has a do ... end
block in it. Any command or instructions placed in here would run once this task starts (after the dependencies are complete).
desc "Ask questions for the yaml" task :ask do print "What is the date (YYYY-MM-DD)? " date = STDIN.gets print "Business? " business = STDIN.gets print "Energy Level? " energy = STDIN.gets print "GTD? " gtd = STDIN.gets print "Morale? " morale = STDIN.gets print "Sleep? " sleep = STDIN.gets print "Work? " work = STDIN.gets print "Hacking? " hacking = STDIN.gets File.open("life.yaml", "a") do |f| f.puts "---" f.puts "date: " + date f.puts "business: " + business f.puts "energy: " + energy f.puts "gtd: " + gtd f.puts "morale: " + morale f.puts "sleep: " + sleep f.puts "work: " + work f.puts "hacking: " + hacking end end
This is a honking piece of code, but only because it is very verbose. It is another Rake Task, called :ask. If you read the code it is basically printing a question out to the shell, getting a response back, and saving that response. Then after all the questions are answered, it opens the life.yaml file and inserts the answers into the file. This is a simple example of how you can use any methods in Ruby inside of Rake.
desc "Build life.png" file 'life.png' => 'life.yaml' do |t| puts "Building life graph...." `ruby life_graph.rb` end
This is the first File Task we find. Notice that this task depends on ‘life.yaml’, this is like make in that if ‘life.yaml’ has been modified after ‘life.png’ then this task will run, otherwise it will not (why regenerate life.png if the data has not been updated). The back-ticks are passed to the shell just like in Ruby and then the life_graph.rb script is run. Since Rake has the full power of Ruby, I could even get rid of life_graph.rb and put it’s code inside this rake task itself.
desc "Send updated png" task :send do |t| puts "Sending life graph..." `gzip -1c life.png | ssh eric@theadmin.org "gzip -d > \ ../websites/theadmin/dropbox/media/life.png"` end
This is the final Rake Task, you should be able to see what it is doing just from the second line in the task.
So now my basic nightly update goes like this, I run rake
which in turn runs :full, which will run :ask. Then I am prompted for the new data, which is inserted into the yaml file. Next control passes back to :full which then tests ‘life.png’ and sees that it is older than ‘life.yaml’ so that block of code is run to update ‘life.png’. Next control is passed back down to :send, which will send life.png over ssh to the server. Finally all of :full’s dependencies are fulfilled so it can run it’s block. So that is the basic flow of this Rakefile. It is simple and not the best but only took a hour or so to fully hack out. The full version follow.
Eric Davis
require 'rake' desc "Build life.png and ssh it to Compuextreme.com" task :default => [ :full ] desc "Do the full build" task :full => [ :ask, 'life.png', :send ] do puts "Full Build" end desc "Ask questions for the yaml" task :ask do print "What is the date (YYYY-MM-DD)? " date = STDIN.gets print "Business? " business = STDIN.gets print "Energy Level? " energy = STDIN.gets print "GTD? " gtd = STDIN.gets print "Morale? " morale = STDIN.gets print "Sleep? " sleep = STDIN.gets print "Work? " work = STDIN.gets print "Hacking? " hacking = STDIN.gets File.open("life.yaml", "a") do |f| f.puts "---" f.puts "date: " + date f.puts "business: " + business f.puts "energy: " + energy f.puts "gtd: " + gtd f.puts "morale: " + morale f.puts "sleep: " + sleep f.puts "work: " + work f.puts "hacking: " + hacking end end desc "Build life.png" file 'life.png' => 'life.yaml' do |t| puts "Building life graph...." `ruby life_graph.rb` end desc "Send updated png" task :send do |t| puts "Sending life graph..." `gzip -1c life.png | ssh eric@theadmin.org "gzip -d > \ ../websites/theadmin/dropbox/media/life.png"` end
Now that’s familiar, even the block task structure is similar. Thanks, things are clear to me now.
agrees, but with this it can not be helped, it is in their blood