1
0
Fork 0
mirror of https://github.com/Oreolek/kohana-migrations.git synced 2024-06-26 03:40:54 +03:00

Starting to refactor stuff.

Renaming locations -> groups
Cut down the size of fetch_required_migrations()
This commit is contained in:
Matt Button 2011-02-17 20:43:04 +00:00
parent 8b550a43d4
commit e0fe87f443
15 changed files with 203 additions and 404 deletions

View file

@ -34,11 +34,11 @@ abstract class Minion_Migration_Base {
public function get_database_connection() public function get_database_connection()
{ {
$config = Kohana::config('minion/migration'); $config = Kohana::config('minion/migration');
$location = $this->_info['location']; $group = $this->_info['group'];
if (isset($config->location_connection[$location])) if (isset($config->group_connection[$group]))
{ {
return $config->location_connection[$location]; return $config->group_connection[$group];
} }
return Database::$default; return Database::$default;

View file

@ -14,7 +14,7 @@ class Minion_Migration_Exception extends Kohana_Exception {
public function __construct($message, array $migration, array $variables = array(), $code = 0) public function __construct($message, array $migration, array $variables = array(), $code = 0)
{ {
$variables[':migration-id'] = $migration['id']; $variables[':migration-id'] = $migration['id'];
$variables[':migration-location'] = $migration['location']; $variables[':migration-group'] = $migration['group'];
$this->_migration = $migration; $this->_migration = $migration;

View file

@ -116,49 +116,49 @@ class Minion_Migration_Manager {
} }
/** /**
* Run migrations in the specified locations so as to reach specified targets * Run migrations in the specified groups so as to reach specified targets
* *
* There are three methods for specifying target versions: * There are three methods for specifying target versions:
* *
* 1. Pass them in with the array of locations, i.e. * 1. Pass them in with the array of groups, i.e.
* *
* array( * array(
* location => target_version * group => target_version
* ) * )
* *
* 2. Pass them in separately, with param1 containing an array of * 2. Pass them in separately, with param1 containing an array of
* locations like: * groups like:
* *
* array( * array(
* location, * group,
* location2, * group2,
* ) * )
* *
* And param2 containing an array structured in the same way as in #1 * And param2 containing an array structured in the same way as in #1
* *
* 3. Perform a mix of the above two methods * 3. Perform a mix of the above two methods
* *
* It may seem odd to use two arrays to specify locations and versions, but * It may seem odd to use two arrays to specify groups and versions, but
* it's this way to allow users to upgrade / downgrade all locations while * it's this way to allow users to upgrade / downgrade all groups while
* migrating a specific location to a specific version * migrating a specific group to a specific version
* *
* If no locations are specified then migrations from all locations will be * If no groups are specified then migrations from all groups will be
* run and be brought up to the latest available version * run and be brought up to the latest available version
* *
* @param array Set of locations to update, empty array means all * @param array Set of groups to update, empty array means all
* @param array Versions for specified locations * @param array Versions for specified groups
* @param boolean The default direction (up/down) for migrations without a specific version * @param boolean The default direction (up/down) for migrations without a specific version
* @return array Array of all migrations that were successfully applied * @return array Array of all migrations that were successfully applied
*/ */
public function run_migration(array $locations = array(), $versions = array(), $default_direction = TRUE) public function run_migration(array $groups = array(), $versions = array(), $default_direction = TRUE)
{ {
$migrations = $this->_model->fetch_required_migrations($locations, $versions, $default_direction); $migrations = $this->_model->fetch_required_migrations($groups, $versions, $default_direction);
foreach ($migrations as $path => $location) foreach ($migrations as $path => $group)
{ {
$method = $location['direction'] ? 'up' : 'down'; $method = $group['direction'] ? 'up' : 'down';
foreach ($location['migrations'] as $migration) foreach ($group['migrations'] as $migration)
{ {
$filename = Minion_Migration_Util::get_filename_from_migration($migration); $filename = Minion_Migration_Util::get_filename_from_migration($migration);
@ -198,7 +198,7 @@ class Minion_Migration_Manager {
} }
else else
{ {
$this->_model->mark_migration($migration, $location['direction']); $this->_model->mark_migration($migration, $group['direction']);
} }
$this->_executed_migrations[] = $migration; $this->_executed_migrations[] = $migration;

View file

@ -30,7 +30,7 @@ class Minion_Migration_Util {
{ {
$migration = Minion_Migration_Util::get_migration_from_filename($file); $migration = Minion_Migration_Util::get_migration_from_filename($file);
$migrations[$migration['location'].':'.$migration['timestamp']] = $migration; $migrations[$migration['group'].':'.$migration['timestamp']] = $migration;
} }
} }
@ -43,10 +43,10 @@ class Minion_Migration_Util {
* Returns an array like: * Returns an array like:
* *
* array( * array(
* 'location' => 'mylocation', * 'group' => 'mygroup',
* 'timestamp' => '1293214439', * 'timestamp' => '1293214439',
* 'description' => 'initial-setup', * 'description' => 'initial-setup',
* 'id' => 'mylocation:1293214439' * 'id' => 'mygroup:1293214439'
* ); * );
* *
* @param string The migration's filename * @param string The migration's filename
@ -57,33 +57,33 @@ class Minion_Migration_Util {
$migration = array(); $migration = array();
// Get rid of the file's "migrations/" prefix, the file extension and then // Get rid of the file's "migrations/" prefix, the file extension and then
// the filename itself. The "location" is essentially a slash delimited // the filename itself. The "group" is essentially a slash delimited
// path from the migrations folder to the migration file // path from the migrations folder to the migration file
$migration['location'] = dirname(substr($file, 11, -strlen(EXT))); $migration['group'] = dirname(substr($file, 11, -strlen(EXT)));
list($migration['timestamp'], $migration['description']) list($migration['timestamp'], $migration['description'])
= explode('_', basename($file, EXT), 2); = explode('_', basename($file, EXT), 2);
$migration['id'] = $migration['location'].':'.$migration['timestamp']; $migration['id'] = $migration['group'].':'.$migration['timestamp'];
return $migration; return $migration;
} }
/** /**
* Gets a migration file from its timestamp, description and location * Gets a migration file from its timestamp, description and group
* *
* @param integer|array The migration's ID or an array of timestamp, description * @param integer|array The migration's ID or an array of timestamp, description
* @param string The migration location * @param string The migration group
* @return string Path to the migration file * @return string Path to the migration file
*/ */
public static function get_filename_from_migration(array $migration) public static function get_filename_from_migration(array $migration)
{ {
$location = $migration['location']; $group = $migration['group'];
$migration = $migration['timestamp'].'_'.$migration['description']; $migration = $migration['timestamp'].'_'.$migration['description'];
$location = ( ! empty($location)) ? (rtrim($location, '/').'/') : ''; $group = ( ! empty($group)) ? (rtrim($group, '/').'/') : '';
return $location.$migration.EXT; return $group.$migration.EXT;
} }
/** /**
@ -101,7 +101,7 @@ class Minion_Migration_Util {
} }
else else
{ {
$migration = str_replace('/', ' ', $migration['location']).'_'.$migration['timestamp']; $migration = str_replace('/', ' ', $migration['group']).'_'.$migration['timestamp'];
} }
return 'Migration_'.str_replace(array(' ', '-'), '_', ucwords($migration)); return 'Migration_'.str_replace(array(' ', '-'), '_', ucwords($migration));

View file

@ -5,15 +5,15 @@
* *
* Available config options are: * Available config options are:
* *
* --location=path/to/migration/location * --group=path/to/migration/group
* *
* This is a required config option, use it specify in which location the * This is a required config option, use it specify in which group the
* migration should be stored. Due to the nature of the cascading filesystem * migration should be stored. Due to the nature of the cascading filesystem
* minion doesn't automatically know where a migration is stored so make sure * minion doesn't automatically know where a migration is stored so make sure
* you pass in the full path to your migrations folder, e.g. * you pass in the full path to your migrations folder, e.g.
* *
* # The location of the migrations folder is modules/myapp/migrations/myapp/ * # The group of the migrations folder is modules/myapp/migrations/myapp/
* --location=modules/myapp/migrations/myapp/ * --group=modules/myapp/migrations/myapp/
* *
* On nix based systems you should be able to tab complete the path * On nix based systems you should be able to tab complete the path
* *
@ -34,7 +34,7 @@ class Minion_Task_Db_Generate extends Minion_Task
* @var array * @var array
*/ */
protected $_config = array( protected $_config = array(
'location', 'group',
'description' 'description'
); );
@ -45,19 +45,19 @@ class Minion_Task_Db_Generate extends Minion_Task
*/ */
public function execute(array $config) public function execute(array $config)
{ {
if (empty($config['location']) OR empty($config['description'])) if (empty($config['group']) OR empty($config['description']))
{ {
return 'Please provide --location and --description'.PHP_EOL. return 'Please provide --group and --description'.PHP_EOL.
'See help for more info'.PHP_EOL; 'See help for more info'.PHP_EOL;
} }
$location = rtrim(realpath($config['location']), '/').'/'; $group = rtrim(realpath($config['group']), '/').'/';
$description = $config['description']; $description = $config['description'];
// {year}{month}{day}{hour}{minute}{second} // {year}{month}{day}{hour}{minute}{second}
$time = date('YmdHis'); $time = date('YmdHis');
$class = $this->_generate_classname($location, $time); $class = $this->_generate_classname($group, $time);
$file = $this->_generate_filename($location, $time, $description); $file = $this->_generate_filename($group, $time, $description);
$data = Kohana::FILE_SECURITY.View::factory('minion/task/db/generate/template') $data = Kohana::FILE_SECURITY.View::factory('minion/task/db/generate/template')
@ -71,20 +71,20 @@ class Minion_Task_Db_Generate extends Minion_Task
} }
/** /**
* Generate a class name from the location * Generate a class name from the group
* *
* @param string location * @param string group
* @param string Timestamp * @param string Timestamp
* @return string Class name * @return string Class name
*/ */
protected function _generate_classname($location, $time) protected function _generate_classname($group, $time)
{ {
// Chop up everything up until the relative path // Chop up everything up until the relative path
$location = substr($location, strrpos($location, 'migrations/') + 11); $group = substr($group, strrpos($group, 'migrations/') + 11);
$class = ucwords(str_replace('/', ' ', $location)); $class = ucwords(str_replace('/', ' ', $group));
// If location is empty then we want to avoid double underscore in the // If group is empty then we want to avoid double underscore in the
// class name // class name
if ( ! empty($class)) if ( ! empty($class))
{ {
@ -97,18 +97,18 @@ class Minion_Task_Db_Generate extends Minion_Task
} }
/** /**
* Generates a filename from the location, time and description * Generates a filename from the group, time and description
* *
* @param string Location to store migration * @param string Location to store migration
* @param string Timestamp * @param string Timestamp
* @param string Description * @param string Description
* @return string Filename * @return string Filename
*/ */
public function _generate_filename($location, $time, $description) public function _generate_filename($group, $time, $description)
{ {
$description = substr(strtolower($description), 0, 100); $description = substr(strtolower($description), 0, 100);
return $location.$time.'_'.preg_replace('~[^a-z]+~', '-', $description).EXT; return $group.$time.'_'.preg_replace('~[^a-z]+~', '-', $description).EXT;
} }
} }

View file

@ -7,10 +7,10 @@
* *
* Available config options are: * Available config options are:
* *
* --versions=[location:]version * --versions=[group:]version
* *
* Used to specify the version to migrate the database to. The location prefix * Used to specify the version to migrate the database to. The group prefix
* is used to specify the target version of an individual location. Version * is used to specify the target version of an individual group. Version
* specifies the target version, which can be either: * specifies the target version, which can be either:
* *
* * A migration version (migrates up/down to that version) * * A migration version (migrates up/down to that version)
@ -19,16 +19,16 @@
* *
* An example of a migration version is 20101229015800 * An example of a migration version is 20101229015800
* *
* If you specify TRUE / FALSE without a location then the default migration * If you specify TRUE / FALSE without a group then the default migration
* direction for locations without a specified version will be up / down respectively. * direction for groups without a specified version will be up / down respectively.
* *
* If you're only specifying a migration version then you *must* specify a location * If you're only specifying a migration version then you *must* specify a group
* *
* --locations=location[,location2[,location3...]] * --groups=group[,group2[,group3...]]
* *
* A list of locations (under the migrations folder in the cascading * A list of groups (under the migrations folder in the cascading
* filesystem) that will be used to source migration files. By default * filesystem) that will be used to source migration files. By default
* migrations will be loaded from all available locations * migrations will be loaded from all available groups
* *
* --dry-run * --dry-run
* *
@ -57,7 +57,7 @@ class Minion_Task_Db_Migrate extends Minion_Task
*/ */
protected $_config = array( protected $_config = array(
'versions', 'versions',
'locations', 'groups',
'dry-run', 'dry-run',
'quiet' 'quiet'
); );
@ -72,13 +72,13 @@ class Minion_Task_Db_Migrate extends Minion_Task
$k_config = Kohana::config('minion/migration'); $k_config = Kohana::config('minion/migration');
// Grab user input, using sensible defaults // Grab user input, using sensible defaults
$specified_locations = Arr::get($config, 'locations', NULL); $specified_groups = Arr::get($config, 'groups', NULL);
$versions = Arr::get($config, 'versions', NULL); $versions = Arr::get($config, 'versions', NULL);
$dry_run = array_key_exists('dry-run', $config); $dry_run = array_key_exists('dry-run', $config);
$quiet = array_key_exists('quiet', $config); $quiet = array_key_exists('quiet', $config);
$targets = $this->_parse_target_versions($versions); $targets = $this->_parse_target_versions($versions);
$locations = $this->_parse_locations($specified_locations); $groups = $this->_parse_groups($specified_groups);
$db = Database::instance(); $db = Database::instance();
$model = new Model_Minion_Migration($db); $model = new Model_Minion_Migration($db);
@ -96,8 +96,8 @@ class Minion_Task_Db_Migrate extends Minion_Task
try try
{ {
// Run migrations for specified locations & versions // Run migrations for specified groups & versions
$manager->run_migration($locations, $targets, $this->_default_direction); $manager->run_migration($groups, $targets, $this->_default_direction);
} }
catch(Minion_Migration_Exception $e) catch(Minion_Migration_Exception $e)
{ {
@ -112,39 +112,39 @@ class Minion_Task_Db_Migrate extends Minion_Task
->set('quiet', $quiet) ->set('quiet', $quiet)
->set('dry_run_sql', $manager->get_dry_run_sql()) ->set('dry_run_sql', $manager->get_dry_run_sql())
->set('executed_migrations', $manager->get_executed_migrations()) ->set('executed_migrations', $manager->get_executed_migrations())
->set('location_versions', $model->fetch_current_versions()); ->set('group_versions', $model->fetch_current_versions());
return $view; return $view;
} }
/** /**
* Parses a comma delimited set of locations and returns an array of them * Parses a comma delimited set of groups and returns an array of them
* *
* @param string Comma delimited string of locations * @param string Comma delimited string of groups
* @return array Locations * @return array Locations
*/ */
protected function _parse_locations($location) protected function _parse_groups($group)
{ {
if (is_array($location)) if (is_array($group))
return $location; return $group;
$location = trim($location); $group = trim($group);
if (empty($location)) if (empty($group))
return array(); return array();
$locations = array(); $groups = array();
$location = explode(',', trim($location, ',')); $group = explode(',', trim($group, ','));
if ( ! empty($location)) if ( ! empty($group))
{ {
foreach ($location as $a_location) foreach ($group as $a_group)
{ {
$locations[] = trim($a_location, '/'); $groups[] = trim($a_group, '/');
} }
} }
return $locations; return $groups;
} }
/** /**
@ -156,7 +156,7 @@ class Minion_Task_Db_Migrate extends Minion_Task
* *
* FALSE * FALSE
* *
* {location}:(TRUE|FALSE|{migration_id}) * {group}:(TRUE|FALSE|{migration_id})
* *
* @param string Target version(s) specified by user * @param string Target version(s) specified by user
* @return array Versions * @return array Versions
@ -179,9 +179,9 @@ class Minion_Task_Db_Migrate extends Minion_Task
if (is_array($target)) if (is_array($target))
{ {
list($location, $version) = $target; list($group, $version) = $target;
$targets[$location] = $version; $targets[$group] = $version;
} }
else else
{ {

View file

@ -1,7 +1,7 @@
<?php defined('SYSPATH') or die('No direct script access.'); <?php defined('SYSPATH') or die('No direct script access.');
/** /**
* Displays the current status of migrations in all locations * Displays the current status of migrations in all groups
* *
* This task takes no config options * This task takes no config options
* *
@ -21,7 +21,7 @@ class Minion_Task_Db_Status extends Minion_Task {
$view = new View('minion/task/db/status'); $view = new View('minion/task/db/status');
$view->locations = $model->get_location_statuses(); $view->groups = $model->get_group_statuses();
echo $view; echo $view;
} }

View file

@ -59,27 +59,27 @@ class Model_Minion_Migration extends Model
} }
/** /**
* Gets the status of all locations, whether they're in the db or not. * Gets the status of all groups, whether they're in the db or not.
* *
* @return array * @return array
*/ */
public function get_location_statuses() public function get_group_statuses()
{ {
// Start out using all the installed locations // Start out using all the installed groups
$locations = $this->fetch_current_versions('location', 'id'); $groups = $this->fetch_current_versions('group', 'id');
$available = $this->available_migrations(); $available = $this->available_migrations();
foreach ($available as $migration) foreach ($available as $migration)
{ {
if (array_key_exists($migration['id'], $locations)) if (array_key_exists($migration['id'], $groups))
{ {
continue; continue;
} }
$locations[$migration['location']] = NULL; $groups[$migration['group']] = NULL;
} }
return $locations; return $groups;
} }
/** /**
@ -109,7 +109,7 @@ class Model_Minion_Migration extends Model
*/ */
protected function _select() protected function _select()
{ {
return DB::select('*', DB::expr('CONCAT(`location`, ":", CAST(`timestamp` AS CHAR)) AS `id`'))->from($this->_table); return DB::select('*', DB::expr('CONCAT(`group`, ":", CAST(`timestamp` AS CHAR)) AS `id`'))->from($this->_table);
} }
/** /**
@ -120,8 +120,8 @@ class Model_Minion_Migration extends Model
*/ */
public function add_migration(array $migration) public function add_migration(array $migration)
{ {
DB::insert($this->_table, array('timestamp', 'location', 'description')) DB::insert($this->_table, array('timestamp', 'group', 'description'))
->values(array($migration['timestamp'], $migration['location'], $migration['description'])) ->values(array($migration['timestamp'], $migration['group'], $migration['description']))
->execute($this->_db); ->execute($this->_db);
return $this; return $this;
@ -133,21 +133,21 @@ class Model_Minion_Migration extends Model
* @param string Migration ID * @param string Migration ID
* @return array Migration info * @return array Migration info
*/ */
public function get_migration($location, $timestamp = NULL) public function get_migration($group, $timestamp = NULL)
{ {
if ($timestamp === NULL) if ($timestamp === NULL)
{ {
if (empty($location) OR strpos(':', $location) === FALSE) if (empty($group) OR strpos(':', $group) === FALSE)
{ {
throw new Kohana_Exception('Invalid migration id :id', array(':id' => $location)); throw new Kohana_Exception('Invalid migration id :id', array(':id' => $group));
} }
list($location, $timestamp) = explode(':', $location); list($group, $timestamp) = explode(':', $group);
} }
return $this->_select() return $this->_select()
->where('timestamp', '=', (string) $timestamp) ->where('timestamp', '=', (string) $timestamp)
->where('location', '=', (string) $location) ->where('group', '=', (string) $group)
->execute($this->_db) ->execute($this->_db)
->current(); ->current();
} }
@ -163,16 +163,16 @@ class Model_Minion_Migration extends Model
if (is_array($migration)) if (is_array($migration))
{ {
$timestamp = $migration['timestamp']; $timestamp = $migration['timestamp'];
$location = $migration['location']; $group = $migration['group'];
} }
else else
{ {
list($timestamp, $location) = explode(':', $migration); list($timestamp, $group) = explode(':', $migration);
} }
DB::delete($this->_table) DB::delete($this->_table)
->where('timestamp', '=', $timestamp) ->where('timestamp', '=', $timestamp)
->where('location', '=', $location) ->where('group', '=', $group)
->execute($this->_db); ->execute($this->_db);
return $this; return $this;
@ -202,7 +202,7 @@ class Model_Minion_Migration extends Model
DB::update($this->_table) DB::update($this->_table)
->set($set) ->set($set)
->where('timestamp', '=', $current['timestamp']) ->where('timestamp', '=', $current['timestamp'])
->where('location', '=', $current['location']) ->where('group', '=', $current['group'])
->execute($this->_db); ->execute($this->_db);
} }
@ -221,7 +221,7 @@ class Model_Minion_Migration extends Model
DB::update($this->_table) DB::update($this->_table)
->set(array('applied' => (int) $applied)) ->set(array('applied' => (int) $applied))
->where('timestamp', '=', $migration['timestamp']) ->where('timestamp', '=', $migration['timestamp'])
->where('location', '=', $migration['location']) ->where('group', '=', $migration['group'])
->execute($this->_db); ->execute($this->_db);
return $this; return $this;
@ -240,14 +240,14 @@ class Model_Minion_Migration extends Model
} }
/** /**
* Fetches the latest version for all installed locations * Fetches the latest version for all installed groups
* *
* If a location does not have any applied migrations then no result will be * If a group does not have any applied migrations then no result will be
* returned for it * returned for it
* *
* @return Kohana_Database_Result * @return Kohana_Database_Result
*/ */
public function fetch_current_versions($key = 'location', $value = NULL) public function fetch_current_versions($key = 'group', $value = NULL)
{ {
// Little hack needed to do an order by before a group by // Little hack needed to do an order by before a group by
return DB::select() return DB::select()
@ -257,176 +257,89 @@ class Model_Minion_Migration extends Model
->order_by('timestamp', 'DESC'), ->order_by('timestamp', 'DESC'),
'temp_table' 'temp_table'
)) ))
->group_by('location') ->group_by('group')
->execute($this->_db) ->execute($this->_db)
->as_array($key, $value); ->as_array($key, $value);
} }
/** /**
* Fetches a list of locations * Fetches a list of groups
* *
* @return array * @return array
*/ */
public function fetch_locations($location_as_key = FALSE) public function fetch_groups($group_as_key = FALSE)
{ {
return DB::select() return DB::select()
->from($this->_table) ->from($this->_table)
->group_by('location') ->group_by('group')
->execute($this->_db) ->execute($this->_db)
->as_array($location_as_key ? 'location' : NULL, 'location'); ->as_array($group_as_key ? 'group' : NULL, 'group');
} }
/** /**
* Fetch a list of migrations that need to be applied in order to reach the * Fetch a list of migrations that need to be applied in order to reach the
* required version * required version
* *
* @param string Migration's location * @param string The groups to get migrations for
* @param string Target migration id * @param mixed Target version
* @param boolean Default direction of versionless migrations
*/ */
public function fetch_required_migrations($locations = NULL, $target_version = TRUE, $default_direction = TRUE) public function fetch_required_migrations($group = NULL, $target = TRUE)
{ {
if ( ! empty($locations) AND ! is_array($locations)) $migrations = array();
{
$locations = array(
$locations => is_array($target_version)
? $default_direction
: $target_version
);
}
// Get an array of the latest migrations, with the location name as the $query = $this->_select();
// array key
$migrations = $this->fetch_current_versions('location');
// The user wants to run all available migrations if (is_bool($target))
if (empty($locations))
{ {
// Fetch a mirrored array of locations => locations // If we want to limit this migration to certain groups
$locations = $this->fetch_locations(TRUE); if ($group !== NULL)
}
// If the calling script has been lazy and given us a numerically
// indexed array of locations then we need to convert it to a mirrored
// array
// We will decide the target version for these within the loop below
elseif ( ! Arr::is_assoc($locations))
{
foreach ($locations as $_pos => $location)
{ {
unset($locations[$_pos]); if (is_array($group))
$locations[$location] = $location;
}
}
// Merge locations with specified target versions
if ( ! empty($target_version) AND is_array($target_version))
{
$locations = $target_version + $locations;
}
$migrations_to_apply = array();
// What follows is a bit of icky code, but there aren't many "nice" ways around it
// Basically we need to get a list of migrations that need to be performed, but
// the ordering of the migrations varies depending on whether we're wanting to
// migrate up or migrate down. As such, we can't just apply a generic "order by x"
// condition, we have to run an individual query for each location
// Again, icky, but this appears to be the only "sane" way of doing it with multiple
// locations
// If you have a better way of doing this, please let me know :)
foreach ($locations as $location => $target)
{
// By default all migrations go "up"
$migrations_to_apply[$location]['direction'] = TRUE;
$migrations_to_apply[$location]['migrations'] = array();
$query = $this->_select()->where('location', '=', $location);
// If this migration was auto-selected from the db then use the
// default migration direction
if ($target === $location)
{
$target = is_bool($target_version)
? $target_version
: (bool) $default_direction;
}
// If the user is rolling this location to either extreme up or
// extreme down
if (is_bool($target))
{
// We're "undoing" all applied migrations, i.e. rolling back
if ($target === FALSE)
{ {
$migrations_to_apply[$location]['direction'] = FALSE; $query->where('group', 'IN', $group);
$query
->where('applied', '=', 1)
->order_by('timestamp', 'DESC');
} }
// We're rolling forward
else else
{ {
$query $query->where('group', '=', $group);
->where('applied', '=', 0)
->order_by('timestamp', 'ASC');
} }
} }
// Else if the user explicitly specified a target version of some kind
// If we're migrating up
if ($target === TRUE)
{
$query
->where('applied', '=', 0)
->order_by('timestamp', 'ASC');
}
// If we're migrating down
else else
{ {
$timestamp = $target; $query
$current_timestamp = isset($migrations[$location]) ->where('applied', '>', 0)
? $migrations[$location]['timestamp'] ->order_by('timestamp', 'DESC');
: NULL;
// If the current version is the requested version then nothing
// needs to be done
if ($current_timestamp === $timestamp)
{
continue;
}
// If they haven't applied any migrations for this location
// yet and are justwhere wanting to apply all migrations
// (i.e. roll forward)
if ($current_timestamp === NULL)
{
$query
->and_where('timestamp', '<=', $timestamp)
->order_by('timestamp', 'ASC');
}
// If we need to move forward
elseif ($timestamp > $current_timestamp)
{
$query
->and_where('timestamp', '<=', $timestamp)
->and_where('applied', '=', 0)
->order_by('timestamp', 'ASC');
}
// If we want to roll back
elseif ($timestamp < $current_timestamp)
{
$query
->and_where('timestamp', '<', $current_timestamp)
->and_where('timestamp', '>', $timestamp)
->and_where('applied', '=', 1)
->order_by('timestamp', 'DESC');
$migrations_to_apply[$location]['direction'] = FALSE;
}
} }
}
else
{
list($target, $up) = $this->resolve_target($group, $target);
$migrations_to_apply[$location]['migrations'] = $query->execute($this->_db)->as_array(); $query->where('group', '=', $group);
unset($query); if ($up)
{
$query
->where('timestamp', '<=', $target)
->where('applied', '=', 0)
->order_by('timestamp', 'ASC');
}
else
{
$query
->where('timestamp', '>', $target)
->order_by('timestamp', 'DESC');
}
} }
return $migrations_to_apply; return $query->execute($this->_db)->as_array();
} }
} }

View file

@ -2,8 +2,8 @@
return array( return array(
// A mapping of location_connections => db_connection to use // A mapping of group_connections => db_connection to use
'location_connection' => array( 'group_connection' => array(
), ),
); );

View file

@ -1,8 +1,8 @@
CREATE TABLE `minion_migrations` ( CREATE TABLE `minion_migrations` (
`timestamp` varchar(14) NOT NULL, `timestamp` varchar(14) NOT NULL,
`description` varchar(100) NOT NULL, `description` varchar(100) NOT NULL,
`location` varchar(100) NOT NULL, `group` varchar(100) NOT NULL,
`applied` tinyint(1) DEFAULT '0', `applied` tinyint(1) DEFAULT '0',
PRIMARY KEY (`timestamp`,`location`), PRIMARY KEY (`timestamp`,`group`),
UNIQUE KEY `MIGRATION_ID` (`timestamp`,`description`) UNIQUE KEY `MIGRATION_ID` (`timestamp`,`description`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1; ) ENGINE=MyISAM DEFAULT CHARSET=latin1;

View file

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<dataset> <dataset>
<test_minion_migrations timestamp="20101215164400" description="create-tables" location="app" applied="1" /> <test_minion_migrations timestamp="20101215164400" description="create-tables" group="app" applied="1" />
<test_minion_migrations timestamp="20101215165000" description="add-name-column-to-members" location="app" applied="0" /> <test_minion_migrations timestamp="20101215165000" description="add-name-column-to-members" group="app" applied="0" />
<test_minion_migrations timestamp="20101216000000" description="add-index-on-name" location="app" applied="0" /> <test_minion_migrations timestamp="20101216000000" description="add-index-on-name" group="app" applied="0" />
<test_minion_migrations timestamp="20101216080000" description="remove-password-salt-column" location="app" applied="1" /> <test_minion_migrations timestamp="20101216080000" description="remove-password-salt-column" group="app" applied="1" />
<test_minion_migrations timestamp="20101215164500" description="create-table" location="dblogger" applied="1" /> <test_minion_migrations timestamp="20101215164500" description="create-table" group="dblogger" applied="1" />
<test_minion_migrations timestamp="20101225000000" description="remove-unique-index" location="dblogger" applied="1" /> <test_minion_migrations timestamp="20101225000000" description="remove-unique-index" group="dblogger" applied="1" />
<test_minion_migrations timestamp="20101226112100" description="add-pk" location="dblogger" applied="0" /> <test_minion_migrations timestamp="20101226112100" description="add-pk" group="dblogger" applied="0" />
</dataset> </dataset>

View file

@ -39,7 +39,7 @@ class Minion_Migration_ModelTest extends Kohana_Unittest_Database_TestCase
public function getDataSet() public function getDataSet()
{ {
return $this->createFlatXMLDataSet( return $this->createFlatXMLDataSet(
Kohana::find_file('tests/test_data', 'minion/migration/model', 'xml') Kohana::find_file('tests/datasets', 'minion/migration/model', 'xml')
); );
} }
@ -78,7 +78,7 @@ class Minion_Migration_ModelTest extends Kohana_Unittest_Database_TestCase
public function test_fetch_current_versions() public function test_fetch_current_versions()
{ {
$versions = $this->getModel() $versions = $this->getModel()
->fetch_current_versions('location', 'timestamp'); ->fetch_current_versions('group', 'timestamp');
$this->assertSame( $this->assertSame(
array ( array (
@ -89,120 +89,6 @@ class Minion_Migration_ModelTest extends Kohana_Unittest_Database_TestCase
); );
} }
/**
* Provides test data for test_fetch_required_migrations
*
* @return array Test data
*/
public function provider_fetch_required_migrations()
{
return array(
// Test a call with no params (i.e. upgrade everything to latest)
array(
array (
'app' => array(
'direction' => true,
'migrations' => array(
array (
'timestamp' => '20101215165000',
'description' => 'add-name-column-to-members',
'location' => 'app',
'applied' => '0',
'id' => 'app:20101215165000'
),
array (
'timestamp' => '20101216000000',
'description' => 'add-index-on-name',
'location' => 'app',
'applied' => '0',
'id' => 'app:20101216000000'
),
),
),
'dblogger' => array(
'direction' => true,
'migrations' => array(
array (
'timestamp' => '20101226112100',
'description' => 'add-pk',
'location' => 'dblogger',
'applied' => '0',
'id' => 'dblogger:20101226112100'
),
),
),
),
NULL,
TRUE,
TRUE
),
array(
array(
'app' => array(
'direction' => FALSE,
'migrations' => array(
array(
'timestamp' => '20101216080000',
'description' => 'remove-password-salt-column',
'location' => 'app',
'applied' => '1',
'id' => 'app:20101216080000'
),
array(
'timestamp' => '20101215164400',
'description' => 'create-tables',
'location' => 'app',
'applied' => '1',
'id' => 'app:20101215164400'
),
)
),
'dblogger' => array(
'direction' => FALSE,
'migrations' => array(
array(
'timestamp' => '20101225000000',
'description' => 'remove-unique-index',
'location' => 'dblogger',
'applied' => '1',
'id' => 'dblogger:20101225000000'
),
array(
'timestamp' => '20101215164500',
'description' => 'create-table',
'location' => 'dblogger',
'applied' => '1',
'id' => 'dblogger:20101215164500'
),
)
),
),
NULL,
FALSE,
TRUE
),
);
}
/**
* Tests that fetch_required_migrations() produces an accurate list of
* migrations that need applying.
*
* @test
* @covers Model_Minion_Migration::fetch_required_migrations
* @dataProvider provider_fetch_required_migrations
* @param array Expected output
* @param NULL|string|array Input Locations
* @param bool|string|array Input Target
* @param bool Input default direction
*/
public function test_fetch_required_migrations($expected, $locations, $target, $default_direction)
{
$results = $this->getModel()
->fetch_required_migrations($locations, $target, $default_direction);
$this->assertSame($expected, $results);
}
/** /**
* Provides test data for test_get_migration * Provides test data for test_get_migration
@ -216,7 +102,7 @@ class Minion_Migration_ModelTest extends Kohana_Unittest_Database_TestCase
array( array(
'timestamp' => '20101215164400', 'timestamp' => '20101215164400',
'description' => 'create-tables', 'description' => 'create-tables',
'location' => 'app', 'group' => 'app',
'applied' => '1', 'applied' => '1',
'id' => 'app:20101215164400' 'id' => 'app:20101215164400'
), ),
@ -234,14 +120,14 @@ class Minion_Migration_ModelTest extends Kohana_Unittest_Database_TestCase
* @covers Model_Minion_Migration::get_migration * @covers Model_Minion_Migration::get_migration
* @dataProvider provider_get_migration * @dataProvider provider_get_migration
* @param array Expected migration * @param array Expected migration
* @param string The migration's location * @param string The migration's group
* @param string The migration's timestamp * @param string The migration's timestamp
*/ */
public function test_get_migration($expected, $location, $timestamp) public function test_get_migration($expected, $group, $timestamp)
{ {
$this->assertSame( $this->assertSame(
$expected, $expected,
$this->getModel()->get_migration($location, $timestamp) $this->getModel()->get_migration($group, $timestamp)
); );
} }
@ -267,9 +153,9 @@ class Minion_Migration_ModelTest extends Kohana_Unittest_Database_TestCase
* @dataProvider provider_get_migration_throws_exception_on_invalid_input * @dataProvider provider_get_migration_throws_exception_on_invalid_input
* @expectedException Kohana_Exception * @expectedException Kohana_Exception
*/ */
public function test_get_migration_throws_exception_on_invalid_input($location, $timestamp) public function test_get_migration_throws_exception_on_invalid_input($group, $timestamp)
{ {
$this->getModel()->get_migration($location, $timestamp); $this->getModel()->get_migration($group, $timestamp);
} }
/** /**
@ -284,13 +170,13 @@ class Minion_Migration_ModelTest extends Kohana_Unittest_Database_TestCase
array( array(
'timestamp' => '20101215165000', 'timestamp' => '20101215165000',
'description' => 'add-name-column-to-members', 'description' => 'add-name-column-to-members',
'location' => 'app', 'group' => 'app',
'applied' => '1', 'applied' => '1',
'id' => 'app:20101215165000', 'id' => 'app:20101215165000',
), ),
array( array(
'timestamp' => '20101215165000', 'timestamp' => '20101215165000',
'location' => 'app', 'group' => 'app',
'description' => 'add-name-column-to-members', 'description' => 'add-name-column-to-members',
), ),
TRUE TRUE
@ -299,13 +185,13 @@ class Minion_Migration_ModelTest extends Kohana_Unittest_Database_TestCase
array( array(
'timestamp' => '20101215165000', 'timestamp' => '20101215165000',
'description' => 'add-name-column-to-members', 'description' => 'add-name-column-to-members',
'location' => 'app', 'group' => 'app',
'applied' => '0', 'applied' => '0',
'id' => 'app:20101215165000', 'id' => 'app:20101215165000',
), ),
array( array(
'timestamp' => '20101215165000', 'timestamp' => '20101215165000',
'location' => 'app', 'group' => 'app',
'description' => 'add-name-column-to-members', 'description' => 'add-name-column-to-members',
), ),
FALSE FALSE
@ -332,7 +218,7 @@ class Minion_Migration_ModelTest extends Kohana_Unittest_Database_TestCase
$this->assertSame( $this->assertSame(
$expected, $expected,
$model->get_migration($migration['location'], $migration['timestamp']) $model->get_migration($migration['group'], $migration['timestamp'])
); );
} }
} }

View file

@ -19,15 +19,15 @@ class Minion_Migration_UtilTest extends Kohana_Unittest_TestCase {
return array( return array(
array( array(
array( array(
'myapp:015151051' => array('location' => 'myapp', 'description' => 'setup', 'timestamp' => '015151051', 'id' => 'myapp:015151051'), 'myapp:015151051' => array('group' => 'myapp', 'description' => 'setup', 'timestamp' => '015151051', 'id' => 'myapp:015151051'),
'myapp:015161051' => array('location' => 'myapp', 'description' => 'add-comments', 'timestamp' => '015161051', 'id' => 'myapp:015161051'), 'myapp:015161051' => array('group' => 'myapp', 'description' => 'add-comments', 'timestamp' => '015161051', 'id' => 'myapp:015161051'),
), ),
array( array(
'migrations/myapp' => array( 'migrations/myapp' => array(
'migrations/myapp/015151051_setup.php' 'migrations/myapp/015151051_setup.php'
=> '/var/www/app/locations/myapp/migrations/myapp/015151051_setup.php', => '/var/www/app/groups/myapp/migrations/myapp/015151051_setup.php',
'migrations/myapp/015161051_add-comments.php' 'migrations/myapp/015161051_add-comments.php'
=> '/var/www/app/locations/myapp/migrations/myapp/015161051_add-comments.php', => '/var/www/app/groups/myapp/migrations/myapp/015161051_add-comments.php',
), ),
) )
), ),
@ -62,7 +62,7 @@ class Minion_Migration_UtilTest extends Kohana_Unittest_TestCase {
return array( return array(
array( array(
array( array(
'location' => 'myapp', 'group' => 'myapp',
'description' => 'initial-setup', 'description' => 'initial-setup',
'timestamp' => '1293214439', 'timestamp' => '1293214439',
'id' => 'myapp:1293214439', 'id' => 'myapp:1293214439',
@ -101,7 +101,7 @@ class Minion_Migration_UtilTest extends Kohana_Unittest_TestCase {
array( array(
'myapp/1293214439_initial-setup.php', 'myapp/1293214439_initial-setup.php',
array( array(
'location' => 'myapp', 'group' => 'myapp',
'timestamp' => '1293214439', 'timestamp' => '1293214439',
'description' => 'initial-setup', 'description' => 'initial-setup',
'id' => 'myapp:1293214439' 'id' => 'myapp:1293214439'
@ -120,13 +120,13 @@ class Minion_Migration_UtilTest extends Kohana_Unittest_TestCase {
* @dataProvider provider_get_filename_from_migration * @dataProvider provider_get_filename_from_migration
* @param string Expected output * @param string Expected output
* @param mixed Migration id * @param mixed Migration id
* @param mixed location * @param mixed group
*/ */
public function test_get_filename_from_migration($expected, $migration, $location) public function test_get_filename_from_migration($expected, $migration, $group)
{ {
$this->assertSame( $this->assertSame(
$expected, $expected,
Minion_Migration_Util::get_filename_from_migration($migration, $location) Minion_Migration_Util::get_filename_from_migration($migration, $group)
); );
} }
@ -144,7 +144,7 @@ class Minion_Migration_UtilTest extends Kohana_Unittest_TestCase {
), ),
array( array(
'Migration_Kohana_201012290258', 'Migration_Kohana_201012290258',
array('location' => 'kohana', 'timestamp' => '201012290258'), array('group' => 'kohana', 'timestamp' => '201012290258'),
), ),
); );
} }

