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