NAME

MooseX::Attribute::Deflator - Deflates and inflates Moose attributes to and from a string

VERSION

version 2.1.10

SYNOPSIS

package MySynopsis;

use Moose;
use DateTime;

use MooseX::Attribute::Deflator;

deflate 'DateTime',
   via { $_->epoch },
   inline_as { '$value->epoch' }; # optional
inflate 'DateTime',
   via { DateTime->from_epoch( epoch => $_ ) },
   inline_as { 'DateTime->from_epoch( epoch => $value )' }; # optional

no MooseX::Attribute::Deflator;

# import default deflators and inflators for Moose types
use MooseX::Attribute::Deflator::Moose;

has now => ( is => 'rw', 
           isa => 'DateTime', 
           default => sub { DateTime->now }, 
           traits => ['Deflator'] );

has hash => ( is => 'rw', 
              isa => 'HashRef', 
              default => sub { { foo => 'bar' } }, 
              traits => ['Deflator'] );

package main;

use Test::More;

my $obj = MySynopsis->new;

{
    my $attr = $obj->meta->get_attribute('now');
    my $deflated = $attr->deflate($obj);
    like($deflated, qr/^\d+$/);

    my $inflated = $attr->inflate($obj, $deflated);
    isa_ok($inflated, 'DateTime');
}

{
    my $attr = $obj->meta->get_attribute('hash');
    my $deflated = $attr->deflate($obj);
    is($deflated, '{"foo":"bar"}');

    my $inflated = $attr->inflate($obj, $deflated);
    is_deeply($inflated, {foo => 'bar'})
}

done_testing;

DESCRIPTION

This module consists of a a registry (MooseX::Attribute::Deflator::Registry) an attribute trait MooseX::Attribute::Deflator::Meta::Role::Attribute and predefined deflators and inflators for Moose MooseX::Attribute::Deflator::Moose and MooseX::Types::Strutured MooseX::Attribute::Deflator::Structured. This class is just sugar to set the inflators and deflators.

You can deflate to whatever data structure you want. Loading MooseX::Attribute::Deflator::Moose will cause HashRefs and ArrayRefs to be encoded as JSON strings. However, you can simply overwrite those deflators (and inflators) to deflate to something different like Storable.

Unlike coerce, you don't need to create a deflator and inflator for every type. Instead this module will bubble up the type hierarchy and use the first deflator or inflator it finds.

This comes at a cost: Union types are not supported.

For extra speed, inflators and deflators can be inlined. All in/deflators that come with this module have an inlined version as well. Whenever you implment custom type in/deflators, you should consider writing the inlining code as well. The performance boost is immense. You can check whether an deflator has been inlined by calling:

$attr->is_deflator_inlined;

Inlining works in Moose >= 1.9 only.

FUNCTIONS

deflate
inflate
deflate 'DateTime',
   via { $_->epoch },
   inline_as { '$value->epoch' }; # optional

inflate 'DateTime',
   via { DateTime->from_epoch( epoch => $_ ) },
   inline_as { 'DateTime->from_epoch( epoch => $value )' }; # optional

Defines a deflator or inflator for a given type constraint. This can also be a type constraint defined via MooseX::Types and parameterized types.

The function supplied to via is called with $_ set to the attribute's value and with the following arguments:

$attr

The attribute on which this deflator/inflator has been called

$constraint

The type constraint attached to the attribute

$deflate/$inflate

A code reference to the deflate or inflate function. E.g. this is handy if you want to call the type's parent's parent inflate or deflate method:

deflate 'MySubSubType', via {
   my ($attr, $constraint, $deflate) = @_;
   return $deflate->($_, $constraint->parent->parent);
};
$instance

The object instance on which this deflator/inflator has been called.

@_

Any other arguments added to "inflate" in MooseX::Attribute::Deflator::Meta::Role::Attribute or "deflate" in MooseX::Attribute::Deflator::Meta::Role::Attribute.

For inline, the parameters are handled a bit differently. The code generating subroutine is called with the following parameters:

$constraint

The type constraint attached to the attribute.

$attr

The attribute on which this deflator/inflator has been called.

$registry
my $parent = $registry->($constraint->parent);
my $code = $parent->($constraint->parent, $attr, $registry, @_);

To get the code generator of a type constraint, call this function.

The inline function is expected to return a string. The generated code has access to a number of variables:

$value

Most important, the value that should be de- or inflated is stored in $value.

$type_constraint

For some more advanced examples, have a look at the source of MooseX::Attribute::Deflator::Moose and MooseX::Attribute::Deflator::Structured.

PERFORMANCE

The overhead for having custom deflators or inflators per attribute is minimal. The file benchmark.pl tests three ways of deflating the value of a HashRef attribute to a json encoded string (using JSON::XS).

my $obj     = MyBenchmark->new( hashref => { foo => 'bar' } );
my $attr    = MyBenchmark->meta->get_attribute('hashref');
deflate
$attr->deflate($obj); 

Using the deflate attribute method, supplied by this module.

accessor
JSON::XS::encode_json($obj->hashref);

If the attribute comes with an accessor, you can use this method, to deflate its value. However, you need to know the name of the accessor in order to use this method.

get_value
JSON::XS::encode_json($attr->get_value($obj, 'hashref'));

This solves the mentioned problem with not knowing the accessor name.

The results clearly states that using the deflate method adds only minimal overhead to deflating the attribute value manually.

              Rate get_value   deflate  accessor
get_value  69832/s        --      -87%      -88%
deflate   543478/s      678%        --       -4%
accessor  564972/s      709%        4%        --

AUTHOR

Moritz Onken

COPYRIGHT AND LICENSE

This software is Copyright (c) 2012 by Moritz Onken.

This is free software, licensed under:

The (three-clause) BSD License