Version 0.32
[gitmo/MooseX-Storage.git] / lib / MooseX / Storage.pm
CommitLineData
e59193fb 1
e59193fb 2package MooseX::Storage;
ec9c1923 3use Moose qw(confess);
e59193fb 4
eebcb6dc 5use MooseX::Storage::Meta::Attribute::DoNotSerialize;
49f81d6c 6use String::RewritePrefix ();
eebcb6dc 7
e44b5f54 8our $VERSION = '0.32';
69b45b7d 9our $AUTHORITY = 'cpan:STEVAN';
7b428d1f 10
e59193fb 11sub import {
12 my $pkg = caller();
ec725183 13
ec9c1923 14 return if $pkg eq 'main';
ec725183 15
ec9c1923 16 ($pkg->can('meta'))
17 || confess "This package can only be used in Moose based classes";
ec725183 18
19 $pkg->meta->add_method('Storage' => __PACKAGE__->meta->find_method_by_name('_injected_storage_role_generator'));
f9143059 20}
21
9ff679e4 22my %HORRIBLE_GC_AVOIDANCE_HACK;
23
61fb1aaa 24sub _rewrite_role_name {
25 my ($self, $base, $string) = @_;
26
27 my $role_name = scalar String::RewritePrefix->rewrite(
28 {
29 '' => "MooseX::Storage::$base\::",
30 '=' => '',
31 },
32 $string,
33 );
34}
35
36sub _expand_role {
37 my ($self, $base, $value) = @_;
f35d2054 38
39 return unless defined $value;
40
41 if (ref $value) {
61fb1aaa 42 confess "too many args in arrayref role declaration" if @$value > 2;
43 my ($class, $param) = @$value;
9ff679e4 44
61fb1aaa 45 $class = $self->_rewrite_role_name($base => $class);
9ff679e4 46 Class::MOP::load_class($class);
47
48 my $role = $class->meta->generate_role(parameters => $param);
49
50 $HORRIBLE_GC_AVOIDANCE_HACK{ $role->name } = $role;
51 return $role->name;
f35d2054 52 } else {
61fb1aaa 53 my $class = $self->_rewrite_role_name($base, $value);
54 Class::MOP::load_class($class);
55
56 my $role = $class;
57
58 if ($class->meta->isa(
59 'MooseX::Role::Parameterized::Meta::Role::Parameterizable'
60 )) {
61 $role = $class->meta->generate_role(parameters => undef);
62 $HORRIBLE_GC_AVOIDANCE_HACK{ $role->name } = $role;
63 return $role->name;
64 }
65
66 return $class;
f35d2054 67 }
49f81d6c 68}
69
f9143059 70sub _injected_storage_role_generator {
71 my %params = @_;
ec725183 72
49f81d6c 73 $params{base} = '=MooseX::Storage::Basic' unless defined $params{base};
ec725183 74
61fb1aaa 75 my @roles = __PACKAGE__->_expand_role(Base => $params{base});
ec725183 76
f9143059 77 # NOTE:
ec725183 78 # you don't have to have a format
79 # role, this just means you dont
f9143059 80 # get anything other than pack/unpack
61fb1aaa 81 push @roles, __PACKAGE__->_expand_role(Format => $params{format});
ec725183 82
f9143059 83 # NOTE:
ec725183 84 # many IO roles don't make sense unless
f9143059 85 # you have also have a format role chosen
86 # too, the exception being StorableFile
49f81d6c 87 #
88 # NOTE:
89 # we dont need this code anymore, cause
90 # the role composition will catch it for
91 # us. This allows the StorableFile to work
92 #(exists $params{'format'})
93 # || confess "You must specify a format role in order to use an IO role";
61fb1aaa 94 push @roles, __PACKAGE__->_expand_role(IO => $params{io});
ec725183 95
76218b46 96 # Note:
97 # These traits alter the behaviour of the engine, the user can
98 # specify these per role-usage
99 for my $trait ( @{ $params{'traits'} ||= [] } ) {
61fb1aaa 100 push @roles, __PACKAGE__->_expand_role(Traits => $trait);
76218b46 101 }
76b60811 102
f9143059 103 return @roles;
e59193fb 104}
105
ec9c1923 1061;
e59193fb 107
ec9c1923 108__END__
e59193fb 109
ec9c1923 110=pod
e59193fb 111
ec9c1923 112=head1 NAME
e9739624 113
d109f069 114MooseX::Storage - A serialization framework for Moose classes
e59193fb 115
ec9c1923 116=head1 SYNOPSIS
e9739624 117
1390c23d 118 package Point;
119 use Moose;
120 use MooseX::Storage;
ec725183 121
c1830046 122 our $VERSION = '0.01';
ec725183 123
1390c23d 124 with Storage('format' => 'JSON', 'io' => 'File');
ec725183 125
1390c23d 126 has 'x' => (is => 'rw', isa => 'Int');
127 has 'y' => (is => 'rw', isa => 'Int');
ec725183 128
1390c23d 129 1;
ec725183 130
1390c23d 131 my $p = Point->new(x => 10, y => 10);
ec725183 132
133 ## methods to pack/unpack an
1390c23d 134 ## object in perl data structures
ec725183 135
1390c23d 136 # pack the class into a hash
c1830046 137 $p->pack(); # { __CLASS__ => 'Point-0.01', x => 10, y => 10 }
ec725183 138
1390c23d 139 # unpack the hash into a class
c1830046 140 my $p2 = Point->unpack({ __CLASS__ => 'Point-0.01', x => 10, y => 10 });
1390c23d 141
ec725183 142 ## methods to freeze/thaw into
1390c23d 143 ## a specified serialization format
144 ## (in this case JSON)
ec725183 145
1390c23d 146 # pack the class into a JSON string
c1830046 147 $p->freeze(); # { "__CLASS__" : "Point-0.01", "x" : 10, "y" : 10 }
ec725183 148
1390c23d 149 # unpack the JSON string into a class
ec725183 150 my $p2 = Point->thaw('{ "__CLASS__" : "Point-0.01", "x" : 10, "y" : 10 }');
1390c23d 151
ec725183 152 ## methods to load/store a class
1390c23d 153 ## on the file system
ec725183 154
1390c23d 155 $p->store('my_point.json');
ec725183 156
1390c23d 157 my $p2 = Point->load('my_point.json');
158
ec9c1923 159=head1 DESCRIPTION
160
ec725183 161MooseX::Storage is a serialization framework for Moose, it provides
1390c23d 162a very flexible and highly pluggable way to serialize Moose classes
163to a number of different formats and styles.
164
7b428d1f 165=head2 Important Note
166
ec725183 167This is still an early release of this module, so use with caution.
168It's outward facing serialization API should be considered stable,
7b428d1f 169but I still reserve the right to make tweaks if I need too. Anything
ec725183 170beyond the basic pack/unpack, freeze/thaw and load/store should not
7b428d1f 171be relied on.
172
1390c23d 173=head2 Levels of Serialization
174
ec725183 175There are 3 levels to the serialization, each of which builds upon
1390c23d 176the other and each of which can be customized to the specific needs
177of your class.
178
179=over 4
180
181=item B<base>
182
ec725183 183The first (base) level is C<pack> and C<unpack>. In this level the
184class is serialized into a Perl HASH reference, it is tagged with the
1390c23d 185class name and each instance attribute is stored. Very simple.
186
ec725183 187This level is not optional, it is the bare minumum that
1390c23d 188MooseX::Storage provides and all other levels build on top of this.
189
f105066e 190See L<MooseX::Storage::Basic> for the fundamental implementation and
c21a034f 191options to C<pack> and C<unpack>
192
1390c23d 193=item B<format>
194
ec725183 195The second (format) level is C<freeze> and C<thaw>. In this level the
196output of C<pack> is sent to C<freeze> or the output of C<thaw> is sent
197to C<unpack>. This levels primary role is to convert to and from the
198specific serialization format and Perl land.
1390c23d 199
ec725183 200This level is optional, if you don't want/need it, you don't have to
1390c23d 201have it. You can just use C<pack>/C<unpack> instead.
202
203=item B<io>
204
ec725183 205The third (io) level is C<load> and C<store>. In this level we are reading
206and writing data to file/network/database/etc.
1390c23d 207
124c2ba5 208This level is also optional, in most cases it does require a C<format> role
f105066e 209to also be used, the exception being the C<StorableFile> role.
1390c23d 210
211=back
212
76218b46 213=head2 Behaviour modifiers
214
215The serialization behaviour can be changed by supplying C<traits>.
216This can be done as follows:
217
218 use MooseX::Storage;
219 with Storage( traits => [Trait1, Trait2,...] );
ec725183 220
76218b46 221The following traits are currently bundled with C<MooseX::Storage>:
222
223=over 4
224
225=item OnlyWhenBuilt
226
ec725183 227Only attributes that have been built (ie, where the predicate returns
76218b46 228'true') will be serialized. This avoids any potentially expensive computations.
229
230See L<MooseX::Storage::Traits::OnlyWhenBuilt> for details.
231
232=back
233
1390c23d 234=head2 How we serialize
235
ec725183 236There are always limits to any serialization framework, there are just
237some things which are really difficult to serialize properly and some
1390c23d 238things which cannot be serialized at all.
239
240=head2 What can be serialized?
241
ec725183 242Currently only numbers, string, ARRAY refs, HASH refs and other
243MooseX::Storage enabled objects are supported.
1390c23d 244
ec725183 245With Array and Hash references the first level down is inspected and
246any objects found are serialized/deserialized for you. We do not do
247this recusively by default, however this feature may become an
1390c23d 248option eventually.
249
ec725183 250The specific serialize/deserialize routine is determined by the
251Moose type constraint a specific attribute has. In most cases subtypes
252of the supported types are handled correctly, and there is a facility
1390c23d 253for adding handlers for custom types as well. This will get documented
254eventually, but it is currently still in development.
255
256=head2 What can not be serialized?
257
ec725183 258We do not support CODE references yet, but this support might be added
259in using B::Deparse or some other deep magic.
1390c23d 260
ec725183 261Scalar refs are not supported, mostly because there is no way to know
262if the value being referenced will be there when the object is inflated.
263I highly doubt will be ever support this in a general sense, but it
1390c23d 264would be possible to add this yourself for a small specific case.
265
ec725183 266Circular references are specifically disallowed, however if you break
1390c23d 267the cycles yourself then re-assemble them later you can get around this.
ec725183 268The reason we disallow circular refs is because they are not always supported
269in all formats we use, and they tend to be very tricky to do for all
270possible cases. It is almost always something you want to have tight control
1390c23d 271over anyway.
272
273=head1 CAVEAT
274
f105066e 275This is B<not> a persistence framework; changes to your object after
ec725183 276you load or store it will not be reflected in the stored class.
1390c23d 277
278=head1 EXPORTS
279
280=over 4
281
282=item B<Storage (%options)>
283
f105066e 284This module will export the C<Storage> method and can be used to
ec725183 285load a specific set of MooseX::Storage roles to implement a specific
286combination of features. It is meant to make things easier, but it
287is by no means the only way. You can still compose your roles by
1390c23d 288hand if you like.
289
25697231 290By default, options are assumed to be short forms. For example, this:
291
292 Storage(format => 'JSON');
293
294...will result in looking for MooseX::Storage::Format::JSON. To use a role
295that is not under the default namespace prefix, start with an equal sign:
296
297 Storage(format => '=My::Private::JSONFormat');
298
299To use a parameterized role (for which, see L<MooseX::Role::Parameterized>) you
300can pass an arrayref of the role name (in short or long form, as above) and its
301parameters:
302
303 Storage(format => [ JSONpm => { json_opts => { pretty => 1 } } ]);
304
1390c23d 305=back
306
ec9c1923 307=head1 METHODS
308
309=over 4
310
311=item B<import>
312
313=back
314
315=head2 Introspection
316
317=over 4
318
319=item B<meta>
320
321=back
322
1390c23d 323=head1 TODO
324
ec725183 325This module needs docs and probably a Cookbook of some kind as well.
7b428d1f 326This is an early release, so that is my excuse for now :)
1390c23d 327
ec725183 328For the time being, please read the tests and feel free to email me
329if you have any questions. This module can also be discussed on IRC
1390c23d 330in the #moose channel on irc.perl.org.
331
ec9c1923 332=head1 BUGS
333
ec725183 334All complex software has bugs lurking in it, and this module is no
ec9c1923 335exception. If you find a bug please either email me, or add the bug
336to cpan-RT.
337
338=head1 AUTHOR
339
340Chris Prather E<lt>chris.prather@iinteractive.comE<gt>
341
342Stevan Little E<lt>stevan.little@iinteractive.comE<gt>
343
6c9f2c85 344Yuval Kogman E<lt>yuval.kogman@iinteractive.comE<gt>
345
ec9c1923 346=head1 COPYRIGHT AND LICENSE
347
1f3074ea 348Copyright 2007-2008 by Infinity Interactive, Inc.
ec9c1923 349
350L<http://www.iinteractive.com>
351
352This library is free software; you can redistribute it and/or modify
353it under the same terms as Perl itself.
e9739624 354
355=cut