How do I execute Rake tasks with arguments multiple times? - ruby

It's not possible to invoke the same rake task from within a loop more than once. But, I want to be able to call rake first and loop through an array and invoke second on each iteration with different arguments. Since invoke only gets executed the first time around, I tried to use execute, but Rake::Task#execute doesn't use the splat (*) operator and only takes a single argument.
desc "first task"
task :first do
other_arg = "bar"
[1,2,3,4].each_with_index do |n,i|
if i == 0
Rake::Task["foo:second"].invoke(n,other_arg)
else
# this doesn't work
Rake::Task["foo:second"].execute(n,other_arg)
end
end
end
task :second, [:first_arg, :second_arg] => :prerequisite_task do |t,args|
puts args[:first_arg]
puts args[:second_arg]
# ...
end
One hack around it is to put the arguments to execute into an array and in second examine the structure of args, but that seems, well, hackish. Is there another (better?) way to accomplish what I'd like to do?

You can use Rake::Task#reenable to allow it to be invoked again.
desc "first task"
task :first do
other_arg = "bar"
[1,2,3,4].each_with_index do |n,i|
if i == 0
Rake::Task["second"].invoke(n,other_arg)
else
# this does work
Rake::Task["second"].reenable
Rake::Task["second"].invoke(n,other_arg)
end
end
end
task :second, [:first_arg, :second_arg] do |t,args|
puts args[:first_arg]
puts args[:second_arg]
# ...
end
$ rake first
1
bar
2
bar
3
bar
4
bar

The execute function asks for a Rake::TaskArguments as a parameter, this is why it only accepts one argument.
You could use
stuff_args = {:match => "HELLO", :freq => '100' }
Rake::Task["stuff:sample"].execute(Rake::TaskArguments.new(stuff_args.keys, stuff_args.values))
However there is another difference between invoke and execute, execute doesn't run the :prerequisite_task when invoke does this first, so invoke and reenable or execute doesn't have exactly the same meaning.

