Implementing a Mixin for Tagging
Monday, May 18th, 2009 by john.beppuRecently, 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.
