arraysrubyrakefile

How can I edit a ruby file (Rakefile) in place from within ruby?


I'm not sure if the question title makes sense, but here's what I'm trying to do.

I have a Rakefile that contains an array. I want to be able to use another ruby script to read the Rakefile, add something to that array, sort it, then write it back to the Rakefile in place.

In pseudo code, this is what I'm essentially trying to achieve:

rake_file = load file('Rakefile')
rake_file.array += 'new data'
rake_file.array.sort!
file.write Rakefile

The Rakefile contains many arrays and tasks, so I can't treat it like a regular text file and just append a value to the file and sort, hence why my idea is to load it and edit the array as above.

Is this even possible?


Solution

  • I can't treat it like a regular text file

    You then need to learn ANTLR, create a Ruby grammar, generate a parser, and write a Java or C# or Python tree walker to edit your array. Simple :-)

    I have imagined a quick and simple solution. The array is kept in a file, say array_source.rb :

        a = [1, 5, 10, 15]
    

    This file can be manually edited, or, if you need to programmatically generate the data, created with a script, say create_array_source.rb :

    # update the file containing the array of data in text representation
    
    array_source_name = 'array_source.rb'
    
    a = [1, 5, 10, 15]
    # add new data
    [2, 6, 11, 16].each { | e | a << e }
    puts a.join(' ')
    
    # Write it to a file
    File.open(array_source_name, 'w') do | out |
        # to avoid a very long single line, the data is pretty printed
        groups = []
        a.sort.each_slice(4) { | slice | groups << slice.join(', ') }
        out.puts '    ['
        out.print '     '
        out.puts groups.join(",\n     ")
        out.puts '    ]'
    end
    
    puts "#{array_source_name} has been created"
    

    Execution :

    $ ruby -w create_array_source.rb 
    1 5 10 15 2 6 11 16
    array_source.rb has been created
    

    File array_source.rb after :

        [
         1, 2, 5, 6,
         10, 11, 15, 16
        ]
    

    In the Rakefile, the array to update is placed between two specific markers :

    task :default => [:ta, :tb] do
    end
    
    task :ta do
        # $$insert array after$$
        a = [1, 5, 10, 15]
        # $$insert array before$$
        # do something with a
    end
    
    task :tb do
        # do something
    end
    

    So it is easy to write a script which processes it as a text file, splitting the lines into two parts :

    and then rewrite the file by concatenating the first part, the array and the second part :

    # modify a specific array in a Rakefile
    
    rakefile_name     = 'Rakefile'
    array_source_name = 'array_source.rb'
    lines = IO.readlines(rakefile_name)
    insert_marker_after_seen  = false
    insert_marker_before_seen = false
    lines1 = []
    lines2 = IO.readlines(array_source_name)
    lines3 = []
    
    # split the Rakefile
    lines.each do | line |
        case
        when insert_marker_after_seen == false
            lines1 << line
    
            if line.include?('$$insert array after$$')
            then
                insert_marker_after_seen = true
            end
        when ! insert_marker_before_seen
            # ignore array lines
    
            if line.include?('$$insert array before$$')
            then
                insert_marker_before_seen = true
                lines3 << line
            end
        else
            lines3 << line
        end
    end
    
    # Rewrite the Rakefile
    File.open(rakefile_name, 'w') do | out |
        [ lines1, lines2, lines3 ].each do | lines_group |
            lines_group.each { | line | out.puts line }
        end
    end
    
    puts "#{rakefile_name} has been updated with #{array_source_name}"
    

    Execution :

    $ ruby -w modify_rakefile.rb 
    Rakefile has been updated with array_source.rb
    

    Rakefile after :

    task :default => [:ta, :tb] do
    end
    
    task :ta do
        # $$insert array after$$
        [
         1, 2, 5, 6,
         10, 11, 15, 16
        ]
        # $$insert array before$$
        # do something with a
    end
    
    task :tb do
        # do something
    end