Version 0.32
[gitmo/MooseX-Storage.git] / lib / MooseX / Storage.pm
index 85ef575..b5731d4 100644 (file)
@@ -3,8 +3,9 @@ package MooseX::Storage;
 use Moose qw(confess);
 
 use MooseX::Storage::Meta::Attribute::DoNotSerialize;
+use String::RewritePrefix ();
 
-our $VERSION   = '0.20';
+our $VERSION   = '0.32';
 our $AUTHORITY = 'cpan:STEVAN';
 
 sub import {
@@ -18,50 +19,85 @@ sub import {
     $pkg->meta->add_method('Storage' => __PACKAGE__->meta->find_method_by_name('_injected_storage_role_generator'));
 }
 
+my %HORRIBLE_GC_AVOIDANCE_HACK;
+
+sub _rewrite_role_name {
+    my ($self, $base, $string) = @_;
+
+    my $role_name = scalar String::RewritePrefix->rewrite(
+        {
+            ''  => "MooseX::Storage::$base\::",
+            '=' => '',
+        },
+        $string,
+    );
+}
+
+sub _expand_role {
+    my ($self, $base, $value) = @_;
+
+    return unless defined $value;
+
+    if (ref $value) {
+        confess "too many args in arrayref role declaration" if @$value > 2;
+        my ($class, $param) = @$value;
+
+        $class = $self->_rewrite_role_name($base => $class);
+        Class::MOP::load_class($class);
+
+        my $role = $class->meta->generate_role(parameters => $param);
+
+        $HORRIBLE_GC_AVOIDANCE_HACK{ $role->name } = $role;
+        return $role->name;
+    } else {
+        my $class = $self->_rewrite_role_name($base, $value);
+        Class::MOP::load_class($class);
+
+        my $role = $class;
+
+        if ($class->meta->isa(
+            'MooseX::Role::Parameterized::Meta::Role::Parameterizable'
+        )) {
+            $role = $class->meta->generate_role(parameters => undef);
+            $HORRIBLE_GC_AVOIDANCE_HACK{ $role->name } = $role;
+            return $role->name;
+        }
+
+        return $class;
+    }
+}
+
 sub _injected_storage_role_generator {
     my %params = @_;
 
-    if (exists $params{'base'}) {
-        $params{'base'} = ('Base::' . $params{'base'});
-    }
-    else {
-        $params{'base'} = 'Basic';
-    }
+    $params{base} = '=MooseX::Storage::Basic' unless defined $params{base};
 
-    my @roles = (
-        ('MooseX::Storage::' . $params{'base'}),
-    );
+    my @roles = __PACKAGE__->_expand_role(Base => $params{base});
 
     # NOTE:
     # you don't have to have a format
     # role, this just means you dont
     # get anything other than pack/unpack
-    push @roles => 'MooseX::Storage::Format::' . $params{'format'}
-        if exists $params{'format'};
+    push @roles, __PACKAGE__->_expand_role(Format => $params{format});
 
     # NOTE:
     # many IO roles don't make sense unless
     # you have also have a format role chosen
     # too, the exception being StorableFile
-    if (exists $params{'io'}) {
-        # NOTE:
-        # we dont need this code anymore, cause
-        # the role composition will catch it for
-        # us. This allows the StorableFile to work
-        #(exists $params{'format'})
-        #    || confess "You must specify a format role in order to use an IO role";
-        push @roles => 'MooseX::Storage::IO::' . $params{'io'};
-    }
+    #
+    # NOTE:
+    # we dont need this code anymore, cause
+    # the role composition will catch it for
+    # us. This allows the StorableFile to work
+    #(exists $params{'format'})
+    #    || confess "You must specify a format role in order to use an IO role";
+    push @roles, __PACKAGE__->_expand_role(IO => $params{io});
 
     # Note:
     # These traits alter the behaviour of the engine, the user can
     # specify these per role-usage
     for my $trait ( @{ $params{'traits'} ||= [] } ) {
-        push @roles, 'MooseX::Storage::Traits::'.$trait;
-    }
-
-    for my $role ( @roles ) {
-        Class::MOP::load_class($role) or die "Could not load role ($role)";
+        push @roles, __PACKAGE__->_expand_role(Traits => $trait);
     }
 
     return @roles;
@@ -151,7 +187,7 @@ class name and each instance attribute is stored. Very simple.
 This level is not optional, it is the bare minumum that
 MooseX::Storage provides and all other levels build on top of this.
 
-See L<Moosex::Storage::Basic> for the fundamental implementation and
+See L<MooseX::Storage::Basic> for the fundamental implementation and
 options to C<pack> and C<unpack>
 
 =item B<format>
@@ -170,7 +206,7 @@ The third (io) level is C<load> and C<store>. In this level we are reading
 and writing data to file/network/database/etc.
 
 This level is also optional, in most cases it does require a C<format> role
-to also be used, the expection being the C<StorableFile> role.
+to also be used, the exception being the C<StorableFile> role.
 
 =back
 
@@ -236,7 +272,7 @@ over anyway.
 
 =head1 CAVEAT
 
-This is B<not> a persistence framework, changes to your object after
+This is B<not> a persistence framework; changes to your object after
 you load or store it will not be reflected in the stored class.
 
 =head1 EXPORTS
@@ -245,12 +281,27 @@ you load or store it will not be reflected in the stored class.
 
 =item B<Storage (%options)>
 
-This module will export the C<Storage> method will can be used to
+This module will export the C<Storage> method and can be used to
 load a specific set of MooseX::Storage roles to implement a specific
 combination of features. It is meant to make things easier, but it
 is by no means the only way. You can still compose your roles by
 hand if you like.
 
+By default, options are assumed to be short forms.  For example, this:
+
+  Storage(format => 'JSON');
+
+...will result in looking for MooseX::Storage::Format::JSON.  To use a role
+that is not under the default namespace prefix, start with an equal sign:
+
+  Storage(format => '=My::Private::JSONFormat');
+
+To use a parameterized role (for which, see L<MooseX::Role::Parameterized>) you
+can pass an arrayref of the role name (in short or long form, as above) and its
+parameters:
+
+  Storage(format => [ JSONpm => { json_opts => { pretty => 1 } } ]);
+
 =back
 
 =head1 METHODS