How do I find the filesystem location of another rake task from a rake task? - ruby

I am basically trying to convert a solution to this question into a rake task: How do I find the source file for a rake task?
I want to type, e.g.:
rake meta:where[test]
And get the path where the test task is defined. Here's what I have, with some unnecessary stuff so I can make some relevant comments:
Rake::TaskManager.record_task_metadata=true
namespace :meta do
desc "meta:where[taskname] - where is taskname defined?"
task :where, [:taskname] do |t, args|
tsk = Rake.application.tasks.each.find {|t| t.name == args.taskname}
puts tsk
tsk.locations.each {|x| puts x}
end
end
So, puts tsk actually outputs 'test', which means (I think!) I'm correctly locating the task here. It's no trouble printing the path from the rails console using the same code (as in the above-mentioned SO post). It says something like:
/Users/me/.rvm/gems/ruby-1.9.3-p362/gems/railties-3.2.11/lib/rails/test_unit/testing.rake:49:in `<top (required)>'
Any idea how I can get the location(s) to print?

As stated above, code from the linked question works, e.g.
namespace :meta do
desc "meta:where[taskname] - where is taskname defined?"
task :where, [:taskname] do |t, args|
puts "looking for #{args.taskname}"
tsk = Rake.application[args.taskname]
# It is mysterious to me why this would need to be so complicated
puts tsk.actions.map(&:source_location)
end
end
But if anyone can explain why the code in the question above doesn't work, or why the source_locations access has to be so complicated, I'd still love to know!

Related

Rake before task hook

Is there a straight forward way to modify a Rake task to run some bit of code before running the existing task? I'm looking for something equivalent to enhance, that runs at the beginning rather than the end of the task.
Rake::Task['lame'].enhance(['i_run_afterwards_ha_ha'])
You can use the dependency of Rake task to do that, and the fact that Rake allows you to redefine existing task.
Rakefile
task :your_task do
puts 'your_task'
end
task :before do
puts "before"
end
task :your_task => :before
As result
$ rake your_task
before
your_task
Or you could use the rake-hooks gem to do before and after hooks:
https://github.com/guillermo/rake-hooks
namespace :greetings do
task :hola do puts "Hola!" end ;
task :bonjour do puts "Bonjour!" end ;
task :gday do puts "G'day!" end ;
end
before "greetings:hola", "greetings:bonjour", "greetings:gday" do
puts "Hello!"
end
rake greetings:hola # => "Hello! Hola!"

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

Passing arguments to an Rspec SpecTask

Rake allows for the following syntax:
task :my_task, :arg1, :arg2 do |t, args|
puts "Args were: #{args}"
end
I'd like to be able to do the same, but with RSpecs SpecTask.
The following unfortunately fails:
desc "Run example with argument"
SpecTask.new('my_task'), :datafile do |t, args|
t.spec_files = FileList['*_spec.rb -datafile=#{args}']
t.spec_opts = ["-c -f specdoc"]
end
Is it possible to achieve this with a SpecTask, or is there an alternative approach?
if rspec doesn't support the args variable, you could pass it in as a command line parameter and/or a variable from another location.
rake datafile=somevalue
#datafile = ENV["datafile"]
desc "Run example with argument"
SpecTask.new :my_task do |t|
t.spec_files = FileList["*._spec.rb -datafile=#{#datafile}"]
#... etc
end

Avoiding duplication in setting properties on the task in Rake tasks

I have a bunch of rake building tasks.
They each have unique input / output properties, but the majority of the properties I set on the tasks are the same each time. Currently I'm doing that via simple repetition like this:
task :buildThisModule => "bin/modules/thisModule.swf"
mxmlc "bin/modules/thisModule.swf" do |t|
t.input = "src/project/modules/ThisModule.as"
t.prop1 = value1
t.prop2 = value2 ... (And many more property=value sets that are the same in each task)
end
task :buildThatModule => "bin/modules/thatModule.swf"
mxmlc "bin/modules/thatModule.swf" do |t|
t.input = "src/project/modules/ThatModule.as"
t.prop1 = value1
t.prop2 = value2 ... (And many more property=value sets that are the same in each task)
end
In my usual programming headspace I'd expect to be able to break out the population of the recurring task properties to a re-usable function.
Is there a rake analogy for this? Some way I can have a single function where the shared properties are set on any task? Something equivalent to:
task :buildThisModule => "bin/modules/thisModule.swf"
mxmlc "bin/modules/thisModule.swf" do |t|
addCommonTaskParameters(t)
t.input = "src/project/modules/ThisModule.as"
end
task :buildThatModule => "bin/modules/thatModule.swf"
mxmlc "bin/modules/thatModule.swf" do |t|
addCommonTaskParameters(t)
t.input = "src/project/modules/ThatModule.as"
end
Thanks.
======
In reply to SR:
Thanks Stephen,
I'm obviously missing something - I've got:
desc 'Compile run the test harness'
unit :test do |t|
populate_test_task(t)
end
def populate_test_task(t)
t.source_path << "support"
t.prepended_args = '+configname=air -define+=CONFIG::LocalDebug,true'
end
I've tried defining the function immediately after the task (there's no namespace: in this file) and right at the end of the file after the last task, and I'm getting "undefined method `populate_test_task' for main:Object" - which seems to me like it's not finding the function.
What am I missing?
You can call regular methods defined lower down the rake file, e.g.
namespace :build do
desc 'builds ABC module'
task :abc do
build_mod('abc')
end
desc 'builds DEF module'
taks :def do
build_mod('def')
end
end
def build_mod(module_name)
# stuff to build the module
end
then call it with rake build:abc and rake build:def
sr
Sorry about the confusion here....
This problem is a side effect of how Project Sprouts creates Rake tasks. Because some parameters in the block need to modify the prerequisites of the task, some of our tasks evaluate their associated block as they are defined.
It's for this reason that your configure function has to be defined before the tasks that use it.
Awesome team work!
Thanks to both, answer was to break out the population of the task properties to a function, but to define that function above the block using it (for project sprouts reasons).
Like this:
def populate_test_task(t)
t.source_path << "support"
t.prepended_args = '+configname=air -define+=CONFIG::LocalDebug,true'
end
desc 'Compile run the test harness'
unit :test do |t|
populate_test_task(t)
end

Resources