NAME
    ORM::Tutorial - Guided tour to ORM module.

SYNOPSIS
    Purpose of this document is to brief introduce usage of PerlORM library
    on simple example. Example is 'Tasks Planner' (or 'Todo List')
    application.

OBJECT MODEL
    Let's start with simple object model, which will be improved and
    modified as needed later. Object classes of our example application are:

    1. Task
        Properties:

        * Title (title)
        * Detailed description (desc)
        * Creation time (created)
        * Start time (start_date), can be undef
        * End time (end_date), can be undef
        * Deadline (deadline), can be undef
        * Responsible worker (worker)

    2. Worker
        Properties:

        * Worker name (name)

    First step in creation of object model is to create so called initial
    class. Initial class is base class for all classes of our object model.

    File Todo/ORM.pm

      package Todo::ORM;
  
      use ORM::Db::DBI::MySQL;
      use base 'ORM';
  
      Todo::ORM->_init
      (
          prefer_lazy_load     => 0,
          emulate_foreign_keys => 1,
          default_cache_size   => 200,
  
          db => ORM::Db::DBI::MySQL->new
          (
              host        => 'localhost',
              database    => 'todo_list',
              user        => 'root',
              password    => '',
          ),
      );
  
      1;

    Now let's declare classes of our model.

    PerlORM was developed with usage simplicity in mind therefore it does
    not require you to declare class properties and relations in both class
    declaration and database. Creation of database table for storing objects
    of the class is quite enough. Fields in this table are correspond to
    object properties.

    Also there is tool-script in development that allows to screate database
    tables and templates of Perl-modules from UML schemes, but if you
    doesn't want to spend time to build additional class or database schemes
    you are always able to use short way.

    One or more database tables are assigned to each class (more than one
    table is used in case of inheritance). Each object of the class is
    represented by single row in table or inner join of rows in case of
    inheritance.

    Initial declaration of classes looks very simple:

    File Todo/Task.pm

      package Todo::Task;
  
      $VERSION=0.1;
  
      use ORM::Base 'Todo::ORM';

    File Todo/Worker.pm

      package Todo::Worker;
  
      $VERSION=0.1;
  
      use ORM::Base 'Todo::ORM';

    There is one question: how PerlORM detects what table to use for certain
    class? If table name is not specified obviously then ORM class calls
    method "_guess_table_name" which is by default uses regexp "$class =~
    s/::/_/g;" to detect table name from class name. You can change this
    behaviour by overriding "_guess_table_name" method in your initial
    class. For example:

      sub _guess_table_name
      {
          my $my_class = shift;
          my $class = shift;
          my $table;
  
          $table = substr( $class, index( $class, '::' )+2 );
          $table =~ s/::/_/g;
  
          return $table;
      }

    Now table for class "Todo::Task" should be named "Task" and not
    "Todo_Task".

    It's time to create database tables. (Name of database being used is
    specified in storage driver constructor.)

      CREATE DATABASE todo_list;
  
      DROP TABLE IF EXISTS `todo_list`.`_ORM_refs`;
      CREATE TABLE `_ORM_refs` (
          `class` varchar(45) NOT NULL default '',
          `prop`  varchar(45) NOT NULL default '',
          `ref_class` varchar(45) NOT NULL default '',
          PRIMARY KEY  (`class`,`prop`)
      ) ENGINE=InnoDB;
  
      INSERT INTO `_ORM_refs` VALUES ( 'Todo::Task', 'worker', 'Todo::Worker' );
  
      DROP TABLE IF EXISTS `todo_list`.`Task`;
      CREATE TABLE `task` (
          `id` bigint(20) unsigned NOT NULL auto_increment,
          `title` varchar(255) NOT NULL default '',
          `desc` text NOT NULL,
          `created` date default NULL,
          `start_date` date default NULL,
          `deadline` date default NULL,
          `worker` bigint(20) unsigned default NULL,
          PRIMARY KEY  (`id`)
      ) ENGINE=InnoDB;
  
      DROP TABLE IF EXISTS `todo_list`.`Worker`;
      CREATE TABLE `worker` (
          `id` bigint(20) unsigned NOT NULL auto_increment,
          `name` varchar(100) NOT NULL default '',
          PRIMARY KEY  (`id`)
      ) ENGINE=InnoDB;

    We just created 3 tables, first of them "_ORM_refs" is special table.
    ORM uses it to detect relations between classes in our model and usage
    of third party classes. In our model "worker" property of class
    "Todo::Task" should be reference to an object of class "Todo::Worker".
    To tell it to ORM we should insert following row in "_ORM_refs":

      class       | prop      | ref_class
      ------------------------------------
      Todo::Task  | worker    | Todo::Worker

    This is the usual way the relations between classes are defined. Note
    that when you define reference property in "Todo::Task" class to
    "Todo::Worker", the second one automatically gets so called reversive
    property or collection property that contains all tasks assigned to
    particular worker object. In RDBMS terms such kind of relation is called
    one-to-many.

    For frequently used classes there is another way to define relations
    between objects, this way is to override "ORM::_db_type_to_class" method
    in our initial class. "_db_type_to_class" method accepts table field
    name and type as its arguments and returns class that should be assigned
    to property.

    The default behavior of "_db_type_to_class" method defined in ORM class
    is to assign classes ORM::Date and ORM::Datetime to properties described
    by fields of type "DATE" and "DATETIME" respectively. This behaviour is
    also can be changed to use another classes to represend dates and times,
    the chapter "THIRD PARTY CLASSES" tells how to do it.

    Every table that is used with ORM should have autoincremented field "id"
    which stored ID of objects of corresponding class. (Functionality that
    allows to use arbitrary column or multiple columns to represent object
    ID is subject to furthrer development.)

