File Coverage

blib/lib/Type/Tiny/Class.pm
Criterion Covered Total %
statement 120 121 99.1
branch 30 42 71.4
condition 6 12 50.0
subroutine 31 31 100.0
pod 7 7 100.0
total 194 213 91.0


line stmt bran cond sub pod time code
1             package Type::Tiny::Class;
2              
3 46     46   218344 use 5.008001;
  46         189  
4 46     46   270 use strict;
  46         103  
  46         1044  
5 46     46   254 use warnings;
  46         109  
  46         2268  
6              
7             BEGIN {
8 46     46   186 $Type::Tiny::Class::AUTHORITY = 'cpan:TOBYINK';
9 46         2128 $Type::Tiny::Class::VERSION = '2.003_000';
10             }
11              
12             $Type::Tiny::Class::VERSION =~ tr/_//d;
13              
14 46     46   364 use Scalar::Util qw< blessed >;
  46         116  
  46         4950  
15              
16 1     1   6 sub _croak ($;@) { require Error::TypeTiny; goto \&Error::TypeTiny::croak }
  1         4  
17              
18 46     46   1895 use Exporter::Tiny 1.004001 ();
  46         15544  
  46         1161  
19 46     46   11623 use Type::Tiny::ConstrainedObject ();
  46         157  
  46         35042  
