convert to Dist::Zilla
[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
c1830046 118 our $VERSION = '0.01';
ec725183 119
1390c23d 120 with Storage('format' => 'JSON', 'io' => 'File');
ec725183 121
1390c23d 122 has 'x' => (is => 'rw', isa => 'Int');
123 has 'y' => (is => 'rw', isa => 'Int');
ec725183 124
1390c23d 125 1;
ec725183 126
1390c23d 127 my $p = Point->new(x => 10, y => 10);
ec725183 128
129 ## methods to pack/unpack an
1390c23d 130 ## object in perl data structures
ec725183 131
1390c23d 132 # pack the class into a hash
c1830046 133 $p->pack(); # { __CLASS__ => 'Point-0.01', x => 10, y => 10 }
ec725183 134
1390c23d 135 # unpack the hash into a class
c1830046 136 my $p2 = Point->unpack({ __CLASS__ => 'Point-0.01', x => 10, y => 10 });
1390c23d 137
ec725183 138 ## methods to freeze/thaw into
1390c23d 139 ## a specified serialization format
140 ## (in this case JSON)
ec725183 141
1390c23d 142 # pack the class into a JSON string
c1830046 143 $p->freeze(); # { "__CLASS__" : "Point-0.01", "x" : 10, "y" : 10 }
ec725183 144
1390c23d 145 # unpack the JSON string into a class
ec725183 146 my $p2 = Point->thaw('{ "__CLASS__" : "Point-0.01", "x" : 10, "y" : 10 }');
1390c23d 147
ec725183 148 ## methods to load/store a class
1390c23d 149 ## on the file system
ec725183 150
1390c23d 151 $p->store('my_point.json');
ec725183 152
1390c23d 153 my $p2 = Point->load('my_point.json');
154
ec9c1923 155=head1 DESCRIPTION
156
ec725183 157MooseX::Storage is a serialization framework for Moose, it provides
1390c23d 158a very flexible and highly pluggable way to serialize Moose classes
159to a number of different formats and styles.
160
7b428d1f 161=head2 Important Note
162
ec725183 163This is still an early release of this module, so use with caution.
164It's outward facing serialization API should be considered stable,
7b428d1f 165but I still reserve the right to make tweaks if I need too. Anything
ec725183 166beyond the basic pack/unpack, freeze/thaw and load/store should not
7b428d1f 167be relied on.
168
1390c23d 169=head2 Levels of Serialization
170
ec725183 171There are 3 levels to the serialization, each of which builds upon
1390c23d 172the other and each of which can be customized to the specific needs
173of your class.
174
175=over 4
176
177=item B<base>
178
ec725183 179The first (base) level is C<pack> and C<unpack>. In this level the
180class is serialized into a Perl HASH reference, it is tagged with the
1390c23d 181class name and each instance attribute is stored. Very simple.
182
ec725183 183This level is not optional, it is the bare minumum that
1390c23d 184MooseX::Storage provides and all other levels build on top of this.
185
f105066e 186See L<MooseX::Storage::Basic> for the fundamental implementation and
c21a034f 187options to C<pack> and C<unpack>
188
1390c23d 189=item B<format>
190
ec725183 191The second (format) level is C<freeze> and C<thaw>. In this level the
192output of C<pack> is sent to C<freeze> or the output of C<thaw> is sent
193to C<unpack>. This levels primary role is to convert to and from the
194specific serialization format and Perl land.
1390c23d 195
ec725183 196This level is optional, if you don't want/need it, you don't have to
1390c23d 197have it. You can just use C<pack>/C<unpack> instead.
198
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
ec725183 223Only attributes that have been built (ie, 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
243this recusively by default, however this feature may become an
1390c23d 244option eventually.
245
ec725183 246The specific serialize/deserialize routine is determined by the
247Moose type constraint a specific attribute has. In most cases subtypes
248of the supported types are handled correctly, and there is a facility
1390c23d 249for adding handlers for custom types as well. This will get documented
250eventually, but it is currently still in development.
251
252=head2 What can not be serialized?
253
ec725183 254We do not support CODE references yet, but this support might be added
255in using B::Deparse or some other deep magic.
1390c23d 256
ec725183 257Scalar refs are not supported, mostly because there is no way to know
258if the value being referenced will be there when the object is inflated.
259I highly doubt will be ever support this in a general sense, but it
1390c23d 260would be possible to add this yourself for a small specific case.
261
ec725183 262Circular references are specifically disallowed, however if you break
1390c23d 263the cycles yourself then re-assemble them later you can get around this.
ec725183 264The reason we disallow circular refs is because they are not always supported
265in all formats we use, and they tend to be very tricky to do for all
266possible cases. It is almost always something you want to have tight control
1390c23d 267over anyway.
268
269=head1 CAVEAT
270
f105066e 271This is B<not> a persistence framework; changes to your object after
ec725183 272you load or store it will not be reflected in the stored class.
1390c23d 273
274=head1 EXPORTS
275
276=over 4
277
278=item B<Storage (%options)>
279
f105066e 280This module will export the C<Storage> method and can be used to
ec725183 281load a specific set of MooseX::Storage roles to implement a specific
282combination of features. It is meant to make things easier, but it
283is by no means the only way. You can still compose your roles by
1390c23d 284hand if you like.
285
25697231 286By default, options are assumed to be short forms. For example, this:
287
288 Storage(format => 'JSON');
289
290...will result in looking for MooseX::Storage::Format::JSON. To use a role
291that is not under the default namespace prefix, start with an equal sign:
292
293 Storage(format => '=My::Private::JSONFormat');
294
295To use a parameterized role (for which, see L<MooseX::Role::Parameterized>) you
296can pass an arrayref of the role name (in short or long form, as above) and its
297parameters:
298
299 Storage(format => [ JSONpm => { json_opts => { pretty => 1 } } ]);
300
1390c23d 301=back
302
ec9c1923 303=head1 METHODS
304
305=over 4
306
307=item B<import>
308
309=back
310
311=head2 Introspection
312
313=over 4
314
315=item B<meta>
316
317=back
318
1390c23d 319=head1 TODO
320
ec725183 321This module needs docs and probably a Cookbook of some kind as well.
7b428d1f 322This is an early release, so that is my excuse for now :)
1390c23d 323
ec725183 324For the time being, please read the tests and feel free to email me
325if you have any questions. This module can also be discussed on IRC
1390c23d 326in the #moose channel on irc.perl.org.
327
ec9c1923 328=head1 BUGS
329
ec725183 330All complex software has bugs lurking in it, and this module is no
ec9c1923 331exception. If you find a bug please either email me, or add the bug
332to cpan-RT.
333
334=head1 AUTHOR
335
336Chris Prather E<lt>chris.prather@iinteractive.comE<gt>
337
338Stevan Little E<lt>stevan.little@iinteractive.comE<gt>
339
6c9f2c85 340Yuval Kogman E<lt>yuval.kogman@iinteractive.comE<gt>
341
ec9c1923 342=head1 COPYRIGHT AND LICENSE
343
1f3074ea 344Copyright 2007-2008 by Infinity Interactive, Inc.
ec9c1923 345
346L<http://www.iinteractive.com>
347
348This library is free software; you can redistribute it and/or modify
349it under the same terms as Perl itself.
e9739624 350
351=cut