File Coverage

blib/lib/Type/Tiny/Duck.pm
Criterion Covered Total %
statement 82 93 88.1
branch 19 30 63.3
condition 5 12 41.6
subroutine 22 23 95.6
pod 6 6 100.0
total 134 164 81.7


line stmt bran cond sub pod time code
1             package Type::Tiny::Duck;
2              
3 25     25   142541 use 5.008001;
  25         106  
4 25     25   155 use strict;
  25         50  
  25         809  
5 25     25   105 use warnings;
  25         52  
  25         2015  
6              
7             BEGIN {
8 25     25   82 $Type::Tiny::Duck::AUTHORITY = 'cpan:TOBYINK';
9 25         1392 $Type::Tiny::Duck::VERSION = '2.010001';
10             }
11              
12             $Type::Tiny::Duck::VERSION =~ tr/_//d;
13              
14 25     25   199 use Scalar::Util qw< blessed >;
  25         49  
  25         3153  
15              
16 1     1   6 sub _croak ($;@) { require Error::TypeTiny; goto \&Error::TypeTiny::croak }
  1         4  
17              
18 25     25   795 use Exporter::Tiny 1.004001 ();
  25         7330  
  25         833  
19 25     25   2540 use Type::Tiny::ConstrainedObject ();
  25         86  
  25         46523  
