Meerkat::Cookbook - Meerkat recipes with dos and don'ts
version 0.004
If you have private attributes that should not be serialized — particularly lazy ones that can be reconstructed on demand — add the DoNotSerialize trait from MooseX::Storage:
DoNotSerialize
use MooseX::AttributeShortcuts; # 'lazy' use MooseX::Storage; has big_object => ( is => 'lazy', isa => 'Heavy::Object', traits => ['DoNotSerialize'], ); sub _build_big_object { ... }
You can extend Meerkat::Collection and add additional methods for things like common queries or collection maintenance. Here is a custom collection class that adds a query method:
package MyCollection::Person; use Moose 2; extends 'Meerkat::Collection'; sub find_by_name { my ( $self, $name ) = @_; return $self->find_one( { name => $name } ); }
To use custom collection classes, put them under a different namespace from your model classes, and pass that as the collection_namespace parameter when creating a Meerkat object:
collection_namespace
my $meerkat = Meerkat->new( model_namespace => "My::Model", collection_namespace => "MyCollection", database_name => "test", );
Then, collection requests will use MyCollection::* or will fall back to Meerkat::Collection:
MyCollection::*
Meerkat::Collection
my $person = $meerkat->collection( "Person" ); # MyCollection::Person my $other = $meerkat->collection( "Other" ); # Meerkat::Collection
Do you really need them in your model? MongoDB does have support for inflating and deflating DateTime and DateTime:Tiny objects, but do you really want the overhead of doing so each time?
The simplest and sanest thing to do, in my opinion, is to keep your times in either epoch seconds or some standardized format like ISO 8601, inflate them when you need to work with them, and store any changes to them in the same format.
use MooseX::Types::ISO8601 qw/ISO8601Date/ has birthday => ( is => 'ro', isa => ISO8601Date, coerce => 1, );
If you try to store DateTime or DateTime::Tiny objects directly, the MongoDB client will translate them to its internal datetime format. Meerkat sets the MongoDB dt_type option to undef, so the MongoDB client will always return epoch seconds for its internal datetime type. With Meerkat, objects go in, but numbers come back out.
dt_type
undef
However, if you aren't consistent about how you store datetimes, you run the risk of getting mixed numbers and internal datetime types in your documents and that's probably a bad idea. Either store as epoch seconds or a standard format or store objects, but don't mix them up.
To make things a little easier at the cost of some complexity, Meerkat offers Meerkat::DateTime and the MeerkatDateTime type.
MeerkatDateTime
use Meerkat::Types qw/MeerkatDateTime/; has birthday => ( is => 'ro', isa => MeerkatDateTime, coerce => 1, );
For an attribute of type MeerkatDateTime, initializing it with epoch seconds, a DateTime object or a DateTime::Tiny object will coerce into a Meerkat::DateTime object.
$person->create( name => "Joe", birthday => $when ); # birthday is coerced from $when to a Meerkat::DateTime object
This object holds epoch seconds and will lazily inflate a DateTime object for you on demand:
my $dt = $obj->birthday->DateTime;
Again, because MongoDB will store a DateTime or DateTime::Tiny object with its internal format, you can then update fields using objects of those types and MongoDB will convert them to an internal type when storing and return them as epoch seconds, which then get coerced back into a Meerkat::DateTime object when the update is synchronized:
$obj->update_set( birthday => $new_datetime_obj ); # birthday winds up an updated Meerkat::DateTime object.
This is all a little convoluted but might save a little coding.
Ultimately, if you want a consistent datetime field type in the database, your options are pretty straightforward:
Option 1: store only epoch seconds or other standard format and inflate them to DateTime yourself
Option 2: store only epoch seconds and let the attribute coerce to Meerkat::DateTime objects
Option 3: store only DateTime(::Tiny) objects and let the attribute coerce them to Meerkat::DateTime objects
What you don't want to do is mix storing DateTime objects and raw epoch seconds or any other format.
Note that Meerkat::DateTime objects only work when your attribute has a MeerkatDateTime type constraint on it. You can't just store Meerkat::DateTime objects into any random field or deep data structure and expect things to work.
Meerkat offers no real support for embedded objects.
Again, do you really want this? If your objects are read-only, it might be OK, but if not, you'll be tempted to modify the data in the embedded object and then it will be out of sync with the database and you're screwed.
If you really want to experiment with this, if an embedded object does the MooseX::Storage role, then it will be packed into the database on object creation as a hash reference following the MooseX::Storage format.
But if you update the field, you have to pack the replacement object yourself.
$obj->update_set( embedded => $new_embedded->pack );
This has not been tested and is all theoretical. Consider yourself suitably warned.
If for any reason, a document in the database contains data that can't be validated against attribute type and BUILD constraints, Meerkat will throw an exception.
This won't happen during create because the new object has just been validated and is being newly inserted, but it could happen during either update or sync if the document in the database (possibly post-update) fails validation.
create
update
sync
If you catch the exception, your object will not have been modified by the changes in the database and thus, by definition, contains a valid document.
One rather crude attempt to fix it would be to force a reinsert. However, this is destructive so use with extreme caution. You probably want to find out why the document got corrupted and fix that rather than just jamming the document back in and hoping for the best.
reinsert
If you have other ideas or questions for the cookbook, please contact the author.
David Golden <dagolden@cpan.org>
This software is Copyright (c) 2013 by David Golden.
This is free software, licensed under:
The Apache License, Version 2.0, January 2004
To install Meerkat, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Meerkat
CPAN shell
perl -MCPAN -e shell install Meerkat
For more information on module installation, please visit the detailed CPAN module installation guide.