File Coverage

blib/lib/Full/Pragmata.pm
Criterion Covered Total %
statement 158 166 95.1
branch 1 4 25.0
condition 2 3 66.6
subroutine 38 39 97.4
pod n/a
total 199 212 93.8


line stmt bran cond sub pod time code
1             package Full::Pragmata;
2              
3 8     8   275051 use strict;
  8         17  
  8         391  
4 8     8   59 use warnings;
  8         32  
  8         915  
5              
6             our $VERSION = '1.004'; # VERSION
7             our $AUTHORITY = 'cpan:TEAM'; # AUTHORITY
8              
9 8     8   4933 use utf8;
  8         6088  
  8         56  
10              
11             =encoding utf8
12              
13             =head1 NAME
14              
15             Full::Pragmata - common pragmata for Perl scripts and modules
16              
17             =head1 SYNOPSIS
18              
19             # in your script or module:
20             use Full::Pragmata;
21             # use strict, warnings, utf8 etc. are all now applied and in scope
22              
23             =head1 DESCRIPTION
24              
25             Perl has many modules and features, including some features which are somewhat discouraged
26             in recent code.
27              
28             This module attempts to provide a good set of functionality for writing code without too
29             many lines of boilerplate. It has been extracted from L so that it can be
30             used in other code without pulling in too many irrelevant dependencies.
31              
32             =head2 Language features
33              
34             The following Perl language features and modules are applied to the caller:
35              
36             =over 4
37              
38             =item * L
39              
40             =item * L
41              
42             =item * L
43              
44             =item * L
45              
46             =item * no L
47              
48             =item * no L
49              
50             =item * no L
51              
52             =item * L
53              
54             =item * L
55              
56             =item * L - or the standard Perl built-in defer since C< :v2 >
57              
58             =item * L - added in C< :v2 >
59              
60             =item * L
61              
62             =item * L - added in C< :v2 >
63              
64             =item * provides L, L, L
65              
66             =item * provides L, L, L
67              
68             =item * provides L - added in C< :v2 >
69              
70             =item * provides L, L
71              
72             =item * provides L, L,
73             L, L, L
74              
75             =item * provides L, L
76              
77             =back
78              
79             In addition, the following core Ls are enabled:
80              
81             =over 4
82              
83             =item * L
84              
85             =item * L
86              
87             =item * L
88              
89             =item * L
90              
91             =item * L
92              
93             =item * L
94              
95             =item * L
96              
97             =item * L
98              
99             =back
100              
101             =head2 Constraints and checks
102              
103             L is imported with the following constraints available:
104              
105             =over 4
106              
107             =item * Defined
108              
109             =item * Object
110              
111             =item * Str
112              
113             =item * Num
114              
115             =item * StrEq
116              
117             =item * NumGT
118              
119             =item * NumGE
120              
121             =item * NumLE
122              
123             =item * NumLT
124              
125             =item * NumRange
126              
127             =item * NumEq
128              
129             =item * Isa
130              
131             =item * ArrayRef
132              
133             =item * HashRef
134              
135             =item * Callable
136              
137             =item * Maybe
138              
139             =item * Any
140              
141             =item * All
142              
143             =back
144              
145             Note that L provides the underlying support for constraints, but
146             actual usage involves a combination of other modules:
147              
148             =head3 Field constraints
149              
150             These are supported through L:
151              
152             package Example;
153             use Full::Class qw(:v2);
154             field $checked :Checked(Str);
155              
156             =head3 Other features
157              
158             This also makes available a L instance in the C<$log> package variable,
159             and for L support you get C<$tracer> as an L
160             instance.
161              
162             =head2 VERSIONING
163              
164             A version tag is required:
165              
166             use Full::Pragmata qw(:v1);
167              
168             Currently C<:v1> is the only version available. It's very likely that future versions
169             will bring in new functionality or enable/disable a different featureset, or may
170             remove functionality or behaviour that's no longer appropriate.
171              
172             =cut
173              
174 8     8   5194 no indirect qw(fatal);
  8         12144  
  8         43  
175 8     8   5080 no multidimensional;
  8         20978  
  8         57  
176 8     8   4943 no bareword::filehandles;
  8         8683  
  8         63  
177 8     8   4149 use mro;
  8         4874  
  8         61  