CREATING OBJECTS
    Creation of objects in ORM is performed by calling 'new' method. For
    example let's create 'Worker' object:

      use Todo::Worker;
  
      $error  = ORM::Error->new;
      $worker = Todo::Worker->new
      (
          prop  => { name => 'E. Cartman' },
          error => $error,
      );
  
      print $error->text;

    If 'new' operation fails then $error object contains information about
    occured errors. Use of $error object is not necessary but strongly
    recomended.

    Now to more easily manage objects of our model we will create perl
    script for object creation "new.pl"

    File new.pl

      #!/usr/bin/perl
      #
      # Use: perl new.pl <Class> <Prop1Name> <Prop1Value> <Prop2Name> <Prop2Value>...
      #
      # Class - Name of the class without 'Todo::' prefix.
      #
  
      use lib "lib";
      use lib "../ORM/lib";
  
      $nick  = shift;
      $class = "Todo::$nick";
  
      eval "require $class" or die $@;
  
      $error = ORM::Error->new;
      %prop  = @ARGV;
      $obj   = $class->new( prop=>\%prop, error=>$error );
  
      if( $obj )
      {
          print "New $nick was created with id:".$obj->id."\n" if( $obj );
          $obj->print;
      }
  
      print $error->text;

    Above script uses "print" method we doesn't declare yet. This method is
    aimed to print plain text information about specified object. This
    method should be defined in initial class so every object of our model
    can acces it.

      sub print
      {
          my $self  = shift;
          my $ident = shift||0;
          my @ref;
  
          # Do not dive deeper that third level of recursion
          # when printing information about related objects.
  
          return if( $ident > 3 );
  
          # Print information about specified object
  
          print ' 'x($ident*2),('-'x20),"\n";
          for my $prop ( (ref $self)->_all_props )
          {
              printf "%".(20+$ident*2)."s %s\n", "$prop:",
                  (defined $self->_property_id($prop) ? $self->_property_id($prop) : '*UNDEF*');

              if( (ref $self)->_prop_is_ref( $prop ) && $self->_property( $prop ) )
              {
                  push @ref, $prop;
              }
          }
          print ' 'x($ident*2),('-'x20),"\n\n";
  
          # Print information about related objects
  
          for my $prop ( @ref )
          {
              print ' 'x(($ident+1)*2),"Related object '$prop':\n";
              $self->_property( $prop )->print( $ident+1 );
          }
      }

    Note the way properties were accessed. For this purpose
    "$obj->_property( $property_name )" were used in above code. To access
    object properties with more grace there is AUTOLOAD method. So you can
    simply call "$obj->$property_name()" (or just "$obj->deadline" for
    example). The "ORM::_property" method is for cases when $property_name
    method should be redefined in child class for some reason.

    Also you can use "$obj->_property_id( $property_name )" to get raw
    database value of the property. Its result is:

    * the same as of "ORM::_property" for plain (non-object) properties
    * database property value for non-ORM third party classes
    * object's ID for ORM classes

    Now we can fill our model with some more objects.

      # perl new.pl Worker name "Kenny McCormic"
      New Worker was created with id:2
      --------------------
                       id: 2
                    class: Todo::Worker
                     name: Kenny McCormic
      --------------------
  
      # perl new.pl Task \
            title "Kill Kenny" \
            desc "Just kill Kenny!" \
            worker 1 \
            created "2005-12-18" \
            start_date "2006-01-01" \
            deadline "2006-01-02"
  
      New Task was created with id:1
      --------------------
                       id: 1
                    class: Todo::Task
                  created: 2005-12-18
                     desc: Just kill Kenny!
                   worker: 1
                 deadline: 2006-01-02
                    title: Kill Kenny
               start_date: 2006-01-01
      --------------------
  
        Related object 'worker':
        --------------------
                         id: 1
                      class: Todo::Worker
                       name: Eric Cartman
        --------------------
  
      # perl new.pl Task \
            title "Eat Chocolate pie" \
            desc "Ask your mummy." \
            worker 1 \
            created "2005-12-18" \
            start_date "2006-01-01" \
            deadline "2006-01-02"
  
      New Task was created with id:2
      --------------------
                       id: 2
                    class: Todo::Task
                  created: 2005-12-18
                     desc: Ask your mummy.
                   worker: 1
                 deadline: 2006-01-02
                    title: Eat Chocolate pie
               start_date: 2006-01-01
      --------------------
  
        Related object 'worker':
        --------------------
                         id: 1
                      class: Todo::Worker
                       name: Eric Cartman
        --------------------

    For more comfort let's modify "Todo::Task" class so it can automatically
    assign current time to "created" property when explicit value is not
    specified:

      sub _validate_prop
      {
          my $self = shift;
          my %arg  = @_;
  
          if( $arg{method} eq 'new' && ! $self->created )
          {
              $self->_fix_prop
              (
                  prop  => { created=>ORM::Date->current },
                  error => $arg{error},
              );
          }
  
          $self->SUPER::_validate_prop( %arg );
      }

    * Method "_validate_prop" is implicitly called when new object is being
    created ("new" method) and when object is being updated ("update"
    method).
    * Condition "( $arg{method} eq 'new' )" is true only when called from
    within "new" method. In another words this means that object is not yet
    stored in database table.
    * Method "ORM::_fix_prop" is intended to use only within
    "_validate_prop".
    * Do not forget to call "SUPER::_validate_prop".

    Let's add one more task:

      # perl new.pl Task \
            title "Keep alive" \
            desc "Just keep alive!" \
            worker 2 \
            start_date "2005-12-31" \
            deadline "2006-01-02"
  
      New Task was created with id:3
      --------------------
                       id: 3
                    class: Todo::Task
                  created: 2005-12-18
                     desc: Just keep alive!
                   worker: 2
                 deadline: 2006-01-02
                    title: Keep alive
               start_date: 2005-12-31
      --------------------
  
        Related object 'worker':
        --------------------
                         id: 2
                      class: Todo::Worker
                       name: Kenny McCormic
        --------------------

    As you can see "created" property is implicitly initialized with default
    value of current time. (It seems like Kenny will die anyway after
    deadline.)

