package DBIx::Lite::Schema::Table;
$DBIx::Lite::Schema::Table::VERSION = '0.17';
use strict;
use warnings;

use Carp qw(croak);
$Carp::Internal{$_}++ for __PACKAGE__;

sub new {
    my $class = shift;
    my (%params) = @_;
    
    my $self = {
        class           => undef,
        resultset_class => undef,
        pk              => delete $params{pk} || [],
        has_many        => {},
        has_one         => {},
    };
    
    for (qw(name)) {
        $self->{$_} = delete $params{$_} or croak "$_ argument needed";
    }
    
    if ($self->{autopk} = delete $params{autopk}) {
        !ref $self->{autopk} or croak "autopk only accepts a single column";
        $self->{pk} = [$self->{autopk}];
    }
    
    !%params
        or croak "Unknown options: " . join(', ', keys %params);
    
    bless $self, $class;
    $self;
}

sub pk {
    my $self = shift;
    my $val = shift;
    
    if ($val) {
        $self->{pk} = [ grep defined $_, (ref $val eq 'ARRAY' ? @$val : $val) ];
        return $self;
    }
    return @{$self->{pk}};
}

sub autopk {
    my $self = shift;
    my $val = shift;
    
    if ($val) {
        $self->{autopk} = $val;
        $self->{pk} = [$val];
        return $self;
    }
    return $self->{autopk};
}

sub class {
    my $self = shift;
    my $class = shift;
    
    if ($class) {
        $self->{class} = $class;
        return $self;
    }
    
    return undef if !$self->{class};
    $self->_init_package($self->{class}, 'DBIx::Lite::Row');
    return $self->{class};
}

sub resultset_class {
    my $self = shift;
    my $class = shift;
    
    if ($class) {
        $self->{resultset_class} = $class;
        return $self;
    }
    
    return undef if !$self->{resultset_class};
    $self->_init_package($self->{resultset_class}, 'DBIx::Lite::ResultSet');
    return $self->{resultset_class};
}

sub _init_package {
    my $self = shift;
    my ($package, $base) = @_;
    
    return if $package->isa($base);
    
    # check that no $base method would be overwritten by the package
    {
        no strict 'refs';
        my %subroutines = map { $_ => 1 }
            grep defined &{"$package\::$_"}, keys %{"$package\::"};
        
        my @base_subroutines = grep defined &{"$base\::$_"}, keys %{"$base\::"};
        for (@base_subroutines) {
            croak "$package defines a '$_' subroutine/method; cannot use it as custom class"
                if $subroutines{$_};
        }
    }
    
    {
        no strict 'refs';
        push @{$package."::ISA"}, $base;
    }
}

1;


__END__
=pod

=head1 NAME

DBIx::Lite::Schema::Table

=head1 VERSION

version 0.17

=head1 OVERVIEW

This class holds the very loose table definitions that enable some advanced
features of L<DBIx::Lite>. Note that you can do all main operations, including
searches and manipulations, with no need to define any schema.

This class is not supposed to be instantiated manually. You usually get your 
Table objects by calling the C<table()> method on a L<DBIx::Lite::Schema> object:

    my $table = $dbix->schema->table('books');

=head2 pk

This method accepts a list of fields to be used as the table primary key. Setting
a primary key enables C<update()> and C<delete()> methods on L<DBIx::Lite::Row>
objects.

    $dbix->schema->table('books')->pk('id');

=head2 autopk

This method works like L<pk> but also marks the supplied column name as an 
autoincrementing key. This will trigger the retrieval of the autoincremented
id upon creation of new records with the C<insert()> method.
C<autopk()> only accepts a single column.

    $dbix->schema->table('books')->autopk('id');

You probably want to use C<autopk()> for most tables, and only use L<pk> for those
many-to-many relationship tables not having an autoincrementing id:

    $dbix->schema->one_to_many('users.id' => 'users_tasks.user_id');
    $dbix->schema->one_to_many('tasks.id' => 'users_tasks.task_id');
    $dbix->schema->table('users')->autopk('id');
    $dbix->schema->table('tasks')->autopk('id');
    $dbix->schema->table('users_tasks')->pk('user_id', 'task_id');

=head2 class

This method accepts a package name that DBIx::Lite will use for this table's 
Result objects. You don't need to declare such package name anywhere else, as
DBIx::Lite will create that class for you.

    $dbix->schema->table('books')->class('My::Book');
    my $book = $dbix->table('books')->find({ id => 2 });
    # $book is a My::Book

The class will subclass L<DBIx::Lite::Row>. You can also supply an existing package
name or declare your methods inline:

    $dbix->schema->table('books')->class('My::Book');
    
    sub My::Book::get_page_count {
        my $self = shift;
        return $self->page_count;
    }

=head2 resultset_class

This method accepts a package name that DBIx::Lite will use for this table's 
ResultSet objects. You don't need to declare such package name anywhere else, as
DBIx::Lite will create that class for you.

    $dbix->schema->table('books')->resultset_class('My::Book::ResultSet');
    my $books_rs = $dbix->table('books')->search({ year => 2012 });
    # $books_rs is a My::Book::ResultSet

The class will subclass L<DBIx::Lite::ResultSet>. You can also supply an existing 
package name or declare your methods inline:

    $dbix->schema->table('books')->resultset_class('My::Book::ResultSet');
    
    sub My::Book::ResultSet::get_multilanguage {
        my $self = shift;
        return $self->search({ multilanguage => 1 });
    }

=for Pod::Coverage new

=head1 AUTHOR

Alessandro Ranellucci <aar@cpan.org>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2015 by Alessandro Ranellucci.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut

