Avoiding duplication in setting properties on the task in Rake tasks - ruby

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

Related

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

Can Rake run each test in a separate Ruby instance?

I have a very simple Rakefile to test a small Ruby gem. It looks like this:
Rake::TestTask.new
task :default => :test
It invokes two tests that define constants with the same name. This results in errors being output by the second test like this:
warning: already initialized constant xxxxx
The reason for this is because Rake executes all of the tests within a single Ruby instance:
/usr/bin/ruby -I"lib" -I"/usr/lib/ruby/gems/2.1.0/gems/rake-10.3.2/lib" "/usr/lib/ruby/gems/2.1.0/gems/rake-10.3.2/lib/rake/rake_test_loader.rb" "test/test*.rb"
How should I specify that each test should be run in a separate Ruby instance ?
I have achieved this as shown below but I wonder if there is a better way because this solution doesn't scale well for lots of tests.
Rake::TestTask.new(:one) { |t| t.test_files = %w(test/test_one.rb) }
Rake::TestTask.new(:two) { |t| t.test_files = %w(test/test_two.rb) }
task :default => [:one, :two]
Instead of using Rake::TestTask, you could define a test task in your Rakefile that loops through each test file and runs them with sh like this:
task :test do
libs = ['lib',
'/usr/lib/ruby/gems/2.1.0/gems/rake-10.3.2/lib',
'/usr/lib/ruby/gems/2.1.0/gems/rake-10.3.2/lib/rake/rake_test_loader.rb']
test_files = FileList['test/**/test*.rb']
test_files.each do |test_file|
includes = libs.map { |l| "-I#{l}"}.join ' '
sh "ruby #{includes} #{test_file}"
end
end

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

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!

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

Are task dependencies always run in a specific order with rake?

I have the following example which is based on the structure i want my rakefile to use:
task :default do
puts 'Tasks you can run: dev, stage, prod'
end
task :dev => [:init,:devrun,:clean]
task :devrun do
puts 'Dev stuff'
end
task :stage => [:init,:stagerun,:clean]
task :stagerun do
puts 'Staging stuff'
end
task :prod => [:init,:prodrun,:clean]
task :prodrun do
puts 'Production stuff'
end
task :init do
puts 'Init...'
end
task :clean do
puts 'Cleanup'
end
Will the tasks always be run in the same order? I read somewhere that they wouldn't, and somewhere else that they would, so i'm not sure.
Or if you can suggest a better way to do what i'm trying to achieve (eg have a common init and cleanup step surrounding a depending-upon-environment step), that'd also be good.
Thanks
From the Rake source code:
# Invoke all the prerequisites of a task.
def invoke_prerequisites(task_args, invocation_chain) # :nodoc:
#prerequisites.each { |n|
prereq = application[n, #scope]
prereq_args = task_args.new_scope(prereq.arg_names)
prereq.invoke_with_call_chain(prereq_args, invocation_chain)
}
end
So it appears that the code normally just iterates the array and runs the prerequisite tasks sequentially.
However:
# Declare a task that performs its prerequisites in parallel. Multitasks does
# *not* guarantee that its prerequisites will execute in any given order
# (which is obvious when you think about it)
#
# Example:
# multitask :deploy => [:deploy_gem, :deploy_rdoc]
#
def multitask(args, &block)
Rake::MultiTask.define_task(args, &block)
end
So you are right, both can be true, but order can only be off if you prefix your task with multitask It looks like regular tasks are run in order.

Resources