Search code examples
arraysrubyloopsrake

loop through newly created array in ruby rake


I have yet another question on Ruby. What I want to do is to:

  1. Create a subfile from a file using grep
  2. Create a new empty array called ARRAY
  3. Push each line of the subfile as an element in ARRAY
  4. Loop through each element of ARRAY and create one file per element (#{element}.txt).

What I have so far:

desc "Create subfile"
file "subfile.txt" => ["infile.txt"] do
sh "grep '>' infile.txt > subfile.txt"
end

desc "Create new array"
task :new_array => ["subfile.txt"] do
ARRAY=Array.new
end

desc "Add elements to array"
task :add_elements => [:new_array] do
File.open("infile.txt").each do |line|
ARRAY.push(line)
end
end

ARRAY.each do |element|
file "#{element}.txt" => [:add_elements] do
sh 'bash command to create #{element}.txt"'
end
end

Unfortunately, I get the error:

NameError: uninitialized constant ARRAY

I think the problem comes from the fact that my ARRAY has not been set from the beginning because created mid-script and because my dependency on the previous task (:add_elements) is with the file task file "#{element}.txt" => [:add_elements] do and not the actual line where I use the ARRAY ARRAY.each do |element|.

A way around it would be:

multitask :create_element_file => [:add_elements] do
ARRAY.each do |element|
file_task
end

def file_task
file "#{element}.txt" do
sh 'bash command to create #{element}.txt"'
end

However, it is now complaining about:

NameError: undefined local variable or method `element' for main:Object

Is there a way to definitively set an array that is created mid-script? Am I doing something wrong with my dependencies? The way I call my tasks?

Any help appreciated.

===========================================================================

EDIT with my chosen solution:

I found my rakefile was becoming too complicated so I decided to write multiple connected rakefiles.

rakefile1.rake:

file "subfile.txt" => ["infile.txt"] do
sh "grep '>' infile.txt > subfile.txt"
end

desc "Create subfile"
task :create_subfile => ["subfile.txt"] do
puts "Subfile created"
end

desc "Call next rakefile"
task :next_rakefile => [:create_subfile] do
sh "rake -f rakefile2.rake --trace"
end

rakefile2.rake:

ARRAY=File.readlines("subfile.txt").each {|locus| locus.chomp!}

ARRAY.each do |element|
file "#{element}.txt" => ["subfile.txt"] do
sh "bash command to create #{element}.txt"
end

Solution

  • I think you're confusing constants and global variables. Here ARRAY is a constant (which is weird imo when talking about an object which content is modified, but that's still valid) but you can't access it within the task, because to do so you must either pass it as a parameter or (I think that's what you intended to do) make it a global variable $array (global variables are usually considered bad practice though)

    To explain the errors :
    - The first one (NameError: uninitialized constant ARRAY) Refers to the fact that ARRAY belongs to Main, it's not accessible from Main > task > File.open.
    - The second one is the same problem with element, in

    ARRAY.each do |element|
        file_task
    end
    

    element belongs to ARRAY.each and is not accessible from file_task. Once again, you must either pass it as a parameter or make it global (and global variables are still a bad thing!)