How to test rake task callbacks - ruby

I create test code with rspec. I want to test callback is executed or not.
task main: [:callback] means run callback before main, doesn't it?
But my test failed. I looks like callback is not executed. Why?
require 'rails_helper'
require 'rake'
RSpec.describe 'Rake::Task' do
before(:all) do
#rake = Rake::Application.new
Rake.application = #rake
Rake.application.rake_require('rspec_before', ["#{Rails.root}/lib/tasks"])
end
subject { #rake['rspec_before:main'].execute }
it "expects run callback before main task" do
expect{ subject }.to output(/Hello, world/).to_stdout
end
end
My Rake task is below.
namespace :rspec_before do
task :callback do
#greeting = "Hello, world"
end
# case 1
# In this case, `callback` is NOT executed in rspec
# In console, `callback` is executed !!!!
desc "main task"
task main: [:callback] do
puts #greeting
end
# case 2
# In this case, `callback` is executed in rspec
# task :main do
# Rake::Task['rspec_before:callback'].execute
# puts #greeting
# end
end

So, I'm not sure I would call :callback a callback, it's more of a dependency. A callback implies it happens after the main task is done, whereas when you do
task main: [:callback]
what you're really saying is that you depend on the other task having run first. So I would end up changing the name of that, though that's probably just a sample/throwaway name for this question. But, I digress and I'll continue calling the task :callback as written for this answer.
The main issue here is that when you call execute on a rake task, only that task gets executed. This is because there may be situations where you don't want or need to call the entire dependency chain. Let's say we add another task to your file:
desc "secondary task"
task :secondary do
#greeting = 'Goodbye, Cruel World'
Rake::Task['rspec_before:main'].execute
end
If we run this, we very likely want the main task to output Goodbye, Cruel World, instead of Hello, World and if we call all the dependencies, main would end up calling :callback which would override our #greeting and end up outputting Hello, World.
There is, however, another task that does call the entire dependency chain: invoke:
desc "secondary task"
task :secondary do
#greeting = 'Goodbye, Cruel World'
Rake::Task['rspec_before:main'].invoke
end
if we run this task, now we'll see Hello, World instead of Goodbye, Cruel World. So, having said all of this, what does that mean for your RSpec test? You simply need to change your subject to:
subject { #rake['rspec_before:main'].invoke }
because you are wanting to run the dependencies.

Related

Get output of previously run rake task

I have a rake task, that depends on another in terms of that it parses its output to determine failure or success.
Currently I do it like this:
task :foo do
puts "OK"
end
task :bar do
if `bundle exec rake foo`.split(/\n/)[-1] == "OK" then
puts "Everything went fine"
else
puts "Something went wrong"
exit 1
end
end
But I would prefer not to run the dependency in a subshell but specify it correctly as in task bar: :foo do and then check its output, is something like that possible?
Follow this, it calls one task from other. It helps.
namespace :abc do
task :aa do
"ok"
end
task :bb do
op = Rake::Task["abc:aa"].enhance(["abc:aa"])
p "op=> #{op}"
end
end

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

How do I know if a rake task has been invoked from another task or from the shell?

Let's say we have:
task :something => [:something_else] do
# some of stuff
end
task :something_else do
# some verbose stuff
# some quiet stuff
end
Now I want something_else to do the verbose stuff when called from the shell (rake something_else) and the silent ones when called as a dependency to rake something.
i think it might be a better idea to work with parameters or different tasks instead.
one thing that you could do is look for top-level task like that:
task :something_else do |t|
puts "some verbose stuff" if t.application.top_level_tasks.include? 'something_else'
puts "some quiet stuff"
end
You could look what was passed to ARGV. For example:
task :something_else do
if ARGV[0] == 'something_else'
puts "Verbose Stuff!"
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.

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