UPDATING OBJECTS
    Update of object is performed with the same simplicitly:

      use Todo::Worker;
  
      $error  = ORM::Error->new;
      $worker = Todo::Worker->find_id( id=>1, error=>$error );
      $worker && $worker->update( prop=>{ name=> 'Eric Cartman' }, error=>$error );
  
      print $error->text;

    To easily update objects from command line there will be another script
    that is very similar to "new.pl".

    File update.pl

      #!/usr/bin/perl
      #
      # Use: perl update.pl <Class> <ObjectID> <Prop1Name> <Prop1Value> <Prop2Name> <Prop2Value>...
      #
      # Class - Name of the class without 'Todo::' prefix.
      #
  
      use lib "lib";
      use lib "../ORM/lib";
  
      $nick  = shift;
      $class = "Todo::$nick";
  
      eval "require $class" or die $@;
  
      $id    = shift;
      $error = ORM::Error->new;
      %prop  = @ARGV;
      $obj   = $class->find_id( id=>$id, error=>$error );
  
      if( $obj )
      {
          $obj->update( prop=>\%prop, error=>$error ) unless( $error->fatal );
          print "Updated $nick with id:".$obj->id."\n";
          $obj->print;
      }
      else
      {
          print STDERR "Object #$id of $class not found!\n";
      }
  
      print $error->text;

