File Coverage

blib/lib/Data/Serializable.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1 1     1   19509 use strict;
  1         3  
  1         32  
2 1     1   4 use warnings;
  1         2  
  1         24  
3 1     1   16 use 5.006; # Found with Perl::MinimumVersion
  1         7  
  1         53  
4              
5             package Data::Serializable;
6             {
7             $Data::Serializable::VERSION = '0.41.0';
8             }
9 1     1   360 use Moose::Role;
  0            
  0            
10              
11             # ABSTRACT: Moose role that adds serialization support to any class
12              
13             use Module::Runtime ();
14             use Carp qw(croak confess);
15              
16             # Wrap data structure that is not a hash-ref
17             sub _wrap_invalid {
18             my ($module, $obj) = @_;
19             # JSON doesn't know how to serialize anything but hashrefs
20             # FIXME: Technically we should allow array-ref, as JSON standard allows it
21             if ( $module eq 'Data::Serializer::JSON' ) {
22             return ref($obj) eq 'HASH' ? $obj : { '_serialized_object' => $obj };
23             }
24             # XML::Simple doesn't know the difference between empty string and undef
25             if ( $module eq 'Data::Serializer::XML::Simple' ) {
26             return { '_serialized_object_is_undef' => 1 } unless defined($obj);
27             return $obj if ref($obj) eq 'HASH';
28             return { '_serialized_object' => $obj };
29             }
30             return $obj;
31             }
32              
33             # Unwrap JSON previously wrapped with _wrap_invalid()
34             sub _unwrap_invalid {
35             my ($module, $obj) = @_;
36             if ( $module eq 'Data::Serializer::JSON' ) {
37             if ( ref($obj) eq 'HASH' and keys %$obj == 1 and exists( $obj->{'_serialized_object'} ) ) {
38             return $obj->{'_serialized_object'};
39             }
40             return $obj;
41             }
42             # XML::Simple doesn't know the difference between empty string and undef
43             if ( $module eq 'Data::Serializer::XML::Simple' ) {
44             if ( ref($obj) eq 'HASH' and keys %$obj == 1 ) {
45             if ( exists $obj->{'_serialized_object_is_undef'}
46             and $obj->{'_serialized_object_is_undef'} )
47             {
48             return undef; ## no critic qw(Subroutines::ProhibitExplicitReturnUndef)
49             }
50             return $obj->{'_serialized_object'} if exists $obj->{'_serialized_object'};
51             return $obj;
52             }
53             return $obj;
54             }
55             return $obj;
56             }
57              
58              
59             has 'throws_exception' => (
60             is => 'rw',
61             isa => 'Bool',
62             default => 1,
63             );
64              
65              
66             has "serializer_module" => (
67             is => 'rw',
68             isa => 'Str',
69             default => 'Storable',
70             );
71              
72              
73             has "serializer" => (
74             is => 'rw',
75             isa => 'CodeRef',
76             lazy => 1,
77             builder => '_build_serializer',
78             );
79              
80             # Default serializer uses Storable
81             sub _build_serializer { ## no critic qw(Subroutines::ProhibitUnusedPrivateSubroutines)
82             my ($self) = @_;
83              
84             # Figure out full package name of serializer
85             my $module = $self->serializer_module;
86             if( $module ne 'Storable' ) {
87             $module = 'Data::Serializer::' . $module;
88             }
89              
90             # Make sure serializer module is loaded
91             Module::Runtime::require_module( $module );
92              
93             # Just return sub if using default
94             if ( $module eq 'Storable' ) {
95             return sub {
96             return Storable::nfreeze( \( $_[0] ) );
97             };
98             }
99              
100             unless ( $module->isa('Data::Serializer') ) {
101             confess("Serializer module '$module' is not a subclass of Data::Serializer");
102             }
103             my $handler = bless {}, $module; # subclasses apparently doesn't implement new(), go figure!
104             unless ( $handler->can('serialize') ) {
105             confess("Serializer module '$module' doesn't implement the serialize() method");
106             }
107              
108             # Return the specified serializer if we know about it
109             return sub {
110             # Data::Serializer::* has an instance method called serialize()
111             return $handler->serialize(
112             _wrap_invalid( $module, $_[0] )
113             );
114             };
115              
116             }
117              
118              
119             has "deserializer" => (
120             is => 'rw',
121             isa => 'CodeRef',
122             lazy => 1,
123             builder => '_build_deserializer',
124             );
125              
126             # Default deserializer uses Storable
127             sub _build_deserializer { ## no critic qw(Subroutines::ProhibitUnusedPrivateSubroutines)
128             my ($self) = @_;
129              
130             # Figure out full package name of serializer
131             my $module = $self->serializer_module;
132             if( $module ne 'Storable' ) {
133             $module = 'Data::Serializer::' . $module;
134             }
135              
136             # Make sure serializer module is loaded
137             Module::Runtime::require_module( $module );
138              
139             # Just return sub if using default
140             if ( $module eq 'Storable' ) {
141             return sub {
142             return if @_ > 0 and not defined( $_[0] );
143             return ${ Storable::thaw( $_[0] ) };
144             };
145             }
146              
147             unless ( $module->isa('Data::Serializer') ) {
148             confess("Serializer module '$module' is not a subclass of Data::Serializer");
149             }
150             my $handler = bless {}, $module; # subclasses apparently doesn't implement new(), go figure!
151             unless ( $handler->can('deserialize') ) {
152             confess("Serializer module '$module' doesn't implement the deserialize() method");
153             }
154              
155             # Return the specified serializer if we know about it
156             return sub {
157             return if @_ > 0 and not defined( $_[0] );
158             # Data::Serializer::* has an instance method called deserialize()
159             return _unwrap_invalid(
160             $module, $handler->deserialize( $_[0] )
161             );
162             };
163              
164             }
165              
166              
167             sub serialize {
168             my ($self,$message) = @_;
169              
170             # Serialize data
171             my $serialized = eval { $self->serializer->($message); };
172             if ($@) {
173             croak("Couldn't serialize data: $@") if $self->throws_exception;
174             return; # FAIL
175             }
176              
177             return $serialized;
178             }
179              
180              
181             sub deserialize {
182             my ($self,$message)=@_;
183              
184             # De-serialize data
185             my $deserialized = eval { $self->deserializer->($message); };
186             if ($@) {
187             croak("Couldn't deserialize data: $@") if $self->throws_exception;
188             return; # FAIL
189             }
190              
191             return $deserialized;
192             }
193              
194             no Moose::Role;
195             1;
196              
197             __END__
198              
199             =pod
200              
201             =encoding utf-8
202              
203             =head1 NAME
204              
205             Data::Serializable - Moose role that adds serialization support to any class
206              
207             =head1 VERSION
208              
209             version 0.41.0
210              
211             =head1 SYNOPSIS
212              
213             package MyClass;
214             use Moose;
215             with 'Data::Serializable';
216             no Moose;
217              
218             package main;
219             my $obj = MyClass->new( serializer_module => 'JSON' );
220             my $json = $obj->serialize( "Foo" );
221             ...
222             my $str = $obj->deserialize( $json );
223              
224             =head1 DESCRIPTION
225              
226             A Moose-based role that enables the consumer to easily serialize/deserialize data structures.
227             The default serializer is L<Storable>, but any serializer in the L<Data::Serializer> hierarchy can
228             be used automatically. You can even install your own custom serializer if the pre-defined ones
229             are not useful for you.
230              
231             =head1 ATTRIBUTES
232              
233             =head2 throws_exception
234              
235             Defines if methods should throw exceptions or return undef. Default is to throw exceptions.
236             Override default value like this:
237              
238             has '+throws_expection' => ( default => 0 );
239              
240             =head2 serializer_module
241              
242             Name of a predefined module that you wish to use for serialization.
243             Any submodule of L<Data::Serializer> is automatically supported.
244             The built-in support for L<Storable> doesn't require L<Data::Serializer>.
245              
246             =head2 serializer
247              
248             If none of the predefined serializers work for you, you can install your own.
249             This should be a code reference that takes one argument (the message to encode)
250             and returns a scalar back to the caller with the serialized data.
251              
252             =head2 deserializer
253              
254             Same as serializer, but to decode the data.
255              
256             =head1 METHODS
257              
258             =head2 serialize($message)
259              
260             Runs the serializer on the specified argument.
261              
262             =head2 deserialize($message)
263              
264             Runs the deserializer on the specified argument.
265              
266             =head1 SEE ALSO
267              
268             =over 4
269              
270             =item *
271              
272             L<Moose::Manual::Roles>
273              
274             =item *
275              
276             L<Data::Serializer>
277              
278             =back
279              
280             =for :stopwords cpan testmatrix url annocpan anno bugtracker rt cpants kwalitee diff irc mailto metadata placeholders metacpan
281              
282             =head1 SUPPORT
283              
284             =head2 Perldoc
285              
286             You can find documentation for this module with the perldoc command.
287              
288             perldoc Data::Serializable
289              
290             =head2 Websites
291              
292             The following websites have more information about this module, and may be of help to you. As always,
293             in addition to those websites please use your favorite search engine to discover more resources.
294              
295             =over 4
296              
297             =item *
298              
299             MetaCPAN
300              
301             A modern, open-source CPAN search engine, useful to view POD in HTML format.
302              
303             L<http://metacpan.org/release/Data-Serializable>
304              
305             =item *
306              
307             Search CPAN
308              
309             The default CPAN search engine, useful to view POD in HTML format.
310              
311             L<http://search.cpan.org/dist/Data-Serializable>
312              
313             =item *
314              
315             RT: CPAN's Bug Tracker
316              
317             The RT ( Request Tracker ) website is the default bug/issue tracking system for CPAN.
318              
319             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Data-Serializable>
320              
321             =item *
322              
323             AnnoCPAN
324              
325             The AnnoCPAN is a website that allows community annotations of Perl module documentation.
326              
327             L<http://annocpan.org/dist/Data-Serializable>
328              
329             =item *
330              
331             CPAN Ratings
332              
333             The CPAN Ratings is a website that allows community ratings and reviews of Perl modules.
334              
335             L<http://cpanratings.perl.org/d/Data-Serializable>
336              
337             =item *
338              
339             CPAN Forum
340              
341             The CPAN Forum is a web forum for discussing Perl modules.
342              
343             L<http://cpanforum.com/dist/Data-Serializable>
344              
345             =item *
346              
347             CPANTS
348              
349             The CPANTS is a website that analyzes the Kwalitee ( code metrics ) of a distribution.
350              
351             L<http://cpants.perl.org/dist/overview/Data-Serializable>
352              
353             =item *
354              
355             CPAN Testers
356              
357             The CPAN Testers is a network of smokers who run automated tests on uploaded CPAN distributions.
358              
359             L<http://www.cpantesters.org/distro/D/Data-Serializable>
360              
361             =item *
362              
363             CPAN Testers Matrix
364              
365             The CPAN Testers Matrix is a website that provides a visual overview of the test results for a distribution on various Perls/platforms.
366              
367             L<http://matrix.cpantesters.org/?dist=Data-Serializable>
368              
369             =item *
370              
371             CPAN Testers Dependencies
372              
373             The CPAN Testers Dependencies is a website that shows a chart of the test results of all dependencies for a distribution.
374              
375             L<http://deps.cpantesters.org/?module=Data::Serializable>
376              
377             =back
378              
379             =head2 Bugs / Feature Requests
380              
381             Please report any bugs or feature requests by email to C<bug-data-serializable at rt.cpan.org>, or through
382             the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Data-Serializable>. You will be automatically notified of any
383             progress on the request by the system.
384              
385             =head2 Source Code
386              
387             The code is open to the world, and available for you to hack on. Please feel free to browse it and play
388             with it, or whatever. If you want to contribute patches, please send me a diff or prod me to pull
389             from your repository :)
390              
391             L<http://github.com/robinsmidsrod/Data-Serializable>
392              
393             git clone git://github.com/robinsmidsrod/Data-Serializable.git
394              
395             =head1 AUTHOR
396              
397             Robin Smidsrød <robin@smidsrod.no>
398              
399             =head1 COPYRIGHT AND LICENSE
400              
401             This software is copyright (c) 2013 by Robin Smidsrød.
402              
403             This is free software; you can redistribute it and/or modify it under
404             the same terms as the Perl 5 programming language system itself.
405              
406             =cut