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   18 use strict;
  2         5  
  2         63  
4 2     2   11 use warnings;
  2         4  
  2         98  
5              
6             our $VERSION = '0.47';
7              
8 2     2   15 use Scalar::Util qw( blessed );
  2         4  
  2         122  
9 2     2   13 use Specio::PartialDump qw( partial_dump );
  2         4  
  2         94  
10 2     2   14 use Storable qw( dclone );
  2         4  
  2         94  
11              
12 2     2   24 use Role::Tiny;
  2         5  
  2         73  
13              
14 2     2   375 use Specio::Constraint::Role::Interface;
  2         4  
  2         1697  
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   20 return $attrs;
36             }
37             }
38              
39             ## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
40             sub _wrap_message_generator {
41 7     7   20 my $self = shift;
42 7         24 my $generator = shift;
43              
44 7         50 my $type = ( split /::/, blessed $self)[-1];
45 7         18 my @methods = @{ $self->methods };
  7         28  
46 7         55 my $all_word_list = _word_list(@methods);
47 7         27 my $allow_classes = $self->_allow_classes;
48              
49 7 50       24 unless ( defined $generator ) {
50             $generator = sub {
51 221     221   373 shift;
52 221         399 my $value = shift;
53              
54             return
55 221 100       602 "An undef will never pass an $type check (wants $all_word_list)"
56             unless defined $value;
57              
58 214         589 my $class = blessed $value;
59 214 100       503 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       280 if ( ref $value ) {
64 37         174 my $dump = partial_dump($value);
65             return
66 37         279 "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       285 "An empty string will never pass an $type check (wants $all_word_list)"
75             unless length $value;
76              
77 61 100       226 if ( ref \$value eq 'GLOB' ) {
78             return
79 5         47 "A glob will never pass an $type check (wants $all_word_list)";
80             }
81              
82 56 100       354 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         338 "A number ($value) will never pass an $type check (wants $all_word_list)";
92             }
93              
94 15 100       55 $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       48 unless ( defined $class ) {
99 12         63 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         291 my @missing = grep { !$value->can($_) } @methods;
  268         1109  
106              
107 112 100       433 my $noun = @missing == 1 ? 'method' : 'methods';
108 112         232 my $list = _word_list( map {qq['$_']} @missing );
  264         807  
109              
110 112         904 return "The $class class is missing the $list $noun";
111 7         54 };
112             }
113              
114 7     221   38 return sub { $generator->( undef, @_ ) };
  221         12448  
115             }
116             ## use critic
117              
118             sub _word_list {
119 119     119   691 my @items = sort { $a cmp $b } @_;
  205         692  
120              
121 119 100       364 return $items[0] if @items == 1;
122 117 100       431 return join ' and ', @items if @items == 2;
123              
124 44         110 my $final = pop @items;
125 44         132 my $list = join ', ', @items;
126 44         134 $list .= ', and ' . $final;
127              
128 44         126 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.47
148              
149             =head1 DESCRIPTION
150              
151             See L<Specio::Constraint::AnyCan> and L<Specio::Constraint::ObjectCan> for
152             details.
153              
154             =head1 SUPPORT
155              
156             Bugs may be submitted at L<https://github.com/houseabsolute/Specio/issues>.
157              
158             I am also usually active on IRC as 'autarch' on C<irc://irc.perl.org>.
159              
160             =head1 SOURCE
161              
162             The source code repository for Specio can be found at L<https://github.com/houseabsolute/Specio>.
163              
164             =head1 AUTHOR
165              
166             Dave Rolsky <autarch@urth.org>
167              
168             =head1 COPYRIGHT AND LICENSE
169              
170             This software is Copyright (c) 2012 - 2021 by Dave Rolsky.
171              
172             This is free software, licensed under:
173              
174             The Artistic License 2.0 (GPL Compatible)
175              
176             The full text of the license can be found in the
177             F<LICENSE> file included with this distribution.
178              
179             =cut