SELECTING AND FILTERING
    Now when we have some tasks planned for workers it's time to make some
    reports about tasks state. Interesting reports are:

    * Tasks planned to be done by specific worker
    * Tasks that should be done due specified date

    Tasks for first report can be selected as follows:

      ORM::DbLog->write_to_stderr( 1 );
      @tasks = Todo::Task->find
      (
          filter => ( Todo::Task->M->worker == $worker ),
          error  => $error,
      );

    "Todo::Task->M->worker" - is so named Meta-property, Meta-property is
    object of class "ORM::Metaprop" or its descendants. In resulting
    SQL-query Meta-properties are replaced with names of corresponding table
    fields. Special meta-property "Todo::Task->M" means object of class
    "Todo::Task" itself. Below you will see that meta-properties is very
    powerful facility and is also easy to use.

    Variable $worker should contain "Todo::Worker" object or just its
    integer ID.

    Variable $error of type "ORM::Error" contains description of error if
    any occured during query. "error" parameter is not required, if it is
    omitted then error is silently ignored. In future version this behavious
    can be changed.

    Call "ORM::DbLog->write_to_stderr( 1 )" enables trace of so called
    SQL-log to STDERR this is useful tool for debugging you code. In
    described case (assuming $worker=1) SQL-log trace looks as follows:

      --------------------------
      [Mon Dec 26 00:14:27 2005]: ORM::find: Success
      SELECT
        DISTINCT `Task`.*
      FROM
        `Task`
      WHERE
        (`worker` = '1')

    If we need to select tasks by worker name, then call looks like this:

      @tasks = Todo::Task->find
      (
          filter => ( Todo::Task->M->worker->name eq $worker_name ),
          order  => ORM::Order->new( [ Todo::Task->M->created, 'DESC' ] ),
          error  => $error,
      );

    Draw attention on using of operators "==" and "eq". Databases usually
    have no sence to this operator because in most cases they are translated
    to SQL "=" operator which is used for both string and numeric
    comparisons. Nevertheless for best readability it is reasonable to use
    this operators as in native Perl.

    Parameter 'order' specifies that found tasks should be sorted by
    "created" time in descendant order.

    Let's try little more complicated query when we need to find tasks
    assigned to workers containing some string in their names:

      @tasks = Todo::Task->find
      (
          filter => ( Todo::Task->M->worker->name->_like( '%Cartman%' ) ),
          order  => ORM::Order->new( [ Todo::Task->M->created, 'DESC' ] ),
          error  => $error,
      );

    Resulting SQL-query for the call:

      SELECT
        DISTINCT `_T1_Task`.*
      FROM
        `Task` AS `_T1_Task`
          LEFT JOIN `Worker` AS `_T2_Worker` ON( `_T1_Task`.`worker`=`_T2_Worker`.`id` )
      WHERE
        (`_T2_Worker`.`name` LIKE '%Cartman%')
      ORDER BY `_T1_Task`.`created` DESC

    Call for second report looks much similar:

      $M     = Todo::Task->M;
      @tasks = Todo::Task->find( filter => ( $M->deadline < '2006-01-30' ) );

    Variable $M is for brevity, such trick is useful when constructing
    complex meta-expressions.

    Filters can be logically grouped with arbitrary complexity using
    "ORM::Expr->_and" and "ORM::Expr->_or" methods. In simplest case you can
    use overloaded '&' and '|' operators:

      $M     = Todo::Task->M;
      @tasks = Todo::Task->find
      (
          filter =>
            ( $M->deadline < '2006-01-30' )
            & Todo::Task->M->worker->name->_like( '%Cartman%' )
      );

    Be careful with operator priorities while constructing expressions with
    these operators.

    There is another interesting report about number of tasks assigned to
    each worker, for this report we will use "stat" method, This method is
    useful when you need info about related objects:

      $M   = Todo::Worker->M;
      $res = Todo::Worker->stat
      (
          data =>
          {
              worker => $M,
              tasks  => $M->_rev( 'Todo::Task' => 'worker' )->_count,
          },
          group_by => [ $M ],
          preload  => { worker=>1 },
      );

    Opposite to "find" method which returns array of objects, "stat" method
    returns array of hashes with requested data.

    Parameter "data" is hash reference that defines what kind of data should
    be retrieved from database. Resulting hash contains records with exactly
    the same keys as in "data" parameter and with values retrieved from
    database as specified by values of "data".

    In our case $res contains hashes with two keys "worker" - "Todo::Worker"
    object and "tasks" - number of assigned tasks.

    Parameter "group_by" similar to SQL "GROUP BY" statement. In resulting
    SQL-query "group_by" is replaced with "GROUP BY". It is used to define
    how to apply grouping method "_count".

    Parameter "preload" defines objects that should be loaded by resulting
    query and not later by separate query.

    Meta-property "$M->_rev( 'Todo::Task' => 'worker' )" so called reversive
    meta-property. It is used to access objects that refer by one of their
    property to objects being selected. In our case objects of class
    "Todo::Task" referring to objects of class "Todo::Worker" by property
    "worker", therefore we can reversively access tasks assigned to a
    worker.

    SQL-query for the call:

      --------------------------
      [Mon Dec 26 00:49:34 2005]: ORM::stat: Success
      SELECT
        'Todo::Worker' AS `_worker class`,
        COUNT( `_T2_Task`.`id` ) AS `tasks`,
        `_T1_Worker`.`id` AS `worker`,
        `_T1_Worker`.`name` AS `_worker name`
      FROM
        `Worker` AS `_T1_Worker`
          LEFT JOIN `Task` AS `_T2_Task` ON( `_T1_Worker`.`id`=`_T2_Task`.`worker` )
      GROUP BY `_T1_Worker`.`id`

DELETING OBJECTS
    Method 'delete' is used for deletion of objects from database.

      $worker->delete( error=>$error );

    If 'emulate_foreign_keys' option to "ORM::_init" method is set to true
    then before deletion ORM checks if there are another objects that refer
    to object being deleted. If so $error object contains corresponding
    error message and object is not deleted.

    After object $worker has been deleted from database it is reblessed to
    be object of class "ORM::Broken". Call to any method of this object will
    croak with error message. This is to be sure that object is not being
    used after it has been deleted.

