Skip to content

DevGuide PerlObjectsRevisionable

Violet edited this page Oct 28, 2010 · 1 revision

MDG: Creating Revisionable Objects

Revisionable objects are objects that Melody will automatically track previous versions of that object for you. The class is primarily used for entries and templates, but could conceivable be used to track previous versions of any object, including custom objects a developer might create, which is why we are providing this documentation.

Furthermore, Melody's revision framework is customizable so that you can control, through the creation of custom drivers, how and where revisions are stored. For example, by default Melody stores revisions in the database, but someone might want revisions tracked through a source code control system like subversion or git. While, Melody does not yet support those revisioning systems, a developer could if they wanted to.

Let's get started.

Subclass Inheritance

To be versioned, an MT::Object subclass must first inherit MT::Revisable:

package MT::Foo;
use base qw( MT::Object MT::Revisable );

When a revision is saved, the entire object is taken and serialized. However, in order to curb bloat, the saving of a revision is only triggered when a designated column has changed. To flag a column as one to trigger a revision to be saved, simply add the keyword revisioned to the column definition like so:

__PACKAGE__->install_properties({
    column_defs => {
        some_string => 'string(255) not null revisioned'
    }
});

If at least one versioned column is changed then a copy of the entire object is saved so that one can easily revert back to it later.

Methods

Classes that inherit from MT::Revisionable automatically inherit the following methods or subroutines:

  • $class->revision_pkg Returned by MT->model($class->datasource . ':revision') - the namespace of the class that stores revisions for the class.

  • $class->revision_props - Returns a hashref of the install properties for the revision_pkg

  • $class->init_revisioning - Called by the base MT::Object class to initialize the revisioning framework for the particular class. This may involve creating the revision_pkg.

  • $class->revisioned_columns - Returns an arrayref of column names that are marked as being revisioned.

  • $class->is_revisioned_column($col) - Checks whether the passed column name has been marked as being revisioned.

  • $obj->gather_changed_cols($orig, $app) - Compares the revisioned columns of $orig with $obj and stores an arrayref of changed columns in $obj->{changed_revisioned_columns}

  • $obj->pack_revision() - Creates the hashref that will be stored as a particular revision of the object. By default, this hashref contains the values of the object's normal and meta columns. The <package>::pack_revision callback can be used to add further values to be stored with the revision.

  • $obj->unpack_revision($packed_obj) - The opposite of pack_revision, takes the $packed_obj hashref and unpacks it, setting the values of $obj as needed. The <package>::unpack_revision callback can be used for any other keys added to $packged_obj that are not part of the normal or meta columns.

  • $obj->save_revision() - Called automatically when an object is saved from the MT web interface or posted via a 3rd party client (on a low priority api/cms_post_save callback). Saves a revision only if at least one revisioned column has changed.

  • $obj->object_from_revision($revision) -

  • $obj->load_revision(\%terms, \%args) - Loads revisions for the $obj. Arguments work similarly to MT::Object->load. Thus, one can simply do $obj->load_revision($rev_numer) or pass terms and args. Terms can be any of the following:

    • id
    • label
    • description
    • rev_number
    • changed

    load_revision should return an/array of arrayref(s) of:

    1. The object stored at the revision
    2. An array ref of the changed columns that triggered the revision
    3. The revision number
  • $obj->apply_revision(\%terms, \%args) - Rolls back to the state of the object at $obj->load_revision(\%terms, \%args) and saves this action as a revision.

  • $obj->diff_object($obj_b) - Returns a hashref of column names with the values being an arrayref representation of the diff:

      [<flag>, <left>, <right>]
    

    with the flag being 'u', '+', '-', 'c'. See the HTML::Diff POD for more information.

  • $obj->diff_revision(\%terms, \%diff_args) - Loads the first object at $obj->load_revision(\%terms, \%args) and returns a hashref of column names with the values being an arrayref representation of the diff:

      [<flag>, <left>, <right>]
    

    with the flag being 'u', '+', '-', 'c'. See the HTML::Diff POD for more information.

Callbacks

  • ::pack_revision

      sub pack_revision {
          my ($cb, $obj, $values) = @_;
      }
    

    This callback is run after $values is initially populated by $obj->pack-revision() and is a hashref of the normal and meta column values and allows you to modify $values before it is saved with the revision. Thus, you can use this callback to augment what is stored with every revision.

  • <package>::unpack_revision

      sub unpack_revision {
          my ($cb, $obj, $packed_obj) = @_;
      }
    

    This callback is the complement of pack_revision and allows you to restore values that are within $packed_obj.

  • <package>::save_revision_filter

      sub save_revision_filter {
          my ($cb, $obj) = @_;
      }
    

    Similar to the cms_save_filter callbacks, this filter will allow you to prevent the saving of a particular revision.

  • <package>::pre_save_revision

      sub pre_save_revision {
          my ($cb, $obj) = @_;
      }
    

    This callback is called just before the revision is saved.

  • <package>::post_save_revision

      sub post_save_revision {
          my ($cb, $obj, $rev_number) = @_;
      }
    

    This callback is called immediately after a revision is saved.

  • <package>::gather_changed_cols

      sub post_save_revision {
          my ($cb, $obj, $rev_number) = @_;
      }
    

    This callback is called when MT gathered changed columns from object columns. Plugins can use the callback to add more column such as the ones added by plugins themselves that may not be detected by the default handler.

Creating Custom Drivers

The majority of the methods MT::Revisable provides are implemented by driver modules. These driver modules specify how versions of an object are saved and retrieved from a data store. By default, MT::Revisable uses the MT::Revisable::Local driver which saves versions within the Melody database. To change this, you would first need to create a driver that implements the following methods:

  • revision_pkg
  • revision_props
  • init_revisioning
  • save_revision
  • load_revision

If some of the above methods are not applicable to your driver, simply return undef.

Continue Reading

 


Questions, comments, can't find something? Let us know at our community outpost on Get Satisfaction.

Credits

  • Author: Arvind Satyanarayan
  • Editor: Byrne Reese
  • Special Thanks to Ezra Cooper, author of HTML::Diff and one of Movable Type's first engineers, like ever. Props to him.
Clone this wiki locally