File Coverage

blib/lib/Data/Record/Serialize.pm
Criterion Covered Total %
statement 34 34 100.0
branch 9 10 90.0
condition n/a
subroutine 9 9 100.0
pod 1 2 50.0
total 53 55 96.3


line stmt bran cond sub pod time code
1              
2             # ABSTRACT: Flexible serialization of a record
3              
4             use 5.010000;
5 18     18   3518838  
  18         150  
6             use strict;
7 18     18   86 use warnings;
  18         32  
  18         312  
8 18     18   70  
  18         44  
  18         489  
9             use warnings::register qw( Encode::dbi::queue );
10 18     18   106  
  18         32  
  18         3157  
11             use Data::Record::Serialize::Error -all;
12 18     18   6623  
  18         45  
  18         135  
13             our $VERSION = '1.03'; # TRIAL
14              
15             use Package::Variant
16             importing => [
17 18         273 'Moo',
18             'String::RewritePrefix' => [
19             rewrite => {
20             -as => 'rewrite_encode',
21             prefixes => {
22             '' => 'Data::Record::Serialize::Encode::',
23             '+' => ''
24             },
25             }
26             ],
27             'String::RewritePrefix' => [
28             rewrite => {
29             -as => 'rewrite_sink',
30             prefixes => {
31             '' => 'Data::Record::Serialize::Sink::',
32             '+' => ''
33             },
34             }
35             ],
36             ],
37             subs => [qw( with has rewrite_encode rewrite_sink )];
38 18     18   10784  
  18         123069  
39             use namespace::clean;
40 18     18   10241  
  18         200792  
  18         104  
