package Fey::Column;
use strict;
use Scalar::Util qw( blessed weaken );
use Fey::Exceptions qw( param_error object_state_error );
qw( validate validate_pos
SCALAR UNDEF OBJECT
SCALAR_TYPE BOOLEAN_TYPE
POS_INTEGER_TYPE POS_OR_ZERO_INTEGER_TYPE
TABLE_TYPE );
use Moose;
use Moose::Util::TypeConstraints qw( subtype as where coerce from via class_type find_type_constraint );
has 'id' =>
( is => 'ro',
lazy_build => 1,
init_arg => undef,
clearer => '_clear_id',
);
has 'name' =>
( is => 'ro',
isa => 'Str',
required => 1,
);
subtype 'Fey.Type.GenericTypeName'
=> as 'Str'
=> where { /^(?:text|blob|integer|float|date|datetime|time|boolean|other)$/xism };
has 'generic_type' =>
( is => 'ro',
isa => 'Fey.Type.GenericTypeName',
lazy_build => 1,
);
has type =>
( is => 'ro',
isa => 'Str',
required => 1,
);
subtype 'Fey.Type.PosInteger'
=> as 'Int'
=> where { $_ > 0 };
has length =>
( is => 'ro',
isa => 'Fey.Type.PosInteger',
required => 0
);
subtype 'Fey.Type.PosOrZeroInteger'
=> as 'Int'
=> where { $_ >= 0 };
# How to say that precision requires length as well?
has precision =>
( is => 'ro',
isa => 'Fey.Type.PosOrZeroInteger',
required => 0
);
has is_auto_increment =>
( is => 'ro',
isa => 'Bool',
default => 0,
);
has is_nullable =>
( is => 'ro',
isa => 'Bool',
default => 0,
);
subtype 'Fey.Type.DefaultValue'
=> as 'Fey::Literal';
coerce 'Fey.Type.DefaultValue'
=> from 'Undef'
=> via { Fey::Literal::Null->new() }
=> from 'Value'
=> via { Fey::Literal->new_from_scalar($_) };
has default =>
( is => 'ro',
isa => 'Fey.Type.DefaultValue',
coerce => 1,
);
{
for my $class ( qw( Fey::Table Fey::Table::Alias ) )
{
class_type($class)
unless find_type_constraint($class);
}
has 'table' =>
( is => 'rw',
isa => 'Fey::Table | Fey::Table::Alias',
weak_ref => 1,
writer => '_set_table',
clearer => '_clear_table',
);
}
after '_set_table', '_clear_table' =>
sub { $_[0]->_clear_id() };
{
my @TypesRe =
( [ text => qr/(?:text|char(?:acter)?)\b/xism ],
[ blob => qr/blob\b|bytea\b/xism ],
# The year type comes from MySQL
[ integer => qr/(?:int(?:eger)?\d*|year)\b/xism ],
[ float => qr/(?:float\d*|decimal|real|double|money|numeric)\b/xism ],
# MySQL's timestamp is not always a datetime, it depends on
# the length of the column, but this is the best _guess_.
[ datetime => qr/datetime\b|^timestamp/xism ],
[ date => qr/date\b/xism ],
[ time => qr/^time|time\b/xism ],
[ boolean => qr/\bbool/xism ],
);
sub _build_generic_type
{
my $self = shift;
my $type = $self->type();
for my $p (@TypesRe)
{
return $p->[0] if $type =~ /$p->[1]/;
}
return 'other';
}
}
sub _clone
{
my $self = shift;
my %clone = %{$self};
return bless \%clone, ref $self;
}
sub is_alias { return 0 }
sub alias
{
my $self = shift;
my %p = @_ == 1 ? ( alias_name => $_[0] ) : @_;
return Fey::Column::Alias->new( column => $self, %p );
}
sub sql
{
my $self = shift;
my $dbh = shift;
return
$dbh->quote_identifier( undef,
$self->_containing_table_name_or_alias(),
$self->name(),
);
}
sub sql_with_alias { goto &sql }
sub sql_or_alias { goto &sql }
sub _build_id
{
my $self = shift;
my $table = $self->table();
object_state_error
'The id attribute cannot be determined for a column object which has no table.'
unless $table;
return $table->id() . q{.} . $self->name();
}
no Moose;
no Moose::Util::TypeConstraints;
__PACKAGE__->meta()->make_immutable();
1;
__END__
=head1 NAME
Fey::Column - Represents a column
=head1 SYNOPSIS
my $column = Fey::Column->new( name => 'user_id',
type => 'integer',
is_auto_increment => 1,
);
=head1 DESCRIPTION
This class represents a column in a table.
=head1 METHODS
This class provides the following methods:
=head2 Fey::Column->new()
This method constructs a new C<Fey::Column> object. It takes the
following parameters:
=over 4
=item * name - required
The name of the column.
=item * type - required
The type of the column. This should be a string. Do not include
modifiers like length or precision.
=item * generic_type - optional
This should be one of the following types:
=over 8
=item * text
=item * blob
=item * integer
=item * float
=item * date
=item * datetime
=item * time
=item * boolean
=item * other
=back
This indicate a generic type for the column, which is intended to
allow for a common description of column types across different DBMS
platforms.
If this parameter is not specified, then the constructor code will
attempt to determine a reasonable value, defaulting to "other" if
necessary.
=item * length - optional
The length of the column. This must be a positive integer.
=item * precision - optional
The precision of the column, for float-type columns. This must be an
integer >= 0.
=item * is_auto_increment - defaults to 0
This indicates whether or not the column is auto-incremented.
=item * is_nullable - defaults to 0
A boolean indicating whether the column is nullab.e
=item * default - optional
This must be either a scalar (including undef) or a C<Fey::Literal>
object. If a scalar is provided, it is turned into a C<Fey::Literal>
object via C<< Fey::Literal->new_from_scalar() >>.
=back
=head2 $column->name()
=head2 $column->type()
=head2 $column->generic_type()
=head2 $column->length()
=head2 $column->precision()
=head2 $column->is_auto_increment()
=head2 $column->is_nullable()
=head2 $column->default()
Returns the specified attribute.
=head2 $column->table()
Returns the C<Fey::Table> object to which the column belongs, if any.
=head2 $column->alias(%p)
=head2 $column->alias($alias_name)
This method returns a new C<Fey::Column::Alias> object based on the
column. Any parameters passed to this method will be passed through to
C<< Fey::Column::Alias->new() >>.
As a shortcut, if you pass a single argument to this method, it will
be passed as the "alias_name" parameter to C<<
Fey::Table::Column->new() >>.
=head2 $column->is_alias()
Always returns false.
=head2 $column->sql()
=head2 $column->sql_with_alias()
=head2 $column->sql_or_alias()
Returns the appropriate SQL snippet for the column.
=head2 $column->id()
Returns a unique identifier for the column.
=head1 ROLES
This class does the C<Fey::Role::ColumnLike> role.
=head1 AUTHOR
Dave Rolsky, <autarch@urth.org>
=head1 BUGS
See L<Fey> for details on how to report bugs.
=head1 COPYRIGHT & LICENSE
Copyright 2006-2008 Dave Rolsky, All Rights Reserved.
This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.
=cut