File Coverage

lib/MooX/Types/CLike.pm
Criterion Covered Total %
statement 100 101 99.0
branch 28 30 93.3
condition 56 91 61.5
subroutine 34 34 100.0
pod n/a
total 218 256 85.1


line stmt bran cond sub pod time code
1             package MooX::Types::CLike;
2              
3             our $VERSION = '0.92'; # VERSION
4             # ABSTRACT: C-like data types for Moo
5              
6 2     2   1020545 use sanity;
  2         420421  
  2         24  
7              
8 2     2   838834 use parent 'Exporter';
  2         6  
  2         16  
9             our @EXPORT_OK = ();
10              
11 2     2   1214 use MooX::Types::MooseLike 0.06;
  2         1729  
  2         128  
12 2     2   1186 use MooX::Types::MooseLike::Base 'is_Num';
  2         5009  
  2         256  
13 2     2   15 use Scalar::Util qw(blessed);
  2         4  
  2         120  
14 2     2   12 use Config;
  2         5  
  2         106  
15 2     2   2257 use POSIX qw(ceil);
  2         17167  
  2         22  
16 2     2   2665 use Math::BigInt;
  2         5  
  2         26  
17 2     2   1068 use Math::BigFloat;
  2         7  
  2         16  
18 2     2   4040 use Data::Float;
  2         23702  
  2         218  
19             use constant {
20 2         11389 _BASE2_LOG => log(2) / log(10),
21 2     2   19 };
  2         8  
