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

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

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 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

Using a rake task that accepts parameters as a prerequisite

According to http://rake.rubyforge.org/files/doc/rakefile_rdoc.html, you can create a task that accepts parameters and also has prerequisites:
task :name, [:first_name, :last_name] => [:pre_name] do |t, args|
But what if :pre_name is a task that also accepts parameters? What is the syntax for passing parameters to :pre_name when it is used as a prerequisite?
It's actually pretty simple - the :pre task will receive the same parameters as the original task. All you need to do is make sure that the signature is similar - for instance if the first task receives :a,:b the :pre task needs to receive them as well.
See more here: rake with params
I know I'm late to the party, but I had the same problem and figured something out that didn't use environment variables. You can use Rake::Task.invoke to do this. Here's an example for a database backup rake task:
namespace :db do
task :dump_db, [:dump_file, :rails_env] do |t, args|
puts "dumping to #{args[:dump_file]} with rails env = #{args[:rails_env]}"
end
task :stop_slave do
puts "stopping slave"
end
task :start_slave do
puts "starting slave"
end
task :upload_dump, [:dump_file] do |t, args|
puts "uploading #{args[:dump_file]}"
end
task :backup_to_s3, [:dump_file, :rails_env] do |t, args|
Rake::Task["db:stop_slave"].invoke()
Rake::Task["db:dump_db"].invoke(args[:dump_file], args[:rails_env])
Rake::Task["db:start_slave"].invoke()
Rake::Task["db:upload_dump"].invoke(args[:dump_file])
end
end
I don't have a direct answer, but I do have an alternative solution that might work for you. None of my rake tasks use parameters. (I think I tried to use parameters and had trouble getting them to work.) Instead, I rely on the ENV array. So, for example, I would write that example task as:
task :name =>:pre_name do
do_something_with_name(ENV['first_name'], ENV['last_name'])
end
which would be invoked as:
$ rake name first_name=John last_name=Smith
The ENV array data would be available to the pre_name task as well.
namespace :shell do
desc "Local hostname"
task :hostname do
puts "Local hostname"
sh "hostname"
end
desc "Local uptime"
task :uptime do
puts "Local uptime"
sh "uptime"
end
desc "Echo something"
task :echo,[:someword] do |t,args|
puts "--- #{args[:someword]} ---"
end
end
desc "Run all tasks"
task :all , [:someword] => ["shell:hostname","shell:uptime","shell:echo"] do
puts "Done."
end

Default task for namespace in Rake

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.

Resources