<commit_before>package Database::Migrator::Core;

use strict;
use warnings;
use namespace::autoclean;

use Database::Migrator::Types qw( ArrayRef Bool Dir File Maybe Str );
use DBI;
use Eval::Closure qw( eval_closure );
use File::Slurp qw( read_file );
use Log::Dispatch;
use Moose::Util::TypeConstraints qw( duck_type );

use Moose::Role;

with 'MooseX::Getopt::Dashes';

MooseX::Getopt::OptionTypeMap->add_option_type_to_map(
    Maybe [Str] => '=s',
);

requires qw(
    _create_database
    _driver_name
    _drop_database
    _run_ddl
);

has database => (
    is       => 'ro',
    isa      => Str,
    required => 1,
);

has [qw( username password host port )] => (
    is      => 'ro',
    isa     => Maybe [Str],
    default => undef,
);

has migration_table => (
    is       => 'ro',
    isa      => Str,
    required => 1,
);

has migrations_dir => (
    is       => 'ro',
    isa      => Dir,
    coerce   => 1,
    required => 1,
);

has schema_file => (
    is       => 'ro',
    isa      => File,
    coerce   => 1,
    required => 1,
);

has _database_exists => (
    is       => 'ro',
    isa      => Bool,
    init_arg => undef,
    lazy     => 1,
    builder  => '_build_database_exists',
);

has __pending_migrations => (
    traits   => ['Array'],
    is       => 'ro',
    isa      => ArrayRef [Dir],
    init_arg => undef,
    lazy     => 1,
    builder  => '_build_pending_migrations',
    handles  => {
        _pending_migrations    => 'elements',
        has_pending_migrations => 'count',
    },
);

has dbh => (
    traits   => ['NoGetopt'],
    is       => 'ro',
    isa      => 'DBI::db',
    init_arg => undef,
    lazy     => 1,
    builder  => '_build_dbh',
);

has logger => (
    traits  => ['NoGetopt'],
    is      => 'ro',
    isa     => duck_type( [qw( debug info )] ),
    lazy    => 1,
    builder => '_build_logger',
);

has verbose => (
    is      => 'ro',
    isa     => Bool,
    default => 0,
);

has quiet => (
    is      => 'ro',
    isa     => Bool,
    default => 0,
);

has dry_run => (
    is      => 'ro',
    isa     => Bool,
    default => 0,
);

around BUILDARGS => sub {
    my $orig  = shift;
    my $class = shift;

    my $p = $class->$orig(@_);

    $p->{username} = delete $p->{user}
        if exists $p->{user};

    return $p;
};

sub BUILD { }
after BUILD => sub {
    my $self = shift;

    die 'Cannot be both quiet and verbose'
        if $self->quiet() && $self->verbose();
};

sub create_or_update_database {
    my $self = shift;

    if ( $self->_database_exists() ) {
        my $database = $self->database();
        $self->logger()->info("The $database database already exists");
    }
    else {
        $self->_create_database();
        $self->_run_ddl( $self->schema_file() );
    }

    $self->_run_migrations();

    return;
}

sub _run_migrations {
    my $self = shift;

    $self->_run_one_migration($_) for $self->_pending_migrations();
}

sub _run_one_migration {
    my $self      = shift;
    my $migration = shift;

    my $name = $migration->basename();

    $self->logger->info("Running migration - $name");

    my @files = grep { !$_->is_dir() } $migration->children( no_hidden => 1 );

    for my $file ( sort _numeric_or_alpha_sort @files ) {
        my $basename = $file->basename();
        if ( $file =~ /\.sql/ ) {
            $self->logger()->debug(" - running $basename as sql");
            $self->_run_ddl($file);
        }
        else {
            $self->logger()->debug(" - running $basename as perl code");

            my $perl = read_file( $file->stringify() );

            my $sub = eval_closure( source => $perl );

            next if $self->dry_run();

            $sub->($self);<commit_msg>Changed a log message to debug<commit_after>package Database::Migrator::Core;

use strict;
use warnings;
use namespace::autoclean;

use Database::Migrator::Types qw( ArrayRef Bool Dir File Maybe Str );
use DBI;
use Eval::Closure qw( eval_closure );
use File::Slurp qw( read_file );
use Log::Dispatch;
use Moose::Util::TypeConstraints qw( duck_type );

use Moose::Role;

with 'MooseX::Getopt::Dashes';

MooseX::Getopt::OptionTypeMap->add_option_type_to_map(
    Maybe [Str] => '=s',
);

requires qw(
    _create_database
    _driver_name
    _drop_database
    _run_ddl
);

has database => (
    is       => 'ro',
    isa      => Str,
    required => 1,
);

has [qw( username password host port )] => (
    is      => 'ro',
    isa     => Maybe [Str],
    default => undef,
);

has migration_table => (
    is       => 'ro',
    isa      => Str,
    required => 1,
);

has migrations_dir => (
    is       => 'ro',
    isa      => Dir,
    coerce   => 1,
    required => 1,
);

has schema_file => (
    is       => 'ro',
    isa      => File,
    coerce   => 1,
    required => 1,
);

has _database_exists => (
    is       => 'ro',
    isa      => Bool,
    init_arg => undef,
    lazy     => 1,
    builder  => '_build_database_exists',
);

has __pending_migrations => (
    traits   => ['Array'],
    is       => 'ro',
    isa      => ArrayRef [Dir],
    init_arg => undef,
    lazy     => 1,
    builder  => '_build_pending_migrations',
    handles  => {
        _pending_migrations    => 'elements',
        has_pending_migrations => 'count',
    },
);

has dbh => (
    traits   => ['NoGetopt'],
    is       => 'ro',
    isa      => 'DBI::db',
    init_arg => undef,
    lazy     => 1,
    builder  => '_build_dbh',
);

has logger => (
    traits  => ['NoGetopt'],
    is      => 'ro',
    isa     => duck_type( [qw( debug info )] ),
    lazy    => 1,
    builder => '_build_logger',
);

has verbose => (
    is      => 'ro',
    isa     => Bool,
    default => 0,
);

has quiet => (
    is      => 'ro',
    isa     => Bool,
    default => 0,
);

has dry_run => (
    is      => 'ro',
    isa     => Bool,
    default => 0,
);

around BUILDARGS => sub {
    my $orig  = shift;
    my $class = shift;

    my $p = $class->$orig(@_);

    $p->{username} = delete $p->{user}
        if exists $p->{user};

    return $p;
};

sub BUILD { }
after BUILD => sub {
    my $self = shift;

    die 'Cannot be both quiet and verbose'
        if $self->quiet() && $self->verbose();
};

sub create_or_update_database {
    my $self = shift;

    if ( $self->_database_exists() ) {
        my $database = $self->database();
        $self->logger()->debug("The $database database already exists");
    }
    else {
        $self->_create_database();
        $self->_run_ddl( $self->schema_file() );
    }

    $self->_run_migrations();

    return;
}

sub _run_migrations {
    my $self = shift;

    $self->_run_one_migration($_) for $self->_pending_migrations();
}

sub _run_one_migration {
    my $self      = shift;
    my $migration = shift;

    my $name = $migration->basename();

    $self->logger()->info("Running migration - $name");

    my @files = grep { !$_->is_dir() } $migration->children( no_hidden => 1 );

    for my $file ( sort _numeric_or_alpha_sort @files ) {
        my $basename = $file->basename();
        if ( $file =~ /\.sql/ ) {
            $self->logger()->debug(" - running $basename as sql");
            $self->_run_ddl($file);
        }
        else {
            $self->logger()->debug(" - running $basename as perl code");

            my $perl = read_file( $file->stringify() );

            my $sub = eval_closure( source => $perl );

            next if $self->dry_run();

            $sub->($self);
{'fraction_non_alphanumeric': 0.16984681537221177, 'fraction_numerical': 0.0037624294544477293, 'mean_word_length': 2.1352148272957034, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 201, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2203458', 'n_tokens_mistral': 2642, 'n_tokens_neox': 2543, 'n_words': 620}