Implementing a Mixin for Tagging
Recently, I’ve been asked to add tagging to the guided reports section. I used this as an opportunity to implement a mixin that’s similar in spirit to acts_as_taggable for Ruby’s ActiveRecord ORM.
I called this class, “C4::Taggable”, and it will inject tagging-related methods to any class that uses it. Normally, it’s bad form to use Exporter in OO code, but that mainly applies to when you’re defining a class. However, when you’re defining a mixin, I believe the use of Exporter is justified, because it’s one of the easiest ways to inject methods into a namespace.
To illustrate this technique, here is an outline of how C4::Taggable is implemented. (For brevity’s sake, the method bodies were omitted.)
C4::Taggable
package C4::Taggable;
use strict;
use warnings;
use base 'Exporter';
use C4::Context;
our @EXPORT_OK = qw( add_tag remove_tag tags search_by_tags );
our %EXPORT_TAGS = ( mixin => [qw(add_tag remove_tag tags search_by_tags)] );
sub add_tag { }
sub remove_tag { }
sub tags { }
sub search_by_tags { }
1;
Notice how C4::Taggable exports tagging-related methods.
Next, C4::Report is outlined. Note that by using C4::Taggable, C4::Report became taggable by virtue of having tagging-related methods for C4::Taggable being mixed into it.
C4::Report
package C4::Report;
use strict;
use warnings;
use C4::Context;
use C4::Taggable ':mixin';
sub new { }
sub id { }
sub borrowernumber { }
sub date_created { }
sub last_modified { }
sub savedsql { }
sub last_run { }
sub report_name { }
sub type { }
sub notes { }
sub update { }
sub table { }
1;
Isn’t that simple? Now any time you want to add tagging to a class, you just use C4::Taggable.
What’s the catch?
The catch is that C4::Taggable will have expectations about the host class and the database schema. The host class has to provide a few methods so that C4::Taggable can introspect enough to generate the right SQL. There will also be a table you have to create that has a predefined structure and naming pattern. I won’t go into any more detail here, but suffice it to say that for C4::Taggable to work, certain conventions have to be followed.
To me, it’s a small price to pay, because it makes it ridiculously easy to add tagging to other classes should that ever be necessary or desired.

May 18th, 2009 at 1:40 pm
Nice idea.
May 18th, 2009 at 2:59 pm
Thanks. This is a technique I’ve known about for a while, but I haven’t had the chance to use this particular one until now. I’m a big fan of expanding behavior of objects “horizontally” via mixins, traits, roles, or whatever you want to call these named collections of behaviors.
May 27th, 2009 at 5:27 am
In situations where you want to have a mixin or role but can’t afford to pull in something like Moose::Role, this technique will get you a lot of mileage. It does not have any non-core dependencies, and it should work with any Perl 5 class. It’s very light on resources, too.