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