ERROR HANDLING
    Error handling is done by passing 'error' argument to almost every ORM
    method. 'error' argument should contain object of class "ORM::Error".
    Consider the following code:

      use Todo::Task;
  
      $task  = Todo::Task->find; # first found task
      $error = ORM::Error->new;
      $task->update
      (
          prop  => { worker=>-1 },
          error => $error,
      );
      print STDERR "Failed to update\n" if( $error->fatal );
      print STDERR $error->text;

    Output for this code:

      Failed to update
      fatal: ORM->_normalize_prop_to_db_value(): Property 'worker' of type 'Todo::Worker' with id='-1' was not found  

    Classes of our object model also can produce their own errors and send
    them to the caller.

    For example we should not permit to set "Todo::Task->deadline" property
    to be less than current date. To implement this functionality we will
    modify "Todo::Task" class by adding new condition to "_validate_prop"
    method:

      if( $self->deadline && $self->deadline < ORM::Datetime->current )
      {
          $arg{error}->add_fatal( "Deadline is in past!" );
      }

    As far as "_validate_prop" method is called only implicitly from 'new'
    and 'delete' methods, you can be sure that $arg{error} contains correct
    "ORM::Error" object. That's why there is no reason to test it before
    use.

    In cases when you not sure that user has passed $error object to your
    method you can use following scheme:

      sub my_metod
      {
          my $self  = shift;
          my %arg   = @_;
          my $error = ORM::Error->new;
  
          $error->add_fatal( "test 1" );
          $error->add_warn( "test 2" );
  
          $error->upto( $arg{error} );
          return undef;
      }

    Call to 'upto' method sends error back to user if $arg{error} contains
    valid error-object. It is equivalent to "$arg{error} &&
    $arg{error}->add( $error )".

    In current ORM version all errors are ignored if 'error' argument is not
    passed to a method. But it seems like a good practice to throw
    exceptions in such cases. This functionality is to be implemented in
    future releases without affecting object interface.

META-CLASSES
    Why do you need meta-classes? Meta-classes give you the way to make
    meta-objects (or meta-properties) behave almost the same as objects
    itself. So you can construct meta-expressions the way similar to those
    for usual Perl-expressions.

    (To be documented)

THIRD PARTY CLASSES (To be documented)
OBJECT CHANGES HISTORY
    To enable built-in feature to trace object changes simply use
    "history_class" argument to "ORM::_init" method as shown below:

      Todo::ORM->_init
      (
          history_class => 'Todo::History',
          %another_args,
      );

    Next step is to declare "Todo::History" class. This class behaves as any
    other regular ORM-class of our model. This means that any change of any
    object is introduced by one or more 'Todo::History' object. History
    class declaration is quite simple:

    File Todo/History.pm

      package Todo::History;
  
      $VERSION=0.1;
  
      use ORM::Base 'Todo::ORM', i_am_history=>1;
  
      1;

    As we have redefined "ORM::_guess_table_name" method, the table for
    "Todo::History" class has assumed name 'History'. Structure of the table
    should be as follows:

      DROP TABLE IF EXISTS `todo_list`.`History`;
      CREATE TABLE `History` (
  
          `id`        bigint(20)   NOT NULL auto_increment,
  
          `obj_class` varchar(100) NOT NULL default '',
          `obj_id`    int(11)      NOT NULL default '0',
  
          `prop_name` varchar(100) NOT NULL default '',
          `old_value` varchar(255)          default '',
          `new_value` varchar(255)          default '',
  
          `date`      datetime     NOT NULL,
          `editor`    varchar(255) NOT NULL default '',
          `slaved_by` bigint(20) unsigned   default NULL,
  
          PRIMARY KEY  (`id`)
  
      ) TYPE=InnoDB;

    From this moment every 'new' (SQL INSERT), 'update' (SQL UPDATE) and
    'delete' (SQL DELETE) actions will be logged in 'History' table.

    Each action is stored in at least one history object. 'new' action
    creates exactly one "Todo::History" object. 'update' and 'delete'
    actions create more than one "Todo::History" objects. 'update' action
    creates number of additional history objects equal to number of
    properties affected by operation. 'delete' action creates number of
    additional history objects equal to number of properties of deleted
    object.

    All history objects created for 'delete' or 'update' actions execpt
    first object has property 'slaved_by' set to id of first history object,
    this object is called 'master'.

    Main use of History class is to store all changes made to objects of our
    model. But there is one more interesting feature. You can undo changes
    with 'Todo::History::rollback' method. For example you can restore
    deleted object by its ID like this:

      $hist = Todo::History->find
      (
          filter => ORM::Expr->_and
          (
              # rollback operation should be called on master history object
              Todo::History->M->master, 
              Todo::History->M->obj_class eq 'Todo::Worker',
              Todo::History->M->obj_id == 1,
          ),
      );
      $hist && $hist->rollback;

    This code does the following:

    1. Find 'slaved' history objects
    2. Create 'Todo::Worker' object with properties from 'slaved' history
    objects
    3. Delete 'slaved' and 'master' history objects

    Also it is possible to restore object not by ID but by arbitrary
    property like this:

      $hist = Todo::History->find
      (
          filter => ORM::Expr->_and
          (
              Todo::History->M->obj_class eq 'Todo::Worker',
              Todo::History->M->prop_name == 'name',
              Todo::History->M->old_value->_like( '%Cartman%' ),
              Todo::History->M->delete_slave,
          ),
      );
      $hist && $hist->slaved_by->rollback;

    It is possible to rollback 'new' and 'update' actions by similar
    fashion. You can find more detailed description of this feature in
    ORM::History.