20             our @ISA = qw( Type::Tiny::ConstrainedObject Exporter::Tiny );
21              
22 3     3   31 sub _short_name { 'Duck' }
23              
24             sub _exporter_fail {
25 1     1   448 my ( $class, $type_name, $methods, $globals ) = @_;
26 1         3 my $caller = $globals->{into};
27 1         5 my $type = $class->new(
28             name => $type_name,
29             methods => [ @$methods ],
30             );
31             $INC{'Type/Registry.pm'}
32             ? 'Type::Registry'->for_class( $caller )->add_type( $type, $type_name )
33             : ( $Type::Registry::DELAYED{$caller}{$type_name} = $type )
34 1 50 33     16 unless( ref($caller) or $caller eq '-lexical' or $globals->{'lexical'} );
    50 33        
35 1         3 return map +( $_->{name} => $_->{code} ), @{ $type->exportables };
  1         7  
36             }
37              
38             sub new {
39 53     53 1 229 my $proto = shift;
40            
41 53 50       299 my %opts = ( @_ == 1 ) ? %{ $_[0] } : @_;
  0         0  
42 53 100       255 _croak "Need to supply list of methods" unless exists $opts{methods};
43            
44 52 50       155 $opts{methods} = [ $opts{methods} ] unless ref $opts{methods};
45            
46 52         78 if ( Type::Tiny::_USE_XS ) {
47 52         75 my $methods = join ",", sort( @{ $opts{methods} } );
  52         298  
48 52         301 my $xsub = Type::Tiny::XS::get_coderef_for( "HasMethods[$methods]" );
49 52 100       3593 $opts{compiled_type_constraint} = $xsub if $xsub;
50             }
51             elsif ( Type::Tiny::_USE_MOUSE ) {
52             require Mouse::Util::TypeConstraints;
53             my $maker = "Mouse::Util::TypeConstraints"->can( "generate_can_predicate_for" );
54             $opts{compiled_type_constraint} = $maker->( $opts{methods} ) if $maker;
55             }
56            
57 52         347 return $proto->SUPER::new( %opts );
58             } #/ sub new
59              
60             sub new_intersection {
61 1     1 1 3 my $proto = shift;
62 1 50       7 my %opts = ( @_ == 1 ) ? %{ $_[0] } : @_;
  0         0  
63 1         2 my @types = @{ delete $opts{type_constraints} };
  1         4  
64 1         2 my %values; ++$values{$_} for map @{$_->methods}, @types;
  1         4  
  2         6  
65 1         7 my @values = sort keys %values;
66 1 50 33     9 if ( $INC{'Types/Standard.pm'} and not keys %opts ) {
67 1         6 return Types::Standard::HasMethods->of( @values );
68             }
69 0         0 return $proto->new( %opts, methods => \@values );
70             }
71              
72             sub _lockdown {
73 49     49   147 my ( $self, $callback ) = @_;
74 49         179 $callback->( $self->{methods} );
75             }
76              
77 118     118 1 564 sub methods { $_[0]{methods} }
78 205   66 205 1 1536 sub inlined { $_[0]{inlined} ||= $_[0]->_build_inlined }
79              
80 396     396 1 1639 sub has_inlined { !!1 }
81              
82 658     658   1957 sub _is_null_constraint { 0 }
83              
84             sub _build_constraint {
85 7     7   17 my $self = shift;
86 7         13 my @methods = @{ $self->methods };
  7         19  
87             return sub {
88 21 50   21   161 blessed( $_[0] )
89             and not grep( !$_[0]->can( $_ ), @methods );
90 7         79 };
91             }
92              
93             sub _build_inlined {
94 45     45   79 my $self = shift;
95 45         67 my @methods = @{ $self->methods };
  45         156  
96            
97 45         69 my $xsub;
98 45         63 if ( Type::Tiny::_USE_XS ) {
99 45         1355 my $methods = join ",", sort( @{ $self->methods } );
  45         117  
100 45         233 $xsub = Type::Tiny::XS::get_subname_for( "HasMethods[$methods]" );
101             }
102            
103             sub {
104 205     205   437 my $var = $_[1];
105 205         491 local $" = q{ };
106            
107             # If $var is $_ or $_->{foo} or $foo{$_} or somesuch, then we
108             # can't use it within the grep expression, so we need to save
109             # it into a temporary variable ($tmp).
110 205 100       1124 my $code =
111             ( $var =~ /\$_/ )
112             ? qq{ Scalar::Util::blessed($var) and not do { my \$tmp = $var; grep(!\$tmp->can(\$_), qw/@methods/) } }
113             : qq{ Scalar::Util::blessed($var) and not grep(!$var->can(\$_), qw/@methods/) };
114            
115 205 100       642 return qq{do { $Type::Tiny::SafePackage use Scalar::Util (); $code }}
116             if $Type::Tiny::AvoidCallbacks;
117 180 50       956 return "$xsub\($var\)"
118             if $xsub;
119 0         0 $code;
120 45         768 };
121             } #/ sub _build_inlined
122              
123             sub _instantiate_moose_type {
124 0     0   0 my $self = shift;
125 0         0 my %opts = @_;
126 0         0 delete $opts{parent};
127 0         0 delete $opts{constraint};
128 0         0 delete $opts{inlined};
129            
130 0         0 require Moose::Meta::TypeConstraint::DuckType;
131 0         0 return "Moose::Meta::TypeConstraint::DuckType"
132             ->new( %opts, methods => $self->methods );
133             } #/ sub _instantiate_moose_type
134              
135             sub validate_explain {
136 1     1 1 3 my $self = shift;
137 1         4 my ( $value, $varname ) = @_;
138 1 50       5 $varname = '$_' unless defined $varname;
139            
140 1 50       21 return undef if $self->check( $value );
141 1 50       6 return ["Not a blessed reference"] unless blessed( $value );
142            
143 1         907 require Type::Utils;
144             return [
145             sprintf(
146             '"%s" requires that the reference can %s',
147             $self,
148 1         10 Type::Utils::english_list( map qq["$_"], @{ $self->methods } ),
149             ),
150             map sprintf( 'The reference cannot "%s"', $_ ),
151             grep !$value->can( $_ ),
152 1         8 @{ $self->methods }
  1         3  
153             ];
154             } #/ sub validate_explain
155              
156             push @Type::Tiny::CMP, sub {
157             my $A = shift->find_constraining_type;
158             my $B = shift->find_constraining_type;
159             return Type::Tiny::CMP_UNKNOWN
160             unless $A->isa( __PACKAGE__ ) && $B->isa( __PACKAGE__ );
161            
162             my %seen;
163             for my $word ( @{ $A->methods } ) {
164             $seen{$word} += 1;
165             }
166             for my $word ( @{ $B->methods } ) {
167             $seen{$word} += 2;
168             }
169            
170             my $values = join( '', CORE::values %seen );
171             if ( $values =~ /^3*$/ ) {
172             return Type::Tiny::CMP_EQUIVALENT;
173             }
174             elsif ( $values !~ /2/ ) {
175             return Type::Tiny::CMP_SUBTYPE;
176             }
177             elsif ( $values !~ /1/ ) {
178             return Type::Tiny::CMP_SUPERTYPE;
179             }
180            
181             return Type::Tiny::CMP_UNKNOWN;
182             };
183              
184             1;
185              
186             __END__