package Contentment::Node::Revision; use strict; use warnings; our $VERSION = '0.06'; use base 'Oryx::Class'; use Class::Date qw( now ); =head1 NAME Contentment::Node::Revision - Nodes with revisions =head1 DESCRIPTION This is the class to subclass if you want to create a node object. Once your class subclasses this object, extends the L schema as desired, and creates any other methods it needs, it will be a full node object. This is really as simple as this: package Foo; use strict; use warnings; use base qw( Contentment::Node::Revision ); our $schema = { attributes => [{ name => 'my_field', type => 'String', }], }; 1 And you are done. You'll probably want to add additional methods to make your node more functional if you want to be reachable by the resolver hook handler defined in L. However, that only requires two more method definitions. See L for further details. =head2 NODE ATTRIBUTES In addition to the node attributes supported by L, C objects also support these fields: =over =item updated_on The date the revision was created or last updated. =item updated_by The username of the user that created or last updated the revision. =item comment A comment describing the revision. =back =cut use Class::Delegator send => 'node_id', to => 'node', as => 'id', send => 'owner', to => 'node', send => 'created_on', to => 'node', send => 'created_by', to => 'node', send => 'revisions', to => 'node'; our $schema = { attributes => [ { name => 'updated_on', type => 'DateTime', }, { name => 'updated_by', type => 'String', }, { name => 'comment', type => 'String', }, ], associations => [ { role => 'node', type => 'Reference', class => 'Contentment::Node', }, ], }; =head2 SPECIAL METHODS The following methods are defined for node revision objects: =over =item $revision = RevisionClass->create(\%args) This is slightly different from the typical L C method. This method actually creates two objects rather than just one and also does some additional setup. This creates a new node and a new revision under that node according the given C<%args>. =cut # The $revise parameter is super-secret and is used by revise() to make sure we # don't create a node when revising. sub create { my $class = shift; my $proto = shift; # Create the object my $self = $class->SUPER::create($proto); # If we're creating a Contentment::Node::Revision (i.e., this is being # called by Oryx::DB[IM]::Parent), create the node. if ($class eq __PACKAGE__ && !$proto->{__node}) { # Create the node and set it for the revision and add the revision to # the node my $node = Contentment::Node->create; $self->node($node); push @{ $node->revisions }, $self; $self->node->update; # Confirm changes and set the updated_{on,by} fields $self->update; # Add the revision to the current collection Contentment::Node::Manager->add_revision_to_current_collection($self); } return $self; } sub update { my $self = shift; $self->updated_on(now); $self->updated_by(Contentment->context->security->get_principal->username); return $self->SUPER::update; } =item $cloned_revision = $revision-Eclone(\%args) This method operates in pretty much the exact same way as the C method, except that it duplicates the fields in the original when not modified in the passed C<%args>. This will attach the new revision to the same node as the original. The clone will become the current revision for the current node collection. =cut sub _all_attributes { my $self = shift; my $class = ref $self; # Start with this object's attributes my $attributes = $self->attributes; # Look through the parent attributes and add them no strict 'refs'; foreach (@{$class.'::ISA'}) { # XXX Does this need to be smarter if they inherit from a non-Oryx # class that also provides an attributes method? if (UNIVERSAL::can($_, 'attributes')) { my $parent_attributes = $_->attributes; $attributes = { %$parent_attributes, %$attributes, }; } } return $attributes; } sub revise { my $self = shift; my $create_parameters = shift || {}; # Copy all the attributes from this object to a new one for my $attribute_name (keys %{ $self->_all_attributes }) { $create_parameters->{$attribute_name} = $self->$attribute_name unless exists $create_parameters->{$attribute_name}; } # Create the clone. Update the parent node. $create_parameters->{__create_revision} = 1; my $clone = (ref $self)->create($create_parameters); # Set the clone's node to the original's node my $node = $self->PARENT('Contentment::Node::Revision')->node; $clone->PARENT('Contentment::Node::Revision')->node($node); $clone->PARENT('Contentment::Node::Revision')->update; # Add the clone to the node's list of revision push @{ $node->revisions }, $clone; $node->update; # Make this revision the current one Contentment::Node::Manager->add_revision_to_current_collection($clone); return $clone; } =item $revision-Etrash This is very similar to C except that nothing is removed from the database. Okay... so it's nothing like C. It removes the object from the current node collection. =cut sub trash { my $self = shift; Contentment::Node::Manager ->remove_revision_from_current_collection($self); } =back =head1 AUTHOR Andrew Sterling Hanenkamp, Ehanenkamp@cpan.orgE =head1 LICENSE AND COPYRIGHT Copyright 2005 Andrew Sterling Hanenkamp Ehanenkamp@cpan.orgE. All Rights Reserved. This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See L. This program 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. =cut 1