CACHING
  ORM-object caching
    Object's in-memory cache implemented on per-primary-class basis. Primary
    class - is class that is a direct descendant of initial class.

    The idea of such cache strategy is to have several caches, by one for
    every primary class. Objects of non-primary class use cache of its
    primary class. With that in mind its time to say that
    "default_cache_size" argument to "ORM::_init" specifies cache size for
    one primary class and not total cache size.

    In our object model this means that there is maximum of 200 cached
    objects for "Todo::Worker" class and 200 cached objects for "Todo::Task"
    class.

    You can change cache size of individual primary class this way:

      package Todo::Task;
  
      $VERSION=0.1;
  
      use ORM::Base 'Todo::ORM', cache_size=>1000;

    You can get current efficiency of in-memory cache by calling
    "ORM::Cache->total_efficiency" and reset the cache counters by calling
    "ORM::Cache->clear_stat"

    When "default_cache_size" is set to zero, it is still possible that
    object will be loaded from cache. This is because of instance caching
    (see below).

    Cache is organized in the fashion when you do not need to purge it
    manually.

  Perl-object caching
    This mechanism is mostly common for ORMs and persistence tools in Perl.
    This means that after

      $task1 = Todo::Task->find_id( id=>1 );
      $task2 = Todo::Task->find_id( id=>1 );

    $task1 and $task2 contain the same blessed reference. And even in the
    following case:

      $task1 = Todo::Task->find_id( id=>1 );
      $task2 = Todo::Task->find( order=>[ Todo::Task->M->id ] );
      $task3 = Todo::Task->find( filter=>( Todo::Task->M->desc->_like( %kill Kenny% ) ) );

    All three variables contain the same object.

    Perl-object caching works even when cache size is set to 0.

    Note: Perl-object caching is performed by means of "weaken" (see
    Scalar::Util). Perl-object caching is significantly restricted for the
    versions of Perl that doesn't support "weaken" feature. The restrictions
    are: with cache size set to 0 Perl-object caching doesn't work at all;
    with cache size set to non-zero value this kind of caching works only
    for objects those are still in cache's pool queue. Once object is
    deleted from cache queue, the consequent requests to load the object
    from the cache will result in 'not cached' responce, even if there are
    instances of the same object still present in the memory.

  Properties caching
    When you first access some object-property of ORM-object, then ORM tries
    to create expected object for you (using find_id for ORM-object
    properties and __ORM_new_db_value for non-ORM-properties) and then store
    it in internal object's hash.

    When you access the same object-property later it is not created again
    but fetched from hash.

    When you need to refresh object's contents simply call
    "$object->refresh".

TRANSACTIONS
    The usual way transactions should be used is to set up a correspondence
    between single method and single transaction. If it is not the case then
    there is probably some conceptual error take place.

      package Todo::Worker;
  
      # Delegate all worker's tasks to new worker
      sub delegate_tasks
      {
          my $self  = shift;
          my %arg   = @_;
          my $error = ORM::Error->new;
          my $ta    = Todo::ORM->new_transaction( error=>$error );
  
          for my $task ( $self->_rev_prop( 'Todo::Task'=>'worker', error=>$error ) )
          {
              $task->worker( new_value=>$arg{worker}, error=>$error );
          }
  
          $error->upto( $arg{error} );
      }

    As you can see the way transactions is used in ORM is different than
    commonly used in other packages. In ORM you do not need to 'finish'
    transaction explicitly. This helps you to avoid situations when you
    forget to call method to finish transaction. $ta contains the object
    assigned to a newly started transactions. Transaction is finished when
    $ta object is destroyed. In our case this happens when method is
    finished.

    If $error contain one or more fatal errors when $ta is being destroyed,
    then transaction is rolled back. Also rollback is performed for active
    transaction when die function called or runtime error occured.

    Note use of '_rev_prop' method in foreach loop:

      $self->_rev_prop( 'Todo::Task'=>'worker', error=>$error );

    That is the way reversive properties (see "CREATING OBJECT MODEL") are
    accessed. If certain reversive property is frequently used then you can
    define shortcut method for it:

      sub tasks { shift->_rev_prop( 'Todo::Task'=>'worker', @_ ); }

    And access it like this:

      @tasks = $self->tasks( error=>$error );
      @tasks = $self->tasks( error=>$error, page=>2, pagesize=>10 );
      $iterator = $self->tasks( error=>$error, return_res=>1 );

    In fact the call to "_rev_prop" first appeared in this chapter is
    similar to:

      Todo::Task->find( filter=>( Todo::Task->M->worker==$self ), error=>$error );

    Transactions in ORM can be nested:

      package Todo::Worker;
  
      # Delegate all old worker's tasks to new worker
      # and delete old worker
      sub discharge
      {
          my $self  = shift;
          my %arg   = @_;
          my $error = ORM::Error->new;
          my $ta    = Todo::ORM->new_transaction( error=>$error );
  
          $self->delegate_tasks( worker=>$arg{worker}, error=>$error );
          $self->delete( error=>$error );
  
          $error->upto( $arg{error} );
      }

    As you can see transactional method 'delegate_tasks' is called from
    another transactional method 'discharge'. Nested transactional object
    simply not issues transaction related statements to SQL-server. In case
    when nested transaction is failed then outer transaction is failed too
    and therefore is rolled back.

    It's planned to support transactions emulation for non-transactional
    databases in cases when it's possible. It's up to ORM users to decide
    how soon this feature will be implemented.

