views:

99

answers:

2

I've been trying to find a Perl module that converts a YAML file into moose objects without having to pre-declare the structure as you seem to need to do when using MooseX::YAML. Does anyone know of such a module (or script)?

+2  A: 

If you don't want to do anything special in your YAML, you can just set up the proper coercions for your Moose classes and then pass the loaded data to new(). For example:

package My::Types;
use Moose::Util::TypeConstraints;

class_type 'My::Related::Class';
coerce 'My::Related::Class', from 'HashRef',
    via { My::Related::Class->new(%$_) };

package My::Class;

use Moose;

has name => (is => 'ro', isa => 'Str');
has related => (is => 'ro', isa => 'My::Related::Class', coerce => 1);

package My::Related::Class;

use Moose;
has flavor => (is => 'ro', isa => 'Str');

Then, in some YAML:

name: "An instance of My::Class"
related: 
    flavor: "Dangerberry!"

Then, in some code:

my $obj = My::Class->new(Load($yaml_data));

print ref $obj->related; # My::Related::Class
print $obj->related->flavor; # Dangerberry!

This isn't roundtrippable, obviously, without some more customization of your Moose classes -- it's just for constructing objects. Also, you need to know what classes the root objects are.

That's probably enough for a lot of simple uses, though.

hdp
but what I really want is to dynamically create the class and properties. It seems your solution requires you to know the structure of the yml file.....basically I'm asking if there is a module that can create the moose classes at runtime from the yml config
ennuikiller
"Dynamically create classes" is very different from "turn YAML into objects". You should consider revising the question.
hdp
sorry about the confusion hope my title change cleared it up
ennuikiller
+6  A: 

Don't.

Moose classes, their attributes, and whatever else belongs to them, have a lot of meta-data attached to them. You can't infer all of that meta-data from the data of a single instance.

I'm assuming, given a yaml document as

---
foo: 42
bar: ['moo', 'kooh']

you'd expect and object back that responds to calls to a foo and a bar method, returning the respective values. But how should those accessors behave? Should they be simple reader-methods, or also allow writing? Should they validate against any kind of typeconstraint? etc.

If all you really need is something that makes some unblessed data-structure accessible like an object, have a look at Data::Hive, Hash::AsObject, and similar modules instead.

If you really want to build proper Moose classes, and are either alright with the guesswork that'd be involved, or happen to have the necessary meta-data available somewhere, you can just use the meta-protocol.

my $class = Moose::Meta::Class->create_anon_class(
    attributes => [map {
        # your particular set of assumptions here
        Moose::Meta::Attribute->new($_ => (is => 'ro', ...))
    } keys %{ $deserialized_yaml }],
);

my $instance = $class->name->new($deserialized_yaml);
$instance->$some_key_in_the_yaml_document;
rafl