make EOL tests pass
[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
ec725183 181This level is not optional, it is the bare minumum 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
197=item B<io>
198
ec725183 199The third (io) level is C<load> and C<store>. In this level we are reading
200and writing data to file/network/database/etc.
1390c23d 201
124c2ba5 202This level is also optional, in most cases it does require a C<format> role
f105066e 203to also be used, the exception being the C<StorableFile> role.
1390c23d 204
205=back
206
76218b46 207=head2 Behaviour modifiers
208
209The serialization behaviour can be changed by supplying C<traits>.
210This can be done as follows:
211
212 use MooseX::Storage;
213 with Storage( traits => [Trait1, Trait2,...] );
ec725183 214
76218b46 215The following traits are currently bundled with C<MooseX::Storage>:
216
217=over 4
218
219=item OnlyWhenBuilt
220
ec725183 221Only attributes that have been built (ie, where the predicate returns
76218b46 222'true') will be serialized. This avoids any potentially expensive computations.
223
224See L<MooseX::Storage::Traits::OnlyWhenBuilt> for details.
225
226=back
227
1390c23d 228=head2 How we serialize
229
ec725183 230There are always limits to any serialization framework, there are just
231some things which are really difficult to serialize properly and some
1390c23d 232things which cannot be serialized at all.
233
234=head2 What can be serialized?
235
ec725183 236Currently only numbers, string, ARRAY refs, HASH refs and other
237MooseX::Storage enabled objects are supported.
1390c23d 238
ec725183 239With Array and Hash references the first level down is inspected and
240any objects found are serialized/deserialized for you. We do not do
241this recusively by default, however this feature may become an
1390c23d 242option eventually.
243
ec725183 244The specific serialize/deserialize routine is determined by the
245Moose type constraint a specific attribute has. In most cases subtypes
246of the supported types are handled correctly, and there is a facility
1390c23d 247for adding handlers for custom types as well. This will get documented
248eventually, but it is currently still in development.
249
250=head2 What can not be serialized?
251
ec725183 252We do not support CODE references yet, but this support might be added
253in using B::Deparse or some other deep magic.
1390c23d 254
ec725183 255Scalar refs are not supported, mostly because there is no way to know
256if the value being referenced will be there when the object is inflated.
257I highly doubt will be ever support this in a general sense, but it
1390c23d 258would be possible to add this yourself for a small specific case.
259
ec725183 260Circular references are specifically disallowed, however if you break
1390c23d 261the cycles yourself then re-assemble them later you can get around this.
ec725183 262The reason we disallow circular refs is because they are not always supported
263in all formats we use, and they tend to be very tricky to do for all
264possible cases. It is almost always something you want to have tight control
1390c23d 265over anyway.
266
267=head1 CAVEAT
268
f105066e 269This is B<not> a persistence framework; changes to your object after
ec725183 270you load or store it will not be reflected in the stored class.
1390c23d 271
272=head1 EXPORTS
273
274=over 4
275
276=item B<Storage (%options)>
277
f105066e 278This module will export the C<Storage> method and can be used to
ec725183 279load a specific set of MooseX::Storage roles to implement a specific
280combination of features. It is meant to make things easier, but it
281is by no means the only way. You can still compose your roles by
1390c23d 282hand if you like.
283
25697231 284By default, options are assumed to be short forms. For example, this:
285
286 Storage(format => 'JSON');
287
288...will result in looking for MooseX::Storage::Format::JSON. To use a role
289that is not under the default namespace prefix, start with an equal sign:
290
291 Storage(format => '=My::Private::JSONFormat');
292
293To use a parameterized role (for which, see L<MooseX::Role::Parameterized>) you
294can pass an arrayref of the role name (in short or long form, as above) and its
295parameters:
296
297 Storage(format => [ JSONpm => { json_opts => { pretty => 1 } } ]);
298
1390c23d 299=back
300
ec9c1923 301=head1 METHODS
302
303=over 4
304
305=item B<import>
306
307=back
308
309=head2 Introspection
310
311=over 4
312
313=item B<meta>
314
315=back
316
1390c23d 317=head1 TODO
318
ec725183 319This module needs docs and probably a Cookbook of some kind as well.
7b428d1f 320This is an early release, so that is my excuse for now :)
1390c23d 321
ec725183 322For the time being, please read the tests and feel free to email me
323if you have any questions. This module can also be discussed on IRC
1390c23d 324in the #moose channel on irc.perl.org.
325
ec9c1923 326=head1 BUGS
327
ec725183 328All complex software has bugs lurking in it, and this module is no
ec9c1923 329exception. If you find a bug please either email me, or add the bug
330to cpan-RT.
331
332=head1 AUTHOR
333
334Chris Prather E<lt>chris.prather@iinteractive.comE<gt>
335
336Stevan Little E<lt>stevan.little@iinteractive.comE<gt>
337
6c9f2c85 338Yuval Kogman E<lt>yuval.kogman@iinteractive.comE<gt>
339
ec9c1923 340=head1 COPYRIGHT AND LICENSE
341
1f3074ea 342Copyright 2007-2008 by Infinity Interactive, Inc.
ec9c1923 343
344L<http://www.iinteractive.com>
345
346This library is free software; you can redistribute it and/or modify
347it under the same terms as Perl itself.
e9739624 348
349=cut