178 8     8   5357 use experimental qw(signatures);
  8         24243  
  8         57  
179 8     8   6511 use meta;
  8         10571  
  8         551  
180 8     8   69 no warnings qw(meta::experimental);
  8         36  
  8         660  
181 8     8   7172 use curry;
  8         4210  
  8         472  
182 8     8   4741 use Data::Checks;
  8         25704  
  8         50  
183 8     8   5737 use Object::Pad::FieldAttr::Checked;
  8         89918  
  8         61  
184 8     8   5991 use Sublike::Extended;
  8         6516  
  8         76  
185 8     8   8742 use Signature::Attribute::Checked;
  8         6810  
  8         99  
186 8     8   5733 use Future::AsyncAwait;
  8         247240  
  8         91  
187 8     8   5289 use Future::AsyncAwait::Hooks;
  8         10231  
  8         61  
188 8     8   5976 use Syntax::Keyword::Try;
  8         16226  
  8         56  
189 8     8   6294 use Syntax::Keyword::Dynamically;
  8         15244  
  8         60  
190 8     8   6061 use Syntax::Keyword::Defer;
  8         11959  
  8         59  
191 8     8   5869 use Syntax::Keyword::Match;
  8         37375  
  8         70  
192 8     8   5860 use Syntax::Operator::Equ;
  8         16044  
  8         66  
193 8     8   1355 use Scalar::Util;
  8         15  
  8         548  
194 8     8   96 use List::Util;
  8         19  
  8         679  
195 8     8   5371 use List::Keywords;
  8         21073  
  8         57  
196 8     8   6141 use Future::Utils;
  8         41830  
  8         1047  
197 8     8   5182 use Module::Load ();
  8         19717  
  8         574  
198              
199 8     8   4208 use JSON::MaybeUTF8;
  8         110293  
  8         890  
200 8     8   140 use Unicode::UTF8;
  8         112  
  8         771  
201              
202 8     8   4557 use Log::Any qw($log);
  8         118630  
  8         76  
203 8     8   30192 use Metrics::Any;
  8         77119  
  8         55  
204              
205 8     8   1163 use constant USE_OPENTELEMETRY => $ENV{USE_OPENTELEMETRY};
  8         17  
  8         2262  