22              
23             # these get repeated a lot
24             my $true = sub { 1 };
25             sub __alias_subtype {
26             return {
27 116     116   939 name => $_[0],
28             subtype_of => $_[1],
29             from => __PACKAGE__,
30             test => $true,
31             message => $true,
32             };
33             };
34              
35             my $bigtwo = Math::BigFloat->new(2);
36             my $bigten = Math::BigFloat->new(10);
37              
38             sub __integer_builder {
39 14     14   29 my ($bits, $signed_names, $unsigned_names) = @_;
40              
41 14         24 my $signed = shift @$signed_names;
42 14         25 my $unsigned = shift @$unsigned_names;
43 14         24 my $sbits = $bits - 1;
44              
45             # Some pre-processing math
46 14         2545 my $is_perl_safe = $Config{ivsize} >= ceil($bits / 8);
47 14         8120 my ($neg, $spos, $upos) = (
48             $bigtwo->copy->bpow($sbits)->bmul(-1),
49             $bigtwo->copy->bpow($sbits)->bsub(1),
50             $bigtwo->copy->bpow( $bits)->bsub(1),
51             );
52 14         27866 my $sdigits = ceil( $sbits * _BASE2_LOG );
53 14         39 my $udigits = ceil( $bits * _BASE2_LOG );
54              
55             return (
56             {
57             name => $signed,
58             subtype_of => 'Int',
59             from => 'MooX::Types::MooseLike::Base',
60             test => $is_perl_safe ?
61 330 100 100 330   1043851 sub { $_[0] >= $neg and $_[0] <= $spos and !is_NaNInf($_[0]) } :
62             sub {
63 36     36   107967 my $val = $_[0];
64 36 100 33     625 blessed($val) and blessed($val) =~ /^Math::Big(?:Int|Float)|^big(?:int|num)/ and
      33        
      33        
      66        
      100        
65             ( $val->accuracy || $val->precision || $val->div_scale ) >= $udigits and
66             $val >= $neg and $val <= $spos and !is_NaNInf($val);
67             },
68 384     384   1188349 message => sub { "$_[0] is not a $bits-bit signed integer!" },
69             },
70 42         78 (map { __alias_subtype($_, $signed) } @$signed_names),
71             {
72             name => $unsigned,
73             subtype_of => 'Int',
74             from => 'MooX::Types::MooseLike::Base',
75             test => $is_perl_safe ?
76 345 100 100 345   873677 sub { $_[0] >= 0 and $_[0] <= $upos and !is_NaNInf($_[0]) } :
77             sub {
78 36     36   124913 my $val = $_[0];
79 36 100 33     717 blessed($val) and blessed($val) =~ /^Math::Big(?:Int|Float)|^big(?:int|num)/ and
      33        
      33        
      66        
      100        
80             ( $val->accuracy || $val->precision || $val->div_scale ) >= $udigits and
81             $val >= 0 and $val <= $upos and !is_NaNInf($val);
82             },
83 379     379   1391441 message => sub { "$_[0] is not a $bits-bit unsigned integer!" },
84             },
85 14 100       193 (map { __alias_subtype($_, $unsigned) } @$unsigned_names),
  44 100       78  
86             );
87             };
88              
89             # Looks like a float, stores like an int
90             sub __money_builder {
91 6     6   14 my ($bits, $scale, $names) = @_;
92 6         14 my $name = shift @$names;
93 6         11 my $sbits = $bits - 1;
94              
95             # So, we have a base-10 scale and a base-2 set of $bits. Lovely.
96             # We can't actually figure out if it's Perl safe until we find the
97             # $max, adjust with the $scale, and then go BACK to base-2 limits.
98 6         22 my $div = $bigten->copy->bpow($scale);
99 6         2028 my ($neg, $pos) = (
100             # bdiv returns (quo,rem) in list context :/
101             scalar $bigtwo->copy->bpow($sbits)->bmul(-1)->bdiv($div),
102             scalar $bigtwo->copy->bpow($sbits)->bsub(1)->bdiv($div),
103             );
104              
105 6         13260 my $digits = ceil( $sbits * _BASE2_LOG );
106 6         21 my $emin2 = ceil( $scale / _BASE2_LOG );
107              
108 6   66     40 my $is_perl_safe = (
109             Data::Float::significand_bits >= $sbits &&
110             Data::Float::min_finite_exp <= -$emin2
111             );
112              
113             return (
114             {
115             name => $name,
116             subtype_of => 'Num',
117             from => 'MooX::Types::MooseLike::Base',
118             test => $is_perl_safe ?
119 10 100   10   51452 sub { $_[0] >= $neg and $_[0] <= $pos } :
120             sub {
121 30     30   69886 my $val = $_[0];
122 30 100 33     560 blessed($val) and blessed($val) =~ /^Math::BigFloat|^bignum/ and
      33        
      33        
      66        
123             ( $val->accuracy || $val->precision || $val->div_scale ) >= $digits and
124             $val >= $neg and $val <= $pos
125             },
126 12     12   28954 message => sub { "$_[0] is not a $bits-bit money number!" },
127             },
128 6 100       163 (map { __alias_subtype($_, $name) } @$names),
  2         9  
129             );
130             };
131              
132             sub __float_builder {
133 16     16   30 my ($bits, $ebits, $names) = @_;
134 16         32 my $name = shift @$names;
135 16         31 my $sbits = $bits - 1 - $ebits; # remove sign bit and exponent bits = significand precision
136              
137             # MAX = (2 - 2**(-$sbits-1)) * 2**($ebits-1)
138 16         61 my $emax = $bigtwo->copy->bpow($ebits-1)->bsub(1); # Y = (2**($ebits-1)-1)
139 16         11130 my $smin = $bigtwo->copy->bpow(-$sbits-1)->bmul(-1)->badd(2); # Z = (2 - X) = -X + 2 (where X = 2**(-$sbits-1) )
140 16         23473 my $max = $bigtwo->copy->bpow($emax)->bmul($smin); # MAX = 2**Y * Z
141              
142 16   100     1142614 my $is_perl_safe = (
143             Data::Float::significand_bits >= $sbits &&
144             Data::Float::max_finite_exp >= 2 ** $ebits - 1 &&
145             Data::Float::have_infinite &&
146             Data::Float::have_nan
147             );
148 16         88 my $digits = ceil( $sbits * _BASE2_LOG );
149              
150             return (
151             {
152             name => $name,
153             subtype_of => 'NumOrNaNInf',
154             from => __PACKAGE__,
155             test => $is_perl_safe ?
156             sub {
157 116     116   11853 my $val = $_[0];
158 116 100 100     5046 $val >= -$max and $val <= $max or is_NaNInf($val);
159             } :
160             sub {
161 130     130   79502 my $val = $_[0];
162 130 50 100     2121 blessed($val) and blessed($val) =~ /^Math::BigFloat|^bignum/ and
      100        
      33        
      33        
      33        
163             ( $val->accuracy || $val->precision || $val->div_scale ) >= $digits and
164             (
165             $val >= -$max and $val <= $max or is_NaNInf($val)
166             );
167             },
168 111     111   361525 message => sub { "$_[0] is not a $bits-bit binary floating point number!" },
169             },
170 16 100       331 (map { __alias_subtype($_, $name) } @$names),
  26         68  
171             );
172             };
173              
174             # Similar to float, but still different enough for another sub
175             sub __decimal_builder {
176 6     6   16 my ($bits, $digits, $emax, $names) = @_;
177 6         14 my $name = shift @$names;
178              
179             # We're not going to worry about the (extreme) edge case that
180             # Perl might be compiled with decimal float NVs, but we still
181             # need to convert to base-2.
182 6         26 my $sbits = ceil( $digits / _BASE2_LOG );
183 6         17 my $emax2 = ceil( $emax / _BASE2_LOG );
184              
185 6   50     57 my $is_perl_safe = (
186             Data::Float::significand_bits >= $sbits &&
187             Data::Float::max_finite_exp >= $emax2 &&
188             Data::Float::have_infinite &&
189             Data::Float::have_nan
190             );
191              
192 6         27 my $max = $bigten->copy->bpow($emax)->bsub(1);
193              
194             return (
195             {
196             name => $name,
197             subtype_of => 'NumOrNaNInf',
198             from => __PACKAGE__,
199             test => $is_perl_safe ?
200             sub {
201 13     13   2569 my $val = $_[0];
202 13 100 100     153 $val >= -$max and $val <= $max or is_NaNInf($val);
203             } :
204             sub {
205 26     26   17294 my $val = $_[0];
206 26 50 100     846 blessed($val) and blessed($val) =~ /^Math::BigFloat|^bignum/ and
      100        
      33        
      33        
      33        
207             ( $val->accuracy || $val->precision || $val->div_scale ) >= $digits and
208             (
209             $val >= -$max and $val <= $max or is_NaNInf($val)
210             );
211             },
212 12     12   37561 message => sub { "$_[0] is not a $bits-bit decimal floating point number!" },
213             },
214 6 100       10335 (map { __alias_subtype($_, $name) } @$names),
  0         0  
215             );
216             };
217              
218             sub __char_builder {
219 10     10   17 my ($bits, $names) = @_;
220 10         13 my $name = shift @$names;
221              
222             return (
223             {
224             name => $name,
225             subtype_of => 'WChar',
226             from => __PACKAGE__,
227 36     36   444 test => sub { ord($_[0]) < 2**$bits },
228 53     53   145287 message => sub { "$_[0] is not a $bits-bit character!" },
229             },
230 10         141 (map { __alias_subtype($_, $name) } @$names),
  2         25  
231             );
232             }
233              
234             my $type_definitions = [
235             ### Integer definitions ###
236             # being careful with char here...
237             __integer_builder( 4, [qw(SNibble SSemiOctet Int4 Signed4)], [qw(Nibble SemiOctet UInt4 Unsigned4)]),
238             __integer_builder( 8, [qw(SByte SOctet TinyInt Int8 Signed8)], [qw(Byte Octet UnsignedTinyInt UInt8 Unsigned8)]),
239             __integer_builder( 16, [qw(Short SmallInt Int16 Signed16)], [qw(UShort UnsignedSmallInt UInt16 Unsigned16)]),
240             __integer_builder( 24, [qw(MediumInt Int24 Signed24)], [qw(UnsignedMediumInt UInt24 Unsigned24)]),
241             __integer_builder( 32, [qw(Int Int32 Signed32)], [qw(UInt UnsignedInt UInt32 Unsigned32)]),
242             __integer_builder( 64, [qw(Long LongLong BigInt Int64 Signed64)], [qw(ULong ULongLong UnsignedBigInt UInt64 Unsigned64)]),
243             __integer_builder(128, [qw(SOctaWord SDoubleQuadWord Int128 Signed128)], [qw(OctaWord DoubleQuadWord UInt128 Unsigned128)]),
244              
245             ### "Money" definitions ###
246             __money_builder( 32, 4, [qw(SmallMoney)]),
247             __money_builder( 64, 4, [qw(Money Currency)]),
248             __money_builder(128, 6, [qw(BigMoney)]),
249              
250             ### Float definitions ###
251             {
252             name => 'NaNInf',
253             test => sub {
254             my $val = $_[0];
255             blessed($val) and blessed($val) =~ /^Math::Big(?:Int|Float)|^big(?:int|num)/ and (
256             $val->is_nan() or
257             $val->is_inf('+') or
258             $val->is_inf('-')
259             ) or
260             defined $_[0] and is_Num($_[0]) and ( # is_Num first, since Data::Float might give "isn't numeric" warnings
261             Data::Float::float_is_infinite($val) or
262             Data::Float::float_is_nan($val)
263             );
264             },
265             message => sub { "$_[0] is not infinity or NaN!" },
266             },
267             {
268             name => 'NonBigInt',
269             test => sub { (blessed($_[0]) and blessed($_[0]) =~ /^Math::BigInt|^bigint/ and not $_[0]->upgrade) ? 0 : 1; },
270             message => sub { "$_[0] is not a float safe number!" },
271             },
272             {
273             name => 'NumOrNaNInf',
274             subtype_of => 'NonBigInt',
275             from => __PACKAGE__,
276             test => sub { is_Num($_[0]) || is_NaNInf($_[0]) },
277             message => sub { "$_[0] is not a number, infinity, or NaN!" },
278             },
279             __float_builder( 16, 4, [qw(ShortFloat)]),
280             __float_builder( 16, 5, [qw(Half Float16 Binary16)]),
281             __float_builder( 32, 8, [qw(Single Real Float Float32 Binary32)]),
282             __float_builder( 40, 8, [qw(ExtendedSingle Float40)]),
283             __float_builder( 64, 11, [qw(Double Float64 Binary64)]),
284             __float_builder( 80, 15, [qw(ExtendedDouble Float80)]),
285             __float_builder(104, 8, [qw(Decimal)]),
286             __float_builder(128, 15, [qw(Quadruple Quad Float128 Binary128)]),
287              
288             ### Decimal definitions ###
289             __decimal_builder( 32, 7, 96, [ 'Decimal32']),
290             __decimal_builder( 64, 16, 384, [ 'Decimal64']),
291             __decimal_builder(128, 34, 6144, ['Decimal128']),
292              
293             ### Char definitions ###
294             {
295             name => 'WChar',
296             subtype_of => 'Str',
297             from => 'MooX::Types::MooseLike::Base',
298             test => sub { length($_[0]) == 1 }, # length() will do a proper Unicode char length
299             message => sub { "$_[0] is not a single character!" },
300             },
301             __char_builder( 8, [qw(Char Char8)]),
302             __char_builder(16, [qw(Char16)]),
303             __char_builder(32, [qw(Char32)]),
304             __char_builder(48, [qw(Char48)]),
305             __char_builder(64, [qw(Char64)]),
306             ];
307              
308             MooX::Types::MooseLike::register_types($type_definitions, __PACKAGE__); ### TODO: MooseX translation ###
309              
310             my %base_tags = (
311             'c' => [qw(Char Byte Short UShort Int UInt Long ULong Float Double ExtendedDouble)],
312             'stdint' => [ map { ('Int'.$_, 'UInt'.$_) } (4,8,16,32,64,128) ],
313             'c#' => [qw(SByte Byte Char16 Short UShort Int UInt Long ULong Float Double Decimal)],
314             'ieee754' => ['Binary16', map { ('Binary'.$_, 'Decimal'.$_) } (32,64,128) ],
315             'tsql' => [qw(TinyInt SmallInt Int BigInt SmallMoney Money Float64 Real)],
316             'mysql' => [ (map { ($_, 'Unsigned'.$_) } qw(TinyInt SmallInt MediumInt Int BigInt)), qw(Float Double)],
317             'ansisql' => [qw(SmallInt Int Float Real Double)],
318             );
319             my %is_tags = (
320             map {
321             my $k = $_;
322             'is_'.$k => [ map { 'is_'.$_ } @{$base_tags{$k}} ];
323             } keys %base_tags
324             );
325              
326             our %EXPORT_TAGS = (
327             %base_tags,
328             %is_tags,
329             ( map { $_.'+is' => [ @{$base_tags{$_}}, @{$is_tags{'is_'.$_}} ] } keys %base_tags ),
330             'all' => \@EXPORT_OK,
331             );
332              
333             1;
334              
335              
336              
337             =pod
338              
339             =encoding utf-8
340              
341             =head1 NAME
342              
343             MooX::Types::CLike - C-like data types for Moo
344              
345             =head1 SYNOPSIS
346              
347             package MyPackage;
348             use Moo;
349             use MooX::Types::CLike qw(:all);
350              
351             has 'foo' => (
352             isa => Int # or Int32, Signed32
353             );
354             has 'bar' => (
355             isa => Short # or SmallInt, Int16, Signed16
356             );
357              
358             use Scalar::Util qw(blessed);
359             use Math::BigFloat;
360             use Sub::Quote;
361              
362             has 'baz' => (
363             isa => Double # or Float64, Binary64
364              
365             # A Double number gets pretty big, so make sure we use big numbers
366             coerce => quote_sub q{
367             Math::BigFloat->new($_[0])
368             unless (blessed $_[0] =~ /^Math::BigFloat|^bignum/);
369             },
370             );
371              
372             =head1 DESCRIPTION
373              
374             Given the popularity of various byte-sized data types in C-based languages, databases, and
375             computers in general, there's a need for validating those data types in Perl & Moo(se). This
376             module covers the gamut of the various number and character types in all of those forms.
377              
378             The number types will validate that the number falls within the right bit length, that unsigned
379             numbers do not go below zero, and "Perl unsafe" numbers are using either Math::Big* or
380             bignum/bigfloat. (Whether a number is "Perl safe" depends on your Perl's
381             L<http://perldoc.perl.org/Config.html#ivsize|ivsize>, or data from L<Data::Float>.) Big
382             numbers are also checked to make sure they have an accuracy that supports the right number of
383             significant decimal digits. (However, BigInt/Float defaults to 40 digits, which is above the
384             34 digits for 128-bit numbers, so you should be safe.)
385              
386             Char types will validate that it's a single character, using Perl's Unicode-complaint C<length>
387             function. The bit check types (all of them other than C<WChar>) will also check that the
388             ASCII/Unicode code (C<ord>) is the right bit length.
389              
390             IEEE 754 decimal floating types are also available, which are floats that use a base-10
391             mantissa. And the SQL Server "money" types, which are basically decimal numbers stored as
392             integers.
393              
394             =head1 NAME
395              
396             MooX::Types::CLike - C-like types for Moo
397              
398             =head1 TYPES
399              
400             All available types (including lots of aliases) are listed below:
401              
402             ### Integers ###
403             [SIGNED] | [UNSIGNED]
404             4-bit = SNibble SSemiOctet Int4 Signed4 | Nibble SemiOctet UInt4 Unsigned4
405             8-bit = SByte SOctet TinyInt Int8 Signed8 | Byte Octet UnsignedTinyInt UInt8 Unsigned8
406             16-bit = Short SmallInt Int16 Signed16 | UShort UnsignedSmallInt UInt16 Unsigned16
407             24-bit = MediumInt Int24 Signed24 | UnsignedMediumInt UInt24 Unsigned24
408             32-bit = Int Int32 Signed32 | UInt UnsignedInt UInt32 Unsigned32
409             64-bit = Long LongLong BigInt Int64 Signed64 | ULong ULongLong UnsignedBigInt UInt64 Unsigned64
410             128-bit = SOctaWord SDoubleQuadWord Int128 Signed128 | OctaWord DoubleQuadWord UInt128 Unsigned128
411              
412             ### Floats (binary) ###
413             (Total, Exponent) bits; Significand precision = Total - Exponent - 1 (sign bit)
414              
415             ( 16, 4) bits = ShortFloat
416             ( 16, 5) bits = Half Float16 Binary16
417             ( 32, 8) bits = Single Real Float Float32 Binary32
418             ( 40, 8) bits = ExtendedSingle Float40
419             ( 64, 11) bits = Double Float64 Binary64
420             ( 80, 15) bits = ExtendedDouble Float80
421             (104, 8) bits = Decimal # not a IEEE 754 decimal, but C#'s bizarre "128-bit" float
422             (128, 15) bits = Quadruple Quad Float128 Binary128
423              
424             ### Floats (decimal) ###
425             (Digits, Exponent Max)
426              
427             ( 32, 8) = Decimal32
428             ( 64, 11) = Decimal64
429             (128, 15) = Decimal128
430              
431             ### "Money" ###
432             (Bits, Scale)
433              
434             ( 32, 4) = SmallMoney
435             ( 64, 4) = Money Currency
436             (128, 6) = BigMoney # doesn't exist; might change if it does suddenly exists
437              
438             ### Chars ###
439             WChar = Single character (with Perl's natural Unicode-compliance)
440             Bit check types = Char/Char8, Char16, Char32, Char48, Char64
441              
442             =head1 EXPORTER TAGS
443              
444             Since there are so many different aliases in this module, using C<:all> (while available) probably
445             isn't a good idea. So, there are some Exporter tags available, grouped by language:
446              
447             # NOTE: Some extra types are included to fill in the gaps for signed vs. unsigned and
448             # byte vs. char.
449              
450             :c = Char Byte Short UShort Int UInt Long ULong Float Double ExtendedDouble
451             :stdint = Int4 UInt4 ... Int128 UInt128 (except 24-bit)
452             :c# = SByte Byte Char16 Short UShort Int UInt Long ULong Float Double Decimal
453             :ieee754 = Binary16,32,64,128 and Decimal32,64,128
454             :tsql = TinyInt SmallInt Int BigInt SmallMoney Money Float64 Real
455             :mysql = TinyInt SmallInt MediumInt Int BigInt (and Unsigned versions) Float Double
456             :ansisql = SmallInt Int Float Real Double
457              
458             :is_* = All of the is_* functions for that tag
459             :*+is = Both the Moo and is_* functions for that tag
460              
461             =head1 CAVEATS
462              
463             The C<Int> type is also used by L<MooX::Types::MooseLike::Base>, and is even used (but not exported)
464             as a subtype for the Integer classes. So be careful not to import both of them at the same time, as
465             they have different meanings.
466              
467             Most C-based languages use a C<char> type to indicate both an 8-bit number and a single character,
468             as all strings in those languages are represented as a series of character codes. Perl, as a dynamic
469             language, has a single scalar to represent both strings and numbers. Thus, to separate the validation
470             of the two, the term C<Byte> or C<Octet> means the numeric 8-bit types, and the term C<Char> means the
471             single character string types.
472              
473             The term C<long> in C/C++ is ambiguous depending on the bits of the OS: 32-bits for 32-bit OSs and
474             64-bits for 64-bit OSs. Since the 64-bit version makes more sense (ie: C<short E<lt> int E<lt> long>),
475             that is the designation chosen. To avoid confusion, you can just use C<LongLong> and C<ULongLong>.
476              
477             The confusion is even worse for float types, with the C<long> modifier sometimes meaning absolutely
478             nothing in certain hardware platforms. C<Long> isn't even used in this module for those types, in
479             favor of IEEE 754's "Extended" keyword.
480              
481             The floats will support infinity and NaN, since C floats support this. This may not be desirable, so
482             you might want to subtype the float and test for Inf/NaN if you don't want these. Furthermore, the
483             "Perl safe" scalar tests for floats include checks to make sure it supports Inf/NaN. However, the odds
484             of it NOT supporting those (since Perl should be using IEEE 754 floats for NV) are practically zero.
485              
486             Hopefully, I've covered all possible types of floats found in the wild. If not, let me know and I'll
487             add it in. (For that matter, let me know if I'm missing I<any> type found in the wild.)
488              
489             =head1 AVAILABILITY
490              
491             The project homepage is L<https://github.com/SineSwiper/MooX-Types-CLike/wiki>.
492              
493             The latest version of this module is available from the Comprehensive Perl
494             Archive Network (CPAN). Visit L<http://www.perl.com/CPAN/> to find a CPAN
495             site near you, or see L<https://metacpan.org/module/MooX::Types::CLike/>.
496              
497             =for :stopwords cpan testmatrix url annocpan anno bugtracker rt cpants kwalitee diff irc mailto metadata placeholders metacpan
498              
499             =head1 SUPPORT
500              
501             =head2 Internet Relay Chat
502              
503             You can get live help by using IRC ( Internet Relay Chat ). If you don't know what IRC is,
504             please read this excellent guide: L<http://en.wikipedia.org/wiki/Internet_Relay_Chat>. Please
505             be courteous and patient when talking to us, as we might be busy or sleeping! You can join
506             those networks/channels and get help:
507              
508             =over 4
509              
510             =item *
511              
512             irc.perl.org
513              
514             You can connect to the server at 'irc.perl.org' and join this channel: #distzilla then talk to this person for help: SineSwiper.
515              
516             =back
517              
518             =head2 Bugs / Feature Requests
519              
520             Please report any bugs or feature requests via L<https://github.com/SineSwiper/MooX-Types-CLike/issues>.
521              
522             =head1 AUTHOR
523              
524             Brendan Byrd <BBYRD@CPAN.org>
525              
526             =head1 COPYRIGHT AND LICENSE
527              
528             This software is Copyright (c) 2013 by Brendan Byrd.
529              
530             This is free software, licensed under:
531              
532             The Artistic License 2.0 (GPL Compatible)
533              
534             =cut
535              
536              
537             __END__
538