class Promotion::Evolver

The Evolver class evolves the database by executing migration scripts from the evolve folder of the project being promoted

This class may be invoked via promote or via evolve or devolve commands

Public Class Methods

new(appname, evolve=true, targetVersion=nil) click to toggle source

Creates a new Evolver

# File lib/promotion/evolver.rb, line 9
def initialize(appname, evolve=true, targetVersion=nil)
        @appname = appname
        @evolve = (evolve == true)
        @target = targetVersion.to_i()
        @currentVersion = nil
        @spec = get_spec()
        db = @spec.elements["Database"]
        @dbms = db.text()
        @database = db.attributes["database"] || ""
end

Public Instance Methods

devolve() click to toggle source

Returns the database to an earlier schema version:

  • find all of the relevant schema migration files in the devolve folder

  • execute in sequence all migrations from the current version down to the target version

  • update the version file with the new version number

# File lib/promotion/evolver.rb, line 95
def devolve()
        $log.info("\n#{'_'*40}\nDevolving the database #{@database}\n")
        devolveFolder = File.expand_path("#{@appname}/devolve", Folders::Staging)
        Dir.chdir(devolveFolder)
        migrations = Dir["*.sql"].collect { |f| f.sub(".sql","").to_i() }.sort()
        migrations.reject! { |v| v > @currentVersion }
        migrations.reject! { |v| v <= @target }               # after devolving we are at the previous version
        migrations.reverse!
        if @target < @currentVersion
                print("Devolving the database")
        else
                puts("Already at version #{@currentVersion}.")
                exit 0
        end
        completed = @currentVersion
        migrations.each { |v|
                success = system("#{@dbms} #{@database} < #{v}.sql")
                if success
                        $log.info("Devolved the database from version #{v}")
                        completed = v
                else
                        $log.error("Failed to devolve the database from version #{v}.")
                        break
                end
        }
        return(completed-1)   # after devolving we are at the previous version
end
evolve() click to toggle source

Starts the database evolution:

  • find all of the relevant schema migration files in the evolve folder

  • execute in sequence all migrations after the current version, updating the version file with the latest successful version number

# File lib/promotion/evolver.rb, line 64
def evolve()
        $log.info("\n#{'_'*40}\nEvolving the database #{@database}\n")
        evolveFolder = File.expand_path("#{@appname}/evolve", Folders::Staging)
        Dir.chdir(evolveFolder)
        migrations = Dir["*.sql"].collect { |f| f.sub(".sql","").to_i() }.sort()
        migrations.reject! { |v| v <= @currentVersion }
        migrations.reject! { |v| v > @target } unless @target == 0
        @target = migrations.last.to_i
        if @target <= @currentVersion
                puts("Already at version #{@currentVersion}.")
                exit 0
        end
        completed = @currentVersion
        migrations.each { |v|
                success = system("#{@dbms} #{@database} < #{v}.sql")
                if success
                        $log.info("Evolved the database to version #{v}")
                        completed = v
                else
                        $log.error("Failed to evolve the database to version #{v}.")
                        break
                end
        }
        return(completed)
end
get_spec() click to toggle source

The deployment descriptor for an application should contain a single Database element in order to make use of the evolve command. SQLite3 also needs a database attribute to specify the file to operate on.

<Database>/usr/bin/mysql</Database>
<Database database="/var/myapp/myapp.db">/usr/bin/sqlite3</Database>
# File lib/promotion/evolver.rb, line 25
def get_spec()
        appFolder = File.expand_path(@appname, Folders::Staging)
        Dir.chdir(appFolder)
        specfile = File.expand_path(Files::Spec, appFolder)
        unless File.exist?(specfile)
                puts("\nSpecification file #{specfile} does not exist.\n" )
                exit 1
        end
        doc = REXML::Document.new(File.new(specfile))
        doc.root
end
start() click to toggle source

Gets the current version from the version file and calls evolve or devolve as required

# File lib/promotion/evolver.rb, line 38
def start()
        versionFilename = File.expand_path("@version.#{@appname}", Folders::Staging)
        if !File.exist?(versionFilename)
                puts("We expect a version file at #{versionFilename} containing a single line")
                puts("with the current version of the database (eg. 1001)")
                exit 1
        end
        versionFile = File.new(versionFilename, 'r')
        @currentVersion = versionFile.gets.chomp().to_i()
        versionFile.close()
        completed = @currentVersion
        if @evolve
                completed = evolve()
        else
                completed = devolve()
        end
        versionFile = File.new(versionFilename, 'w')
        versionFile.puts(completed)
        versionFile.close()
        puts("Evolved the database to version #{completed} ")
end