File Coverage

blib/lib/Type/Tiny/ConstrainedObject.pm
Criterion Covered Total %
statement 35 37 97.3
branch 3 4 75.0
condition n/a
subroutine 12 13 92.3
pod 6 6 100.0
total 56 60 95.0


line stmt bran cond sub pod time code
1             package Type::Tiny::ConstrainedObject;
2              
3 57     57   1028 use 5.008001;
  57         218  
4 57     57   352 use strict;
  57         135  
  57         1550  
5 57     57   301 use warnings;
  57         122  
  57         2625  
6              
7             BEGIN {
8 57     57   358 $Type::Tiny::ConstrainedObject::AUTHORITY = 'cpan:TOBYINK';
9 57         4309 $Type::Tiny::ConstrainedObject::VERSION = '2.003_000';
10             }
11              
12             $Type::Tiny::ConstrainedObject::VERSION =~ tr/_//d;
13              
14 9     9   49 sub _croak ($;@) { require Error::TypeTiny; goto \&Error::TypeTiny::croak }
  9         42  
15              
16 57     57   4044 use Type::Tiny ();
  57         146  
  57         40446  
17             our @ISA = 'Type::Tiny';
18              
19             my %errlabel = (
20             parent => 'a parent',
21             constraint => 'a constraint coderef',
22             inlined => 'an inlining coderef',
23             );
24              
25             sub new {
26 334     334 1 644 my $proto = shift;
27 334 50       1319 my %opts = ( @_ == 1 ) ? %{ $_[0] } : @_;
  0         0  
28 334         772 for my $key ( qw/ parent constraint inlined / ) {
29 993 100       2254 next unless exists $opts{$key};
30             _croak(
31             '%s type constraints cannot have %s passed to the constructor',
32             $proto->_short_name,
33 9         25 $errlabel{$key},
34             );
35             }
36 325         1591 $proto->SUPER::new( %opts );
37             } #/ sub new
38              
39             sub has_parent {
40 8496     8496 1 22937 !!1;
41             }
42              
43             sub parent {
44 276     276 1 3011 require Types::Standard;
45 276         794 Types::Standard::Object();
46             }
47              
48             sub _short_name {
49 0     0   0 die "subclasses must implement this"; # uncoverable statement
50             }
51              
52             my $i = 0;
53             my $_where_expressions = sub {
54             my $self = shift;
55             my $name = shift;
56             $name ||= "where expression check";
57             my ( %env, @codes );
58             while ( @_ ) {
59             my $expr = shift;
60             my $constraint = shift;
61             if ( !ref $constraint ) {
62             push @codes, sprintf( 'do { local $_ = %s; %s }', $expr, $constraint );
63             }
64             else {
65             require Types::Standard;
66             my $type =
67             Types::Standard::is_RegexpRef( $constraint )
68             ? Types::Standard::StrMatch()->of( $constraint )
69             : Types::TypeTiny::to_TypeTiny( $constraint );
70             if ( $type->can_be_inlined ) {
71             push @codes,
72             sprintf(
73             'do { my $tmp = %s; %s }', $expr,
74             $type->inline_check( '$tmp' )
75             );
76             }
77             else {
78             ++$i;
79             $env{ '$chk' . $i } = do { my $chk = $type->compiled_check; \$chk };
80             push @codes, sprintf( '$chk%d->(%s)', $i, $expr );
81             }
82             } #/ else [ if ( !ref $constraint )]
83             } #/ while ( @_ )
84            
85             if ( keys %env ) {
86            
87             # cannot inline
88             my $sub = Eval::TypeTiny::eval_closure(
89             source =>
90             sprintf( 'sub ($) { local $_ = shift; %s }', join( q( and ), @codes ) ),
91             description => sprintf( '%s for %s', $name, $self->name ),
92             environment => \%env,
93             );
94             return $self->where( $sub );
95             } #/ if ( keys %env )
96             else {
97             return $self->where( join( q( and ), @codes ) );
98             }
99             };
100              
101             sub stringifies_to {
102 16     16 1 681 my $self = shift;
103 16         40 my ( $constraint ) = @_;
104 16         48 $self->$_where_expressions( "stringification check", q{"$_"}, $constraint );
105             }
106              
107             sub numifies_to {
108 15     15 1 146 my $self = shift;
109 15         37 my ( $constraint ) = @_;
110 15         41 $self->$_where_expressions( "numification check", q{0+$_}, $constraint );
111             }
112              
113             sub with_attribute_values {
114 24     24 1 319 my $self = shift;
115 24         94 my %constraint = @_;
116             $self->$_where_expressions(
117             "attributes check",
118 24         112 map { my $attr = $_; qq{\$_->$attr} => $constraint{$attr} }
  48         70  
  48         180  
119             sort keys %constraint,
120             );
121             }
122              
123             1;
124              
125             __END__
126              
127             =pod
128              
129             =encoding utf-8
130              
131             =head1 NAME
132              
133             Type::Tiny::ConstrainedObject - shared behavour for Type::Tiny::Class, etc
134              
135             =head1 STATUS
136              
137             This module is considered experiemental.
138              
139             =head1 DESCRIPTION
140              
141             =head2 Methods
142              
143             The following methods exist for L<Type::Tiny::Class>, L<Type::Tiny::Role>,
144             L<Type::Tiny::Duck>, and any type constraints that inherit from
145             C<Object> or C<Overload> in L<Types::Standard>.
146              
147             These methods will also work for L<Type::Tiny::Intersection> if at least
148             one of the types in the intersection provides these methods.
149              
150             These methods will also work for L<Type::Tiny::Union> if all of the types
151             in the union provide these methods.
152              
153             =over
154              
155             =item C<< stringifies_to($constraint) >>
156              
157             Generates a new child type constraint which checks the object's
158             stringification against a constraint. For example:
159              
160             my $type = Type::Tiny::Class->new(class => 'URI');
161             my $child = $type->stringifies_to( StrMatch[qr/^http:/] );
162            
163             $child->assert_valid( URI->new("http://example.com/") );
164              
165             In the above example, C<< $child >> is a type constraint that
166             checks objects are blessed into (or inherit from) the URI class,
167             and when stringified (e.g. though overloading) the result
168             matches the regular expression C<< qr/^http:/ >>.
169              
170             C<< $constraint >> may be a type constraint, something that
171             can be coerced to a type constraint (such as a coderef returning
172             a boolean), a string of Perl code operating on C<< $_ >>, or
173             a reference to a regular expression.
174              
175             So the following would work:
176              
177             my $child = $type->stringifies_to( sub { qr/^http:/ } );
178             my $child = $type->stringifies_to( qr/^http:/ );
179             my $child = $type->stringifies_to( 'm/^http:/' );
180            
181             my $child = $type->where('"$_" =~ /^http:/');
182              
183             =item C<< numifies_to($constraint) >>
184              
185             The same as C<stringifies_to> but checks numification.
186              
187             The following might be useful:
188              
189             use Types::Standard qw(Int Overload);
190             my $IntLike = Int | Overload->numifies_to(Int)
191              
192             =item C<< with_attribute_values($attr1 => $constraint1, ...) >>
193              
194             This is best explained with an example:
195              
196             use Types::Common qw( InstanceOf StrMatch IntRange );
197            
198             my $person = InstanceOf['Local::Human'];
199             my $woman = $person->with_attribute_values(
200             gender => StrMatch[ qr/^F/i ],
201             age => IntRange[ 18 => () ],
202             );
203            
204             $woman->assert_valid($alice);
205              
206             This assertion will firstly check that C<< $alice >> is a
207             Local::Human, then check that C<< $alice->gender >> starts
208             with an "F", and lastly check that C<< $alice->age >> is
209             an integer at least 18.
210              
211             Again, constraints can be type constraints, coderefs,
212             strings of Perl code, or regular expressions.
213              
214             Technically the "attributes" don't need to be Moo/Moose/Mouse
215             attributes, but any methods which can be called with no
216             parameters and return a scalar.
217              
218             =back
219              
220             =head1 BUGS
221              
222             Please report any bugs to
223             L<https://github.com/tobyink/p5-type-tiny/issues>.
224              
225             =head1 SEE ALSO
226              
227             L<Type::Tiny::Manual>.
228              
229             L<Type::Tiny>.
230              
231             =head1 AUTHOR
232              
233             Toby Inkster E<lt>tobyink@cpan.orgE<gt>.
234              
235             =head1 COPYRIGHT AND LICENCE
236              
237             This software is copyright (c) 2019-2023 by Toby Inkster.
238              
239             This is free software; you can redistribute it and/or modify it under
240             the same terms as the Perl 5 programming language system itself.
241              
242             =head1 DISCLAIMER OF WARRANTIES
243              
244             THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
245             WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
246             MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.