41              
42              
43              
44              
45             my ( $class, $target, %attr ) = @_;
46              
47 70     70 0 449357 error( 'attribute::value', "must specify <encode> attribute" )
48             unless defined $attr{encode};
49              
50 70 100       279 with 'Data::Record::Serialize::Role::Base';
51              
52 69         256 my $encoder = rewrite_encode( $attr{encode} );
53              
54 69         235796 with $encoder;
55              
56 69         2741 if ( $target->does( 'Data::Record::Serialize::Role::Sink' ) ) {
57              
58 69 100       96348 error( 'attribute::value',
59             "encoder ($encoder) is already a sink; don't specify a sink attribute\n"
60             ) if defined $attr{sink};
61             }
62 45 100       1023  
63             else {
64             with rewrite_sink( $attr{sink} ? $attr{sink} : 'stream' );
65             }
66 24 100       808  
67             with 'Data::Record::Serialize::Role::Default';
68             }
69 68         24416  
70              
71              
72              
73              
74              
75              
76              
77              
78              
79              
80              
81              
82              
83              
84              
85              
86              
87              
88              
89              
90              
91              
92              
93              
94              
95              
96              
97              
98              
99              
100              
101              
102              
103              
104              
105              
106              
107              
108              
109              
110              
111              
112              
113              
114              
115              
116              
117              
118              
119              
120              
121              
122              
123              
124              
125              
126              
127              
128              
129              
130              
131              
132              
133              
134              
135              
136              
137              
138              
139              
140              
141              
142              
143              
144              
145              
146              
147              
148              
149              
150              
151              
152              
153              
154              
155              
156              
157              
158              
159              
160              
161              
162              
163              
164              
165              
166              
167              
168              
169              
170              
171              
172              
173              
174              
175              
176              
177              
178              
179              
180              
181              
182              
183              
184              
185              
186              
187              
188              
189              
190              
191              
192              
193              
194              
195              
196              
197              
198              
199              
200              
201              
202              
203              
204              
205              
206              
207              
208              
209              
210              
211              
212              
213              
214              
215              
216              
217              
218              
219              
220              
221              
222              
223              
224              
225              
226              
227              
228              
229              
230              
231              
232              
233              
234              
235              
236              
237              
238              
239              
240              
241              
242              
243              
244              
245              
246              
247              
248              
249              
250              
251              
252              
253              
254              
255              
256              
257              
258              
259              
260              
261              
262              
263              
264              
265              
266              
267              
268              
269              
270              
271              
272              
273              
274              
275              
276              
277              
278              
279             my $class = shift;
280             my $attr = 'HASH' eq ref $_[0] ? shift : {@_};
281              
282 70     70 1 290725 my %class_attr = (
283 70 50       362 encode => $attr->{encode},
284             sink => $attr->{sink},
285             );
286              
287             $class = Package::Variant->build_variant_of( __PACKAGE__, %class_attr );
288 70         275  
289             return $class->new( $attr );
290 70         438 }
291              
292 68         122224 1;
293              
294              
295             #
296             # This file is part of Data-Record-Serialize
297             #
298             # This software is Copyright (c) 2017 by Smithsonian Astrophysical Observatory.
299             #
300             # This is free software, licensed under:
301             #
302             # The GNU General Public License, Version 3, June 2007
303             #
304              
305              
306             =pod
307              
308             =for :stopwords Diab Jerius Smithsonian Astrophysical Observatory deterministically
309              
310             =head1 NAME
311              
312             Data::Record::Serialize - Flexible serialization of a record
313              
314             =head1 VERSION
315              
316             version 1.03
317              
318             =head1 SYNOPSIS
319              
320             use Data::Record::Serialize;
321              
322             # simple output to json
323             $s = Data::Record::Serialize->new( encode => 'json', \%attr );
324             $s->send( \%record );
325              
326             # cleanup record before sending
327             $s = Data::Record::Serialize->new( encode => 'json',
328             fields => [ qw( obsid chip_id phi theta ) ],
329             format => 1,
330             format_types => { N => '%0.4f' },
331             format_fields => { obsid => '%05d' },
332             rename_fields => { chip_id => 'CHIP' },
333             types => { obsid => 'I', chip_id => 'S',
334             phi => 'N', theta => 'N' },
335             );
336             $s->send( \%record );
337              
338              
339             # send to an SQLite database
340             $s = Data::Record::Serialize->new(
341             encode => 'dbi',
342             dsn => [ 'SQLite', [ dbname => $dbname ] ],
343             table => 'stuff',
344             format => 1,
345             fields => [ qw( obsid chip_id phi theta ) ],
346             format_types => { N => '%0.4f' },
347             format_fields => { obsid => '%05d' },
348             rename_fields => { chip_id => 'CHIP' },
349             types => { obsid => 'I', chip_id => 'S',
350             phi => 'N', theta => 'N' },
351             );
352             $s->send( \%record );
353              
354             =head1 DESCRIPTION
355              
356             B<Data::Record::Serialize> encodes data records and sends them
357             somewhere. This module is primarily useful for output of sets of
358             uniformly structured data records. It provides a uniform, thin,
359             interface to various serializers and output sinks. Its I<raison
360             d'etre> is its ability to manipulate the records prior to encoding
361             and output.
362              
363             =over
364              
365             =item *
366              
367             A record is a collection of fields, i.e. keys and I<scalar>
368             values.
369              
370             =item *
371              
372             All records are assumed to have the same structure.
373              
374             =item *
375              
376             Fields may have simple types.
377              
378             =item *
379              
380             Fields may be renamed upon output.
381              
382             =item *
383              
384             A subset of the fields may be selected for output.
385              
386             =item *
387              
388             Field values may be transformed prior to output.
389              
390             =back
391              
392             =head2 Types
393              
394             Some output encoders care about the type of a
395             field. B<Data::Record::Serialize> recognizes these types:
396              
397             =over
398              
399             =item *
400              
401             C<N> - Number (any number)
402              
403             =item *
404              
405             C<I> - Integer
406              
407             =item *
408              
409             C<S> - String
410              
411             =item *
412              
413             C<B> - Boolean
414              
415             =back
416              
417             Not all encoders support separate integer or Boolean types. Where not supported,
418             integers are encoded as numbers and Booleans as integers.
419              
420             Types may be specified for fields, or may be automatically determined
421             from the first record which is output. It is not possible to
422             deterministically determine if a field is Boolean, so such fields
423             must be explicitly specified. Boolean fields should be "truthy",
424             e.g., when used in a conditional, they evaluate to true or false.
425              
426             =head2 Field transformation
427              
428             Transformations can be applied to fields prior to output, and may be
429             specified globally for data types as well as for specifically for
430             fields. The latter take precedence.
431              
432             Transformations are specified via the L</format_fields> and
433             L</format_types> parameters. They can either be a C<sprintf>
434             compatible format string,
435              
436             format_types => { N => '%0.4f' },
437             format_fields => { obsid => '%05d' },
438              
439             or a code reference:
440              
441             format_types => { B => sub { Lingua::Boolean::Tiny::boolean( $_[0] ) } }
442              
443             =head2 Encoders
444              
445             The available encoders and their respective documentation are:
446              
447             =over
448              
449             =item *
450              
451             C<dbi> - L<Data::Record::Serialize::Encode::dbi>
452              
453             Write to a database via B<DBI>. This is a combined
454             encoder and sink.
455              
456             =item *
457              
458             C<ddump> - L<Data::Record::Serialize::Encode::ddump>
459              
460             encode via L<Data::Dumper>
461              
462             =item *
463              
464             C<json> - L<Data::Record::Serialize::Encode::json>
465              
466             =item *
467              
468             C<null> - L<Data::Record::Serialize::Sink::null>
469              
470             This is a combined encoder and sink.
471              
472             =item *
473              
474             C<rdb> - L<Data::Record::Serialize::Encode::rdb>
475              
476             =item *
477              
478             C<yaml> - L<Data::Record::Serialize::Encode::yaml>
479              
480             =back
481              
482             =head2 Sinks
483              
484             Sinks are where encoded data are sent.
485              
486             The available sinks and their documentation are:
487              
488             =over
489              
490             =item *
491              
492             C<stream> - L<Data::Record::Serialize::Sink::stream>
493              
494             write to a stream
495              
496             =item *
497              
498             C<null> - L<Data::Record::Serialize::Sink::null>
499              
500             send the encoded data to the bit bucket.
501              
502             =item *
503              
504             C<array> - L<Data::Record::Serialize::Sink::array>
505              
506             append the encoded data to an array.
507              
508             =back
509              
510             Refer to the documentation for additional constructor options, and
511             object and class methods and attributes;
512              
513             =head2 Fields and their types
514              
515             Which fields are output and how their types are determined depends
516             upon the L</fields>, L</types>, and L</default_type> attributes.
517              
518             This seems a bit complicated, but the idea is to provide a DWIM
519             interface requiring minimal specification.
520              
521             In the following table:
522              
523             N => not specified
524             Y => specified
525             X => doesn't matter
526             all => the string 'all'
527              
528             Automatic type determination is done by examining the first
529             record sent to the output stream.
530              
531             Automatic output field determination is done by examining the first
532             record sent to the output stream. Only those fields will be
533             output for subsequent records.
534              
535             fields types default_type Result
536             ------ ----- ------------ ------
537              
538             N/all N N Output fields are automatically determined.
539             Types are automatically determined.
540              
541             N/all N Y Output fields are automatically determined.
542             Types are set to <default_type>.
543              
544             Y N N Fields in <fields> are output.
545             Types are automatically determined.
546              
547             Y Y N Fields in <fields> are output.
548             Fields in <types> get the specified type.
549             Types for other fields are automatically determined.
550              
551             Y Y Y Fields in <fields> are output.
552             Fields in <types> get the specified type.
553             Types for other fields are set to <default_type>.
554              
555             all Y N Output fields are automatically determined.
556             Fields in <types> get the specified type.
557             Types for other fields are automatically determined.
558              
559             all Y Y Output fields are automatically determined.
560             Fields in <types> get the specified type.
561             Types for other fields are set to <default_type>.
562              
563             N Y X Fields in <types> are output.
564             Types are specified by <types>.
565              
566             =head2 Errors
567              
568             Most errors result in exception objects being thrown, typically in the
569             L<Data::Record::Serialize::Error> hierarchy.
570              
571             =head1 CLASS METHODS
572              
573             =head2 B<new>
574              
575             $s = Data::Record::Serialize->new( %args );
576             $s = Data::Record::Serialize->new( \%args );
577              
578             Construct a new object. In addition to any arguments required or provided
579             by the specified encoders and sinks, the following arguments are recognized:
580              
581             =over
582              
583             =item C<types> => I<hashref>|I<arrayref>
584              
585             This specifies types (C<N>, C<I>, C<S>, C<B> ) for fields.
586              
587             If an array, the fields will be output in the specified order,
588             provided the encoder permits it (see below, however). For example,
589              
590             # use order if possible
591             types => [ c => 'N', a => 'N', b => 'N' ]
592              
593             # order doesn't matter
594             types => { c => 'N', a => 'N', b => 'N' }
595              
596             If L</fields> is specified, then its order will override that specified
597             here.
598              
599             To understand how this attribute works in concert with L</fields> and
600             L</default_type>, please see L</Fields and their types>.
601              
602             =item C<default_type> => C<S>|C<N>|C<I>|C<B>
603              
604             The default input type for fields whose types were not specified via
605             the L</types>.
606              
607             To understand how this attribute works in concert with L</fields> and
608             L</types>, please see L</Fields and their types>.
609              
610             =item C<fields> => I<arrayref>|C<all>
611              
612             The fields to output. If it is the string C<all>,
613             all input fields will be output. If it is an arrayref, the
614             fields will be output in the specified order, provided the encoder
615             permits it.
616              
617             To understand how this attribute works in concert with L</types> and
618             L</default_type>, please see L</Fields and their types>.
619              
620             =item C<encode> => I<encoder>
621              
622             I<Required>. The module which will encode the data.
623              
624             With no prefix, it is assumed to be in the
625             C<Data::Record::Serialize::Encode> namespace. With a prefix of C<+>
626             it is a fully qualified module name.
627              
628             Specific encoders may provide additional, or require specific,
629             attributes. See L</Encoders> for more information.
630              
631             =item C<sink> => I<sink>
632              
633             The module which writes the encoded data.
634              
635             With no prefix, it is assumed to be in the
636             C<Data::Record::Serialize::Sink> namespace. With a prefix of C<+>
637             it is a fully qualified module name.
638              
639             Specific sinks may provide additional, or require specific
640             attributes. See L</Sinks> for more information.
641              
642             The value is C<stream> (corresponding to
643             L<Data::Record::Serialize::Sink::stream>), unless the encoder is also
644             a sink.
645              
646             It is an error to specify a sink if the encoder already acts as one.
647              
648             =item C<nullify> => I<arrayref>|I<coderef>|Boolean
649              
650             Fields that should be set to C<undef> if they are
651             empty. Sinks should encode C<undef> as the C<null> value.
652              
653             B<nullify> may be passed:
654              
655             =over
656              
657             =item * an arrayref of input field names
658              
659             =item * a coderef
660              
661             The coderef is called as
662              
663             @input_field_names = $code->( $serializer_object )
664              
665             =item * a Boolean
666              
667             If true, all field names are added to the list. When false, the list
668             is emptied.
669              
670             =back
671              
672             Names are verified against the input fields after the first record is
673             sent. A C<Data::Record::Serialize::Error::Role::Base::fields> error is thrown
674             if non-existent fields are specified.
675              
676             =item C<numify> => I<arrayref>|I<coderef>|Boolean
677              
678             Specify fields that should be explicitly transformed into a number. It
679             defaults to C<false>, unless a particular encoder requires it
680             (e.g. C<json>). To avoid unnecessary conversion overhead, set this to
681             C<false> if you are sure that your data are appropriately numified, or
682             if only a few fields need to be transformed and can be done via the
683             L</format_fields> option.
684              
685             B<numify> may be passed:
686              
687             =over
688              
689             =item * an arrayref of input field names
690              
691             =item * a coderef
692              
693             The coderef is called as
694              
695             @input_field_names = $code->( $serializer_object )
696              
697             =item * a Boolean
698              
699             If true, all numeric fields are added to the list. When false, the list
700             is emptied.
701              
702             =back
703              
704             Names are verified against the input fields after the first record is
705             sent. A C<Data::Record::Serialize::Error::Role::Base::fields> error is thrown
706             if non-existent fields are specified.
707              
708             =item C<stringify> => I<arrayref>|I<coderef>|Boolean
709              
710             Specify fields that should be explicitly transformed into a string. It
711             defaults to C<false>, unless a particular encoder requires it
712             (e.g. C<json>). To avoid unnecessary conversion overhead, set this to
713             C<false> if you are sure that your data are appropriately stringified, or
714             if only a few fields need to be transformed and can be done via the
715             L</format_fields> option.
716              
717             B<stringify> may be passed:
718              
719             =over
720              
721             =item * an arrayref of input field names
722              
723             =item * a coderef
724              
725             The coderef is called as
726              
727             @input_field_names = $code->( $serializer_object )
728              
729             =item * a Boolean
730              
731             If true, all string fields are added to the list. When false, the list
732             is emptied.
733              
734             =back
735              
736             Names are verified against the input fields after the first record is
737             sent. A C<Data::Record::Serialize::Error::Role::Base::fields> error is thrown
738             if non-existent fields are specified.
739              
740             =item C<format> => I<Boolean>
741              
742             If true, format the output fields using the formats specified in the
743             L</format_fields> and/or L</format_types> options. The default is true.
744              
745             =item C<format_fields> => I<hashref>
746              
747             Specify transforms specific to fields. The hash keys are input field names;
748             each value is either a C<sprintf> style format or a coderef. The
749             transformations will be applied prior to encoding the record, but only
750             if the L</format> attribute is also set. Formats specified here
751             override those specified in L</format_types>.
752              
753             The coderef will be called with the value to format as its first
754             argument, and should return the formatted value.
755              
756             =item C<format_types> => I<hashref>
757              
758             Specify transforms specific to types. The hash keys are field types
759             (C<N>, C<I>, C<S>, C<B>); each value is either a C<sprintf> style format or a coderef.
760             The transformations will be applied prior to encoding the record, but
761             only if the L</format> attribute is also set. Formats specified here
762             may be overridden for specific fields using the L</format_fields>
763             attribute.
764              
765             The coderef will be called with the value to format as its first
766             argument, and should return the formatted value.
767              
768             =item C<rename_fields> => I<hashref>
769              
770             A hash mapping input to output field names. By default the input
771             field names are used unaltered.
772              
773             =back
774              
775             =head1 METHODS
776              
777             For additional methods, see the following modules
778              
779             =over
780              
781             =item L</Data::Serialize::Record::Role::Base>
782              
783             =item L</Data::Serialize::Record::Role::Default>
784              
785             =item L</Data::Serialize::Record::Encode>
786              
787             =item L</Data::Serialize::Record::Sink>,
788              
789             =back
790              
791             =head2 B<send>
792              
793             $s->send( \%record );
794              
795             Encode and send the record to the associated sink.
796              
797             B<WARNING>: the passed hash is modified. If you need the original
798             contents, pass in a copy.
799              
800             =for Pod::Coverage make_variant
801              
802             =head1 ATTRIBUTES
803              
804             Object attributes are provided by the following modules
805              
806             =over
807              
808             =item L</Data::Serialize::Record::Role::Base>
809              
810             =item L</Data::Serialize::Record::Role::Default>
811              
812             =item L</Data::Serialize::Record::Encode>
813              
814             =item L</Data::Serialize::Record::Sink>,
815              
816             =back
817              
818             =head1 EXAMPLES
819              
820             =head2 Generate a JSON stream to the standard output stream
821              
822             $s = Data::Record::Serialize->new( encode => 'json' );
823              
824             =head2 Only output select fields
825              
826             $s = Data::Record::Serialize->new(
827             encode => 'json',
828             fields => [ qw( obsid chip_id phi theta ) ],
829             );
830              
831             =head2 Format numeric fields
832              
833             $s = Data::Record::Serialize->new(
834             encode => 'json',
835             fields => [ qw( obsid chip_id phi theta ) ],
836             format => 1,
837             format_types => { N => '%0.4f' },
838             );
839              
840             =head2 Override formats for specific fields
841              
842             $s = Data::Record::Serialize->new(
843             encode => 'json',
844             fields => [ qw( obsid chip_id phi theta ) ],
845             format_types => { N => '%0.4f' },
846             format_fields => { obsid => '%05d' },
847             );
848              
849             =head2 Rename fields
850              
851             $s = Data::Record::Serialize->new(
852             encode => 'json',
853             fields => [ qw( obsid chip_id phi theta ) ],
854             format_types => { N => '%0.4f' },
855             format_fields => { obsid => '%05d' },
856             rename_fields => { chip_id => 'CHIP' },
857             );
858              
859             =head2 Specify field types
860              
861             $s = Data::Record::Serialize->new(
862             encode => 'json',
863             fields => [ qw( obsid chip_id phi theta ) ],
864             format_types => { N => '%0.4f' },
865             format_fields => { obsid => '%05d' },
866             rename_fields => { chip_id => 'CHIP' },
867             types => { obsid => 'N', chip_id => 'S', phi => 'N', theta => 'N' }'
868             );
869              
870             =head2 Switch to an SQLite database in C<$dbname>
871              
872             $s = Data::Record::Serialize->new(
873             encode => 'dbi',
874             dsn => [ 'SQLite', [ dbname => $dbname ] ],
875             table => 'stuff',
876             fields => [ qw( obsid chip_id phi theta ) ],
877             format_types => { N => '%0.4f' },
878             format_fields => { obsid => '%05d' },
879             rename_fields => { chip_id => 'CHIP' },
880             types => { obsid => 'N', chip_id => 'S', phi => 'N', theta => 'N' }'
881             );
882              
883             =for Pod::Coverage BUILD
884              
885             =head1 SUPPORT
886              
887             =head2 Bugs
888              
889             Please report any bugs or feature requests to bug-data-record-serialize@rt.cpan.org or through the web interface at: https://rt.cpan.org/Public/Dist/Display.html?Name=Data-Record-Serialize
890              
891             =head2 Source
892              
893             Source is available at
894              
895             https://gitlab.com/djerius/data-record-serialize
896              
897             and may be cloned from
898              
899             https://gitlab.com/djerius/data-record-serialize.git
900              
901             =head1 SEE ALSO
902              
903             Please see those modules/websites for more information related to this module.
904              
905             =over 4
906              
907             =item *
908              
909             L<Data::Serializer>
910              
911             =back
912              
913             =head1 AUTHOR
914              
915             Diab Jerius <djerius@cpan.org>
916              
917             =head1 COPYRIGHT AND LICENSE
918              
919             This software is Copyright (c) 2017 by Smithsonian Astrophysical Observatory.
920              
921             This is free software, licensed under:
922              
923             The GNU General Public License, Version 3, June 2007
924              
925             =cut