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