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