Rake

From WhyNotWiki

Jump to: navigation, search

Rake  edit   (Category  edit) .


Contents

[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!)
  • rake command lists available tasks with descriptions (using task name and description given to desc)
  • 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!

[edit] Links

Retrieved from "http://whynotwiki.com/Rake"
Personal tools