File Coverage

blib/lib/CXC/Number/Sequence/Utils.pm
Criterion Covered Total %
statement 70 72 97.2
branch 11 14 78.5
condition 2 3 66.6
subroutine 15 15 100.0
pod 2 2 100.0
total 100 106 94.3


line stmt bran cond sub pod time code
1             package CXC::Number::Sequence::Utils;
2              
3             # ABSTRACT: Utilities for CXC::Number::Sequence generators
4 12     12   247127 use strict;
  12         32  
  12         553  
5 12     12   68 use warnings;
  12         24  
  12         754  
6              
7 12     12   187 use v5.28;
  12         41  
8 12     12   679 use experimental 'signatures';
  12         1540  
  12         108  
9              
10             our $VERSION = '0.13';
11              
12             # ABSTRACT: sequence utilities
13              
14 12     12   8058 use Exporter::Shiny qw( buildargs_factory load_class );
  12         14047  
  12         149  
15 12     12   9916 use Type::Params qw( compile_named compile_named_oo );
  12         216439  
  12         156  
16 12     12   6695 use Types::Standard -types;
  12         57  
  12         109  
17 12     12   114479 use Types::Common::Numeric qw( PositiveInt );
  12         39680  
  12         152  
18              
19 12     12   13129 use CXC::Number::Sequence::Failure -all;
  12         58  
  12         172  
20              
21             use Hash::Wrap 0.11 {
22 12         161 -as => 'wrap_attrs_ro',
23             -immutable => 1,
24             -exists => 'has',
25 12     12   8155 };
  12         42965  
26 12     12   53207 use Hash::Wrap { -as => 'wrap_attrs_rw' };
  12         30  
  12         88  
27              
28 12     12   22629 use namespace::clean;
  12         161218  
  12         113  
