Alzabo

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

- If you had previously installed Alzabo, and then provided a new
  Alzabo root directory or a new directory for the Mason components,
  this was not respected during the installation process.

- Alzabo's referential integrity checks will no longer complain if you
  attempt to set a foreign key column to NULL.  Previously it would
  throw an exception if the column was part of the dependent table in
  a foreign key relationship.  Now, it just assumes you really meant
  to allow the column to be NULLable.

- The schema class's load_from_file() method now blesses the loaded
  schema into the calling class.  So if you use MethodMaker to
  generate classes, and then call My::Schema->load_from_file, it
  should always return an object blessed into the My::Schema class.
  Reported by Ken Williams.

- When checking for the MySQL variable sql_mode, the value may be
  simply '' as opposed to 0.  Patch by Andrew Baumhauer.

BACKWARDS INCOMPATIBILITIES:

- Alzabo now requires at least Perl 5.6.0 (5.6.1+ recommended).

- The old caching system has been removed, as it had way too much

lib/Alzabo/ChangeTracker.pm  view on Meta::CPAN

1;

sub new
{
    my $proto = shift;
    my $class = ref $proto || $proto;

    ++$STACK;

    my $self = $STACK;
    bless \$self, $class;
}

sub add
{
    my $self = shift;

    validate_pos( @_, { type => CODEREF } );

    push @CHANGES, shift;
}

lib/Alzabo/Create/Column.pm  view on Meta::CPAN


$VERSION = 2.0;

1;

sub new
{
    my $proto = shift;
    my $class = ref $proto || $proto;

    my $self = bless {}, $class;

    $self->_init(@_);

    return $self;
}