FWIW this might help someone so I'll post it.
I wanted to be able to run one command from the CLI to run one Rake task multiple times (each time with new arguments, but that's not important).
Example:
rake my_task[x] my_task[y] my_task[z]
However, since Rake sees all my_task as the same task regardless of the args, it will only invoke the first time my_task[x] and will not invoke my_task[y] and my_task[z].
Using the Rake::Task#reenable method as mentioned in the other answers, I wrote a reenable Rake task which you can position to run after a task to allow it to run again.
Result:
rake my_task[x] reenable[my_task] my_task[y] reenable[my_task] my_task[z]
I wouldn't say this is ideal but it works for my case.
reenable Rake task source:
task :reenable, [:taskname] do |_task, args|
Rake::Task[args[:taskname]].reenable
Rake::Task[:reenable].reenable
end

This worked for me, it's quite easy to understand you just need to loop you bash command.
task :taskname, [:loop] do |t, args|
$i = 0
$num = args.loop.to_i
while $i < $num do
sh 'your bash command''
$i +=1
end
end

Related

Unified way in Rake to work with arguments passed by #invoke and #execute

I expected a Task to get arguments the same way, transparent to the caller methods #invoke or #execute:
desc "Add task"
task :add do |t, args|
puts args.class
puts "Add"
end
desc "Sub task"
task :sub do |t, args|
puts args.class
puts "Sub"
end
desc "all"
task :all do
Rake::Task['add'].execute("arg1") # cannot set multiple arguments, will fail with "wrong number of arguments"
Rake::Task['sub'].invoke("arg1", "arg2")
end
The result was:
ยป rake all
String
Add
Rake::TaskArguments
Sub
Upon checking the Rake source code it is clear these implementations are different.
Is there an unified way to manage arguments regardless where they come from? (command line, #invoke or #execution?). I use OptParse for the command line arguments so I have two ugly workarounds in my code now.
i assume that in case of execute if number of arguments > 1 then you want to execute with an argument is an array collect all those arguments, otherwise execute will execute with nil or the only input argument (a String, for example). So the way you call execute will match with the way you call invoke and execute still do the same as origin.
you could create a wrapper (alias) for Rake::Task#execute and handle the input arguments as below
# Rakefile
Rake::Task.alias_method :old_execute, :execute
Rake::Task.define_method("execute") do |*args|
if args&.size > 1
old_execute(args)
else
old_execute(args&.first)
end
end
# ...
Rake::Task['add'].execute # Rake::TaskArguments
Rake::Task['add'].execute("arg1") # String
Rake::Task['add'].execute("arg1", "arg2") # Array

Passing Argument To Rake Dependency

I understand how to pass an argument to rake task, but I can't work out how to pass an argument to a dependent task.
I have a task that just runs a list of dependent tasks:
task foo [:alpha, :bravo, :charlie, :delta]
I need to add a task called :omega:
task foo [:omega, :alpha, :bravo, :charlie, :delta]
But I need to pass a parameter to it. I don't want to pass the parameter in to :foo - I just want to hardcode the parameter into the dependent task. I want to do this (I know it isn't valid):
task foo [:omega('Some Param'), :alpha, :bravo, :charlie, :delta]
How do I pass in the parameter?
Rake will run all the "dependent" tasks before running your task...so I doubt you will be able to achieve the above
Can you explain further what you are trying to achieve?
Jusy as an example if the task is run from the command line you may be able to do the following
rake foo omegaVar
In you code
def confirmDestruction
print "Are you sure you want to do that?\n"
answer=gets()
if answer == 'y'
return true
else
exit 0
end
end
task :omega do
$conrimedDestruction = confirmDestruction if $conrimedDestruction.nil?
if $conrimedDestruction
print "${$omegaVar}\n"
else
print "non destructive functionality\n"
end
end
task :alpha do
if $conrimedDestruction
print "${$omegaVar}\n"
else
print "non destructive functionality\n"
end
end
task :foo => [:omega, :alpha, :bravo, :charlie, :delta]
For better or worse, that is not possible. The available options are:
task :t do
task :t, :a, :b, ... do
task :t, [:a, :b, ...] do
if you don't specify dependencies. And:
task :t => [:d1, d2, ...] do
task :t, [:a, :b, ...] => [:d1, d2, ...] do
if you do.
Unless you want to invoke it manually:
task :a, :p do |task, args|
puts "p: #{args[:p]}"
end
task :b do
Rake::Task[:a].invoke(1)
end
$ rake b
p: 1

Times loop isn't looping

I'm creating a little script that will send 10 jobs to a printer. I'm trying to loop one of my methods using a times loop except when I run the program, it won't loop, it just send one job..
Example:
[<user>#<server> ruby]$ ruby clean_printer laser32
request id is laser32-1038115 (1 file(s))
[<user>#<server> ruby]$
Am I doing something wrong to where this won't loop..?
Source:
#!/usr/local/bin/ruby
class CleanPrinter
attr_accessor :printer
def initialize(printer)
#printer = printer
end
def create_jobs
exec("lp -d #{#printer} test.txt")
end
def loop
10.times do
create_jobs
end
end
end
test = CleanPrinter.new(ARGV[0])
test.loop
Yes it's a class, yes I will take it out of the class.
Note that exec() actually replaces the current process image, thereby effectively exiting your program on the first iteration. You might want to switch to using system() instead.

Access Uncaptured Proc Arguments

Can I access uncaptured arguments to an invoked ruby Proc?
Ruby Procs are very much like JavaScript functions. In Javascript, I can do arguments[someIndex]. Is there a ruby version of this?
my_proc = Proc.new { |a| p a } #This captures the first argument but not the second
my_proc.call(42,43)
#Can I still access the second argument without capturing it (|a,b|) (with something some kind of a ruby equivalent to `arguments[1]` or doing anything within the pipes)?
(Why I'm asking: I've written myself a strace-based dependency-tracking sh replacement for rake and if I am to use it in a Rake task, it needs to know what task to assign the discovered dependencies to. In other words, it needs access to the first argument to the task block in which it is invoked, but I don't want it to depend on the first argument always being captured in a particular way.)
Ruby does not have an equivalent to Javascripts arguments accessor. As #mudasobwa noted, you will have to use the splat operator in order to "soak" up all remaining arguments, if you wish to access them at a later time:
my_proc = Proc.new { |expected_arg1, expected_arg2, *rest| p expected_arg1 }
Use a double-splat when using keyword arguments:
my_proc = Proc.new { |expected_arg1:, **rest| p expected_arg1 }

Receiving Forked Process Output Outside Method

I'm new to ruby and object oriented languages, and I'm having trouble figuring out a way to accomplish forking a process inside a method and passing the delayed output to be used outside the method while also returning the process id.
def method(arg)
proc_id = fork do
var = `command #{arg}`
end
return both = [proc_id, var]
end
This doesn't work as var will return nil since the process has not yet finished. How could I accomplish something like this?
UPDATE:
Using IO.pipe I was able to accomplish Inter-Process Communication. However, trying to use this solution inside a method will not allow me to return both proc_id and var without first waiting for the process to finish which forces me to create new arrays and iterations that would be otherwise unnecessary. The objective here is to have freedom to execute code outside the method while the fork process inside the method is still working.
arg_array = ["arg1", "arg2", "arg3", "arg4"]
input = []
output = []
proc_id = []
arg_array.each_index do |i|
input[i], output[i] = IO.pipe
proc_id[i] = fork do
input[i].close
output[i].write `command #{arg_array[i]}`
end
output[i].close
end
command2
command3
include Process
waitpid(proc_id[0])
command4
Process.waitall
arg_array.each_index do |x|
puts input[x].read
end
You need to use a little more time studying the concept of fork. The parent and child process after a fork cannot communicate (exchange variables) each other without using IPC (Inter-Processs Communication) which is somewhat complicated.
But for your purpose (getting the child process id, and its output), it's easier with Open3.popen2 or Open3.popen3.
http://www.ruby-doc.org/stdlib-1.9.3/libdoc/open3/rdoc/Open3.html#method-c-popen2
if you want to kick something off and save the child pid, that's fairly simple.
pid = fork
if pid
return pid
else
system("command #{arg}")
exit
end
a little bit clumsy, but basically, fork returns the child pid to the parent process, and nil to the child process. Make sure you exit the child, it won't do that automatically.
Thanks to jaeheung's suggestion, I've solved using Open3.popen2 (requires version 1.9.3).
arguments = ["arg1", "arg2", "arg3", "arg4"]
require 'open3'
include Open3
def method(arg)
input, output, thread = Open3.popen2("command #{arg}")
input.close
return [thread.pid, output]
end
thread_output = []
arguments.each do |i|
thread_output << method("#{i}")
end
command1
command2
include Process
waitpid(thread_output[0][0])
command3
Process.waitall
thread_output.each do |x|
puts x[1].read
end

Resources