7

I am new to Moose and doing quite well until I have hit a snag using a PDL as a property. I want to be able to write an object to a file (I have been using use MooseX::Storage; with Storage('io' => 'StorableFile');, and this object has a PDL as a attribute. PDL::IO::Storable provides the necessary methods to use Storable in this way, however I am at a loss as to how to do this in Moose.

Here is an example, it is a little long, I know, but it is as minimal as I can make it:

#!/usr/bin/perl

package LinearPDL;
use Moose;

use PDL::Lite;
use PDL::IO::Storable;

use MooseX::Storage; 
with Storage('io' => 'StorableFile');

has 'length' => (is => 'ro', isa => 'Num', required => 1);
has 'divisions' => (is => 'ro', isa => 'Int', required => 1);
has 'linear_pdl' => (is => 'ro', isa => 'PDL', lazy => 1, builder => '_build_pdl');

sub _build_pdl {
  my $self = shift;

  my $pdl = $self->length() / ( $self->divisions() - 1 ) * PDL::Basic::xvals($self->divisions());

  return $pdl;
}

no Moose;
__PACKAGE__->meta->make_immutable;

use strict;
use warnings;

my $linear_pdl = LinearPDL->new('length' => 5, 'divisions' => 10);
print $linear_pdl->linear_pdl;

$linear_pdl->store('file'); # blows up here!

my $loaded_lpdl = load('file');
print $loaded_lpdl->linear_pdl;

I think I may have to make a PDL type or perhaps even wrap PDL into something (using MooseX::NonMoose::InsideOut), but perhaps someone can save me from that (or point me down the right road if it is).

Joel Berger
  • 20,180
  • 5
  • 49
  • 104

2 Answers2

7

You don't say what actually goes wrong. At a guess you'll need to tell MooseX::Storage how to handle the PDL object using the PDL object's Storable hooks. The documentation for this feature in MooseX::Storage is very poor but MooseX::Storage::Engine has a add_custom_type_handler() method that takes a typename (PDL in your case) and a HashRef of handlers.

MooseX::Storage::Engine->add_custom_type_handler(
    'PDL' => (
        expand   => sub { my ($data) = @_;   ...  },
        collapse => sub { my ($object) = @_; ...  },
    )
);

Please swing past #moose on irc.perl.org or the Moose mailing list and ask.

[Edit: Update with an example based on the tests.]

perigrin
  • 4,433
  • 19
  • 22
  • 1
    this looks very promising, can you expand a little further? Do I create the `$engine` object in the class definition? What are the inputs and outputs for the expand and collapse subs? Is there somewhere where better documentation or tutorials may be found? Thanks! – Joel Berger May 23 '11 at 23:50
  • Joel unfortunately this is an under-documented part of MooseX::Storage. That's why I was suggesting talking to the irc channel and/or list. I did a quick glance through [the source](http://cpansearch.perl.org/src/BOBTFISH/MooseX-Storage-0.30/lib/MooseX/Storage/Engine.pm) for MooseX::Storage earlier because I knew this feature existed but could't remember how/where. – perigrin May 24 '11 at 02:06
  • 1
    And I posted that last comment then realized I could look at the test suite and found [an example](http://cpansearch.perl.org/src/BOBTFISH/MooseX-Storage-0.30/t/006_w_custom_type_handlers.t) of exactly what you want. – perigrin May 24 '11 at 02:11
  • 1
    Excellent! That has definite promise! I'm marking you correct for having the knowledge. I will try to post my results as another answer for future readers for the specifics of Moose/PDL interaction. – Joel Berger May 24 '11 at 03:25
1

The question by Joel and response from perigrin helped me solve a storage problem that had been sitting in the back of my mind for a while. I'm posting a working example here. It doesn't use PDL but it's related and may help someone in the future.

{
package MVR;
use Moose;
use MooseX::Storage;
use Math::Vector::Real;
use Data::Structure::Util qw (unbless);

with Storage('format' => 'JSON', 'io' => 'File');

MooseX::Storage::Engine->add_custom_type_handler(
  'Math::Vector::Real' => (
      expand    => sub {my $v   = shift; Math::Vector::Real->new(@{$v})},
      collapse  => sub {my $mvr = shift; return (unbless($mvr)) },
  )
);

has 'mvr' => (is => 'rw', isa => 'Math::Vector::Real');

1;
}

use Math::Vector::Real;
my $p = MVR->new(mvr => V(0,1,3));

print $p->dump;
$p->store('my_point.json');

my $p1 = MVR->load('my_point.json');

print $p1->dump; 
Demian
  • 215
  • 2
  • 9