File Coverage

blib/lib/MooX/Tag/TO_HASH/Util.pm
Criterion Covered Total %
statement 49 62 79.0
branch 19 32 59.3
condition 6 16 37.5
subroutine 10 12 83.3
pod 0 1 0.0
total 84 123 68.2


line stmt bran cond sub pod time code
1             package MooX::Tag::TO_HASH::Util;
2              
3 17     17   237 use v5.10;
  17         67  
4              
5 17     17   104 use strict;
  17         52  
  17         413  
6 17     17   101 use warnings;
  17         44  
  17         794  
7              
8             our $VERSION = '0.05';
9              
10 17     17   103 use Exporter 'import';
  17         44  
  17         646  
11              
12 17     17   123 use Sub::Util qw( set_subname );
  17         56  
  17         2116  
13              
14              
15             our %CONSTANTS;
16              
17             BEGIN {
18             %CONSTANTS = (
19             LC_TO_JSON => 'to_json',
20             LC_TO_HASH => 'to_hash',
21             UC_TO_JSON => 'TO_JSON',
22             UC_TO_HASH => 'TO_HASH',
23 17     17   104 map { uc() => $_ } 'omit_if_empty',
  170         1198  
24             'if_exists', 'if_defined', 'no_recurse', 'alt_name', 'predicate',
25             'type', 'bool', 'num', 'str',
26             );
27             }
28              
29 17     17   153 use constant \%CONSTANTS;
  17         74  
  17         3138  
30              
31 17     17   119 use constant TYPES => ( BOOL, NUM, STR );
  17         56  
  17         14400  
