oracular (3) UR::Object.3pm.gz

Provided by: libur-perl_0.470+ds-3_all bug

NAME

       UR::Object - transactional, queryable, process-independent entities

SYNOPSIS

       Create a new object in the current context, and return it:

         $elmo = Acme::Puppet->create(
           name => 'Elmo',
           father => $ernie,
           mother => $bigbird,
           jobs => [$dance, $sing],
           favorite_color => 'red',
         );

       Plain accessors work in the typial fashion:

         $color = $elmo->favorite_color();

       Changes occur in a transaction in the current context:

         $elmo->favorite_color('blue');

       Non-scalar (has_many) properties have a variety of accessors:

         @jobs = $elmo->jobs();
         $jobs = $elmo->job_arrayref();
         $set  = $elmo->job_set();
         $iter = $elmo->job_iterator();
         $job  = $elmo->add_job($snore);
         $success = $elmo->remove_job($sing);

       Query the current context to find objects:

         $existing_obj  = Acme::Puppet->get(name => 'Elmo');
         # same reference as $existing_obj

         @existing_objs = Acme::Puppet->get(
           favorite_color => ['red','yellow'],
         );
         # this will not get elmo because his favorite color is now blue

         @existing_objs = Acme::Puppet->get(job => $snore);
         # this will return $elmo along with other puppets that snore,
         # though we haven't saved the change yet..

       Save our changes:

         UR::Context->current->commit;

       Too many puppets...:

         $elmo->delete;

         $elmo->play; # this will throw an exception now

         $elmo = Acme::Puppet->get(name => 'Elmo'); # this returns nothing now

       Just kidding:

         UR::Context->current->rollback; # not a database rollback, an in-memory undo

       All is well:

         $elmo = Acme::Puppet->get(name => 'Elmo'); # back again!

DESCRIPTION

       UR::Objects are transactional, queryable, representations of entities, built to maintain separation
       between the physical reference in a program, and the logical entity the reference represents, using a
       well-defined interface.

       UR uses that separation to automatically handle I/O.  It provides a query API, and manages the difference
       between the state of entities in the application, and their state in external persistence systems.  It
       aims to do so transparently, keeping I/O logic orthogonally to "business logic", and hopefully making
       code around I/O unnecessary to write at all for most programs.

       Rather than explicitly constructing and serializing/deserializing objects, the application layer just
       requests objects from the current "context", according to their characteristics.  The context manages
       database connections, object state changes, references, relationships, in-memory transactions, queries
       and caching in tunable ways.

       Accessors dynamically fabricate references lazily, as needed through the same query API, so objects work
       as the developer would traditionally expect in most cases.  The goal of UR::Object is that your
       application doesn't have to do data management.  Just ask for what you want, use it, and let it go.

       UR::Objects support full reflection and meta-programming.  Its meta-object layer is fully self-
       bootstrapping (most classes of which UR is composed are themselves UR::Objects), so the class data can
       introspect itself, such that even classes can be created within transactions and discarded.

INHERITANCE

         UR::ModuleBase    Basic error, warning, and status messages for modules in UR.
           UR::Object      This class - general OO transactional OO features

WRITING CLASSES

       See UR::Manual::Tutorial for a narrative explanation of how to write clases.

       For a complete reference see UR::Manual::WritingClasses.

       For the meta-object API see UR::Object::Type.

       A simple example, declaring the class used above:

         class Acme::Puppet {
             id_by => 'name',
             has_optional => [
                 father => { is => 'Acme::Puppet' },
                 mother => { is => 'Acme::Puppet' },
                 jobs   => { is => 'Acme::Job', is_many => 1 },
             ]
         };

       You can also declare the same API, but specifying additional internal details to make database mapping
       occur the way you'd like:

         class Acme::Puppet {
             id_by => 'name',
             has_optional => [
                 father => { is => 'Acme::Puppet', id_by => 'father_id' },
                 mother => { is => 'Acme::Puppet', id_by => 'mother_id' },
             },
             has_many_optional => [
                 job_assignments => { is => 'Acme::PuppetJob', im_its => 'puppet' },
                 jobs            => { is => 'Acme::Job', via => 'job_assignments', to => 'job'  },
             ]
         };

