Rake
From WhyNotWiki
[edit] What's so great about rake?
- reuse: common patterns can be abstracted further, for example Rake::RDocTask, which does more of the work for you
- easy syntax (domain specific language): makes it easy to add your own tasks...so you actually end up doing so! (Imagine that!)
rakecommand lists available tasks with descriptions (using task name and description given todesc)- even if you use someone else's tasks, you can inject prerequisites to them
- easy-to-use and powerful prerequisite system: parallel prerequisites are run at the same time, for example [1]
- file tasks and rules
[edit] How do I see what tasks are available?
rake --tasks
[edit] Can I add prerequisites to existing tasks?
Yes, and that's one of the things I think is cool about rake.
For example, say you're writing a sharable rake task that you want to be a prerequisite for the :rdoc task.
task :rdoc => :do_something_before_rdoc
task :do_something_before_rdoc do
puts 'Doing that thing that we want done before the :rdoc task...'
end
Rake::RDocTask.new(:rdoc) do |rdoc|
...
end
> rake rerdoc
Doing that thing that we want done before the :rdoc task...
rm -r doc
README:
whatever_class.rb: c.......
whatever_module.rb: m..............
Generating HTML...
http://docs.rubyrake.org/read/chapter/4#page16
- A task may be specified more than once. Each specification adds its prerequisites and actions to the existing definition. This allows one part of a rakefile to specify the actions and a different rakefile (perhaps separately generated) to specify the dependencies.
[edit] Can I override an existing task?
I don't know, but I figured out that you can append to an existing task!
[edit] Can I "append" to / add a postrequisite to a task?
Observe:
> edit Rakefile desc 'The original.' task :do_it do puts 'The original.' end desc 'I forgot to mention the first time: This is something else I also want to happen.' task :do_it do puts 'I forgot to mention the first time: This is something else I also want to happen.' end > rake --tasks rake do_it # The original. / I forgot to mention the first time: This is something else I also want to happen. > rake do_it The original. I forgot to mention the first time: This is something else I also want to happen.
[edit] What if I really want to override the task rather than append to it?
Well, I'm sure there's a way... we just have to figure out what that way is.
Probably just delete that task from Rake's internal hash of tasks and then declare it again.
[edit] What if I really want to prepend something to a task rather than append to it?
Also, I have no control over what order the files are loaded, and my file ends up getting loaded last...
/usr/lib/ruby/gems/1.8/gems/rails-1.1.6/lib/tasks/databases.rake
namespace :db do
desc "Migrate the database through scripts in db/migrate. Target specific version with VERSION=x"
task :migrate => :environment do
puts "In the Rails default migrate task"
ActiveRecord::Migrator.migrate("db/migrate/", ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
end
...
vendor/plugins/shared_rails_tasks/tasks/rails.rake
Rake::Task["db:migrate"].prerequisites << "update_migrations" if Rake::Task.task_defined?("db:migrate")
p Rake::Task["db:migrate"].prerequisites
task :update_migrations do
puts "In the prerequisite that I *prepended* before db:migrate"
end
$ rake migrate
["environment", "update_migrations"]
In the prerequisite that I *prepended* before db:migrate
In the Rails default migrate task
Hey, that was easy! Thank goodness for the prerequisites attr_accessor!!
[edit] How do I invoke other tasks from my task?
Rake::Task["name:of:task"].invoke
Here's an example from (/usr/lib/ruby/gems/1.8/gems/rails-1.1.6/lib/tasks/testing.rake):
desc 'Test all units and functionals'
task :test do
Rake::Task["test:units"].invoke rescue got_error = true
Rake::Task["test:functionals"].invoke rescue got_error = true
if File.exist?("test/integration")
Rake::Task["test:integration"].invoke rescue got_error = true
end
raise "Test failures" if got_error
end
Use this technique to chain tasks together, avoid duplication, and promote code reuse.
[edit] Namespaces / name resolution
http://rake.rubyforge.org/files/doc/rakefile_rdoc.html
When looking up a task name, rake will start with the current namespace and attempt to find the name there. If it fails to find a name in the current namespace, it will search the parent namespaces until a match is found (or an error occurs if there is no match).
The "rake" namespace is a special implicit namespace that refers to the toplevel names.
If a task name begins with a "^" character, the name resolution will start in the parent namespace. Multiple "^" characters are allowed.
Here is an example file with multiple :run tasks and how various names resolve in different locations.
task :run namespace "one" do task :run namespace "two" do task :run # :run => "one:two:run" # "two:run" => "one:two:run" # "one:two:run" => "one:two:run" # "one:run" => "one:run" # "^run" => "one:run" # "^^run" => "rake:run" (the top level task) # "rake:run" => "rake:run" (the top level task) end # :run => "one:run" # "two:run" => "one:two:run" # "^run" => "rake:run" end # :run => "rake:run" # "one:run" => "one:run" # "one:two:run" => "one:two:run"
[edit] How does it look up tasks? / Dissection of Task class
/usr/lib/ruby/gems/1.8/gems/rake-0.7.1/lib/rake.rb
def [](task_name)
Rake.application[task_name]
end
require 'pp'
pp Rake::Task["db:migrate"]
#<Rake::Task:0xb7ab6028
@actions=
[#<Proc:0xb7ac8764@/usr/lib/ruby/gems/1.8/gems/rails-1.1.6/lib/tasks/databases.rake:3>],
@already_invoked=false,
@application=
#<Rake::Application:0xb7e56d54
@default_loader=#<Rake::DefaultLoader:0xb7e56cb4>,
@imported=[],
@last_comment=nil,
@loaders=
{".rake"=>#<Rake::DefaultLoader:0xb7e56c28>,
".rf"=>#<Rake::DefaultLoader:0xb7e56c78>},
@options=#<OpenStruct rakelib="rakelib">,
@original_dir="/home/tyler/svn/glass.net/backend",
@pending_imports=[],
@rakefile="Rakefile",
@rules=[],
@scope=[],
@tasks= # ... [the whole list]
@comment=
"Migrate the database through scripts in db/migrate. Target specific version with VERSION=x",
@lock=#<Mutex:0xb7ab5d6c @locked=false, @waiting=[]>,
@name="db:migrate",
@prerequisites=["environment"],
@scope=[:db]>
[edit] Default tasks
You can set a default task in your Rake file like this:
task :default => :test
It doesn't look like can have a default task for a namespace though. This doesn't work:
namespace :db do
namespace :woo do
task :default do
puts 'hoo!'
end
end
end
> rake db:woo
rake aborted!
Don't know how to build task 'db:woo'
You can get around that though by creating a task with the same name as the namespace that you want to have the default:
namespace :db do
task :woo => :"woo:default"
namespace :woo do
task :default do
puts 'hoo!'
end
end
end
> rake db:woo
hoo!