INHERITANCE
    Suppose there are tasks that have to be done by group of workers. For
    that reason "Todo::Worker::Group" class could be useful that is
    derivative of "Todo::Worker" and have additional properties:

    1. Team leader
    2. Team members

    To implement inheritance ORM uses so called vertical mapping. This means
    that for every child class there is one table must be created which
    contains only child's direct properties and not contain inheritted
    properties of base classes. When you do fetch objects of child class
    it's direct table is joined with tables of all base classes.

    This approach is most useful, its only disadvantage compared to another
    inheritance implementations is that it makes additional overheat to
    RDBMS to do table joins. But for large heavy-loaded sites this drawback
    is exhausted by using various types of RDBMS clustering and replication
    technologies.

    To implement "Todo::Worker::Group" we need following steps:

    1. Create class's module
    2. Add 'class' property to base class
    3. Create DB table for child class

    File Todo/Worker/Group.pm

      package Todo::Worker::Group;
  
      $VERSION=0.1;
  
      use ORM::Base 'Todo::Worker';

    Note that argument to "use ORM::Base" is 'Todo::Worker' not 'ORM'.

    Now we have to made changes to database:

      CREATE TABLE  `Worker_Group` (
        `id` bigint(20) unsigned NOT NULL auto_increment,
        `leader` bigint(20) unsigned NOT NULL default '0',
        PRIMARY KEY  (`id`)
      ) ENGINE=InnoDB;
  
      INSERT INTO '_ORM_refs' VALUES ( 'Todo::Worker::Group', 'leader', 'Todo::Worker' );
      ALTER TABLE `Worker` ADD COLUMN `class` VARCHAR(45) NOT NULL AFTER `id`;
      UPDATE `Worker` SET class='Todo::Worker';

    Now call to "Todo::Worker->find" returns objects of "Todo::Worker" and
    "Todo::Worker::Group". To get only "Todo::Worker" object you should
    filter the result set like this:

      @workers = Todo::Worker->find( filter=>( Todo::Worker->M->class eq 'Todo::Worker' ) );

    The best way to get only "Todo::Worker::Group" objects:

      @workers = Todo::Worker::Group->find;

    Next chapter describes how to implement property of
    "Todo::Worker::Group" containing list of group members.

MANY TO MANY RELATIONS
    In previous chapters one-to-many relation type was shortly described. In
    this chapter you will get to know how to implement many-to-many
    relations using ORM.

    ORM does not share the way other persistence tools and mappers implement
    this kind of relations. In fact ORM does nothing to implement it :)
    Probably because of lazy-loaded author (see below).

    The trick here is easy enought. Let's take a look at the nature of
    many-to-many relations.

    To define members of the workers group we should establish relation
    between "Todo::Worker::Group" and "Todo::Worker". To describe the
    relation in RDBMS terms we need separate linking table called e.g.
    'worker_to_group' or in worst case 'workers_to_groups' (avoid to use
    plural form in class names). Roughly the same we need when using ORM but
    the table is assigned to linking class, let's call it
    "Todo::Worker::GroupMember". (I usually use pair of colons to separate
    child classes so "Todo::Worker::Group::Member" is not so good, but this
    is just my subjective rule).

    "Todo::Worker::GroupMember" class is nothing else but plain ORM-class:

      package Todo::Worker::GroupMember;
  
      $VERSION=0.1;
  
      use ORM::Base 'Todo::ORM';

    We should execute some SQL queries:

      CREATE TABLE `Worker_GroupMember` (
        `id` bigint(20) unsigned NOT NULL auto_increment,
        `group` bigint(20) unsigned NOT NULL default '0',
        `worker` bigint(20) unsigned NOT NULL default '0',
        PRIMARY KEY  (`id`),
        UNIQUE KEY `group_worker` (`group`,`worker`)
      ) TYPE=InnoDB;
  
      INSERT INTO '_ORM_refs' VALUES ( 'Todo::Worker::GroupMember', 'group', 'Todo::Worker::Group' );
      INSERT INTO '_ORM_refs' VALUES ( 'Todo::Worker::GroupMember', 'worker', 'Todo::Worker' );

    After this we can access collecions on both sides.

    To get members of the $group:

      @workers = Todo::Worker->find
      (
          filter=>
          (
              Todo::Worker->M->_rev( 'Todo::Worker::GroupMember'=>'worker' )->group == $group
          )
      );

    or

      @workers = Todo::Worker::GroupMember->stat
      (
          data    => { worker => Todo::Worker::GroupMember->M->worker },
          preload => { worker => 1 },
          filter  => ( Todo::Worker::GroupMember->M->group == $group ),
      );

    To get groups the $worker belongs to:

      @groups = Todo::Worker::Group->find
      (
          filter=>
          (
              Todo::Worker::Group->M->_rev( 'Todo::Worker::GroupMember'=>'group' )->worker == $worker
          )
      );

    or

      @groups = Todo::Worker::GroupMember->stat
      (
          data    => { group => Todo::Worker::GroupMember->M->group },
          preload => { group => 1 },
          filter  => ( Todo::Worker::GroupMember->M->worker == $worker ),
      );

    The advantage of this approach is that you easily can add property to
    "Todo::Worker::GroupMember" that for example describes member's position
    in each group, that is impossible with commonly used 'object collection'
    properties.

    The only drawback of this approach is that linking table should contain
    'ID' field. This is to be fixed in the future by allowing object IDs to
    be multi-field primary keys.

    That's it!

