Object Marginalia
Scribbling Notes on an Object
Mixin::ExtraFields is hard to summarize, but here's a whack at it: it's a system for storing extra data related to an object and creating methods for accessing and altering that state.
Here's a really simple example to start with. We have an object that's a blessed hashref and we want to have a bucket for arbitrary state:
1: | package Holiday; |
Now we've got this bucket for extra values in our Holiday objects which we can use in a few ways, like:
1: | my $holiday = Holiday->new('Christmas'); |
If we use Data::Dumper on our object, we might see something like this:
1: | $VAR1 = bless( { |
That horrible key is generated as a place in our object to put a reference to its associated data. That's only one reference, though. We keep track of data for that object externally, too. That means if we create another object with the same id, it will have the same traditions.
This means we can also have objects share their extra fields:
1: | package Holiday; |
Then we can say:
1: | my $christmas = Holiday->new({ id => 'Christmas', month => 'Dec' }); |
This can be a quick way to add state to a class you don't control for the sake of debugging:
1: | use Weird::Object; |
Alternate Sets of Methods
It's easy to provide methods other than the get_extra
and set_extra
(and the half dozen related methods) that we saw above. For example, you could use Mixin::ExtraFields::Param instead and get a method like CGI.pm's param
:
1: | package Holiday; |
Or, my favorite, Mixin::ExtraFields::Hive to get a Data::Hive-based hierarchical-looking set of methods. The hive makes it look like you have a deep storage structure, but flattens everything out to key/value pairs for storage. It lets us do tricks like this:
1: | package Holiday; |
The fact that Data::Hive takes this apparently structured data and stores it in a flat table is extremely useful, because it keeps to a known quantity the number of methods that alternate drivers have to provide.
Alternate Storage Drivers
Alternate storage is why we had to keep saying we wanted the HashGuts driver, above. If you wanted, you could write a driver that stored all attributes only in some otherwise inaccessible hashref. You could store attributes by calling another set of methods on the object being altered. There are lots of possibilites, but the one I've used the most is storing the table of extra values in a database table.
We can decorate objects in our program with these extra fields, storing any set values in a database and retrieving them back from the database. So we can write a class like this:
1: | package Holiday; |
It's about the simplest object you could get, with just one attribute stored in a plain old hash, but then it has this "hive" mixed in that persists things in your DBIx::Class database schema. You can turn your little holiday objects into flyweights for accessing (apparently) structured data like:
1: | printf "Tonight we will eat %s!", |
...and we'll pull that out of the database from the initial load of all our favorite holiday traditions. (To keep things easy for you, the DBIC driver can actually set up the DBIC resultsource for you, so your HolidayTradition source above, need only be two or three lines of code.
Sometimes you might just want to stick your extra data into an object's guts, and sometimes you might just want to use a full database layer to map your whole object to SQL, and sometimes you might want a full meta-programming layer for adding attributes to your classes. Other times, though, you need a quick way to add data to objects and share it correctly without having to think about it too much. That's exactly when I start mixing this library in.
See Also
Object::Annotate - a related, failed experiment