32              
33             our %EXPORT_TAGS = ( all => [ 'make_tag_handler', keys %CONSTANTS ] );
34             our @EXPORT_OK = ( map { @$_ } values %EXPORT_TAGS );
35              
36             our %ALLOWED = (
37             +( LC_TO_JSON ) => { map { $_ => undef } OMIT_IF_EMPTY, IF_EXISTS, IF_DEFINED, BOOL, NUM, STR },
38             +( LC_TO_HASH ) => { map { $_ => undef } OMIT_IF_EMPTY, IF_EXISTS, IF_DEFINED, NO_RECURSE },
39             );
40              
41             sub _croak {
42 0     0   0 require Carp;
43 0         0 goto \&Carp::croak;
44             }
45              
46             sub _croak_tag_attr {
47 0     0   0 my ( $tag, $attr, $msg ) = @_;
48 0 0       0 $attr = join( ', ', @$attr ) if 'ARRAY' eq ref $attr;
49 0         0 _croak( "[$attr] => $tag: $msg" );
50             }
51              
52              
53              
54              
55              
56              
57             sub make_tag_handler { ## no critic(Subroutines::ProhibitExcessComplexity)
58 52     52 0 162 my ( $tag ) = @_;
59 52         139 my $package = caller();
60              
61 52         152 my $allowed = $ALLOWED{$tag};
62              
63 52 50       209 _croak( "unsupported tag: $tag" )
64             unless defined $allowed;
65              
66             set_subname "$package\::${tag}_tag_handler" => sub {
67 134     134   184489 my ( $orig, $attrs, %opt ) = @_;
68 134         344 my $spec = $opt{$tag};
69              
70             # if no to_$tag, or to_$tag has been processed (e.g. it's now a json ref)
71             # pass on to original routine.
72 134 100 100     1152 return $orig->( $attrs, %opt )
73             if !defined $spec || ref( $spec );
74              
75 114         207 my %spec;
76 114 100       310 if ( $spec ne '1' ) {
77 76         304 my ( $alt_name, @stuff ) = split( /,/, $spec );
78             defined() and _croak_tag_attr( $tag, $attrs, "unknown option: $_ " )
79 76   0     181 for grep { !exists $allowed->{$_} } @stuff;
  77         301  
80 76 100       223 $spec{ +ALT_NAME } = $alt_name if length( $alt_name );
81 76         241 $spec{$_} = 1 for @stuff;
82              
83             # consistency checks if more than one attribute is passed to has.
84 76 50 33     240 if ( ref $attrs && @{$attrs} > 1 ) {
  0         0  
85             _croak_tag_attr( $tag, $attrs,
86             q{can't specify alternate name if more than one attribute is defined} )
87 0 0       0 if exists $spec{ +ALT_NAME };
88             _croak_tag_attr( $tag, $attrs,
89             q{can't specify predicate name if more than one attribute is defined} )
90 0 0 0     0 if defined $opt{ +PREDICATE } && $opt{ +PREDICATE } ne '1';
91             }
92              
93 76 50       213 if ( $tag eq UC_TO_JSON ) {
94 0         0 $spec{ +TYPE } = do {
95 0         0 my ( $type, @types ) = grep exists $spec{$_}, TYPES;
96 0 0       0 _croak_tag_attr( $tag, $attrs, 'specify exactly zero or one of ' . join( ', ', TYPES ) )
97             if @types;
98 0         0 $type;
99             };
100             }
101              
102             $spec{ +IF_EXISTS } = delete $spec{ +OMIT_IF_EMPTY }
103 76 100       253 if exists $spec{ +OMIT_IF_EMPTY };
104              
105             $opt{ +PREDICATE } //= '1'
106 76 100 50     324 if $spec{ +IF_EXISTS };
107             }
108              
109              
110             # if there is more than one attribute being defined by this
111             # has call, we can either call has multiple times, setting the
112             # tag value individually for each, or we can call has a single
113             # time, as the user has done, but then the tag value has to
114             # have the same value for both. We choose the latter, just in
115             # case the underlying Moo::has code does something special.
116             # but this results in duplicate information (see the top level
117             # TO_HASH and TO_JSON implementations for more info).
118              
119 114         181 my %to;
120 114 50       317 for my $attr ( ref $attrs ? @{$attrs} : $attrs ) {
  0         0  
121 114         494 $to{$attr} = {%spec};
122 114 100       682 if ( $spec{ +IF_EXISTS } ) {
123 36   50     112 $opt{ +PREDICATE } //= 1;
124             $to{$attr}{ +PREDICATE }
125             = $opt{ +PREDICATE } eq '1'
126             ? ( substr( $attr, 0, 1 ) eq '_' ? '_has' : 'has_' ) . $attr
127 36 100       237 : $opt{ +PREDICATE };
    50          
128             }
129             }
130 114         248 $opt{$tag} = \%to;
131 114         2376 return $orig->( $attrs, %opt );
132 52         763 };
133             }
134              
135             1;
136              
137             __END__
138              
139             =pod
140              
141             =for :stopwords Diab Jerius Smithsonian Astrophysical Observatory
142              
143             =head1 NAME
144              
145             MooX::Tag::TO_HASH::Util
146              
147             =head1 VERSION
148              
149             version 0.05
150              
151             =for Pod::Coverage make_tag_handler
152              
153             =head1 SUPPORT
154              
155             =head2 Bugs
156              
157             Please report any bugs or feature requests to bug-moox-tag-to_hash@rt.cpan.org or through the web interface at: L<https://rt.cpan.org/Public/Dist/Display.html?Name=MooX-Tag-TO_HASH>
158              
159             =head2 Source
160              
161             Source is available at
162              
163             https://gitlab.com/djerius/moox-tag-to_hash
164              
165             and may be cloned from
166              
167             https://gitlab.com/djerius/moox-tag-to_hash.git
168              
169             =head1 SEE ALSO
170              
171             Please see those modules/websites for more information related to this module.
172              
173             =over 4
174              
175             =item *
176              
177             L<MooX::Tag::TO_HASH|MooX::Tag::TO_HASH>
178              
179             =back
180              
181             =head1 AUTHOR
182              
183             Diab Jerius <djerius@cpan.org>
184              
185             =head1 COPYRIGHT AND LICENSE
186              
187             This software is Copyright (c) 2022 by Smithsonian Astrophysical Observatory.
188              
189             This is free software, licensed under:
190              
191             The GNU General Public License, Version 3, June 2007
192              
193             =cut