CONSTRUCTING OBJECTS

       New objects are returned by create() and get(), which delegate to the current context for all object
       construction.

       The create() method will always create something new or will return undef if the identity is already
       known to be in use.

       The get() method lets the context internally decide whether to return a cached reference for the
       specified logical entities or to construct new objects by loading data from the outside.

METHODS

       The examples below use $obj where an actual object reference is required, and SomeClass where the class
       name can be used.  In some cases the example in the synopsisis is continued for deeper illustration.

   Base API
       get
             $obj = SomeClass->get($id);
             $obj = SomeClass->get(property1 => value1, ...);
             @obj = SomeClass->get(property1 => value1, ...);
             @obj = SomeClass->get('property1 operator1' => value1, ...);

           Query the current context for objects.

           It turns the passed-in parameters into a UR::BoolExpr and returns all objects of the given class
           which match.  The current context determines whether the request can be fulfilled without external
           queries.  Data is loaded from underlying database(s) lazliy as needed to fulfuill the request.

           In the simplest case of requesting an object by id which is cached, the call to get() is an immediate
           hash lookup, and is very fast.

           See UR::Manual::Queries, or look at UR::Object::Set, UR::BoolExpr, and UR::Context for details.

           If called in scalar context and more than one object matches the given parameters, get() will raise
           an exception through "die".

       create
             $obj = SomeClass->create(
               property1 => $value1,
               properties2 => \@values2,
             );

           Create a new entity in the current context, and return a reference to it.

           The only required property to create an object is the "id", and that is only required for objects
           which do not autogenerate their own ids.  This requirement may be overridden in subclasses to be more
           restrictive.

           If entities of this type persist in an underlying context, the entity will not appear there until
           commit.  (i.e. no insert is done until just before a real database commit)  The object in question
           does not need to pass its own constraints when initially created, but must be fully valid before the
           transaction which created it commits.

       delete
             $obj->delete

           Deletes an object in the current context.

           The $obj reference will be garbage collected at the discretion of the Perl interpreter as soon as
           possible.  Any attempt to use the reference after delete() is called will result in an exception.

           If the represented entity was loaded from the parent context (i.e. persistent database objects), it
           will not be deleted from that context (the database) until commit is called.  The commit call will do
           both the delete and the commit, presuming the complete save works across all involved data sources.

           Should the transaction roll-back, the deleted object will be re-created in the current context, and a
           fresh reference will later be returnable by get().  See the documentation on UR::Context for details
           on how deleted objects are rememberd and removed later from the database, and how deleted objects are
           re-constructed on STM rollback.

       copy
             $obj->copy(%overrides)

           Copies the existing $obj by copying the values of all direct properties, except for ID properties, to
           a newly created object of the same type.  A list of params and values may be provided as overrides to
           the existing values or to specify an ID.

       class
            $class_name = $obj->class;
            $class_name = SomeClass->class;

           Returns the name of the class of the object in question.  See __meta__ below for the class meta-
           object.

       id
            $id = $obj->id;

           The unique identifier of the object within its class.

           For database-tracked entities this is the primary key value, or a composite blob containing the
           primary key values for multi-column primary keys.

           For regular objects private to the process, the default id embeds the hostname, process ID, and a
           timestamp to uniquely identify the UR::Context::Process object which is its final home.

           When inheritance is involved beneath UR::Object, the 'id' may identify the object within the super-
           class as well.  It is also possible for an object to have a different id upon sub-classification.

   Accessors
       Every relationship declared in the class definition results in at least one accesor being generated for
       the class in question.

       Identity properties are read-only, while non-identity properties are read-write unless is_mutable is
       explicitly set to false.

       Assigning an invalid value is allowed temporarily, but the current transaction will be in an invalid
       state until corrected, and will not be commitable.

       The return value of an the accessor when it mutates the object is the value of the property after the
       mutation has occurred.

       Single-value property accessors:

       By default, properties are expected to return a single value.

       NAME
           Regular accessors have the same name as the property, as declared, and also work as mutators as is
           commonly expected:

             $value = $obj->property_name;
             $obj->property_name($new_value);

           When the property is declared with id_by instead of recording the refereince, it records the id of
           the object automatically, such that both will return different values after either changes.

       Muli-value property accessors:

       When a property is declared with the "is_many" flag, a variety of accessors are made available on the
       object.  See "UR::Manual::WritingClasses" for more details on the ways to declare relationships between
       objects when writing classes.

       Using the example from the synopsis:

       NAMEs (the property name pluralized)
           A "has_many" relationship is declared using the plural form of the relationship name.  An accessor
           returning the list of property values is generated for the class.  It is usable with or without
           additional filters:

             @jobs = $elmo->jobs();
             @fun_jobs = $elmo->jobs(is_fun => 1);

           The singular name is used for the remainder of the accessors...

       NAME (the property name in singular form)
           Returns one item from the group, which must be specified in parameters.  If more than one item is
           matched, an exception is thrown via die():

            $job = $elmo->job(name => 'Sing');

            $job = $elmo->job(is_fun => 1);
            # die: too many things are fun for Elmo

       NAME_list
           The default accessor is available as *_list.  Usable with or without additional filters:

             @jobs = $elmo->job_list();
             @fun_jobs = $elmo_>job_list(is_fun => 1);

       NAME_set
           Return a UR::Object::Set value representing the values with *_set:

             $set  = $elmo->job_set();
             $set  = $elmo->job_set(is_hard => 1);

       NAME_iterator
           Create a new iterator for the set of property values with *_iterator:

             $iter = $elmo->job_iterator();
             $iter = $elmo->job_iterator(is_fun => 1, -order_by => ['name]);
             while($obj = $iter->next()) { ... }

       add_NAME
           Add an item to the set of values with add_*:

             $added  = $elmo->add_job($snore);

           A variation of the above will construt the item and add it at once.  This second form of add_*
           automatically would identify that the line items also reference the order, and establish the correct
           converse relationship automatically.

             @lines = $order->lines;
             # 2 lines, for instance

             $line = $order->add_line(
                product => $p,
                quantity => $q,
             );
             print $line->num;
             # 3, if the line item has a multi-column primary key with auto_increment on the 2nd column called num

       remove_NAME
           Items can be removed from the assigned group in a way symetrical with how they are added:

             $removed = $elmo->remove_job($sing);

   Extended API
       These methods are available on any class defined by UR.  They are convenience methods around UR::Context,
       UR::Object::Set, UR::BoolExpr, UR::Object::View, UR::Observer and Mock::Object.

       create_iterator
             $iter = SomeClass->create_iterator(
               property1 => $explicit_value,
               property2 => \@my_in_clause,
               'property3 like' => 'some_pattern_with_%_as_wildcard',
               'property4 between' => [$low,$high],
             );

             while (my $obj = $iter->next) {
               ...
             }

           Takes the same sort of parameters as get(), but returns a UR::Object::Iterator for the matching
           objects.

           The next() method will return one object from the resulting set each time it is called, and undef
           when the results have been exhausted.

           "UR::Object::Iterator" instances are normal object references in the current process, not context-
           oriented UR::Objects.  They vanish upon dereference, and cannot be retrieved by querying the context.

           When using an iterator, the system attempts to return objects matching the params at the time the
           iterator is created, even if those objects do not match the params at the time they are returned from
           next().  Consider this case:

             # many objects in the DB match this
             my $iter = SomeClass->create_iterator(job => 'cleaner');

             my $an_obj = SomeClass->get(job => 'cleaner', id => 1);
             $an_obj->job('messer-upper');    # This no longer matches the iterator's params

             my @iter_objs;
             while (my $o = $iter->next) {
                 push @iter_objs, $o;
             }

           At the end, @iter_objs will contain several objects, including the object with id 1, even though its
           job is no longer 'cleaner'.  However, if an object matching the iterator's params is deleted between
           the time the iterator is created and the time next() would return that object, then next() will throw
           an exception.

       define_set
            $set = SomeClass->define_set(
               property1 => $explicit_value,
               property2 => \@my_in_clause,
               'property3 like' => 'some_pattern_with_%_as_wildcard',
               'property4 between' => [$low,$high],
            );

            @subsets = $set->group_by('property3','property4');

            @some_members = $subsets[0]->members;

           Takes the same sort of parameters as get(), but returns a set object.

           Sets are lazy, and only query underlying databases as much as necessary.  At any point in time the
           members() method returns all matches to the specified parameters.

           See UR::Object::Set for details.

       define_boolexpr
            $bx = SomeClass->define_boolexpr(
               property1 => $explicit_value,
               property2 => \@my_in_clause,
               'property3 like' => 'some_pattern_with_%_as_wildcard',
               'property4 between' => [$low,$high],
            );

            $bx->evaluate($obj1); # true or false?

           Takes the same sort of parameters as get(), but returns a UR::BoolExpr object.

           The boolean expression can be used to evaluate other objects to see if they match the given
           condition.  The "id" of the object embeds the complete "where clause", and as a semi-human-readable
           blob, such is reconstitutable from it.

           See UR::BoolExpr for details on how to use this to do advanced work on defining sets, comparing
           objects, creating query templates, adding object constraints, etc.

       add_observer
            $o = $obj1->add_observer(
               aspect => 'someproperty'
               callback => sub { print "change!\n" },
            );

            $obj1->property1('new value');

            # observer callback fires....

            $o->delete;

           Adds an observer to an object, monitoring one or more of its properties for changes.

           The specified callback is fired upon property changes which match the observation request.

           See UR::Observer for details.

       create_mock
            $mock = SomeClass->create_mock(
               property1 => $value,
               method1 => $return_value,
            );

           Creates a mock object using using the class meta-data for "SomeClass" via Mock::Object.

           Useful for test cases.

   Meta API
       The following methods allow the application to interrogate UR for information about the object in
       question.

       __meta__
             $class_obj = $obj->__meta__();

           Returns the class metadata object for the given object's class.  Class objects are from the class
           UR::Object::Type, and hold information about the class' properties, data source, relationships to
           other classes, etc.

       __extend_namespace__
             package Foo::Bar;

             class Foo::Bar { has => ['stuff','things'] };

             sub __extend_namespace__ {
                my $class = shift;
                my $ext = shift;
                return class {$class . '::' . $ext} { has => ['more'] };
             }

           Dynamically generate new classes under a given namespace.  This is called automatically by
           UR::ModuleLoader when an unidentified class name is used.

           If Foo::Bar::Baz is not a UR class, and this occurs:

             Foo::Bar::Baz->some_method()

           This is called:

             Foo::Bar->__extend_namespace__("Baz")

           If it returns a new class meta, the code will proceed on as though the class had always existed.

           If Foo::Bar does not exist, the above will be called recursively:

             Foo->__extend_namespace__("Bar")

           If Foo::Bar, whether loaded or generated, cannot extend itself for "Baz", the loader will go up the
           tree before giving up.  This means a top-level module could dynamically define classes for any given
           class name used under it:

             Foo->__extend_namespace__("Bar::Baz")

       __errors__
             @tags = $obj->__errors__()

           Return a list of UR::Object::Tag values describing the issues which would prevent a commit in the
           current transaction.

           The base implementation check the validity of an object by applying any constraints layed out in the
           class such as making sure any non-optional properties contain values, numeric properties contain
           numeric data, and properties with enumerated values only contain valid values.

           Sub-classes can override this method to add additional validity checking.

       __display_name__
            $text = $obj->__display_name__;
            # the class and id of $obj, by default

            $text = $line_item->__display_name__($order);

           Stringifies an object.  Some classes may choose to actually overload the stringification operator
           with this method.  Even if they do not, this method will still attempt to identify this object in
           text form.  The default returns the class name and id value of the object within a string.

           It can be overridden to do a more nuanced job.  The class might also choose to overload the
           stringification operator itself with this method, but even if it doesn not the system will presume
           this method can be called directly on an object for reasonable stringificaiton.

       __context__
            $c = $self->__context__;

           Return the UR::Context for the object reference in question.

           In UR, a "context" handles connextions between objects, instead of relying on having objects directly
           reference each other.  This allows an object to have a relationship with a large number of other
           logical entities, without having a "physical" reference present within the process in question.

           All attempts to resolve non-primitive attribute access go through the context.

   Extension API
       These methods are primarily of interest for debugging, for test cases, and internal UR development.

       They are likely to change before the 1.0 release.

       __signal_change__
           Called by all mutators to tell the current context about a state change.

       __changes__
             @tags = $obj->__changes__()

             @tags = $obj->__changes__('prop1', 'prop2', ...)

           Return a list of changes present on the object _directly_.  This is really only useful internally
           because the boundary of the object is internal/subjective.  Callers may also request only changes to
           particular properties.

           Changes to objects' properties are tracked by the system.  If an object has been changed since it was
           defined or loaded from its external data source, then changed() will return a list of UR::Object::Tag
           objects describing which properties have been changed.

           Work is in-progress on an API to request the portion of the changes in effect in the current
           transaction which would impact the return value of a given list of properties.  This would be
           directly usable by a view/observer.

       __define__
           This is used internally to "virtually load" things.  Simply assert they already existed externally,
           and act as though they were just loaded...  It is used for classes defined in the source code (which
           is the default) by the "class {}" magic instead of in some database, as we'd do for regular objects.

       __strengthen__
             $obj->__strengthen__();

           Mark this object as unloadable by the object cache pruner.

           UR objects are normally tracked by the current Context for the life of the application, but the
           programmer can specify a limit to cache size, in which case old, unchanged objects are periodically
           pruned from the cache.  If strengthen() is called on an object, it will effectively be locked in the
           cache, and will not be considered for pruning.

           See UR::Context for more information about the pruning mechanism.

       is_strengthened
           Check if an object has been stengthened, "__stengthen__".

       __weaken__
             $obj->__weaken__();

           Give a hint to the object cache pruner that this instance is not going to be used in the application
           in the future, and should be removed with preference when pruning the cache.

       is_weakened
           Check if an object has been weakened, "__weaken__".

       DESTROY
           Perl calls this method on any object before garbage collecting it.  It should never by called by your
           application explicitly.

           The DESTROY handler is overridden in UR::Object.  If you override it in a subclass, be sure to call
           $self->SUPER::DESTROY() before exiting your override, or errors will occur.

ERRORS, WARNINGS and STATUS MESSAGES

       When an error occurs which is "exceptional" the API will throw an exception via die().

       In some cases, when the possibility of failure is "not-exceptional", the method will simply return false.
       In scalar context this will be undef.  In list context an empty list.

       When there is ambiguity as to whether this is an error or not (get() for instance, might simply match
       zero items, ...or fail to understand your parameters), an exception is used.

       error_message
           The standard way to convey the error which has occurred is to set ->error_message() on the object.
           This will propagate to the class, and through its inheritance.  This is much like DBI's errstr
           method, which affects the handle on which it was called, its source handle, and the DBI package
           itself.

       warning_message
           Calls to warning_message also record themselves on the object in question, and its class(es).

           They also emit a standard Perl warn(), which will invoke $SIG{__WARN__};

       status_message
           Calls to status_message are also recorded on the object in question.  They can be monitored through
           hooks, as can the other messages.

       See UR::ModuleBase for more information.

SEE ALSO

       UR, UR::Object::Type, UR::Context

       UR::Maual::Tutorial, UR::Manual::WritingClasses, UR::Manual::Queries, UR::Manual::Transactions

       UR::ObjectDeprecated contains additional methods which are deprecated in the API.