package Myco;

###############################################################################
# $Id: Myco.pm,v 1.1.1.1 2004/11/22 19:16:01 owensc Exp $
#
# See license and copyright near the end of this file.
###############################################################################

=head1 NAME

Myco - The myco object framework

=head1 VERSION

=over 4

=item Release

0.11

=cut

our $VERSION = 0.01;

=item Repository

$Revision: 1.1.1.1 $ $Date: 2004/11/22 19:16:01 $

=back

=head1 SYNOPSIS

 use Myco;

 ### DB connection
 Myco->db_connect(@dbconn);
 Myco->db_disconnect(@dbconn);

 $storage = Myco->storage;      # Tangram connection object

 ### Object retrieval
 $obj = Myco->load($id);        # retrieval by Tangram object id

 # Retrieve all of given class
 @objects = Myco->select('Myco::Foo');

 # Retrieve all of given class, using 'remote' object and filtering
 $remote = Myco->remote('Myco::Foo');
 @objects = Myco->select($remote, $filter);

 # Retrieve all of given class, by cursor
 $cursor = Myco->select('Myco::Foo');
 while (my $obj = $cursor->current()) {
     # process $obj
     $cursor->next();
 }

 ### Object insertion and update
                                # Myco::Base::Entity alternative
 Myco->insert($obj);               # $obj->save;
 Myco->update($obj);               # $obj->save;
 Myco->update(@objects);

 ### Object removal - from db and memory
 Myco->destroy($obj);              # $obj->destroy;
 Myco->destroy(@objects);

 ### Object removal - from just db
 Myco->erase(@objects);

See L<Tangram::Storage|Tangram::Storage> for other miscellany.

=head1 DESCRIPTION

Encapsulates functionality of Tangram::Storage but treats the storage
connection object as class data, allowing access to object
persistence functionality via class method calls.

Intended for use with so-called myco "entity" objects, that is those
belonging to classes that inherit from Myco::Base::Entity.  Use of
inherited instance methods for managing object persistence state where
possible is preferred.  (ie. use C<$obj-E<gt>save> instead of both
C<Myco-E<gt>insert($obj)> and C<Myco-E<gt>update($obj)>.)

Pulls in all other required classes of entire Myco class system.

=cut

### Module Dependencies and Compiler Pragma
require 5.006;

use strict;
use warnings;
use Myco::Exceptions;

use Tangram;
use Myco::Schema;
use WeakRef;
use Myco::Base::Entity::Event;

### Class data
my $_Tstorage;
my $_event_cache;

### Methods
sub storage {
    my ($class, $newval) = @_;
    $_Tstorage = $newval if defined $newval;
    $_event_cache = Myco::Base::Entity::Event->get_event_cache;
    return $_Tstorage;
}

sub is_transient {
    my ($class, $arg) = @_;
    my $id = ref $arg ? $_Tstorage->id($arg) : $arg;
    return 0 unless $id;
    return exists $_Tstorage->{objects}{$id};
}

# We'll have DBI use this coderef to throw exceptions.
my $dbi_err_handler = sub { Myco::Exception::DB->throw(error => shift) };

sub db_connect {
    unless (Myco::Schema->schema) {
	# Hack to allow debugging of what would otherwise be compile-time
	# behavior
	Myco::Schema::mkschema();
    }
    my ($self, $dsn, $user, $pw) = @_;
    return $self->storage(Tangram::Storage->connect(Myco::Schema->schema,
                                                    $dsn, $user, $pw,
                                                    {HandleError =>
                                                     $dbi_err_handler}) )
      unless $self->storage;
}

sub db_disconnect {
    my $self = shift;
    $self->storage->disconnect;
    $self->storage('');
}


##### Localized Tangram::Storage methods

sub load {
    my $obj;
    eval {
	$obj = $_Tstorage->load($_[1]);
    };
    $@ ? undef : $obj;
}

sub destroy {
    my $class = shift;
    my $dbh = $_Tstorage;
    for (@_) {
	my $id = $dbh->id($_);
	Myco->erase($_) if $id;
	$_->clear_refs;
	undef $_;
	$dbh->unload($id) if $id;
    }
    1;
}

sub unload { shift; $_Tstorage->unload(@_) }
sub select { shift; $_Tstorage->select(@_) }
sub remote { shift; $_Tstorage->remote(@_) }
sub id { shift; $_Tstorage->id(@_) }

sub insert {
    my $self = shift;
    my @ids = $_Tstorage->insert(@_);
    for my $entity (@_) {
        Myco::Base::Entity::Event->flush_event($entity)
            if exists $_event_cache->{"$entity"};
    }
    return wantarray ? @ids : shift @ids;
}

sub update {
    my $self = shift;
    for my $entity (@_) {
        Myco::Base::Entity::Event->flush_event($entity)
            if exists $_event_cache->{"$entity"};
    }
    $_Tstorage->update(@_);
}

sub erase {
    my $self = shift;
    for my $entity (@_) {
        my $event = 
          Myco::Base::Entity::Event->new( entity => $entity, kind => 2,
                                          entity_id => $_Tstorage->id($entity)
                                        );
        Myco->insert($event) if $event;
    }
    $_Tstorage->erase(@_);
}

our $AUTOLOAD;
sub AUTOLOAD {
    my $self = shift;
    return if $AUTOLOAD =~ /DESTROY$/;
    $AUTOLOAD =~ /.*::(\w+)/;
    $_Tstorage->$1(@_);
}


package main;

use strict;
use warnings;

1;
__END__

=head1 BUGS

Use of AUTOLOAD for Tangram::Storage encapsulation should be retired
for performance reasons.

=head1 LICENSE AND COPYRIGHT

Copyright (c) 2004 the myco project. All rights reserved.
This software is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.



=head1 SEE ALSO

all L<Tangram|Tangram> -related perldoc,
L<Myco::Base::Entity|Myco::Base::Entity>,

=cut