sub _init
{
    my $self = shift;

lib/Alzabo/Create/ColumnDefinition.pm  view on Meta::CPAN


$VERSION = 2.0;

1;

sub new
{
    my $proto = shift;
    my $class = ref $proto || $proto;

    my $self = bless {}, $class;

    $self->_init(@_);

    return $self;
}

sub _init
{
    my $self = shift;

lib/Alzabo/Create/ForeignKey.pm  view on Meta::CPAN

    validate( @_, { columns_from => { type => ARRAYREF | OBJECT },
                    columns_to   => { type => ARRAYREF | OBJECT },
                    cardinality  => { type => ARRAYREF },
                    from_is_dependent => { type => SCALAR },
                    to_is_dependent   => { type => SCALAR },
                    comment => { type => UNDEF | SCALAR,
                                 default => '' },
                  } );
    my %p = @_;

    my $self = bless {}, $class;

    $self->set_columns_from( $p{columns_from} );
    $self->set_columns_to( $p{columns_to} );

    $self->set_cardinality( @{ $p{cardinality} } );
    $self->set_from_is_dependent( $p{from_is_dependent} );
    $self->set_to_is_dependent( $p{to_is_dependent} );

    $self->set_comment( $p{comment} );

lib/Alzabo/Create/Index.pm  view on Meta::CPAN

    my $class = ref $proto || $proto;

    validate( @_, { table    => { isa => 'Alzabo::Create::Table' },
                    columns  => { type => ARRAYREF },
                    unique   => { type => BOOLEAN, default => 0 },
                    fulltext => { type => BOOLEAN, default => 0 },
                    function => { type => UNDEF | SCALAR,  default => undef },
                  } );
    my %p = @_;

    my $self = bless {}, $class;

    $self->{table} = $p{table};
    $self->{unique} = $p{unique} || 0;
    $self->{fulltext} = $p{fulltext} || 0;
    $self->{function} = $p{function};

    $self->{columns} = Tie::IxHash->new;

    foreach my $c (@{ $p{columns} })
    {

lib/Alzabo/Create/Schema.pm  view on Meta::CPAN

{
    my $proto = shift;
    my $class = ref $proto || $proto;

    validate( @_, { rdbms    => { type => SCALAR },
                    name     => { type => SCALAR },
                    no_cache => { type => SCALAR, default => 0 },
                  } );
    my %p = @_;

    my $self = bless {}, $class;

    params_exception "Alzabo does not support the '$p{rdbms}' RDBMS"
        unless ( ( grep { $p{rdbms} eq $_ } Alzabo::Driver->available ) &&
                 ( grep { $p{rdbms} eq $_ } Alzabo::RDBMSRules->available ) );

    $self->{driver} = Alzabo::Driver->new( rdbms => $p{rdbms},
                                           schema => $self );
    $self->{rules} = Alzabo::RDBMSRules->new( rdbms => $p{rdbms} );

    $self->{sql} = Alzabo::SQLMaker->load( rdbms => $p{rdbms} );

lib/Alzabo/Create/Schema.pm  view on Meta::CPAN

    foreach my $f ( qw( original instantiated rules driver ) )
    {
        delete $clone->{$f};
    }

    foreach my $t ($clone->tables)
    {
        foreach my $c ($t->columns)
        {
            my $def = $c->definition;
            bless $def, 'Alzabo::Runtime::ColumnDefinition';
            bless $c, 'Alzabo::Runtime::Column';

            delete $c->{last_instantiation_name};
        }

        foreach my $fk ($t->all_foreign_keys)
        {
            bless $fk, 'Alzabo::Runtime::ForeignKey';
        }

        foreach my $i ($t->indexes)
        {
            bless $i, 'Alzabo::Runtime::Index';
        }

        delete $t->{last_instantiation_name};

        bless $t, 'Alzabo::Runtime::Table';
    }
    bless $clone, 'Alzabo::Runtime::Schema';

    return $clone;
}

sub save_current_name
{
    my $self = shift;

    $self->{last_instantiated_name} = $self->name;

lib/Alzabo/Create/Table.pm  view on Meta::CPAN


    validate( @_, { schema => { isa => 'Alzabo::Create::Schema' },
                    name => { type => SCALAR },
                    attributes => { type => ARRAYREF,
                                    optional => 1 },
                    comment => { type => UNDEF | SCALAR,
                                 default => '' },
                  } );
    my %p = @_;

    my $self = bless {}, $class;

    $self->{schema} = $p{schema};

    $self->set_name($p{name});

    $self->{columns} = Tie::IxHash->new;
    $self->{pk} = [];
    $self->{indexes} = Tie::IxHash->new;

    my %attr;

lib/Alzabo/Driver.pm  view on Meta::CPAN

                                                 optional => 1 },
                                    };

sub new_no_execute
{
    my $proto = shift;
    my $class = ref $proto || $proto;

    my %p = validate( @_, NEW_NO_EXECUTE_SPEC );

    my $self = bless {}, $class;

    $self->{limit} = $p{limit} ? $p{limit}[0] : 0;
    $self->{offset} = $p{limit} && $p{limit}[1] ? $p{limit}[1] : 0;
    $self->{rows_fetched} = 0;

    eval
    {
        $self->{sth} = $p{dbh}->prepare( $p{sql} );

        $self->{bind} = exists $p{bind} ? ( ref $p{bind} ? $p{bind} : [ $p{bind} ] ) : [];

lib/Alzabo/Driver.pm  view on Meta::CPAN

The next step is to implement a C<new> method and the methods listed
under L<Virtual Methods>.  The C<new> method should look a bit like
this:

 1:  sub new
 2:  {
 3:      my $proto = shift;
 4:      my $class = ref $proto || $proto;
 5:      my %p = @_;
 6:
 7:      my $self = bless {}, $class;
 8:
 9:      return $self;
 10:  }

The hash %p contains any values passed to the
C<Alzabo::Driver-E<gt>new> method by its caller.

Lines 1-7 should probably be copied verbatim into your own C<new>
method.  Line 5 can be deleted if you don't need to look at the
parameters.

lib/Alzabo/Driver/MySQL.pm  view on Meta::CPAN


$VERSION = 2.0;

use base qw(Alzabo::Driver);

sub new
{
    my $proto = shift;
    my $class = ref $proto || $proto;

    my $self = bless {}, $class;

    return $self;
}

sub connect
{
    my $self = shift;

    $self->disconnect if $self->{dbh};
    $self->{dbh} = $self->_make_dbh( @_,

lib/Alzabo/Driver/PostgreSQL.pm  view on Meta::CPAN


$VERSION = 2.0;

use base qw(Alzabo::Driver);

sub new
{
    my $proto = shift;
    my $class = ref $proto || $proto;

    return bless {}, $class;
}

sub connect
{
    my $self = shift;

    $self->{tran_count} = undef;

    # This database handle is stale or nonexistent, so we need to (re)connect
    $self->disconnect if $self->{dbh};

lib/Alzabo/ForeignKey.pm  view on Meta::CPAN

    return ( $self->id eq $other->id
             or
             $self->id eq $other->reverse->id
           );
}

sub reverse
{
    my $self = shift;

    return bless { table_from        => $self->table_to,
                   table_to          => $self->table_from,
                   columns_from      => [ $self->columns_to ],
                   columns_to        => [ $self->columns_from ],
                   from_is_dependent => $self->to_is_dependent,
                   to_is_dependent   => $self->from_is_dependent,
                   cardinality       => [ reverse @{ $self->{cardinality} } ],
		 }, ref $self;
}

sub id

lib/Alzabo/MethodMaker.pm  view on Meta::CPAN

        {
            $class_root = caller($x++);
            die "No base class could be determined\n" unless $class_root;
        } while ( $class_root->isa(__PACKAGE__) );
    }

    my $self;

    $p{name_maker} = sub { $self->name(@_) } unless ref $p{name_maker};

    $self = bless { opts => \%p,
                    class_root => $class_root,
                    schema => $s,
                  }, $class;

    return $self;
}

sub make
{
    my $self = shift;

    $self->{schema_class} = join '::', $self->{class_root}, 'Schema';
    bless $self->{schema}, $self->{schema_class};

    $self->eval_schema_class;
    $self->load_class( $self->{schema_class} );

   {
       # Users can add methods to these superclasses
       no strict 'refs';
       foreach my $thing ( qw( Table Row ) )
       {
           @{ "$self->{class_root}::${thing}::ISA" }
               = ( "Alzabo::Runtime::$thing", "Alzabo::DocumentationContainer" );
       }
    }

    foreach my $t ( sort { $a->name cmp $b->name  } $self->{schema}->tables )
    {
        $self->{table_class} = join '::', $self->{class_root}, 'Table', $t->name;
        $self->{row_class} = join '::', $self->{class_root}, 'Row', $t->name;

        bless $t, $self->{table_class};
        $self->eval_table_class;
        $self->{schema}->add_contained_class( table => $self->{table_class} );

        $self->eval_row_class;
        $t->add_contained_class( row => $self->{row_class} );

        if ( $self->{opts}{tables} )
        {
            $self->make_table_method($t);
        }

lib/Alzabo/MethodMaker.pm  view on Meta::CPAN

sub new
{
    my $class = shift;
    my %p = validate( @_, { name    => { type => SCALAR },
                            group   => { type => SCALAR },
                            description => { type => SCALAR },
                            spec    => { type => SCALAR | ARRAYREF | HASHREF,
                                         default => undef },
                          } );

    return bless \%p, $class;
}

sub name { shift->{name} }
sub spec { shift->{spec} }

sub as_pod
{
    my $self = shift;

    my $desc = ucfirst $self->{description};

lib/Alzabo/MethodMaker.pm  view on Meta::CPAN


use base qw(Alzabo::Docs);

sub new
{
    my $class = shift;
    my %p = validate( @_, { group   => { type => SCALAR },
                            description => { type => SCALAR },
                          } );

    return bless \%p, $class;
}

sub as_pod
{
    my $self = shift;

    return ucfirst "$self->{description}\n\n";
}

1;

lib/Alzabo/MethodMaker.pm  view on Meta::CPAN

conflicts.

=back

=head1 EFFECTS

Using this module has several effects on your schema's objects.

=head2 New Class Names

Your schema, table, and row objects to be blessed into subclasses of
L<C<Alzabo::Runtime::Schema>|Alzabo::Runtime::Schema>,
L<C<Alzabo::Runtime::Table>|Alzabo::Runtime::Table>,
L<C<Alzabo::Runtime::Row>|Alzabo::Runtime::Row>, respectively.  These
subclasses contain the various methods created by this module.  The
new class names are formed by using the
L<"class_root"|Alzabo::MethodMaker/PARAMETERS> parameter and adding
onto it.

In order to make it convenient to add new methods to the table and row
classes, the created table classes are all subclasses of a new class

lib/Alzabo/MethodMaker.pm  view on Meta::CPAN

 My::MovieDB::Schema

 My::MovieDB::Table::Movie - subclass of My::MovieDB::Table
 My::MovieDB::Row::Movie   - subclass of My::MovieDB::Row

 My::MovieDB::Table::Image - subclass of My::MovieDB::Table
 My::MovieDB::Row::Image   - subclass of My::MovieDB::Row

=head2 Loading Classes

For each class into which an object is blessed, this module will
attempt to load that class via a C<use> statement.  If there is no
module found this will not cause an error.  If this class defines any
methods that have the same name as those this module generates, then
this module will not attempt to generate them.

=head1 METHOD CREATION OPTIONS

When using Alzabo::MethodMaker, you may specify any of the following
parameters.  Specifying "all" causes all of them to be used.

lib/Alzabo/QuickRef.pod  view on Meta::CPAN

These modules are mostly used just to load other modules.  The
C<Alzabo::Runtime> module can be used to preload schemas at compile
time by doing:

  use Alzabo::Runtime qw( schema1 schema2 schema3 );

=head2 Alzabo::MethodMaker

This module can be used to generate many useful convenience methods.
This is done by auto-generating methods in new packages and
re-blessing some of the schema objects into these packages.  To have
it generate all the possible methods for a schema you would do:

  use Alzabo::MethodMaker ( schema => 'some_schema',
                            # Root for new packages
                            class_root => 'My::Data',
                            # Make all possible methods
                            all => 1 );

This will make convenience methods for such things as getting table
and column objects, following various types of foreign keys, and

lib/Alzabo/RDBMSRules.pm  view on Meta::CPAN

The next step is to implement a C<new()> method and the methods listed
under the section L<Virtual Methods>.  The new method should look a
bit like this:

 1:  sub new
 2:  {
 3:      my $proto = shift;
 4:      my $class = ref $proto || $proto;
 5:      my %p = @_;
 6:
 7:      my $self = bless {}, $self;
 8:
 9:      return $self;
 10:  }

The hash %p contains any values passed to the
L<C<Alzabo::RDBMSRules-E<gt>new>|Alzabo::RDBMSRules/new> method by its
caller.

Lines 1-7 should probably be copied verbatim into your own C<new>
method.  Line 5 can be deleted if you don't need to look at the

lib/Alzabo/RDBMSRules/MySQL.pm  view on Meta::CPAN


use base qw(Alzabo::RDBMSRules);

$VERSION = 2.0;

sub new
{
    my $proto = shift;
    my $class = ref $proto || $proto;

    return bless {}, $class;
}

sub validate_schema_name
{
    my $self = shift;
    my $name = shift->name;

    Alzabo::Exception::RDBMSRules->throw( error => "Schema name must be at least one character long" )
        unless length $name;

lib/Alzabo/RDBMSRules/PostgreSQL.pm  view on Meta::CPAN


$VERSION = 2.0;

1;

sub new
{
    my $proto = shift;
    my $class = ref $proto || $proto;

    return bless {}, $class;
}

sub validate_schema_name
{
    my $self = shift;
    my $name = shift->name;

    $self->_check_name($name, 'schema');

    Alzabo::Exception::RDBMSRules->throw( error => "Schema name ($name) contains a single quote char (')" )

lib/Alzabo/Runtime/Column.pm  view on Meta::CPAN

    my $self = shift;

    my %p = validate( @_, { table => { isa => 'Alzabo::Runtime::Table' },
                          } );

    my $clone;

    %$clone = %$self;
    $clone->{table} = $p{table};

    bless $clone, ref $self;

    return $clone;
}

sub alias
{
    my $self = shift;
    my %p = validate( @_, { as => { type => SCALAR } } );

    my $clone;
    %$clone = %$self;

    bless $clone, ref $self;

    $clone->{alias_name} = $p{as};
    $clone->{real_column} = $self;

    return $clone;
}

sub alias_name
{
    return $_[0]->{alias_name} || $_[0]->{name};

lib/Alzabo/Runtime/ForeignKey.pm  view on Meta::CPAN

sub new
{
    my $proto = shift;
    my $class = ref $proto || $proto;

    validate( @_, { columns_from => { type => ARRAYREF | OBJECT },
                    columns_to   => { type => ARRAYREF | OBJECT },
                  } );
    my %p = @_;

    my $self = bless {}, $class;

    # XXX - needs a little more validation, like that both "sides"
    # have the same number of columns
    $self->{columns_from} = $p{columns_from};
    $self->{columns_to}   = $p{columns_to};

    return $self;
}

sub register_insert

lib/Alzabo/Runtime/InsertHandle.pm  view on Meta::CPAN

                           columns => { type => ARRAYREF },
                           values  => { type => HASHREF, default => {} },
                         };

sub new
{
    my $class = shift;

    my %p = validate( @_, NEW_SPEC );

    my $self = bless \%p, $class;

    $self->{handle} =
        $self->{table}->schema->driver->statement_no_execute( sql => $p{sql}->sql );

    return $self;
}

sub insert
{
    my $self = shift;

lib/Alzabo/Runtime/JoinCursor.pm  view on Meta::CPAN

                           tables => { type => ARRAYREF },
                         };

sub new
{
    my $proto = shift;
    my $class = ref $proto || $proto;

    my %p = validate( @_, NEW_SPEC );

    my $self = bless { %p,
                       count => 0,
                     }, $class;

    return $self;
}

sub next
{
    my $self = shift;

lib/Alzabo/Runtime/Row.pm  view on Meta::CPAN

sub new
{
    my $proto = shift;
    my $class = ref $proto || $proto;

    my %p =
        validate( @_, NEW_SPEC );

    my $self = $p{potential_row} ? $p{potential_row} : {};

    bless $self, $class;

    $self->{table} = $p{table};
    $self->{state} = $p{state};

    $self->{state}->_init($self, @_) or return;

    return $self;
}

sub table

lib/Alzabo/Runtime/Row.pm  view on Meta::CPAN

    # dumb hack to fix bugs in Storable 2.00 - 2.03 w/ a non-threaded
    # Perl
    #
    # Basically, Storable somehow screws up the hooks business the
    # _first_ time an object from a class with hooks is stored.  So
    # we'll just _force_ it do it once right away.
    if ( $Storable::VERSION >= 2 && $Storable::VERSION <= 2.03 )
    {
        eval <<'EOF';
        { package ___name; sub name { 'foo' } }
        { package ___table;  @table::ISA = '___name'; sub schema { bless {}, '___name' } }
        my $row = bless { table => bless {}, '___table' }, __PACKAGE__;
        Storable::thaw(Storable::nfreeze($row));
EOF
    }
}


1;

__END__

lib/Alzabo/Runtime/RowCursor.pm  view on Meta::CPAN

                           table => { isa => 'Alzabo::Runtime::Table' },
                         };

sub new
{
    my $proto = shift;
    my $class = ref $proto || $proto;

    my %p = validate( @_, NEW_SPEC );

    my $self = bless { %p,
                       count => 0,
                     }, $class;

    return $self;
}

sub next
{
    my $self = shift;

lib/Alzabo/Runtime/Table.pm  view on Meta::CPAN

}

my $alias_num = '000000000';
sub alias
{
    my $self = shift;

    my $clone;
    %$clone = %$self;

    bless $clone, ref $self;

    $clone->{alias_name} = $self->name . ++$alias_num;
    $clone->{real_table} = $self;

    $clone->{columns} = Tie::IxHash->new( map { $_->name => $_ } $self->columns );

    # Force clone of primary key columns right away.
    $clone->column($_) foreach map { $_->name } $self->primary_key;

    return $clone;

lib/Alzabo/SQLMaker.pm  view on Meta::CPAN

use constant NEW_SPEC => { driver => { isa => 'Alzabo::Driver' },
                           quote_identifiers  => { type => BOOLEAN,
                                                   default => 0 },
                         };

sub new
{
    my $class = shift;
    my %p = validate( @_, NEW_SPEC );

    return bless { last_op => undef,
		   expect => undef,
		   type => undef,
		   sql => '',
		   bind => [],
		   placeholders => [],
		   as_id => 'aaaaa10000',
                   alias_in_having => 1,
                   %p,
		 }, $class;
}

# this just needs to be some unique thing that won't ever look like a
# valid bound parameter
my $placeholder = do { my $x = 1; bless \$x, 'Alzabo::SQLMaker::Placeholder' };
sub placeholder { $placeholder }

sub last_op
{
    return shift->{last_op};
}

sub select
{
    my $self = shift;

lib/Alzabo/SQLMaker.pm  view on Meta::CPAN

                  } $fk->column_pairs );
    }

    @{ $self->{tables} }{ $join_from, $join_on } = (1, 1);

    if ($where)
    {
        $sql .= ' AND ';

        # make a clone
        my $sql_maker = bless { %$self }, ref $self;
        $sql_maker->{sql} = '';
        # sharing same ref intentionally
        $sql_maker->{bind} = $self->{bind};
        $sql_maker->{tables} = $self->{tables};

        # lie to Alzabo::Runtime::process_where_clause
        $sql_maker->{last_op} = 'where';

        Alzabo::Runtime::process_where_clause( $sql_maker, $where );

lib/Alzabo/SQLMaker.pm  view on Meta::CPAN

Params::Validate::validation_options( on_fail => sub { Alzabo::Exception::Params->throw( error => join '', @_ ) } );

sub new
{
    my $class = shift;
    my %p = @_;

    $p{args} = [] unless defined $p{args};
    $p{quote} ||= [];

    return bless \%p, $class;
}

sub allows_alias { shift->{allows_alias} }

sub as_string
{
    my $self = shift;
    my $driver = shift;
    my $quote = shift;

lib/Alzabo/Schema.pm  view on Meta::CPAN

    my $rdbms = join '', <$fh>;
    close $fh
        or Alzabo::Exception::System->throw( error => "Unable to close $rdbms_file: $!" );

    $rdbms =~ s/\s//g;

    ($rdbms) = $rdbms =~ /(\w+)/;

    # This is important because if the user is using MethodMaker, they
    # might be calling this as My::Schema->load_from_file ...
    bless $schema, $class;

    $schema->{driver} = Alzabo::Driver->new( rdbms => $rdbms,
                                             schema => $schema );

    $schema->{rules} = Alzabo::RDBMSRules->new( rdbms => $rdbms );

    $schema->{sql} = Alzabo::SQLMaker->load( rdbms => $rdbms );

    $schema->_save_to_cache;

lib/Alzabo/Utils.pm  view on Meta::CPAN

package Alzabo::Utils;

use strict;

use Scalar::Util qw( blessed reftype );


sub safe_can
{
    return blessed( $_[0] ) && $_[0]->can( $_[1] );
}

sub safe_isa
{
    return blessed( $_[0] ) && $_[0]->isa( $_[1] );
}

sub is_arrayref
{
    return defined $_[0] && ref $_[0] && reftype $_[0] eq 'ARRAY';
}

sub is_hashref
{
    return defined $_[0] && ref $_[0] && ( ! blessed $_[0] ) && reftype $_[0] eq 'HASH';
}


1;

__END__

=head1 NAME

Alzabo::SQLMaker - Utility functions for other Alzabo modules

t/12-rev-engineer-pg-fk.t  view on Meta::CPAN


unless ( keys %$config )
{
    plan skip_all => 'no Postgres test config provided';
    exit;
}

{
    package FakeSchema;

    sub new { return bless { name => $_[1] }, $_[0] }

    sub db_schema_name { $_[0]->{name} }
}

require DBD::Pg;
require Alzabo::Driver::PostgreSQL;


plan tests => 29;

t/20-rev-engineer-pg-now.t  view on Meta::CPAN


unless ( keys %$config )
{
    plan skip_all => 'no Postgres test config provided';
    exit;
}

{
    package FakeSchema;

    sub new { return bless { name => $_[1] }, $_[0] }

    sub db_schema_name { $_[0]->{name} }
}

require DBD::Pg;
require Alzabo::Driver::PostgreSQL;


plan tests => 6;



( run in 1.907 second using v1.01-cache-2.11-cpan-b32c08c6d1a )