206              
207             BEGIN {
208 8     8   6115 if(USE_OPENTELEMETRY) {
209             require OpenTelemetry;
210             require OpenTelemetry::Context;
211             require OpenTelemetry::Trace;
212             require OpenTelemetry::Constants;
213             }
214             }
215              
216 26     26   362 sub import ($called_on, $version_tag, %args) {
  26         57  
  26         50  
  26         56  
  26         40  
217 26 50       188 die "invalid version, expecting something like use @{[caller]} qw(:v1);"
  0         0  
218             unless my $version = $version_tag =~ /^:v([0-9]+)/;
219              
220 26   66     123 my $pkg = $args{target} // caller(0);
221              
222 26         50 my $class = __PACKAGE__;
223              
224             # Apply core syntax and rules
225 26         174 strict->import;
226 26         823 warnings->import;
227 26         158 utf8->import;
228              
229             # We want mostly the 5.36 featureset, but since that includes `say` and `switch`
230             # we need to customise the list somewhat
231 26         3092 feature->import(qw(
232             bitwise
233             current_sub
234             evalbytes
235             fc
236             postderef_qq
237             state
238             unicode_eval
239             unicode_strings
240             ));
241              
242             # Indirect syntax is problematic due to `unknown_sub { ... }` compiling and running
243             # the block without complaint, and only failing at runtime *after* the code has
244             # executed once - particularly unfortunate with try/catch
245 26         182 indirect->unimport(qw(fatal));
246             # Multidimensional array access - $x{3,4} - is usually a sign that someone wanted
247             # `@x{3,4}` or similar instead, so we disable this entirely
248 26         1110 multidimensional->unimport;
249             # Plain STDIN/STDOUT/STDERR are still allowed, although hopefully never used by
250             # service code - new filehandles need to be lexical.
251 26         421 bareword::filehandles->unimport;
252              
253             # This one's needed for nested scope, e.g. { package XX; use Full::Service; method xxx (%args) ... }
254 26         361 experimental->import('signatures');
255              
256             # We don't really care about diamond inheritance, since microservices are expected
257             # to have minimal inheritance in the first place, but might as well have a standard
258             # decision to avoid surprises in future
259 26         986 mro::set_mro($pkg => 'c3');
260              
261             # Helper functions which are used often enough to be valuable as a default
262 26         2542 Scalar::Util->export($pkg => qw(refaddr blessed weaken));
263 26         1551 List::Util->export($pkg => qw(min max sum0));
264              
265             # Additional features in :v2 onwards
266 26         1026 List::Util->export($pkg => qw(uniqstr));
267             # eval "package $pkg; use Object::Pad::FieldAttr::Checked; use Data::Checks qw(NumGE); 1" or die $@;
268 26         247 Object::Pad::FieldAttr::Checked->import($pkg);
269 26         285 Sublike::Extended->import;
270 26         370 Signature::Attribute::Checked->import($pkg);
271 26         294 Data::Checks->import(qw(
272             Defined
273             Object
274             Str
275             Num
276             StrEq
277             NumGT
278             NumGE
279             NumLE
280             NumLT
281             NumRange
282             NumEq
283             Isa
284             ArrayRef
285             HashRef
286             Callable
287             Maybe
288             Any
289             All
290             ));
291              
292             {
293 8     8   74 no strict 'refs';
  8         21  
  8         2716  
294 26         311 *{$pkg . '::' . $_} = JSON::MaybeUTF8->can($_) for qw(
  130         763  
295             encode_json_text
296             encode_json_utf8
297             decode_json_text
298             decode_json_utf8
299             format_json_text
300             );
301 26         195 *{$pkg . '::' . $_} = Unicode::UTF8->can($_) for qw(
  52         303  
302             encode_utf8
303             decode_utf8
304             );
305             }
306             {
307 8     8   64 no strict 'refs';
  8         16  
  8         1934  
  26         14945  
308 26         188 *{$pkg . '::' . $_} = Future::Utils->can($_) for qw(
  130         633  
309             fmap_void
310             fmap_concat
311             fmap_scalar
312             fmap0
313             fmap1
314             );
315             }
316              
317             {
318 8     8   65 no strict 'refs';
  8         19  
  8         4726  
  26         51  
  26         44  
319             # trim() might appear in core perl at some point, so let's reserve the
320             # word and include a basic implementation first. Avoiding Text::Trim
321             # here because it sometimes returns an empty list, which would be
322             # dangerous - my %hash = (key => trim($value)) for example.
323 26     0   95 *{$pkg . '::trim'} = sub ($txt) {
  0            
  0            
  0            
324 0 0       0 return undef unless defined $txt;
325 0         0 $txt =~ s{^\s+}{};
326 0         0 $txt =~ s{\s+$}{};
327 0         0 return $txt;
328 26         132 };
329             }
330              
331             # Some well-designed modules provide direct support for import target
332 26         227 Syntax::Keyword::Try->import_into($pkg, try => ':experimental(typed)');
333 26         1965 Syntax::Keyword::Dynamically->import_into($pkg);
334 26         2939 Syntax::Keyword::Defer->import_into($pkg);
335 26         952 Syntax::Operator::Equ->import_into($pkg);
336 26         2582 Future::AsyncAwait->import_into($pkg, ':experimental(cancel)');
337 26         967 Metrics::Any->import_into($pkg, '$metrics');
338              
339 26         1193 Future::AsyncAwait::Hooks->import_into($pkg);
340              
341             # Others use lexical hints
342 26         987 List::Keywords->import(qw(any all));
343 26         2155 Syntax::Keyword::Match->import(qw(match));
344              
345             {
346 8     8   94 no strict 'refs';
  8         16  
  8         4071  
  26         1099  
347 26         44 if(USE_OPENTELEMETRY) {
348             my $provider = OpenTelemetry->tracer_provider;
349             *{$pkg . '::tracer'} = \($provider->tracer(
350             name => $args{app} // 'perl',
351             version => $args{version} // $version,
352             ));
353             }
354 26         169 *{$pkg . '::log'} = \Log::Any->get_logger(
  26         5192  
355             category => $pkg
356             );
357             }
358              
359 26         854 return $pkg;
360             }
361              
362             1;
363              
364             __END__