File Coverage

blib/lib/Const/Dual.pm
Criterion Covered Total %
statement 33 35 94.2
branch 16 18 88.8
condition 7 12 58.3
subroutine 9 10 90.0
pod n/a
total 65 75 86.6


line stmt bran cond sub pod time code
1             package Const::Dual;
2              
3 5     5   226512 use 5.006;
  5         41  
4 5     5   32 use strict;
  5         6  
  5         83  
5 5     5   18 use warnings;
  5         5  
  5         97  
6              
7 5     5   18 use Carp ();
  5         5  
  5         48  
8 5     5   15 use Scalar::Util ();
  5         17  
  5         338  
9              
10             our $VERSION = '0.02';
11              
12             =head1 NAME
13              
14             Const::Dual - numeric constants that know their names
15              
16             =cut
17              
18              
19             =head1 SYNOPSIS
20              
21             # create constants
22             use Const::Dual (
23             TYPE_FOO => 1,
24             TYPE_BAR => 2,
25             # ... more constants ...
26             TYPE_BAZ => 99,
27             );
28              
29             $type = TYPE_BAR;
30             print $type + 0; # 2
31             print $type == 2 ? "bar" : "not bar"; # bar
32             print $type == TYPE_BAR ? "bar" : "not bar"; # bar
33             print "type = $type"; # type = TYPE_BAR
34              
35             # create constants and store them in %TYPES
36             use Const::Dual \%TYPES => (
37             TYPE_FOO => 1,
38             TYPE_BAR => 2,
39             # ... more constants ...
40             TYPE_BAZ => 99,
41             );
42             @EXPORT_OK = keys %TYPES;
43             @EXPORT_TAGS = (types => [ keys %TYPES ]);
44              
45             # get dual value from non-dual value
46             my $type = $ARGV[0] // 99;
47             my %TYPES_REVERSE; @TYPES_REVERSE{ map { int $_ } values %TYPES } = values %TYPES;
48             die "Invalid type $type" unless exists $TYPES_REVERSE{$type};
49             $type = $TYPES_REVERSE{$type};
50             print int $type; # 99
51             print "type = $type"; # type = TYPE_BAZ
52              
53             # dual constants are always true!
54             use Const::Dual FALSE => 0;
55             print int FALSE; # 0
56             print "FALSE is ", FALSE ? "true" : "false"; # FALSE is true
57              
58             =cut
59              
60             BEGIN {
61             # forbid utf8 constant names on old perl
62 5 50 33 5   912 *_DOWNGRADE = $] >= 5.008 && $] < 5.015004 ? sub { 1 } : sub { 0 };
  0     14   0  
  14         21  
63             }
64              
65             # some names are evil choices
66             my %forbidden = map { $_ => 1 } qw/BEGIN INIT CHECK UNITCHECK END DESTROY AUTOLOAD/, qw/STDIN STDOUT STDERR ARGV ARGVOUT ENV INC SIG/;
67              
68             sub import {
69 12     12   11712 my $class = shift;
70              
71 12 100       34 my $storehash = ref $_[0] ? shift : undef;
72 12 100 100     113 Carp::croak "Only hashref accepted to store constants" if $storehash && ref $storehash ne 'HASH';
73 11 100       113 Carp::croak "Odd number of elements provided" if @_ % 2;
74              
75 10         37 while (@_) {
76 16         37 my ($name, $value) = splice @_, 0, 2;
77 16 100 66     235 Carp::croak "Invalid constant name '$name'" if ref $name || $name !~ /^[a-zA-Z_][a-zA-Z0-9_]*$/;
78 15 100       97 Carp::croak "Invalid constant name '$name': registered keyword" if $forbidden{$name}; #TODO utf?
79              
80 14 100       52 my $value_copy = Scalar::Util::looks_like_number($value) ? Scalar::Util::dualvar($value, $name) : $value;
81 14 100       29 $storehash->{$name} = $value_copy if $storehash;
82              
83 14 50 33     20 utf8::encode $name if _DOWNGRADE && utf8::is_utf8 $name;
84 14         31 $name = caller() . '::' . $name;
85              
86 5     5   25 no strict 'refs';
  5         8  
  5         366  
87 14     0   77 *{ $name } = sub () { $value_copy };
  14         137  
  0            
88             }
89             }
90              
91             =head1 DESCRIPTION
92              
93             This module can be helpful when you use a lot of constants and really tired to deal with them. Numeric constants created
94             with this module are dual (see L). They have their given numeric values when are used in numeric
95             context. When used in string context, such constants are strings with constants' names. This can be useful for debug purposes:
96             constant's value "knows" constant's name and it can be printed. This behavior does not apply to non-numberic constants,
97             they are created as usual.
98              
99             =head1 CAVEATS
100              
101             Developer should ALWAYS keep in mind that he works with dual values and should force numeric context when necessary.
102             This is strict rule and it's violation can lead to bugs. Common ways to force numeric context is C or C<$value+0>.
103              
104             Dual constant in bool context is always TRUE, because one of constant's value is it's name and it can not be FALSE.
105              
106             =head1 SOURCE
107              
108             The development version is on github at L
109              
110             =head1 AUTHOR
111              
112             Sergey Panteleev, Ebambr@cpan.orgE
113              
114             =head1 COPYRIGHT AND LICENSE
115              
116             Copyright (C) 2018 by Sergey Panteleev
117              
118             This library is free software; you can redistribute it and/or modify
119             it under the same terms as Perl itself, either Perl version 5.8.8 or,
120             at your option, any later version of Perl 5 you may have available.
121              
122             =cut
123              
124             1;