hsgubert/cassandra_migrations

View on GitHub
lib/cassandra_migrations/migrator.rb

Summary

Maintainability
A
0 mins
Test Coverage
# encoding: utf-8

module CassandraMigrations
  module Migrator

    METADATA_TABLE = 'cassandra_migrations_metadata'

    def self.up_to_latest!
      current_version = read_current_version

      new_migrations = get_all_migration_names.sort.select do |migration_name|
        get_version_from_migration_name(migration_name) > current_version
      end

      if !new_migrations.empty?
        new_migrations.each { |migration| up(migration) }
      end

      new_migrations.size
    end

    def self.rollback!(count=1)
      current_version = read_current_version

      executed_migrations = get_all_migration_names.sort.reverse.select do |migration_name|
        get_version_from_migration_name(migration_name) <= current_version
      end

      down_count = 0

      if !executed_migrations.empty?
        count.times do |i|
          if executed_migrations[i]
            down(executed_migrations[i], executed_migrations[i+1])
            down_count += 1
          end
        end
      end

      down_count
    end

    def self.read_current_version
      begin
        Cassandra.select(METADATA_TABLE, :selection => "data_name='version'", :projection => 'data_value').first['data_value'].to_i
      rescue ::Cassandra::Errors::InvalidError => e # table cassandra_migrations_metadata does not exist
        Cassandra.execute("CREATE TABLE #{METADATA_TABLE} (data_name varchar PRIMARY KEY, data_value varchar)")
        Cassandra.write!(METADATA_TABLE, {:data_name => 'version', :data_value => '0'})
        return 0
      end
    end

private

    def self.up(migration_name)
      # load migration
      require migration_name
      # run migration
      get_class_from_migration_name(migration_name).new.migrate(:up)

      # update version
      Cassandra.write!(METADATA_TABLE, {:data_name => 'version', :data_value => get_version_from_migration_name(migration_name).to_s})
    end

    def self.down(migration_name, previous_migration_name=nil)
      # load migration
      require migration_name
      # run migration
      get_class_from_migration_name(migration_name).new.migrate(:down)

      # downgrade version
      if previous_migration_name
        Cassandra.write!(METADATA_TABLE, {:data_name => 'version', :data_value => get_version_from_migration_name(previous_migration_name).to_s})
      else
        Cassandra.write!(METADATA_TABLE, {:data_name => 'version', :data_value => '0'})
      end
    end

    def self.get_all_migration_names
      Dir[Rails.root.join("db", "cassandra_migrate/[0-9]*_*.rb")]
    end

    def self.get_class_from_migration_name(filename)
      migration_name = filename.match(/[0-9]+_(.+)\.rb$/).captures.first.camelize
      migration_name.constantize
    rescue NameError => e
      raise Errors::MigrationNamingError, "Migration file names must match the class name in the migration—could not find class #{migration_name}."
    end

    def self.get_version_from_migration_name(filename)
      filename.match(/\/([0-9]+)_.+\.rb$/).captures.first.to_i
    rescue
      raise Errors::MigrationNamingError, "Migration file names must start with a numeric version prefix."
    end
  end
end