View file

@ -1,21 +1,21 @@
<?php if( ! $quiet): ?> <?php if( ! $quiet): ?>
Executed <?php echo count($executed_migrations); ?> migrations Executed <?php echo count($executed_migrations); ?> migrations
Current versions of locations: Current versions of groups:
<?php foreach($location_versions as $location): ?> <?php foreach($group_versions as $group): ?>
* <?php echo $location['location'] ?> : <?php echo $location['timestamp'] ?> (<?php echo $location['description']; ?>) * <?php echo $group['group'] ?> : <?php echo $group['timestamp'] ?> (<?php echo $group['description']; ?>)
<?php endforeach; ?> <?php endforeach; ?>
<?php if($dry_run): ?> <?php if($dry_run): ?>
This was a dry run, if it was a real run the following SQL would've been executed: This was a dry run, if it was a real run the following SQL would've been executed:
<?php endif; ?> <?php endif; ?>
<?php endif; ?> <?php endif; ?>
<?php foreach($dry_run_sql as $location => $migrations): ?> <?php foreach($dry_run_sql as $group => $migrations): ?>
<?php $location_padding = str_repeat('#', strlen($location)); ?> <?php $group_padding = str_repeat('#', strlen($group)); ?>
##################<?php echo $location_padding ?>## ##################<?php echo $group_padding ?>##
# Begin Location: <?php echo $location; ?> # # Begin Location: <?php echo $group; ?> #
##################<?php echo $location_padding ?>## ##################<?php echo $group_padding ?>##
<?php foreach($migrations as $timestamp => $sql): ?> <?php foreach($migrations as $timestamp => $sql): ?>
# Begin <?php echo $timestamp; ?> # Begin <?php echo $timestamp; ?>
@ -29,8 +29,8 @@ This was a dry run, if it was a real run the following SQL would've been execute
<?php endforeach; ?> <?php endforeach; ?>
################<?php echo $location_padding ?>## ################<?php echo $group_padding ?>##
# End Location: <?php echo $location; ?> # # End Location: <?php echo $group; ?> #
################<?php echo $location_padding ?>## ################<?php echo $group_padding ?>##
<?php endforeach; ?> <?php endforeach; ?>

View file

@ -1,4 +1,4 @@
<?php foreach($locations as $location => $status): ?> <?php foreach($groups as $group => $status): ?>
* <?php echo $location ?> <?php echo ($status !== NULL ? $status : 'Not installed'); ?> * <?php echo $group ?> <?php echo ($status !== NULL ? $status : 'Not installed'); ?>
<?php endforeach; ?> <?php endforeach; ?>