20             our @ISA = qw( Type::Tiny::ConstrainedObject Exporter::Tiny );
21              
22 3     3   9 sub _short_name { 'Class' }
23              
24             sub _exporter_fail {
25 2     2   330 my ( $class, $name, $opts, $globals ) = @_;
26 2         5 my $caller = $globals->{into};
27            
28 2 50       16 $opts->{name} = $name unless exists $opts->{name}; $opts->{name} =~ s/:://g;
  2         7  
29 2 100       8 $opts->{class} = $name unless exists $opts->{class};
30 2         4 my $type = $class->new($opts);
31            
32             $INC{'Type/Registry.pm'}
33             ? 'Type::Registry'->for_class( $caller )->add_type( $type )
34             : ( $Type::Registry::DELAYED{$caller}{$type->name} = $type )
35 2 50 33     38 unless( ref($caller) or $caller eq '-lexical' or $globals->{'lexical'} );
    50 33        
36 2         6 return map +( $_->{name} => $_->{code} ), @{ $type->exportables };
  2         9  
37             }
38              
39             sub new {
40 209     209 1 2685 my $proto = shift;
41 209 100       773 return $proto->class->new( @_ ) if blessed $proto; # DWIM
42            
43 206 100       799 my %opts = ( @_ == 1 ) ? %{ $_[0] } : @_;
  3         15  
44 206 100       612 _croak "Need to supply class name" unless exists $opts{class};
45            
46 205         285 if ( Type::Tiny::_USE_XS ) {
47             my $xsub =
48 205         1009 Type::Tiny::XS::get_coderef_for( "InstanceOf[" . $opts{class} . "]" );
49 205 50       8152 $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_isa_predicate_for" );
54             $opts{compiled_type_constraint} = $maker->( $opts{class} ) if $maker;
55             }
56            
57 205         1098 return $proto->SUPER::new( %opts );
58             } #/ sub new
59              
60 304     304 1 2079 sub class { $_[0]{class} }
61 735   66 735 1 3354 sub inlined { $_[0]{inlined} ||= $_[0]->_build_inlined }
62              
63 1376     1376 1 4418 sub has_inlined { !!1 }
64              
65 2411     2411   5908 sub _is_null_constraint { 0 }
66              
67             sub _build_constraint {
68 35     35   72 my $self = shift;
69 35         85 my $class = $self->class;
70 35 50   67   219 return sub { blessed( $_ ) and $_->isa( $class ) };
  67         566  
71             }
72              
73             sub _build_inlined {
74 134     134   235 my $self = shift;
75 134         298 my $class = $self->class;
76            
77 134         228 my $xsub;
78 134         574 $xsub = Type::Tiny::XS::get_subname_for( "InstanceOf[$class]" )
79             if Type::Tiny::_USE_XS;
80            
81             sub {
82 735     735   1072 my $var = $_[1];
83             return
84 735 100       1683 qq{do { use Scalar::Util (); Scalar::Util::blessed($var) and $var->isa(q[$class]) }}
85             if $Type::Tiny::AvoidCallbacks;
86 619 50       2203 return "$xsub\($var\)"
87             if $xsub;
88 0         0 qq{Scalar::Util::blessed($var) and $var->isa(q[$class])};
89 134         1871 };
90             } #/ sub _build_inlined
91              
92             sub _build_default_message {
93 46     46   387 no warnings 'uninitialized';
  46         101  
  46         23495  
94 2     2   15 my $self = shift;
95 2         8 my $c = $self->class;
96             return sub {
97 52     52   8192 sprintf '%s did not pass type constraint (not isa %s)',
98             Type::Tiny::_dd( $_[0] ), $c;
99             }
100 2 100       10 if $self->is_anon;
101 1         4 my $name = "$self";
102             return sub {
103 1     1   5 sprintf '%s did not pass type constraint "%s" (not isa %s)',
104             Type::Tiny::_dd( $_[0] ), $name, $c;
105 1         7 };
106             } #/ sub _build_default_message
107              
108             sub _instantiate_moose_type {
109 2     2   3 my $self = shift;
110 2         8 my %opts = @_;
111 2         4 delete $opts{parent};
112 2         3 delete $opts{constraint};
113 2         3 delete $opts{inlined};
114 2         9 require Moose::Meta::TypeConstraint::Class;
115 2         8 return "Moose::Meta::TypeConstraint::Class"
116             ->new( %opts, class => $self->class );
117             } #/ sub _instantiate_moose_type
118              
119             sub plus_constructors {
120 3     3 1 1252 my $self = shift;
121            
122 3 100       16 unless ( @_ ) {
123 1         8 require Types::Standard;
124 1         8 push @_, Types::Standard::HashRef(), "new";
125             }
126            
127 3         31 require B;
128 3         14 require Types::TypeTiny;
129            
130 3         18 my $class = B::perlstring( $self->class );
131            
132 3         9 my @r;
133 3         14 while ( @_ ) {
134 4         10 my $source = shift;
135 4 50       119 Types::TypeTiny::is_TypeTiny( $source )
136             or _croak "Expected type constraint; got $source";
137            
138 4         20 my $constructor = shift;
139 4 50       23 Types::TypeTiny::is_StringLike( $constructor )
140             or _croak "Expected string; got $constructor";
141            
142 4         39 push @r, $source, sprintf( '%s->%s($_)', $class, $constructor );
143             } #/ while ( @_ )
144            
145 3         36 return $self->plus_coercions( \@r );
146             } #/ sub plus_constructors
147              
148             sub parent {
149 328   66 328 1 1730 $_[0]{parent} ||= $_[0]->_build_parent;
150             }
151              
152             sub _build_parent {
153 111     111   194 my $self = shift;
154 111         260 my $class = $self->class;
155            
156             # Some classes (I'm looking at you, Math::BigFloat) include a class in
157             # their @ISA to inherit methods, but then override isa() to return false,
158             # so that they don't appear to be a subclass.
159             #
160             # In these cases, we don't want to list the parent class as a parent
161             # type constraint.
162             #
163             my @isa = grep $class->isa( $_ ),
164 46     46   394 do { no strict "refs"; no warnings; @{"$class\::ISA"} };
  46     46   155  
  46         2067  
  46         315  
  46         123  
  46         9506  
  111         185  
  111         152  
  111         824  
165            
166 111 100       380 if ( @isa == 0 ) {
167 76         2216 require Types::Standard;
168 76         313 return Types::Standard::Object();
169             }
170            
171 35 100       115 if ( @isa == 1 ) {
172 31         155 return ref( $self )->new( class => $isa[0] );
173             }
174            
175 4         1051 require Type::Tiny::Intersection;
176 4         29 "Type::Tiny::Intersection"->new(
177             type_constraints => [ map ref( $self )->new( class => $_ ), @isa ],
178             );
179             } #/ sub _build_parent
180              
181             *__get_linear_isa_dfs =
182             eval { require mro }
183             ? \&mro::get_linear_isa
184             : sub {
185 46     46   362 no strict 'refs';
  46         114  
  46         14075  
186            
187             my $classname = shift;
188             my @lin = ( $classname );
189             my %stored;
190            
191             foreach my $parent ( @{"$classname\::ISA"} ) {
192             my $plin = __get_linear_isa_dfs( $parent );
193             foreach ( @$plin ) {
194             next if exists $stored{$_};
195             push( @lin, $_ );
196             $stored{$_} = 1;
197             }
198             }
199            
200             return \@lin;
201             };
202            
203             sub validate_explain {
204 1     1 1 3 my $self = shift;
205 1         4 my ( $value, $varname ) = @_;
206 1 50       3 $varname = '$_' unless defined $varname;
207            
208 1 50       12 return undef if $self->check( $value );
209 1 50       9 return ["Not a blessed reference"] unless blessed( $value );
210            
211 1         2 my @isa = @{ __get_linear_isa_dfs( ref $value ) };
  1         11  
212            
213 1 50       5 my $display_var = $varname eq q{$_} ? '' : sprintf( ' (in %s)', $varname );
214            
215 1         538 require Type::Utils;
216             return [
217 1         8 sprintf( '"%s" requires that the reference isa %s', $self, $self->class ),
218             sprintf(
219             'The reference%s isa %s', $display_var, Type::Utils::english_list( @isa )
220             ),
221             ];
222             } #/ sub validate_explain
223              
224             1;
225              
226             __END__
227              
228             =pod
229              
230             =encoding utf-8
231              
232             =head1 NAME
233              
234             Type::Tiny::Class - type constraints based on the "isa" method
235              
236             =head1 SYNOPSIS
237              
238             Using via L<Types::Standard>:
239              
240             package Local::Horse {
241             use Moo;
242             use Types::Standard qw( Str InstanceOf );
243            
244             has name => (
245             is => 'ro',
246             isa => Str,
247             );
248            
249             has owner => (
250             is => 'ro',
251             isa => InstanceOf[ 'Local::Person' ],
252             default => sub { Local::Person->new },
253             );
254             }
255              
256             Using Type::Tiny::Class's export feature:
257              
258             package Local::Horse {
259             use Moo;
260             use Types::Standard qw( Str );
261             use Type::Tiny::Class 'Local::Person';
262            
263             has name => (
264             is => 'ro',
265             isa => Str,
266             );
267            
268             has owner => (
269             is => 'ro',
270             isa => LocalPerson,
271             default => sub { LocalPerson->new },
272             );
273             }
274              
275             Using Type::Tiny::Class's object-oriented interface:
276              
277             package Local::Horse {
278             use Moo;
279             use Types::Standard qw( Str );
280             use Type::Tiny::Class;
281            
282             my $Person = Type::Tiny::Class->new( class => 'Local::Person' );
283            
284             has name => (
285             is => 'ro',
286             isa => Str,
287             );
288            
289             has owner => (
290             is => 'ro',
291             isa => $Person,
292             default => sub { $Person->new },
293             );
294             }
295              
296             Using Type::Utils's functional interface:
297              
298             package Local::Horse {
299             use Moo;
300             use Types::Standard qw( Str );
301             use Type::Utils;
302            
303             my $Person = class_type 'Local::Person';
304            
305             has name => (
306             is => 'ro',
307             isa => Str,
308             );
309            
310             has owner => (
311             is => 'ro',
312             isa => $Person,
313             default => sub { $Person->new },
314             );
315             }
316              
317             =head1 STATUS
318              
319             This module is covered by the
320             L<Type-Tiny stability policy|Type::Tiny::Manual::Policies/"STABILITY">.
321              
322             =head1 DESCRIPTION
323              
324             Type constraints of the general form C<< { $_->isa("Some::Class") } >>.
325              
326             This package inherits from L<Type::Tiny>; see that for most documentation.
327             Major differences are listed below:
328              
329             =head2 Constructor
330              
331             =over
332              
333             =item C<new>
334              
335             When the constructor is called on an I<instance> of Type::Tiny::Class, it
336             passes the call through to the constructor of the class for the constraint.
337             So for example:
338              
339             my $type = Type::Tiny::Class->new(class => "Foo::Bar");
340             my $obj = $type->new(hello => "World");
341             say ref($obj); # prints "Foo::Bar"
342              
343             This little bit of DWIM was borrowed from L<MooseX::Types::TypeDecorator>,
344             but Type::Tiny doesn't take the idea quite as far.
345              
346             =back
347              
348             =head2 Attributes
349              
350             =over
351              
352             =item C<class>
353              
354             The class for the constraint.
355              
356             =item C<constraint>
357              
358             Unlike Type::Tiny, you I<cannot> pass a constraint coderef to the constructor.
359             Instead rely on the default.
360              
361             =item C<inlined>
362              
363             Unlike Type::Tiny, you I<cannot> pass an inlining coderef to the constructor.
364             Instead rely on the default.
365              
366             =item C<parent>
367              
368             Parent is automatically calculated, and cannot be passed to the constructor.
369              
370             =back
371              
372             =head2 Methods
373              
374             =over
375              
376             =item C<< plus_constructors($source, $method_name) >>
377              
378             Much like C<plus_coercions> but adds coercions that go via a constructor.
379             (In fact, this is implemented as a wrapper for C<plus_coercions>.)
380              
381             Example:
382              
383             package MyApp::Minion;
384            
385             use Moose; extends "MyApp::Person";
386            
387             use Types::Standard qw( HashRef Str );
388             use Type::Utils qw( class_type );
389            
390             my $Person = class_type({ class => "MyApp::Person" });
391            
392             has boss => (
393             is => "ro",
394             isa => $Person->plus_constructors(
395             HashRef, "new",
396             Str, "_new_from_name",
397             ),
398             coerce => 1,
399             );
400            
401             package main;
402            
403             MyApp::Minion->new(
404             ...,
405             boss => "Bob", ## via MyApp::Person->_new_from_name
406             );
407            
408             MyApp::Minion->new(
409             ...,
410             boss => { name => "Bob" }, ## via MyApp::Person->new
411             );
412              
413             Because coercing C<HashRef> via constructor is a common desire, if
414             you call C<plus_constructors> with no arguments at all, this is the
415             default.
416              
417             $classtype->plus_constructors(HashRef, "new")
418             $classtype->plus_constructors() ## identical to above
419              
420             This is handy for Moose/Mouse/Moo-based classes.
421              
422             =item C<< stringifies_to($constraint) >>
423              
424             See L<Type::Tiny::ConstrainedObject>.
425              
426             =item C<< numifies_to($constraint) >>
427              
428             See L<Type::Tiny::ConstrainedObject>.
429              
430             =item C<< with_attribute_values($attr1 => $constraint1, ...) >>
431              
432             See L<Type::Tiny::ConstrainedObject>.
433              
434             =back
435              
436             =head2 Exports
437              
438             Type::Tiny::Class can be used as an exporter.
439              
440             use Type::Tiny::Class 'HTTP::Tiny';
441              
442             This will export the following functions into your namespace:
443              
444             =over
445              
446             =item C<< HTTPTiny >>
447              
448             =item C<< is_HTTPTiny( $value ) >>
449              
450             =item C<< assert_HTTPTiny( $value ) >>
451              
452             =item C<< to_HTTPTiny( $value ) >>
453              
454             =back
455              
456             You will also be able to use C<< HTTPTiny->new(...) >> as a shortcut for
457             C<< HTTP::Tiny->new(...) >>.
458              
459             Multiple types can be exported at once:
460              
461             use Type::Tiny::Class qw( HTTP::Tiny LWP::UserAgent );
462              
463             =head1 BUGS
464              
465             Please report any bugs to
466             L<https://github.com/tobyink/p5-type-tiny/issues>.
467              
468             =head1 SEE ALSO
469              
470             L<Type::Tiny::Manual>.
471              
472             L<Type::Tiny>.
473              
474             L<Moose::Meta::TypeConstraint::Class>.
475              
476             =head1 AUTHOR
477              
478             Toby Inkster E<lt>tobyink@cpan.orgE<gt>.
479              
480             =head1 COPYRIGHT AND LICENCE
481              
482             This software is copyright (c) 2013-2014, 2017-2023 by Toby Inkster.
483              
484             This is free software; you can redistribute it and/or modify it under
485             the same terms as the Perl 5 programming language system itself.
486              
487             =head1 DISCLAIMER OF WARRANTIES
488              
489             THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
490             WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
491             MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.