Default task for namespace in Rake - ruby

Given something like:
namespace :my_tasks do
task :foo do
do_something
end
task :bar do
do_something_else
end
task :all => [:foo, :bar]
end
How do I make :all be the default task, so that running rake my_tasks will call it (instead of having to call rake my_tasks:all)?

Place it outside the namespace like this:
namespace :my_tasks do
task :foo do
do_something
end
task :bar do
do_something_else
end
end
task :all => ["my_tasks:foo", "my_tasks:bar"]
Also... if your tasks require arguments then:
namespace :my_tasks do
task :foo, :arg1, :arg2 do |t, args|
do_something
end
task :bar, :arg1, :arg2 do |t, args|
do_something_else
end
end
task :my_tasks, :arg1, :arg2 do |t, args|
Rake::Task["my_tasks:foo"].invoke( args.arg1, args.arg2 )
Rake::Task["my_tasks:bar"].invoke( args.arg1, args.arg2 )
end
Notice how in the 2nd example you can call the task the same name as the namespace, ie 'my_tasks'

Not very intuitive, but you can have a namespace and a task that have the same name, and that effectively gives you what you want. For instance
namespace :my_task do
task :foo do
do_foo
end
task :bar do
do_bar
end
end
task :my_task do
Rake::Task['my_task:foo'].invoke
Rake::Task['my_task:bar'].invoke
end
Now you can run commands like,
rake my_task:foo
and
rake my_task

I suggest you to use this if you have lots of tasks in the namespace.
task :my_tasks do
Rake.application.in_namespace(:my_tasks){|namespace| namespace.tasks.each(&:invoke)}
end
And then you can run all tasks in the namespace by:
rake my_tasks
With this, you don't need to worry to change your :all task when you add new tasks into that namespace.

I use this Rakefile for cucumber:
require 'cucumber'
require 'cucumber/rake/task'
namespace :features do
Cucumber::Rake::Task.new(:fast) do |t|
t.profile = 'fast'
end
Cucumber::Rake::Task.new(:slow) do |t|
t.profile = 'slow'
end
task :ci => [:fast, :slow]
end
task :default => "features:ci"
Then if I type just:
rake
It runs the default task, which runs both fast and slow tests.
I learned this from Cheezy's blog.

The way I'm reading obvio171's question is that he is asking1) for a systematic way to invoke a certain task in a namespace by invoking the namespace as a task.
I've frequently encountered the same need. I like to logically group tasks into namespaces. Often that grouping resembles a hierarchy. Hence the desire to invoke the group makes very much sense to me.
Here's my take:
module Rake::DSL
def group(name, &block)
ns = namespace name, &block
default = ns[:default]
task name => "#{name}:default" if default
ns
end
end
group :foo do
task :foo1 do |t| puts t.name end
task :foo2 do |t| puts t.name end
task :default => [:foo1, :foo2]
end
task :default => :foo
1)...or was asking, years ago. Nonetheless a still interesting question.

Add the following task outside of the namespace:
desc "Run all my tasks"
task :my_tasks => ["my_tasks:all"]
Keep in mind, that you can have a task with the same name as the namespace.
And hier a bigger example, that shows, how you can make use of tasks, which have the same name as the namespace, even when nesting namespaces:
namespace :job1 do
task :do_something1 do
puts "job1:do_something1"
end
task :do_something2 do
puts "job1:do_something2"
end
task :all => [:do_something1, :do_something2]
end
desc "Job 1"
task :job1 => ["job1:all"]
# You do not need the "all"-task, but it might be handier to have one.
namespace :job2 do
task :do_something1 do
puts "job2:do_something1"
end
task :do_something2 do
puts "job2:do_something2"
end
end
desc "Job 2"
task :job2 => ["job2:do_something1", "job2:do_something2"]
namespace :superjob do
namespace :job1 do
task :do_something1 do
puts "superjob:job1:do_something1"
end
task :do_something2 do
puts "superjob:job1:do_something2"
end
end
desc "Job 1 in Superjob"
task :job1 => ["job1:do_something1", "job1:do_something2"]
namespace :job2 do
task :do_something1 do
puts "superjob:job2:do_something1"
end
task :do_something2 do
puts "superjob:job2:do_something2"
end
end
desc "Job 2 in Superjob"
task :job2 => ["job2:do_something1", "job2:do_something2"]
end
desc "My Super Job"
task :superjob => ["superjob:job1", "superjob:job2"]
# Do them all just by calling "$ rake"
task :default => [:job1, :job2, :superjob]
Just copy it and try it out.