LAZY LOAD
    Lazy loading is a technique in ORMs that attempts to delay fetching of
    data from database to your program object space until the data is known
    to be needed.

    The implementation of ORM supports per-field lazy load. But interface
    currently allows only per-table lazy load. Per-field lazy loading is
    currently used only in cases of updates with server-side expressions
    (see below).

    There are three methods that supports 'lazy_load' option:

    First of them is "find". Suppose we need to get full list of workers. It
    can be done with or without lazy load.

      @workers      = Todo::Worker->find;
      @lazy_workers = Todo::Worker->find( lazy_load=>1 );

    In first case ORM load all data from "Worker" table, if there are
    "Todo::Worker::Group" objects exist then data from "Worker_Group" table
    is loaded and joined.

    In second case only "Worker" table is read and objects are being put in
    lazy loaded state.

    Second method that supports lazy load is "find_id".

      $worker = Todo::Worker->find_id( id=>1, lazy_load=>1 );

    This means not to load any data from database at all. When you try to
    access some $worker property ORM attempts to load object from storage,
    if loading is failed then $worker is reblessed to "ORM::Broken".

    Also note that while $worker is in lazy loaded state it is assumed to be
    object of "Todo::Worker" class even if in fact it is object of
    "Todo::Worker::Group".

    To force load of lazy loaded object you can use "finish_loading" or
    "refresh" methods.

    Finally "stat" method accepts 'lazy_load' option that is forwarded to
    "find_id" when "stat" makes decision to load requested objects.

      $result = Todo::Task->stat
      (
          data      => { task=>Todo::Task->M, worker=>Todo::Task->M->worker },
          lazy_load => $lazy_load,
      );

    This means to load set of tasks and their related workers. If $lazy_load
    is "true" then ORM do not execute separate queries to load complete
    objects, it just calls to "find_id" with "lazy_load" option set to
    "true".

    Note that with using "stat" you can load all objects in one query:

      $result = Todo::Task->stat
      (
          data    => { task=>Todo::Task->M, worker=>Todo::Task->M->worker },
          preload => { task=>1, worker=>1 },
      );

    Also note that Objects of "Todo::Worker::Group" are still not completely
    loaded (only "Worker" table is read).

    There is corresponding query you can see with
    "ORM::DbLog->write_to_stderr(1)".

      --------------------------
      [Sat Jan 21 13:40:57 2006]: ORM::stat: Success
      SELECT
        `_T1_Task`.`worker` AS `_task worker`,
        `_T1_Task`.`title` AS `_task title`,
        `_T1_Task`.`deadline` AS `_task deadline`,
        `_T1_Task`.`id` AS `task`,
        `_T1_Task`.`desc` AS `_task desc`,
        'Todo::Worker' AS `_worker class`,
        `_T1_Task`.`created` AS `_task created`,
        `_T1_Task`.`start_date` AS `_task start_date`,
        `_T2_Worker`.`id` AS `worker`,
        `_T2_Worker`.`name` AS `_worker name`,
        'Todo::Task' AS `_task class`
      FROM
        `Task` AS `_T1_Task`
          LEFT JOIN `Worker` AS `_T2_Worker` ON( `_T1_Task`.`worker`=`_T2_Worker`.`id` )

SERVER-SIDE UPDATES
    There is useful feature in ORM to do updates using server-side
    expressions (or meta expressions):

      $cartman->update
      (
          prop=>{ name=>Todo::Worker->M->name->_append( ' (fatboy)' ) }
      );

    Meta-expressions are constructed in the same fashion as for "find".

    After execution of above code $cartman will fall in lazy loaded state.
    More specifically it will have no idea about new value of "name"
    property (Note also that if history is enabled for method or for entire
    class then the object is being brought to fully loaded state to consider
    history changes).

    This feature is useful to do non-transactional updates in situations
    when you are not sure about freshness of the object being updated.
    Without using server-side update you will get error message if your
    object's data is not up to date.

    There is one unsolved issue about server-side updates. For sake of data
    integrity execution of server-side updates should be permitted only to
    class itself and to defined class friends. The idea how to make it is
    still high in the air. This issue is definetely should be solved earlier
    than version 1.0 of ORM is to be released.

SOME REFACTORING
  Move property to base class (To be documented)
  Move property to child class (To be documented)
  Delete property (To be documented)
ORM + APACHE (To be documented)
SEE ALSO
    http://perlorm.sourceforge.net/

    ORM

    TODO.txt

AUTHOR
    Alexey V. Akimov

COPYRIGHT AND LICENSE
    Copyright (C) 2005-2006 Alexey V. Akimov

    This library is free software; you can redistribute it and/or modify it
    under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation; either version 2.1 of the License, or (at
    your option) any later version.

    This library is distributed in the hope that it will be useful, but
    WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
    General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this library; if not, write to the Free Software Foundation,
    Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA