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.

3 Responses to “Implementing a Mixin for Tagging”

  1. Joe Atzberger Says:

    Nice idea.

  2. john.beppu Says:

    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.

  3. john.beppu Says:

    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.

Leave a Reply