Based on Rocky's solution Default task for namespace in Rake
And this dexter's answer Is there a way to know the current rake task?
namespace :root do
namespace :foo do
end
namespace :target do
task :all do |task_all|
Rake.application.in_namespace(task_all.scope.path) do |ns|
ns.tasks.each { |task| task.invoke unless task.name == task_all.name }
end
end
task :one do
end
task :another do
end
end
end

Combining Szymon LipiƄski's and Shyam Habarakada's answers, here is what I think is the most idiomatic and consise answer:
namespace :my_tasks do
task :foo do
do_something
end
task :bar do
do_something_else
end
end
task :my_tasks => ["my_tasks:foo", "my_tasks:bar"]
allows you to do rake my_tasks while avoiding cumbersome invocation of the subtasks.

Related

Limit scope of rule defined inside namespace

I have the following Rakefile (this is a simplified example):
namespace :green do
rule(/^build:/) do |t|
puts "[green] #{t}"
end
task :start do
puts '[green] start'
end
task run: ['build:app', :start]
end
namespace :blue do
rule(/^build:/) do |t|
puts "[blue] #{t}"
end
task :start do
puts '[blue] start'
end
task run: ['build:app', :start]
end
I would like each "build" rule to apply only within the namespace where it's defined. In other words, this is what I want to happen:
$ rake blue:run
[blue] build:app
[blue] start
But what actually happens is this (with Rake 12.3.1):
$ rake blue:run
[green] build:app
[blue] start
Is there a way to limit the scope of the "build" rules so that the rule defined in the "green" namespace isn't accessible from the "blue" namespace?
It appears that Rake doesn't support this natively. Tasks are scoped to the namespace they are defined in (by adding the scope path as a prefix), but rules get no such prefix.
I was able to get this to work by monkey-patching Rake, which is not ideal:
# Monkey-patch rake
module Rake
module TaskManager
# Copied from rake 12.3.1 and enhanced for scoped rules
def lookup_in_scope(name, scope)
loop do
tn = scope.path_with_task_name(name)
task = #tasks[tn]
return task if task
break if scope.empty?
# BEGIN ADDED LINES
task = enhance_with_matching_rule(tn)
return task if task
# END ADDED LINES
scope = scope.tail
end
nil
end
end
module DSL
# Create a rule inside a namespace scope
def scoped_rule(name, &block)
pattern = "^#{Rake.application.current_scope.path}:#{name}:"
Rake.application.create_rule(Regexp.new(pattern), &block)
end
end
end
namespace :green do
scoped_rule :build do |t|
puts t
end
task :start do |t|
puts t
end
task run: ['build:app', :start]
end
namespace :blue do
scoped_rule :build do |t|
puts t
end
task :start do |t|
puts t
end
task run: ['build:app', :start]
end
Output:
$ rake green:run
green:build:app
green:start
$ rake blue:run
blue:build:app
blue:start
I know that it's not the best solution, but I think it maybe can help you.
rule(/^build*/) do |t|
Rake::Task["green:build"].invoke if ARGV[0].start_with? "green"
Rake::Task["blue:build"].invoke if ARGV[0].start_with? "blue"
end
namespace :green do
task :build do
puts '[green] build'
end
task :start do
puts '[green] start'
end
task run: ['build.app', :start]
end
namespace :blue do
task :build do
puts '[blue] build'
end
task :start do
puts '[blue] start'
end
task run: ['build.app', :start]
end
Testing it we have:
rake green:run # [green] build
# [green] start
rake blue:run # [blue] build
# [blue] start

How can I call a rake task with default args from the commad line?

I have a rake task to generate a given amount of items in a xml file. If nothing is given I want to have a default. By now only 50 gets printed, but I would like to have a variable amount here, how would I call it from the command line?
namespace :utilites do
desc "generate xml"
task generate_xml: :environment do |t, args|
args.with_defaults(:length => 50)
p args[:length]
end
end
If you define length as a parameter it works correct:
namespace :utilities do
desc "generate xml"
task :generate_xml, :length do |t, args|
args.with_defaults(:length => 50)
p args[:length]
end
end
The result:
C:\Temp>rake utilities:generate_xml
50
C:\Temp>rake utilities:generate_xml[99]
"99"
As you see, the given parameter is a string! If you need a number you must convert it before you use it.
I don't know what your :environment is. If it should be a prerequisite, you can use:
namespace :utilities do
task :environment do
puts "Run environment"
end
desc "generate xml"
task :generate_xml, [:length] => :environment do |t, args|
args.with_defaults(:length => 50)
p args[:length]
end
end

How to know which task is being executed with rake

