package Type::Tiny::Duck; use 5.006001; use strict; use warnings; BEGIN { $Type::Tiny::Duck::AUTHORITY = 'cpan:TOBYINK'; $Type::Tiny::Duck::VERSION = '1.004004'; } use Scalar::Util qw< blessed >; sub _croak ($;@) { require Error::TypeTiny; goto \&Error::TypeTiny::croak } use Type::Tiny (); our @ISA = 'Type::Tiny'; sub new { my $proto = shift; my %opts = (@_==1) ? %{$_[0]} : @_; _croak "Duck type constraints cannot have a parent constraint passed to the constructor" if exists $opts{parent}; _croak "Duck type constraints cannot have a constraint coderef passed to the constructor" if exists $opts{constraint}; _croak "Duck type constraints cannot have a inlining coderef passed to the constructor" if exists $opts{inlined}; _croak "Need to supply list of methods" unless exists $opts{methods}; $opts{methods} = [$opts{methods}] unless ref $opts{methods}; if (Type::Tiny::_USE_XS) { my $methods = join ",", sort(@{$opts{methods}}); my $xsub = Type::Tiny::XS::get_coderef_for("HasMethods[$methods]"); $opts{compiled_type_constraint} = $xsub if $xsub; } elsif (Type::Tiny::_USE_MOUSE) { require Mouse::Util::TypeConstraints; my $maker = "Mouse::Util::TypeConstraints"->can("generate_can_predicate_for"); $opts{compiled_type_constraint} = $maker->($opts{methods}) if $maker; } return $proto->SUPER::new(%opts); } sub methods { $_[0]{methods} } sub inlined { $_[0]{inlined} ||= $_[0]->_build_inlined } sub has_inlined { !!1 } sub _build_constraint { my $self = shift; my @methods = @{$self->methods}; return sub { blessed($_[0]) and not grep(!$_[0]->can($_), @methods) }; } sub _build_inlined { my $self = shift; my @methods = @{$self->methods}; if (Type::Tiny::_USE_XS) { my $methods = join ",", sort(@{$self->methods}); my $xsub = Type::Tiny::XS::get_subname_for("HasMethods[$methods]"); return sub { my $var = $_[1]; "$xsub\($var\)" } if $xsub; } sub { my $var = $_[1]; local $" = q{ }; # If $var is $_ or $_->{foo} or $foo{$_} or somesuch, then we # can't use it within the grep expression, so we need to save # it into a temporary variable ($tmp). ($var =~ /\$_/) ? qq{ Scalar::Util::blessed($var) and not do { my \$tmp = $var; grep(!\$tmp->can(\$_), qw/@methods/) } } : qq{ Scalar::Util::blessed($var) and not grep(!$var->can(\$_), qw/@methods/) }; }; } sub _instantiate_moose_type { my $self = shift; my %opts = @_; delete $opts{parent}; delete $opts{constraint}; delete $opts{inlined}; require Moose::Meta::TypeConstraint::DuckType; return "Moose::Meta::TypeConstraint::DuckType"->new(%opts, methods => $self->methods); } sub has_parent { !!1; } sub parent { require Types::Standard; Types::Standard::Object(); } sub validate_explain { my $self = shift; my ($value, $varname) = @_; $varname = '$_' unless defined $varname; return undef if $self->check($value); return ["Not a blessed reference"] unless blessed($value); require Type::Utils; return [ sprintf( '"%s" requires that the reference can %s', $self, Type::Utils::english_list(map qq["$_"], @{$self->methods}), ), map sprintf('The reference cannot "%s"', $_), grep !$value->can($_), @{$self->methods} ]; } 1; __END__ =pod =encoding utf-8 =head1 NAME Type::Tiny::Duck - type constraints based on the "can" method =head1 STATUS This module is covered by the L. =head1 DESCRIPTION Type constraints of the general form C<< { $_->can("method") } >>. This package inherits from L; see that for most documentation. Major differences are listed below: =head2 Attributes =over =item C An arrayref of method names. =item C Unlike Type::Tiny, you I pass a constraint coderef to the constructor. Instead rely on the default. =item C Unlike Type::Tiny, you I pass an inlining coderef to the constructor. Instead rely on the default. =item C Parent is always Types::Standard::Object, and cannot be passed to the constructor. =back =head1 BUGS Please report any bugs to L. =head1 SEE ALSO L. L. L. =head1 AUTHOR Toby Inkster Etobyink@cpan.orgE. =head1 COPYRIGHT AND LICENCE This software is copyright (c) 2013-2014, 2017-2019 by Toby Inkster. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =head1 DISCLAIMER OF WARRANTIES THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.