29              
30              
31              
32              
33              
34              
35              
36              
37              
38              
39              
40              
41              
42              
43              
44              
45              
46              
47              
48              
49              
50              
51              
52              
53              
54              
55              
56              
57              
58              
59              
60              
61              
62              
63              
64              
65              
66              
67              
68              
69              
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             sub buildargs_factory {
118              
119 6     6 1 58 state $check = compile_named_oo(
120             map => HashRef [
121             Dict [
122             flag => PositiveInt,
123             type => InstanceOf ['Type::Tiny'],
124             ],
125             ],
126             build => Map [ PositiveInt, CodeRef ],
127             xvalidate => Optional [ ArrayRef [ Tuple [ PositiveInt, CodeRef ] ] ],
128             adjust => Optional [CodeRef],
129             );
130              
131 6         895217 my $arg = $check->( @_ );
132              
133             return sub {
134              
135             ## no critic( Variables::ProhibitReusedNames )
136              
137             state $check = compile_named(
138 80     80   1998563 map { $_ => $arg->map->{$_}{type} }
  36         136  
139             keys $arg->map->%*,
140             );
141              
142 80         68768 my ( $class, undef ) = ( shift, shift );
143              
144 80 50       581 my %attrs = @_ == 1 ? $_[0]->%* : @_;
145              
146             # don't touch attributes we don't know about
147 80         187 my %build_attrs;
148 80         299 $build_attrs{$_} = delete $attrs{$_} foreach grep { exists $arg->map->{$_} } keys %attrs;
  279         1528  
149              
150 80 50       439 if ( $arg->adjust ) {
151 0         0 local $_ = \%build_attrs;
152 0         0 $arg->adjust->();
153             }
154              
155 80         501 my $attrs = $check->( %build_attrs );
156              
157 79         19721 my $attrs_set = 0;
158 79         1044 $attrs_set |= $arg->map->{$_}{flag} for keys %build_attrs;
159              
160 79   66     1158 my $build = $arg->build->{$attrs_set} // parameter_IllegalCombination->throw(
161             'illegal combination of parameters: ' . join( ', ', sort keys %build_attrs ) );
162              
163 68 50       369 if ( $arg->has_xvalidate ) {
164 68         2536 local $_ = wrap_attrs_rw( $attrs );
165 68         1310 foreach my $pair ( $arg->xvalidate->@* ) {
166 453         22060 my ( $key, $validate ) = $pair->@*;
167 453 100       1264 next unless $key == ( $attrs_set & $key );
168 57         249 $validate->();
169             }
170 66         2053 $attrs = { $attrs->%* };
171             }
172              
173 66         2032 local $_ = wrap_attrs_ro( $attrs );
174 66         3257 return { %attrs, $build->()->%* };
175 6         1646 };
176             }
177              
178              
179              
180              
181              
182              
183              
184              
185              
186             # based on Mojo::Plugin::load_plugin, Mojo::Loader::load_class, Mojo::Util::camelize
187 5     5 1 10 sub load_class ( $name ) {
  5         12  
  5         28  
188              
189             $name = join q{::}, map {
190 5 100       41 join( q{}, map { ucfirst lc } split /_/ )
  3         11  
  3         24  
191             } split( /-/, $name )
192             unless $name =~ /^[[:upper:]]/;
193              
194 5         20 for my $class ( "CXC::Number::Sequence::${name}", $name ) {
195             ## no critic (BuiltinFunctions::ProhibitStringyEval)
196 8 100       2532 eval "require $class; 1"
197             && return $class;
198              
199             ## no critic( RegularExpressions::ProhibitUnusualDelimiters )
200 5 100       948 loadclass_CompileError->throw( "$class had a compile error: $@" )
201 5         196 unless $@ =~ m|Can't locate \Q@{[ $class =~ s{::}{/}gr . '.pm' ]}|;
202             }
203              
204 1         36 loadclass_NoClass->throw( "unable to find Sequence class matching $name" );
205             }
206              
207             #
208             # This file is part of CXC-Number
209             #
210             # This software is Copyright (c) 2019 by Smithsonian Astrophysical Observatory.
211             #
212             # This is free software, licensed under:
213             #
214             # The GNU General Public License, Version 3, June 2007
215             #
216              
217             1;
218              
219             __END__
220              
221             =pod
222              
223             =for :stopwords Diab Jerius Smithsonian Astrophysical Observatory CamelCased bitmask
224             xvalidate
225              
226             =head1 NAME
227              
228             CXC::Number::Sequence::Utils - Utilities for CXC::Number::Sequence generators
229              
230             =head1 VERSION
231              
232             version 0.13
233              
234             =head1 SYNOPSIS
235              
236             =head1 SUBROUTINES
237              
238             =head2 buildargs_factory
239              
240             $sub = buildargs_factory( %args );
241              
242             Generate a subroutine wrapper for L<Moo/BUILDARGS> for use with L<Moo/around>, e.g.
243              
244             around BUILDARGS => buildargs_factory(
245             map => \%ArgMap,
246             build => \%ArgBuild,
247             xvalidate => \@ArgsCrossValidate
248             );
249              
250             It takes the following arguments:
251              
252             =over
253              
254             =item map => \%hash
255              
256             This hash maps a parameter name to a L<Type::Tiny> type and a bitmask
257             flag which uniquely identifies the parameter. The hash keys are the
258             parameter names, and the values are hashes with elements keys C<type>
259             (the C<Type::Tiny> type) and C<flag> (an integer bitmask flag). For
260             example,
261              
262             use enum qw( BITMASK: MIN MAX SOFT_MIN SOFT_MAX NBINS BINW RATIO GROW );
263              
264             my %ArgMap = (
265             binw => { type => BinWidth, flag => BINW },
266             max => { type => Optional [BigNum], flag => MAX },
267             min => { type => Optional [BigNum], flag => MIN },
268             nbins => { type => Optional [PositiveInt], flag => NBINS },
269             ratio => { type => Ratio, flag => RATIO },
270             soft_max => { type => Optional [BigNum], flag => SOFT_MAX },
271             soft_min => { type => Optional [BigNum], flag => SOFT_MIN },
272             );
273              
274             =item build => \%hash
275              
276             This hash maps I<combinations> of parameters with subroutines which
277             return parameters to be returned by L<Moo/BUILDARGS>. The keys are
278             masks which specify the parameters, and the values are subroutines
279             which operate on C<$_> (an object with methods named for the
280             parameters). For example,
281              
282             ( MIN | NBINS | BINW | RATIO ),
283             sub {
284             my $nbins = $_->nbins;
285             if ( $_->binw > 0 ) {
286             ...
287             }
288             ...
289             { elements => { } };
290             },
291              
292             =item xvalidate => \@array
293              
294             This optional argument provides subroutines to cross-validate
295             parameters. The array elements are themselves arrays with two
296             elements; the first is a mask which represents the combination of
297             parameters to test, the second is a subroutine which operates on C<$_>
298             (an object with methods named for the parameters). It should
299             throw if the validation fails. Validation subroutines are called
300             in the order presented in the array.
301              
302             For example, the following entry ensures that the specified minimum
303             values are less than maximum values:
304              
305             [
306             MIN | MAX,
307             sub {
308             parameter_constraint->throw( "min < max\n" ) unless $_->min < $_->max;
309             },
310             ],
311              
312             =item adjust => \&sub
313              
314             This is an optional parameter providing a subroutine which is passed
315             (via C<$_>) a hash containing the passed build parameters. It can
316             adjust them in place as required.
317              
318             =back
319              
320             =head2 load_class
321              
322             $class_name = load_seq_class( $class_or_submodule );
323              
324             C<$class_or_submodule> is CamelCased.
325              
326             =head1 INTERNALS
327              
328             =for Pod::Coverage BUILDARGS
329              
330             =head1 SUPPORT
331              
332             =head2 Bugs
333              
334             Please report any bugs or feature requests to bug-cxc-number@rt.cpan.org or through the web interface at: L<https://rt.cpan.org/Public/Dist/Display.html?Name=CXC-Number>
335              
336             =head2 Source
337              
338             Source is available at
339              
340             https://gitlab.com/djerius/cxc-number
341              
342             and may be cloned from
343              
344             https://gitlab.com/djerius/cxc-number.git
345              
346             =head1 SEE ALSO
347              
348             Please see those modules/websites for more information related to this module.
349              
350             =over 4
351              
352             =item *
353              
354             L<CXC::Number|CXC::Number>
355              
356             =back
357              
358             =head1 AUTHOR
359              
360             Diab Jerius <djerius@cpan.org>
361              
362             =head1 COPYRIGHT AND LICENSE
363              
364             This software is Copyright (c) 2019 by Smithsonian Astrophysical Observatory.
365              
366             This is free software, licensed under:
367              
368             The GNU General Public License, Version 3, June 2007
369              
370             =cut