[Jifty-commit] jifty branch, setupwizard-refactor, updated. 39cbe3f733621f48f7c509876b351505db59e2a4
Jifty commits
jifty-commit at lists.jifty.org
Wed Jul 7 15:16:26 EDT 2010
The branch, setupwizard-refactor has been updated
via 39cbe3f733621f48f7c509876b351505db59e2a4 (commit)
from a7ab5e963c3b5b1453a5242c0c39617c684c4389 (commit)
Summary of changes:
lib/Jifty/Handle.pm | 11 +-
lib/Jifty/Schema.pm | 589 +++++++++++++++++++++++++++++++++++++++++++-
lib/Jifty/Script/Schema.pm | 553 +----------------------------------------
3 files changed, 596 insertions(+), 557 deletions(-)
mode change 100755 => 100644 lib/Jifty/Script/Schema.pm
- Log -----------------------------------------------------------------
commit 39cbe3f733621f48f7c509876b351505db59e2a4
Author: Thomas Sibley <trs at bestpractical.com>
Date: Wed Jul 7 15:15:45 2010 -0400
Extract schema management logic into Jifty::Schema
This works as is, but definitely wants some cleanup of print statements
and the like so that actions and output are separate.
diff --git a/lib/Jifty/Handle.pm b/lib/Jifty/Handle.pm
index 97109c7..dbff226 100644
--- a/lib/Jifty/Handle.pm
+++ b/lib/Jifty/Handle.pm
@@ -288,10 +288,7 @@ sub drop_database {
sub _create_original_database {
my $self = shift;
- my $hack = {};
- require Jifty::Script::Schema;
- bless $hack, "Jifty::Script::Schema";
- $hack->create_all_tables;
+ Jifty::Schema->new->create_all_tables;
# reconnect for consistency
# SQLite complains about the schema being changed
@@ -301,11 +298,7 @@ sub _create_original_database {
sub _upgrade_schema {
my $self = shift;
-
- my $hack = {};
- require Jifty::Script::Schema;
- bless $hack, "Jifty::Script::Schema";
- $hack->run_upgrades;
+ Jifty::Schema->new->run_upgrades;
}
sub _fetch_dbv {
diff --git a/lib/Jifty/Schema.pm b/lib/Jifty/Schema.pm
index 6baad86..c6238b6 100644
--- a/lib/Jifty/Schema.pm
+++ b/lib/Jifty/Schema.pm
@@ -2,7 +2,9 @@ use warnings;
use strict;
package Jifty::Schema;
-use base qw/Jifty::Object/;
+use Any::Moose;
+extends 'Jifty::Object';
+
use SQL::ReservedWords;
=head1 NAME
@@ -32,21 +34,39 @@ foreach my $dialect ( 'SQL::ReservedWords', &_sql_dialects ) {
delete $_SQL_RESERVED{ lc($_) } for (@_SQL_RESERVED_OVERRIDE);
+=head1 ATTRIBUTES
+
+=head2 flags
+
+Takes and returns a hashref which holds schema management flags. You
+shouldn't need to set these yourself unless you're doing something funky with
+the database.
+
+=cut
+
+has 'flags' => (
+ is => 'rw',
+ isa => 'HashRef',
+ default => sub { {} }
+);
+
+=head1 METHODS
+
=head2 new
Returns a new Jifty::Schema. Takes no arguments. Will automatically figure out and initialize the models defined in the app's source.
=cut
-sub new {
- my $class = shift;
- my $self = {};
- bless $self, $class;
+sub BUILD {
+ my $self = shift;
$self->_init_model_list();
- return $self;
-
}
+=head2 flags
+
+=cut
+
=head2 _init_model_list
Reads in our application class from the config file and finds all our app's models.
@@ -89,6 +109,558 @@ sub serialize_current_schema {
}
+sub setup_database {
+ my $self = shift;
+
+ $self->probe_database_existence();
+ $self->manage_database_existence();
+
+ if ( $self->flags->{'create_all_tables'} ) {
+ $self->create_all_tables();
+ } elsif ( $self->flags->{'setup_tables'} ) {
+ $self->run_upgrades();
+ }
+}
+
+=head2 probe_database_existence [NO_HANDLE]
+
+Probes our database to see if it exists and is up to date. This sets various
+L</flags> for later use.
+
+If AutoUpgrade is true in the application's config, this may cause the
+database to be automatically upgraded.
+
+Optionally takes a boolean to indicate whether or not we should bother to try
+to create an actual database handle.
+
+=cut
+
+sub probe_database_existence {
+ my $self = shift;
+ my $no_handle = 0;
+
+ if ( $self->flags->{'create_database'} or $self->flags->{'drop_database'} ) {
+ $no_handle = 1;
+ }
+
+ # Now try to connect. We trap expected errors and deal with them.
+ eval {
+ Jifty->setup_database_connection(
+ no_handle => $no_handle,
+ logger_component => 'SchemaTool',
+ );
+ };
+ my $error = $@;
+
+ if ( $error =~ /doesn't match (application schema|running jifty|running plugin) version/i
+ or $error =~ /plugin isn't installed in database/i ) {
+
+ # We found an out-of-date DB. Upgrade it
+ $self->flags->{setup_tables} = 1;
+ } elsif ( $error =~ /no version in the database/i ) {
+
+ # No version table. Assume the DB is empty.
+ $self->flags->{create_all_tables} = 1;
+ } elsif ( $error =~ /(database .*? (?:does not|doesn't) exist|unknown database)/i) {
+
+ # No database exists; we'll need to make one and fill it up
+ $self->flags->{drop_database} = 0;
+ $self->flags->{create_database} = 1;
+ $self->flags->{create_all_tables} = 1;
+ } elsif ($error) {
+
+ # Some other unexpected error; rethrow it
+ die $error;
+ }
+
+ # Setting up tables requires creating the DB if we just dropped it
+ $self->flags->{create_database} = 1
+ if $self->flags->{drop_database} and $self->flags->{setup_tables};
+
+ # Setting up tables on a just-created DB is the same as setting them all up
+ $self->flags->{create_all_tables} = 1
+ if $self->flags->{create_database} and $self->flags->{setup_tables};
+
+ # Give us some kind of handle if we don't have one by now
+ Jifty->handle( Jifty::Handle->new() ) unless Jifty->handle;
+}
+
+=head2 manage_database_existence
+
+If the user wants the database created or it doesn't exist, creates the
+database. If the user wants the old database deleted, does that too.
+
+Dies with an error message if the database drop or create fails.
+
+=cut
+
+sub manage_database_existence {
+ my $self = shift;
+
+ my $handle = $self->connect_to_db_for_management();
+
+ if ( $self->flags->{'print'} ) {
+ $handle->drop_database('print') if ( $self->flags->{'drop_database'} );
+ $handle->create_database('print') if ( $self->flags->{'create_database'} );
+ } else {
+ if ( $self->flags->{'drop_database'} ) {
+ my $ret = $handle->drop_database('execute');
+ die "Error dropping database: ". $ret->error_message
+ unless $ret or $ret->error_message =~ /database .*?(?:does not|doesn't) exist|unknown database/i;
+ }
+
+ if ( $self->flags->{'create_database'} ) {
+ my $ret = $handle->create_database('execute');
+ die "Error creating database: ". $ret->error_message unless $ret;
+ }
+
+ $handle->disconnect;
+ $self->_reinit_handle() if ( $self->flags->{'create_database'} );
+ }
+}
+
+sub _reinit_handle {
+ Jifty->handle( Jifty::Handle->new() );
+ Jifty->handle->connect();
+}
+
+=head2 create_all_tables
+
+Create all tables for this application's models. Generally, this
+happens on installation or running C<jifty schema>.
+
+=cut
+
+sub create_all_tables {
+ my $self = shift;
+
+ my $log = Log::Log4perl->get_logger("SchemaTool");
+ $log->info("Generating SQL for application @{[Jifty->app_class]}...");
+
+ my $appv
+ = version->new( Jifty->config->framework('Database')->{'Version'} );
+ my $jiftyv = version->new($Jifty::VERSION);
+
+ # Start a transaction
+ Jifty->handle->begin_transaction;
+
+ $self->create_tables_for_models( grep { $_->isa('Jifty::DBI::Record') }
+ $self->models );
+
+ # Update the versions in the database
+ Jifty::Model::Metadata->store( application_db_version => $appv );
+ Jifty::Model::Metadata->store( jifty_db_version => $jiftyv );
+
+ # For each plugin, update the plugin version
+ for my $plugin ( Jifty->plugins ) {
+ my $pluginv = version->new( $plugin->version );
+ Jifty::Model::Metadata->store(
+ ( ref $plugin ) . '_db_version' => $pluginv );
+ }
+
+ unless ( $self->flags->{'no_bootstrap'} ) {
+
+ # Load initial data
+ eval {
+ my $bootstrapper = Jifty->app_class("Bootstrap");
+ Jifty::Util->require($bootstrapper);
+ $bootstrapper->run() if $bootstrapper->can('run');
+
+ for my $plugin ( Jifty->plugins ) {
+ my $plugin_bootstrapper = $plugin->bootstrapper;
+ Jifty::Util->require($plugin_bootstrapper);
+ $plugin_bootstrapper->run() if $plugin_bootstrapper->can('run');
+ }
+ };
+ die $@ if $@;
+ }
+
+ # Commit it all
+ Jifty->handle->commit or exit 1;
+
+ Jifty::Util->require('IPC::PubSub');
+ IPC::PubSub->new(
+ JiftyDBI => (
+ db_config => Jifty->handle->{db_config},
+ table_prefix => '_jifty_pubsub_',
+ db_init => 1,
+ )
+ )->disconnect;
+ $log->info("Set up version $appv, jifty version $jiftyv");
+}
+
+=head2 create_tables_for_models TABLEs
+
+Given a list of items that are the scalar names of subclasses of Jifty::Record,
+either prints SQL or creates all those models in your database.
+
+=cut
+
+sub create_tables_for_models {
+ my $self = shift;
+ my @models = (@_);
+ my $sql = '';
+
+ my $log = Log::Log4perl->get_logger("SchemaTool");
+ my $appv
+ = version->new( Jifty->config->framework('Database')->{'Version'} );
+ my $jiftyv = version->new($Jifty::VERSION);
+
+ my %pluginvs;
+ for my $plugin ( Jifty->plugins ) {
+ my $plugin_class = ref $plugin;
+ $pluginvs{$plugin_class} = version->new( $plugin->version );
+ }
+
+MODEL:
+ for my $model (@models) {
+
+ # Skip autogenerated models; that is, those that are overridden by plugins.
+ next MODEL if Jifty::ClassLoader->autogenerated($model);
+ my $plugin_root = Jifty->app_class('Plugin') . '::';
+
+ # TODO XXX FIXME:
+ # This *will* try to generate SQL for abstract base classes you might
+ # stick in $AC::Model::.
+ if ( $model->can('since') ) {
+ my $app_class = Jifty->app_class;
+
+ my $installed_version = 0;
+
+ # Is it a Jifty core model?
+ if ( $model =~ /^Jifty::Model::/ ) {
+ $installed_version = $jiftyv;
+ }
+
+ # Is it a Jifty or application plugin model?
+ elsif ( $model =~ /^(?:Jifty::Plugin::|$plugin_root)/ ) {
+ my $plugin_class = $model;
+ $plugin_class =~ s/::Model::(.*)$//;
+
+ $installed_version = $pluginvs{$plugin_class};
+ }
+
+ # Otherwise, an application model
+ else {
+ $installed_version = $appv;
+ }
+
+ if ( $installed_version < $model->since ) {
+
+ # XXX Is this message correct?
+ $log->info(
+ "Skipping $model, as it should already be in the database"
+ );
+ next MODEL;
+ }
+ }
+
+ if ( $model =~ /^(?:Jifty::Plugin::|$plugin_root)/
+ and $model =~ /::Model::(.*)$/ )
+ {
+ my $model_name = $1;
+
+ # Check to make sure this model is not overridden in the app,
+ # in such cases we don't want to try to create the same table
+ # twice, so let the app model do it rather than the plugin
+ my $app_model = Jifty->app_class( "Model", $model_name );
+ $app_model->require;
+ next MODEL unless Jifty::ClassLoader->autogenerated($app_model);
+ }
+
+ $log->info("Using $model, as it appears to be new.");
+
+ $self->_check_reserved($model)
+ unless ( $self->flags->{'ignore_reserved'}
+ or !Jifty->config->framework('Database')->{'CheckSchema'} );
+
+ if ( $self->flags->{'print'} ) {
+ print $model->printable_table_schema;
+ } else {
+ $model->create_table_in_db;
+ }
+ }
+}
+
+=head2 run_upgrades
+
+Take the actions we need in order to bring an existing database up to current.
+
+=cut
+
+sub run_upgrades {
+ my $self = shift;
+ $self->upgrade_jifty_tables();
+ $self->upgrade_plugin_tables();
+ $self->upgrade_application_tables();
+}
+
+=head2 upgrade_jifty_tables
+
+Upgrade Jifty's internal tables.
+
+=cut
+
+sub upgrade_jifty_tables {
+ my $self = shift;
+ my $dbv = Jifty::Model::Metadata->load('jifty_db_version');
+ unless ($dbv) {
+
+ # Backwards combatibility -- it usd to be 'key' not 'data_key';
+ eval {
+ local $SIG{__WARN__} = sub { };
+ $dbv
+ = Jifty->handle->fetch_result(
+ "SELECT value FROM _jifty_metadata WHERE key = 'jifty_db_version'"
+ );
+ };
+ }
+
+ $dbv = version->new( $dbv || '0.60426' );
+ my $appv = version->new($Jifty::VERSION);
+
+ return
+ unless $self->upgrade_tables(
+ "Jifty" => $dbv,
+ $appv, "Jifty::Upgrade::Internal"
+ );
+ if ( $self->flags->{'print'} ) {
+ warn "Need to upgrade jifty_db_version to $appv here!";
+ } else {
+ Jifty::Model::Metadata->store( jifty_db_version => $appv );
+ }
+}
+
+=head2 upgrade_application_tables
+
+Upgrade the application's tables.
+
+=cut
+
+sub upgrade_application_tables {
+ my $self = shift;
+ my $dbv = version->new(
+ Jifty::Model::Metadata->load('application_db_version') );
+ my $appv
+ = version->new( Jifty->config->framework('Database')->{'Version'} );
+
+ return unless $self->upgrade_tables( Jifty->app_class, $dbv, $appv );
+ if ( $self->flags->{'print'} ) {
+ warn "Need to upgrade application_db_version to $appv here!";
+ } else {
+ Jifty::Model::Metadata->store( application_db_version => $appv );
+ }
+}
+
+=head2 upgrade_plugin_tables
+
+Upgrade the tables for each plugin.
+
+=cut
+
+sub upgrade_plugin_tables {
+ my $self = shift;
+
+ for my $plugin ( Jifty->plugins ) {
+ my $plugin_class = ref $plugin;
+
+ my $dbv
+ = Jifty::Model::Metadata->load( $plugin_class . '_db_version' );
+ my $appv = version->new( $plugin->version );
+
+ # Upgrade this plugin from dbv -> appv
+ if ( defined $dbv ) {
+ $dbv = version->new($dbv);
+
+ next
+ unless $self->upgrade_tables( $plugin_class, $dbv, $appv,
+ $plugin->upgrade_class );
+ if ( $self->flags->{'print'} ) {
+ warn
+ "Need to upgrade ${plugin_class}_db_version to $appv here!";
+ } else {
+ Jifty::Model::Metadata->store(
+ $plugin_class . '_db_version' => $appv );
+ }
+ }
+
+ # Install this plugin
+ else {
+ my $log = Log::Log4perl->get_logger("SchemaTool");
+ $log->info("Generating SQL to set up $plugin_class...");
+ Jifty->handle->begin_transaction;
+
+ # Create the tables
+ $self->create_tables_for_models(
+ grep {
+ $_->isa('Jifty::DBI::Record')
+ and /^\Q$plugin_class\E::Model::/
+ } $self->models
+ );
+
+ # Save the plugin version to the database
+ Jifty::Model::Metadata->store(
+ $plugin_class . '_db_version' => $appv )
+ unless $self->flags->{'print'};
+
+ # Run the bootstrapper for initial data
+ unless ( $self->flags->{'print'} ) {
+ eval {
+ my $bootstrapper = $plugin->bootstrapper;
+ Jifty::Util->require($bootstrapper);
+ $bootstrapper->run if $bootstrapper->can('run');
+ };
+ die $@ if $@;
+ }
+
+ # Save them records
+ Jifty->handle->commit;
+ $log->info("Set up $plugin_class version $appv");
+ }
+ }
+}
+
+=head2 upgrade_tables BASECLASS, FROM, TO, [UPGRADECLASS]
+
+Given a C<BASECLASS> to upgrade, and two L<version> objects, C<FROM>
+and C<TO>, performs the needed transforms to the database.
+C<UPGRADECLASS>, if not specified, defaults to C<BASECLASS>::Upgrade
+
+=cut
+
+sub upgrade_tables {
+ my $self = shift;
+ my ( $baseclass, $dbv, $appv, $upgradeclass ) = @_;
+ $upgradeclass ||= $baseclass . "::Upgrade";
+
+ my $log = Log::Log4perl->get_logger("SchemaTool");
+
+ # Find current versions
+
+ if ( $appv < $dbv ) {
+ print
+ "$baseclass version $appv from module older than $dbv in database!\n";
+ return;
+ } elsif ( $appv == $dbv ) {
+
+ # Shouldn't happen
+ print "$baseclass database version $appv up to date.\n";
+ return;
+ }
+ $log->info("Generating SQL to upgrade $baseclass $dbv database to $appv");
+
+ # Figure out what versions the upgrade knows about.
+ Jifty::Util->require($upgradeclass) or return;
+ my %UPGRADES;
+ eval {
+ $UPGRADES{$_} = [ $upgradeclass->upgrade_to($_) ]
+ for grep { $appv >= version->new($_) and $dbv < version->new($_) }
+ $upgradeclass->versions();
+ };
+
+ for my $model_class ( grep {/^\Q$baseclass\E::Model::/} $self->models ) {
+
+ # We don't want to get the Collections, for example.
+ next unless $model_class->isa('Jifty::DBI::Record');
+
+ # Set us up the table
+ my $model = $model_class->new;
+
+ # If this whole table is new Create it
+ if ( $model->can('since')
+ and defined $model->since
+ and $appv >= $model->since
+ and $model->since > $dbv )
+ {
+ unshift @{ $UPGRADES{ $model->since } },
+ $model->table_schema_statements();
+ } else {
+
+ # Go through the currently-active columns
+ for my $col ( grep { not $_->virtual and not $_->computed } $model->columns ) {
+
+ # If they're new, add them
+ if ( $col->can('since')
+ and defined $col->since
+ and $appv >= $col->since
+ and $col->since > $dbv )
+ {
+ unshift @{ $UPGRADES{ $col->since } }, sub {
+ my $renamed = $upgradeclass->just_renamed || {};
+
+ # skip it if this was added by a rename
+ $model->add_column_in_db( $col->name ) unless
+ defined $renamed->{ $model->table }->{'add'}
+ ->{ $col->name };
+ };
+ }
+ }
+ }
+ }
+
+ if ( $self->flags->{'print'} ) {
+ $self->_print_upgrades(%UPGRADES);
+ } else {
+ eval {
+ $self->_execute_upgrades(%UPGRADES);
+ $log->info("Upgraded to version $appv");
+ };
+ die $@ if $@;
+ }
+ return 1;
+}
+
+sub _execute_upgrades {
+ my $self = shift;
+ my %UPGRADES = (@_);
+ Jifty->handle->begin_transaction;
+ my $log = Log::Log4perl->get_logger("SchemaTool");
+ for my $version ( sort { version->new($a) <=> version->new($b) }
+ keys %UPGRADES )
+ {
+ $log->info("Upgrading through $version");
+ for my $thing ( @{ $UPGRADES{$version} } ) {
+ if ( ref $thing ) {
+ $log->info("Running upgrade script");
+ $thing->();
+ } else {
+ $self->_exec_sql($thing);
+ }
+ }
+ }
+ Jifty->handle->commit;
+}
+
+sub _print_upgrades {
+ my $self = shift;
+ my %UPGRADES = (@_);
+ for (
+ map { @{ $UPGRADES{$_} } }
+ sort { version->new($a) <=> version->new($b) }
+ keys %UPGRADES
+ )
+ {
+ if ( ref $_ ) {
+ print "-- Upgrade subroutine:\n";
+ require Data::Dumper;
+ $Data::Dumper::Pad = "-- ";
+ $Data::Dumper::Deparse = 1;
+ $Data::Dumper::Indent = 1;
+ $Data::Dumper::Terse = 1;
+ print Data::Dumper::Dumper($_);
+ } else {
+ print "$_;\n";
+ }
+ }
+}
+
+sub _exec_sql {
+ my $self = shift;
+ my $sql = shift;
+ my $ret = Jifty->handle->simple_query($sql);
+ die "error updating a table: " . $ret->error_message unless $ret;
+}
+
sub _check_reserved {
my $self = shift;
my $model = shift;
@@ -150,5 +722,6 @@ sub connect_to_db_for_management {
return $handle;
}
-
+no Any::Moose;
+__PACKAGE__->meta->make_immutable;
1;
diff --git a/lib/Jifty/Script/Schema.pm b/lib/Jifty/Script/Schema.pm
old mode 100755
new mode 100644
index 063b3f6..fe4fbb5
--- a/lib/Jifty/Script/Schema.pm
+++ b/lib/Jifty/Script/Schema.pm
@@ -87,6 +87,12 @@ sub options {
my $self = shift;
return (
$self->SUPER::options,
+ $self->_schema_options
+ );
+}
+
+sub _schema_options {
+ return (
"setup" => "setup_tables",
"print|p" => "print",
"create-database|c" => "create_database",
@@ -143,28 +149,9 @@ sub run {
$self->print_help();
$self->setup_environment();
- $self->probe_database_existence();
- $self->manage_database_existence();
- if ( $self->{create_all_tables} ) {
- $self->create_all_tables();
- } elsif ( $self->{'setup_tables'} ) {
- $self->run_upgrades();
- } else {
- print "Done.\n";
- }
-}
+ $self->schema->setup_database();
-=head2 run_upgrades
-
-Take the actions we need in order to bring an existing database up to current.
-
-=cut
-
-sub run_upgrades {
- my $self = shift;
- $self->upgrade_jifty_tables();
- $self->upgrade_plugin_tables();
- $self->upgrade_application_tables();
+ print "Done.\n";
}
=head2 setup_environment
@@ -181,11 +168,15 @@ sub setup_environment {
Jifty::Util->require("Jifty::Model::Metadata");
Jifty->new( no_handle => 1, logger_component => 'SchemaTool', )
unless Jifty->class_loader;
+
+ # Set the flags for Jifty::Schema
+ $self->schema->flags({ map { $_ => $self->{$_} }
+ values %{{$self->_schema_options}} });
}
=head2 schema
-Returns a Jifty::Schema object.
+Returns the same Jifty::Schema object for each invocation of this script.
=cut
@@ -196,522 +187,4 @@ sub schema {
return $self->{'SCHEMA'};
}
-=head2 probe_database_existence
-
-Probes our database to see if it exists and is up to date.
-
-=cut
-
-sub probe_database_existence {
- my $self = shift;
- my $no_handle = 0;
- if ( $self->{'create_database'} or $self->{'drop_database'} ) {
- $no_handle = 1;
- }
-
- # Now try to connect. We trap expected errors and deal with them.
- eval {
- Jifty->setup_database_connection(
- no_handle => $no_handle,
- logger_component => 'SchemaTool',
- );
- };
- my $error = $@;
-
- if ( $error =~ /doesn't match (application schema|running jifty|running plugin) version/i
- or $error =~ /plugin isn't installed in database/i ) {
-
- # We found an out-of-date DB. Upgrade it
- $self->{setup_tables} = 1;
- } elsif ( $error =~ /no version in the database/i ) {
-
- # No version table. Assume the DB is empty.
- $self->{create_all_tables} = 1;
- } elsif ( $error =~ /(database .*? (?:does not|doesn't) exist|unknown database)/i) {
-
- # No database exists; we'll need to make one and fill it up
- $self->{drop_database} = 0;
- $self->{create_database} = 1;
- $self->{create_all_tables} = 1;
- } elsif ($error) {
-
- # Some other unexpected error; rethrow it
- die $error;
- }
-
- # Setting up tables requires creating the DB if we just dropped it
- $self->{create_database} = 1
- if $self->{drop_database} and $self->{setup_tables};
-
- # Setting up tables on a just-created DB is the same as setting them all up
- $self->{create_all_tables} = 1
- if $self->{create_database} and $self->{setup_tables};
-
- # Give us some kind of handle if we don't have one by now
- Jifty->handle( Jifty::Handle->new() ) unless Jifty->handle;
-}
-
-=head2 create_all_tables
-
-Create all tables for this application's models. Generally, this
-happens on installation.
-
-=cut
-
-sub create_all_tables {
- my $self = shift;
-
- my $log = Log::Log4perl->get_logger("SchemaTool");
- $log->info("Generating SQL for application @{[Jifty->app_class]}...");
-
- my $appv
- = version->new( Jifty->config->framework('Database')->{'Version'} );
- my $jiftyv = version->new($Jifty::VERSION);
-
- # Start a transaction
- Jifty->handle->begin_transaction;
-
- $self->create_tables_for_models( grep { $_->isa('Jifty::DBI::Record') }
- $self->schema->models );
-
- # Update the versions in the database
- Jifty::Model::Metadata->store( application_db_version => $appv );
- Jifty::Model::Metadata->store( jifty_db_version => $jiftyv );
-
- # For each plugin, update the plugin version
- for my $plugin ( Jifty->plugins ) {
- my $pluginv = version->new( $plugin->version );
- Jifty::Model::Metadata->store(
- ( ref $plugin ) . '_db_version' => $pluginv );
- }
-
- unless ( $self->{'no_bootstrap'} ) {
-
- # Load initial data
- eval {
- my $bootstrapper = Jifty->app_class("Bootstrap");
- Jifty::Util->require($bootstrapper);
- $bootstrapper->run() if $bootstrapper->can('run');
-
- for my $plugin ( Jifty->plugins ) {
- my $plugin_bootstrapper = $plugin->bootstrapper;
- Jifty::Util->require($plugin_bootstrapper);
- $plugin_bootstrapper->run() if $plugin_bootstrapper->can('run');
- }
- };
- die $@ if $@;
- }
-
- # Commit it all
- Jifty->handle->commit or exit 1;
-
- Jifty::Util->require('IPC::PubSub');
- IPC::PubSub->new(
- JiftyDBI => (
- db_config => Jifty->handle->{db_config},
- table_prefix => '_jifty_pubsub_',
- db_init => 1,
- )
- )->disconnect;
- $log->info("Set up version $appv, jifty version $jiftyv");
-}
-
-=head2 create_tables_for_models TABLEs
-
-Given a list of items that are the scalar names of subclasses of Jifty::Record,
-either prints SQL or creates all those models in your database.
-
-=cut
-
-sub create_tables_for_models {
- my $self = shift;
- my @models = (@_);
-
- my $log = Log::Log4perl->get_logger("SchemaTool");
- my $appv
- = version->new( Jifty->config->framework('Database')->{'Version'} );
- my $jiftyv = version->new($Jifty::VERSION);
-
- my %pluginvs;
- for my $plugin ( Jifty->plugins ) {
- my $plugin_class = ref $plugin;
- $pluginvs{$plugin_class} = version->new( $plugin->version );
- }
-
-MODEL:
- for my $model (@models) {
-
- # Skip autogenerated models; that is, those that are overridden by plugins.
- next MODEL if Jifty::ClassLoader->autogenerated($model);
- my $plugin_root = Jifty->app_class('Plugin') . '::';
-
- # TODO XXX FIXME:
- # This *will* try to generate SQL for abstract base classes you might
- # stick in $AC::Model::.
- if ( $model->can('since') ) {
- my $app_class = Jifty->app_class;
-
- my $installed_version = 0;
-
- # Is it a Jifty core model?
- if ( $model =~ /^Jifty::Model::/ ) {
- $installed_version = $jiftyv;
- }
-
- # Is it a Jifty or application plugin model?
- elsif ( $model =~ /^(?:Jifty::Plugin::|$plugin_root)/ ) {
- my $plugin_class = $model;
- $plugin_class =~ s/::Model::(.*)$//;
-
- $installed_version = $pluginvs{$plugin_class};
- }
-
- # Otherwise, an application model
- else {
- $installed_version = $appv;
- }
-
- if ( $installed_version < $model->since ) {
-
- # XXX Is this message correct?
- $log->info(
- "Skipping $model, as it should already be in the database"
- );
- next MODEL;
- }
- }
-
- if ( $model =~ /^(?:Jifty::Plugin::|$plugin_root)/
- and $model =~ /::Model::(.*)$/ )
- {
- my $model_name = $1;
-
- # Check to make sure this model is not overridden in the app,
- # in such cases we don't want to try to create the same table
- # twice, so let the app model do it rather than the plugin
- my $app_model = Jifty->app_class( "Model", $model_name );
- $app_model->require;
- next MODEL unless Jifty::ClassLoader->autogenerated($app_model);
- }
-
- $log->info("Using $model, as it appears to be new.");
-
- $self->schema->_check_reserved($model)
- unless ( $self->{'ignore_reserved'}
- or !Jifty->config->framework('Database')->{'CheckSchema'} );
-
- if ( $self->{'print'} ) {
- print $model->printable_table_schema;
- } else {
- $model->create_table_in_db;
- }
- }
-}
-
-=head2 upgrade_jifty_tables
-
-Upgrade Jifty's internal tables.
-
-=cut
-
-sub upgrade_jifty_tables {
- my $self = shift;
- my $dbv = Jifty::Model::Metadata->load('jifty_db_version');
- unless ($dbv) {
-
- # Backwards combatibility -- it usd to be 'key' not 'data_key';
- eval {
- local $SIG{__WARN__} = sub { };
- $dbv
- = Jifty->handle->fetch_result(
- "SELECT value FROM _jifty_metadata WHERE key = 'jifty_db_version'"
- );
- };
- }
-
- $dbv = version->new( $dbv || '0.60426' );
- my $appv = version->new($Jifty::VERSION);
-
- return
- unless $self->upgrade_tables(
- "Jifty" => $dbv,
- $appv, "Jifty::Upgrade::Internal"
- );
- if ( $self->{print} ) {
- warn "Need to upgrade jifty_db_version to $appv here!";
- } else {
- Jifty::Model::Metadata->store( jifty_db_version => $appv );
- }
-}
-
-=head2 upgrade_application_tables
-
-Upgrade the application's tables.
-
-=cut
-
-sub upgrade_application_tables {
- my $self = shift;
- my $dbv = version->new(
- Jifty::Model::Metadata->load('application_db_version') );
- my $appv
- = version->new( Jifty->config->framework('Database')->{'Version'} );
-
- return unless $self->upgrade_tables( Jifty->app_class, $dbv, $appv );
- if ( $self->{print} ) {
- warn "Need to upgrade application_db_version to $appv here!";
- } else {
- Jifty::Model::Metadata->store( application_db_version => $appv );
- }
-}
-
-=head2 upgrade_plugin_tables
-
-Upgrade the tables for each plugin.
-
-=cut
-
-sub upgrade_plugin_tables {
- my $self = shift;
-
- for my $plugin ( Jifty->plugins ) {
- my $plugin_class = ref $plugin;
-
- my $dbv
- = Jifty::Model::Metadata->load( $plugin_class . '_db_version' );
- my $appv = version->new( $plugin->version );
-
- # Upgrade this plugin from dbv -> appv
- if ( defined $dbv ) {
- $dbv = version->new($dbv);
-
- next
- unless $self->upgrade_tables( $plugin_class, $dbv, $appv,
- $plugin->upgrade_class );
- if ( $self->{print} ) {
- warn
- "Need to upgrade ${plugin_class}_db_version to $appv here!";
- } else {
- Jifty::Model::Metadata->store(
- $plugin_class . '_db_version' => $appv );
- }
- }
-
- # Install this plugin
- else {
- my $log = Log::Log4perl->get_logger("SchemaTool");
- $log->info("Generating SQL to set up $plugin_class...");
- Jifty->handle->begin_transaction;
-
- # Create the tables
- $self->create_tables_for_models(
- grep {
- $_->isa('Jifty::DBI::Record')
- and /^\Q$plugin_class\E::Model::/
- } $self->schema->models
- );
-
- # Save the plugin version to the database
- Jifty::Model::Metadata->store(
- $plugin_class . '_db_version' => $appv )
- unless $self->{print};
-
- # Run the bootstrapper for initial data
- unless ( $self->{print} ) {
- eval {
- my $bootstrapper = $plugin->bootstrapper;
- Jifty::Util->require($bootstrapper);
- $bootstrapper->run if $bootstrapper->can('run');
- };
- die $@ if $@;
- }
-
- # Save them records
- Jifty->handle->commit;
- $log->info("Set up $plugin_class version $appv");
- }
- }
-}
-
-=head2 upgrade_tables BASECLASS, FROM, TO, [UPGRADECLASS]
-
-Given a C<BASECLASS> to upgrade, and two L<version> objects, C<FROM>
-and C<TO>, performs the needed transforms to the database.
-C<UPGRADECLASS>, if not specified, defaults to C<BASECLASS>::Upgrade
-
-=cut
-
-sub upgrade_tables {
- my $self = shift;
- my ( $baseclass, $dbv, $appv, $upgradeclass ) = @_;
- $upgradeclass ||= $baseclass . "::Upgrade";
-
- my $log = Log::Log4perl->get_logger("SchemaTool");
-
- # Find current versions
-
- if ( $appv < $dbv ) {
- print
- "$baseclass version $appv from module older than $dbv in database!\n";
- return;
- } elsif ( $appv == $dbv ) {
-
- # Shouldn't happen
- print "$baseclass database version $appv up to date.\n";
- return;
- }
- $log->info("Generating SQL to upgrade $baseclass $dbv database to $appv");
-
- # Figure out what versions the upgrade knows about.
- Jifty::Util->require($upgradeclass) or return;
- my %UPGRADES;
- eval {
- $UPGRADES{$_} = [ $upgradeclass->upgrade_to($_) ]
- for grep { $appv >= version->new($_) and $dbv < version->new($_) }
- $upgradeclass->versions();
- };
-
- for my $model_class ( grep {/^\Q$baseclass\E::Model::/}
- $self->schema->models )
- {
-
- # We don't want to get the Collections, for example.
- next unless $model_class->isa('Jifty::DBI::Record');
-
- # Set us up the table
- my $model = $model_class->new;
-
- # If this whole table is new Create it
- if ( $model->can('since')
- and defined $model->since
- and $appv >= $model->since
- and $model->since > $dbv )
- {
- unshift @{ $UPGRADES{ $model->since } },
- $model->table_schema_statements();
- } else {
-
- # Go through the currently-active columns
- for my $col ( grep { not $_->virtual and not $_->computed } $model->columns ) {
-
- # If they're new, add them
- if ( $col->can('since')
- and defined $col->since
- and $appv >= $col->since
- and $col->since > $dbv )
- {
- unshift @{ $UPGRADES{ $col->since } }, sub {
- my $renamed = $upgradeclass->just_renamed || {};
-
- # skip it if this was added by a rename
- $model->add_column_in_db( $col->name ) unless
- defined $renamed->{ $model->table }->{'add'}
- ->{ $col->name };
- };
- }
- }
- }
- }
-
- if ( $self->{'print'} ) {
- $self->_print_upgrades(%UPGRADES);
-
- } else {
- eval {
- $self->_execute_upgrades(%UPGRADES);
- $log->info("Upgraded to version $appv");
- };
- die $@ if $@;
- }
- return 1;
-}
-
-sub _execute_upgrades {
- my $self = shift;
- my %UPGRADES = (@_);
- Jifty->handle->begin_transaction;
- my $log = Log::Log4perl->get_logger("SchemaTool");
- for my $version ( sort { version->new($a) <=> version->new($b) }
- keys %UPGRADES )
- {
- $log->info("Upgrading through $version");
- for my $thing ( @{ $UPGRADES{$version} } ) {
- if ( ref $thing ) {
- $log->info("Running upgrade script");
- $thing->();
- } else {
- _exec_sql($thing);
- }
- }
- }
- Jifty->handle->commit;
-}
-
-sub _print_upgrades {
- my $self = shift;
- my %UPGRADES = (@_);
- for (
- map { @{ $UPGRADES{$_} } }
- sort { version->new($a) <=> version->new($b) }
- keys %UPGRADES
- )
- {
- if ( ref $_ ) {
- print "-- Upgrade subroutine:\n";
- require Data::Dumper;
- $Data::Dumper::Pad = "-- ";
- $Data::Dumper::Deparse = 1;
- $Data::Dumper::Indent = 1;
- $Data::Dumper::Terse = 1;
- print Data::Dumper::Dumper($_);
- } else {
- print "$_;\n";
- }
- }
-}
-
-=head2 manage_database_existence
-
-If the user wants the database created, creates the database. If the
-user wants the old database deleted, does that too. Exits with a
-return value of 1 if the database drop or create fails.
-
-=cut
-
-sub manage_database_existence {
- my $self = shift;
-
- my $handle = Jifty::Schema->connect_to_db_for_management();
-
- if ( $self->{print} ) {
- $handle->drop_database('print') if ( $self->{'drop_database'} );
- $handle->create_database('print') if ( $self->{'create_database'} );
- } else {
- if ( $self->{'drop_database'} ) {
- my $ret = $handle->drop_database('execute');
- die "Error dropping database: ". $ret->error_message
- unless $ret or $ret->error_message =~ /database .*?(?:does not|doesn't) exist|unknown database/i;
- }
-
- if ( $self->{'create_database'} ) {
- my $ret = $handle->create_database('execute');
- die "Error creating database: ". $ret->error_message unless $ret;
- }
-
- $handle->disconnect;
- $self->_reinit_handle() if ( $self->{'create_database'} );
- }
-}
-
-sub _reinit_handle {
- Jifty->handle( Jifty::Handle->new() );
- Jifty->handle->connect();
-}
-
-sub _exec_sql {
- my $sql = shift;
- my $ret = Jifty->handle->simple_query($sql);
- die "error updating a table: " . $ret->error_message unless $ret;
-}
-
1;
-----------------------------------------------------------------------
More information about the Jifty-commit
mailing list