File Coverage

blib/lib/Specio/Constraint/Role/CanType.pm
Criterion Covered Total %
statement 64 64 100.0
branch 23 24 95.8
condition n/a
subroutine 12 12 100.0
pod n/a
total 99 100 99.0


line stmt bran cond sub pod time code
1              
2             use strict;
3 2     2   12 use warnings;
  2         3  
  2         47  
4 2     2   8  
  2         3  
  2         68  
5             our $VERSION = '0.48';
6              
7             use Scalar::Util qw( blessed );
8 2     2   8 use Specio::PartialDump qw( partial_dump );
  2         4  
  2         85  
9 2     2   9 use Storable qw( dclone );
  2         3  
  2         73  
10 2     2   9  
  2         4  
  2         73  
11             use Role::Tiny;
12 2     2   16  
  2         3  
  2         11  
13             use Specio::Constraint::Role::Interface;
14 2     2   293 with 'Specio::Constraint::Role::Interface';
  2         4  
  2         1395  
15              
16             {
17             ## no critic (Subroutines::ProtectPrivateSubs)
18             my $attrs = dclone( Specio::Constraint::Role::Interface::_attrs() );
19             ## use critic
20              
21             for my $name (qw( parent _inline_generator )) {
22             $attrs->{$name}{init_arg} = undef;
23             $attrs->{$name}{builder}
24             = $name =~ /^_/ ? '_build' . $name : '_build_' . $name;
25             }
26              
27             $attrs->{methods} = {
28             isa => 'ArrayRef',
29             required => 1,
30             };
31              
32             ## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
33             return $attrs;
34             }
35 8     8   16 }
36              
37             ## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
38             my $self = shift;
39             my $generator = shift;
40              
41 7     7   15 my $type = ( split /::/, blessed $self)[-1];
42 7         18 my @methods = @{ $self->methods };
43             my $all_word_list = _word_list(@methods);
44 7         36 my $allow_classes = $self->_allow_classes;
45 7         15  
  7         21  
46 7         39 unless ( defined $generator ) {
47 7         23 $generator = sub {
48             shift;
49 7 50       21 my $value = shift;
50              
51 221     221   327 return
52 221         340 "An undef will never pass an $type check (wants $all_word_list)"
53             unless defined $value;
54              
55 221 100       564 my $class = blessed $value;
56             if ( !defined $class ) {
57              
58 214         528 # If we got here we know that blessed returned undef, so if
59 214 100       482 # it's a ref then it must not be blessed.
60             if ( ref $value ) {
61             my $dump = partial_dump($value);
62             return
63 105 100       222 "An unblessed reference ($dump) will never pass an $type check (wants $all_word_list)";
64 37         147 }
65              
66 37         261 # If it's defined and not an unblessed ref it must be a
67             # string. If we allow classes (vs just objects) then it might
68             # be a valid class name. But an empty string is never a valid
69             # class name. We cannot call q{}->can.
70             return
71             "An empty string will never pass an $type check (wants $all_word_list)"
72             unless length $value;
73              
74 68 100       265 if ( ref \$value eq 'GLOB' ) {
75             return
76             "A glob will never pass an $type check (wants $all_word_list)";
77 61 100       182 }
78              
79 5         41 if (
80             $value =~ /\A
81             \s*
82 56 100       337 -?[0-9]+(?:\.[0-9]+)?
83             (?:[Ee][\-+]?[0-9]+)?
84             \s*
85             \z/xs
86             ) {
87             return
88             "A number ($value) will never pass an $type check (wants $all_word_list)";
89             }
90              
91 41         305 $class = $value if $allow_classes;
92              
93             # At this point we either have undef or a non-empty string in
94 15 100       50 # $class.
95             unless ( defined $class ) {
96             my $dump = partial_dump($value);
97             return
98 15 100       38 "A plain scalar ($dump) will never pass an $type check (wants $all_word_list)";
99 12         52 }
100             }
101 12         108  
102             my @missing = grep { !$value->can($_) } @methods;
103              
104             my $noun = @missing == 1 ? 'method' : 'methods';
105 112         258 my $list = _word_list( map {qq['$_']} @missing );
  268         932  
106              
107 112 100       304 return "The $class class is missing the $list $noun";
108 112         207 };
  264         689  
109             }
110 112         763  
111 7         66 return sub { $generator->( undef, @_ ) };
112             }
113             ## use critic
114 7     221   35  
  221         10853  
115             my @items = sort { $a cmp $b } @_;
116              
117             return $items[0] if @items == 1;
118             return join ' and ', @items if @items == 2;
119 119     119   464  
  205         571  
120             my $final = pop @items;
121 119 100       329 my $list = join ', ', @items;
122 117 100       376 $list .= ', and ' . $final;
123              
124 44         89 return $list;
125 44         113 }
126 44         119  
127             1;
128 44         117  
129             # ABSTRACT: Provides a common implementation for Specio::Constraint::AnyCan and Specio::Constraint::ObjectCan
130              
131              
132             =pod
133              
134             =encoding UTF-8
135              
136             =head1 NAME
137              
138             Specio::Constraint::Role::CanType - Provides a common implementation for Specio::Constraint::AnyCan and Specio::Constraint::ObjectCan
139              
140             =head1 VERSION
141              
142             version 0.48
143              
144             =head1 DESCRIPTION
145              
146             See L<Specio::Constraint::AnyCan> and L<Specio::Constraint::ObjectCan> for
147             details.
148              
149             =head1 SUPPORT
150              
151             Bugs may be submitted at L<https://github.com/houseabsolute/Specio/issues>.
152              
153             =head1 SOURCE
154              
155             The source code repository for Specio can be found at L<https://github.com/houseabsolute/Specio>.
156              
157             =head1 AUTHOR
158              
159             Dave Rolsky <autarch@urth.org>
160              
161             =head1 COPYRIGHT AND LICENSE
162              
163             This software is Copyright (c) 2012 - 2022 by Dave Rolsky.
164              
165             This is free software, licensed under:
166              
167             The Artistic License 2.0 (GPL Compatible)
168              
169             The full text of the license can be found in the
170             F<LICENSE> file included with this distribution.
171              
172             =cut