I'd like to know from within a rake ask what is the name of the task that is being executed? How to do this? For example, in the code bellow, when I run rake my_incredible_task, it should print "my_incredible_task":
task :boot do
task_name = <what comes here?>
puts task_name
end
task :my_incredible_task => [:boot] do
#do some stuff
end
I'm not sure there is a way out of the box, however you can do something like this:
require 'rake'
module Rake
class Application
attr_accessor :current_task_name
end
class Task
alias :old_execute :execute
def execute(args=nil)
Rake.application.current_task_name = #name
old_execute(args)
end
end
end
namespace :so do
task :my_task do
puts Rake.application.current_task_name
end
end
Not sure how this will work out with tasks that run in parallel...
You can use a parameter in the block :
task :my_incredible_task do |t|
puts t.name
end
EDIT
You could use methods instead :
task :boot do |t|
boot(t.name)
end
task :my_incredible_task do |t|
boot(t.name)
#do some stuff
end
def boot task_name
# do stuff
end

Alias of task name in Rake

When I need to alias some task's name, how should I do it?
For example, how do I turn the task name:
rake db:table
rake db:create
rake db:schema
rake db:migration
to:
rake db:t
rake db:c
rake db:s
rake db:m
Editing after getting the answer:
def alias_task(tasks)
tasks.each do |new_name, old_name|
task new_name, [*Rake.application[old_name].arg_names] => [old_name]
end
end
alias_task [
[:ds, :db_schema],
[:dc, :db_create],
[:dr, :db_remove]
]
Why do you need an alias? You may introduce a new task without any code, but with a prerequisite to the original task.
namespace :db do
task :table do
puts "table"
end
#kind of alias
task :t => :table
end
This can be combined with parameters:
require 'rake'
desc 'My original task'
task :original_task, [:par1, :par2] do |t, args|
puts "#{t}: #{args.inspect}"
end
#Alias task.
#Parameters are send to prerequisites, if the keys are identic.
task :alias_task, [:par1, :par2] => :original_task
To avoid to search for the parameters names you may read the parameters with arg_names:
#You can get the parameters of the original
task :alias_task2, *Rake.application[:original_task].arg_names, :needs => :original_task
Combine it to a define_alias_task-method:
def define_alias_task(alias_task, original)
desc "Alias #{original}"
task alias_task, *Rake.application[original].arg_names, :needs => original
end
define_alias_task(:alias_task3, :original_task)
Tested with ruby 1.9.1 and rake-0.8.7.
Hmmm, well, I see that's more or less exactly the same solution RyanTM already posted some hours ago.
Here is some code someone wrote to do it: https://gist.github.com/232966
def alias_task(name, old_name)
t = Rake::Task[old_name]
desc t.full_comment if t.full_comment
task name, *t.arg_names do |_, args|
# values_at is broken on Rake::TaskArguments
args = t.arg_names.map { |a| args[a] }
t.invoke(args)
end
end

Is it possible to get a list of all available rake tasks in a namespace?

Is it possible from within a rake task to get a list of tasks in a namespace? A sort of programatic 'rake -T db' ?
I've found out the answer:
tasks = Rake.application.tasks
This will return an array of Rake::Task objects that can be examined. Further details at http://rake.rubyforge.org/
As you wrote, with Rake.application.tasks you get all tasks.
But inside the namespace, you can select only the tasks of the namespace (task mytest:tasklist)
And you may restrict the tasks to a namespace (task tasklist_mytest).
require 'rake'
namespace :mytest do |ns|
task :foo do |t|
puts "You called task #{t}"
end
task :bar do |t|
puts "You called task #{t}"
end
desc 'Get tasks inside actual namespace'
task :tasklist do
puts 'All tasks of "mytest":'
puts ns.tasks #ns is defined as block-argument
end
end
desc 'Get all tasks'
task :tasklist do
puts 'All tasks:'
puts Rake.application.tasks
end
desc 'Get tasks outside the namespace'
task :tasklist_mytest do
puts 'All tasks of "mytest":'
Rake.application.in_namespace(:mytest){|x|
puts x.tasks
}
end
if $0 == __FILE__
Rake.application['tasklist'].invoke() #all tasks
Rake.application['mytest:tasklist'].invoke() #tasks of mytest
Rake.application['tasklist_mytest'].invoke() #tasks of mytest
end
You can use the grep command like this
desc 'Test'
task :test do
# You can change db: by any other namespaces
result = %x[rake -T | sed -n '/db:/{/grep/!p;}' | awk '{print$2}']
result.each_line do |t|
puts t # Where t is your task name
end
end

Resources