NAME
Object::POOF - Persistent Object Oriented Framework (for mod_perl)
VERSION
This document describes Object::POOF version 0.0.6
SYNOPSIS
some of this doc may be wrong. this suite is still highly experimental and subject to change but i am releasing this version because i have a contract starting and i am always suspicious of corporations trying to lay claim to my work. - Mark Hedges 2005-09-06
package MyApp::Foo;
# a table named 'foo' exists in db with autoinc 'id' field
use Readonly;
use base qw(Object::POOF);
# make sure to make @more_tables and %relatives Readonly in 'our' scope.
# (outside of the private block used by Class::Std)
# this makes them accessible through the class without making an object.
# (but they cannot be changed by anything but you, with a text editor.)
# other tables containing fields to be joined
# to 'foo' by 'id' field when fields are requested:
Readonly our @more_tables => qw( foo_text foo_info foo_html );
# relationships to other classes:
Readonly our %relatives => (
# self to other relationships (one to one):
s2o => {
# a table named 'bar' exists in db, and foo.bar_id = bar.id
bar => {
class => 'MyApp::Bar',
},
},
# self contains many -
# each foo has only one rel to each sCm entity
sCm => {
# table named 'biz'; foo.id = biz.foo_id
biz => {
class => 'MyApp::Biz',
},
},
# self to many - possibly more than one rel to each s2m entity,
# uniquely enforced by a field of the interim relational table
s2m => {
baz => {
class => 'MyApp::Baz',
reltable => 'r_foo_baz',
relkey => 'boz_id', # a 'boz' will be made here
},
},
# many to many - possibly more than one rel to each m2m entity,
# with no restrictions (reltable entry can be exactly duplicated)
m2m => {
boz__noz => {
class => 'MyApp::Boz::Noz',
reltable => 'r_foo_boz__noz',
},
},
);
use Class::Std;
{
sub BUILD {
my ($self, $ident, $arg_ref) = @_;
$self->set_more_tables( \@more_tables );
$self->set_relatives( \%relatives );
}
}
1; # end of package definition, unless you add custom object methods.
########
# later in program...
# a MyApp::DB thread inheriting from Object::POOF::DB
my $db = MyApp::DB->new();
my $foo = MyApp::Foo->new( {
db => $db,
where => {
# where a field of foo = some value
fooprop => 'value',
},
follow => {
sCm => {
biz => {
what => 'all',
},
},
s2m => {
baz => {
what => [ qw( baz_prop1 baz_prop2 baz_prop3 ) ],
follow => {
m2m => {
# MyApp::Baz m2m MyApp::Schnoz, follow it
schnoz => {
what => [qw( schnoz_prop1 schnoz_prop2 )],
},
},
},
},
},
},
});
print "$foo->bar->barprop\n"; # s2m's are constructed by default
# call a herd in array context (similar in arrayref context):
foreach my $biz ($foo->bar->biz) {
$biz->somemethod( $foo->fooprop ); # or something
}
# call a herd in hash context (similar in hashref context):
while (my ($baz_id, $baz) = each $foo->baz) {
print "adding rel for $baz_id\n";
$baz->add_rel( {
m2m => {
schnoz => [ $schnoz1, $schnoz2, $schnoz3, ],
},
});
$baz->delete_rel( {
m2m => {
schnoz => $schnoz4,
},
});
}
DESCRIPTION
Object::POOF provides a Persistent Object Oriented Framework using Perl Best Practices (as outlined in the O'Reilly book by that name.) It also provides a framework for applications running under mod_perl custom handlers (or, yuck, CGI scripts) that will handle de-tainting from easy form patterns, exporting of patterns to Javascript form validation, users and uri-based function permissions, and hopefully an accounting suite eventually.
For an OO application designer to get started, all they have to do is define the relationships of entity packages to other entities. Calls to various select and save routines are usually passed a hash reference similar in structure to the hash used to define relationships. Once you are into 'the zone' of writing with Object::POOF, you can stay in the mindset of the relationships between entities and (mostly) forget about writing SQL. For large group selects such as the one above using the 'follow' hashref, Object::POOF internals will format a huge join statement, parse it and populate all objects. It does not duplicate population of objects with the same id, instead it keeps a 'Ranch' or pool of entities and then links them into the appropriate 'Herds' related to your point of view. The Object::POOF::Ranch can be used to do mass-selects of heavy data after following relations, and is intended to be an 'entity pool' to improve performance of mod_perl apps under Apache2 worker mpm. (This will require considerable thread-safe development.)
Then, you can call values of fields by identical accessor names and the next "herd" of related objects by accessors named the short names in the rel hashes. If you call an accessor that refers to valid fields or related entities that haven't been populated yet, they will be followed, so you can do 'lazy population.' But beware, with large groups of entities (like for a tabular report, for example), you'll have to use a follow call to get them all at once. But this can have its drawbacks too, since it uses a big left join that will make some data redundant. So, if there are big text fields or blobs or something in an intermediary relation of your depth of selection, and you think that will slow down the query, leave them out and then call a mass-select method for them:
# following the above example, populate the many schnoz herds
# with heavy text fields left out of the original follow query:
my $ranch = $foo->get_ranch();
$ranch->load( {
class => 'MyApp::Schnoz',
fields => [ qw( text1 text2 ) ],
});
The Schnoz's will remain linked in the original related locations under your point of view from $foo->baz, but now for each Schnoz, $schnoz->text1 will not have to do a lazy select. The point is if you have 10,000 Schnoz's linked through in $foo, and you want to get the big text1 field from each of them, you don't want to slow down the original follow join with the field, but you don't want to have it do 10,000 queries either. The general rule is you have to think about what you're doing to make it most efficient.
INTERFACE
DIAGNOSTICS
Error message here, perhaps with %s placeholders
-
[Description of error here]
Another error message here
-
[Description of error here]
[Et cetera, et cetera]
CONFIGURATION AND ENVIRONMENT
Object::POOF requires no configuration files or environment variables.
DEPENDENCIES
Relies on use of transactional database. Currently only uses MySQL's InnoDB engine.
Object::POOF and its sub-modules require the following modules:
Class::Std
Class::Std::Utils
Attribute::Types
Contextual::Return
Exception::Class
Perl6::Export::Attrs
Readonly
List::MoreUtils
Regexp::Common
Data::Serializer
YAML
INCOMPATIBILITIES
None reported. (Yeah right.)
BUGS AND LIMITATIONS
The LISP-ish excessive ending braces for call statements are annoying.
Because internals format a giant left join, it often decreases efficiency of SQL calls to select all information at once. Sticking to the id fields and small data is a good idea, then call mass-select population methods in the ranch (see Object::POOF::Ranch).
The major bug as of this writing is that nothing actually works. :-D
Please report any bugs or feature requests to bug-object-poof@rt.cpan.org
, or through the web interface at http://rt.cpan.org.
PLANS
At some point the Shepherd should start constructing custom Herd objects that implement more complex data structures based on key name fields like 'up_id', 'child_id', etc. that self-refer to objects of the same class/table.
SEE ALSO
Object::POOF::App(3pm), Object::POOF::DB(3pm), Object::POOF::Ranch(3pm), Object::POOF::Shepherd(3pm)
AUTHOR
Mark Hedges <hedges@ucsd.edu>
LICENCE AND COPYRIGHT
Copyright (c) 2005, Mark Hedges <hedges@ucsd.edu>
. All rights reserved.
This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See perlartistic.
DISCLAIMER OF WARRANTY
BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR CORRECTION.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.