package Fey::Column; use strict; use warnings; use Scalar::Util qw( blessed weaken ); use Fey::Exceptions qw( param_error object_state_error ); use Fey::Validate qw( validate validate_pos SCALAR UNDEF OBJECT SCALAR_TYPE BOOLEAN_TYPE POS_INTEGER_TYPE POS_OR_ZERO_INTEGER_TYPE TABLE_TYPE ); use Fey::Column::Alias; use Fey::Literal; use Fey::Table; use Fey::Table::Alias; use Moose::Policy 'MooseX::Policy::SemiAffordanceAccessor'; use MooseX::StrictConstructor; use Moose::Util::TypeConstraints qw( subtype as where coerce from via class_type find_type_constraint ); with 'Fey::Role::ColumnLike'; 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; return Fey::Column::Alias->new( column => $self, @_ ); } 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 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 object. If a scalar is provided, it is turned into a C 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 object to which the column belongs, if any. =head2 $column->alias(%p) This method returns a new C object based on the column. Any parameters passed to this method will be passed through to C<< Fey::Column::Alias->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 role. =head1 AUTHOR Dave Rolsky, =head1 BUGS See L 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