File Coverage

blib/lib/MooX/Types/MooseLike.pm
Criterion Covered Total %
statement 91 102 89.2
branch 25 32 78.1
condition 3 6 50.0
subroutine 18 22 81.8
pod 0 5 0.0
total 137 167 82.0


line stmt bran cond sub pod time code
1             package MooX::Types::MooseLike;
2 14     14   104769 use strict;
  14         28  
  14         564  
3 14     14   79 use warnings FATAL => 'all';
  14         24  
  14         667  
4 14     14   70 use Exporter 5.57 'import';
  14         380  
  14         930  
5             our @EXPORT_OK;
6             push @EXPORT_OK, qw( exception_message inflate_type );
7 14     14   8670 use Module::Runtime qw(require_module);
  14         13027  
  14         101  
8 14     14   1430 use Carp qw(confess croak);
  14         27  
  14         1247  
9 14     14   82 use List::Util qw(first);
  14         25  
  14         5635  
10              
11             our $VERSION = '0.27';
12              
13             sub register_types {
14 17     17 0 683 my ($type_definitions, $into, $moose_namespace) = @_;
15 17         31 foreach my $type_def (@{$type_definitions}) {
  17         53  
16 257         510 my $coderefs = make_type($type_def, $moose_namespace);
17 257         623 install_type($type_def->{name}, $coderefs, $into);
18             }
19 17         109 return;
20             }
21              
22             sub install_type {
23 257     257 0 421 my ($type_name, $coderefs, $namespace) = @_;
24 257         437 my $is_type_name = 'is_' . $type_name;
25 257         491 my $type_full_name = $namespace . '::' . $type_name;
26 257         369 my $is_type_full_name = $namespace . '::' . $is_type_name;
27              
28             {
29 14     14   82 no strict 'refs'; ## no critic qw(TestingAndDebugging::ProhibitNoStrict)
  14         25  
  14         3097  
  257         295  
30 257         717 *{$type_full_name} = $coderefs->{type};
  257         1684  
31 257         588 *{$is_type_full_name} = $coderefs->{is_type};
  257         1828  
32 257         313 push @{"${namespace}::EXPORT_OK"}, $type_name, $is_type_name;
  257         1171  
33             }
34 257         1566 return;
35             }
36              
37             sub make_type {
38 257     257 0 347 my ($type_definition, $moose_namespace) = @_;
39 257         398 my $test = $type_definition->{test};
40              
41 257 100       884 if (my $subtype_of = $type_definition->{subtype_of}) {
42 3 100       19 if (!ref $subtype_of) {
43 1   33     3 my $from = $type_definition->{from}
44             || croak "Must define a 'from' namespace for the parent type: $subtype_of when defining type: $type_definition->{name}";
45 1         2 $subtype_of = do {
46 14     14   171 no strict 'refs';
  14         25  
  14         16228  
47 1         7 &{$from . '::' . $subtype_of}();
  1         16  
48             };
49             }
50             # Assume a (base) test always exists even if you must write: test => sub {1}
51 3         6 my $base_test = $test;
52             $test = sub {
53 9     9   15 my $value = shift;
54 9         14 local $@;
55 9 100       15 eval { $subtype_of->($value); 1 } or return;
  9         21  
  7         41  
56             # TODO implement: eval { $base_test->($value); 1 } paradigm
57 7 50       20 if ($base_test) {
58 7 100       22 $base_test->($value) or return;
59             }
60 5         113 return 1;
61 3         10 };
62             }
63              
64             my $isa = sub {
65 238 100   238   71098 return if $test->(@_);
66 101         1257 local $Carp::Internal{"MooX::Types::MooseLike"} = 1; ## no critic qw(Variables::ProhibitPackageVars)
67 101         421 confess $type_definition->{message}->(@_) ; ## no critic qw(ErrorHandling::RequireUseOfExceptions)
68 257         1364 };
69              
70 257 100 66     1007 if (ref $type_definition->{inflate}) {
    100          
71 81         280 $Moo::HandleMoose::TYPE_MAP{$isa} = $type_definition->{inflate};
72             }
73             elsif (exists $type_definition->{inflate} and not $type_definition->{inflate}) {
74             # no-op
75             }
76             else {
77 165 50       427 my $full_name =
78             defined $moose_namespace
79             ? "${moose_namespace}::" . $type_definition->{name}
80             : $type_definition->{name};
81              
82             $Moo::HandleMoose::TYPE_MAP{$isa} = sub {
83 0 0   0   0 require_module($moose_namespace) if $moose_namespace;
84 0         0 Moose::Util::TypeConstraints::find_type_constraint($full_name);
85 165         1184 };
86             }
87              
88             return {
89             type => sub {
90              
91             # If we have a parameterized type then we want to check its values
92 82 100   82   161234 if (ref($_[0]) eq 'ARRAY') {
93 38         62 my @params = @{$_[0]};
  38         106  
94             my $parameterized_isa = sub {
95              
96             # Types that take other types as a parameter have a parameterizable
97             # part with the one exception: 'AnyOf'
98 100 100   100   115478 if (my $parameterizer = $type_definition->{parameterizable}) {
99              
100             # Can we assume @params is a list of coderefs?
101 54 50       281 if(my $culprit = first { (ref($_) ne 'CODE') } @params) {
  61         225  
102 0         0 croak "Expect all parameters to be coderefs, but found: $culprit";
103             }
104              
105             # Check the containing type. We could pass @_, but it is such that:
106             # scalar @_ = 1 always in this context. In other words,
107             # an $isa only type checks one thing at a time.
108 54         241 $isa->($_[0]);
109              
110             # Run the nested type coderefs on each value
111 49         98 foreach my $coderef (@params) {
112 52         296 foreach my $value ($parameterizer->($_[0])) {
113 59         117 $coderef->($value);
114             }
115             }
116             }
117             else {
118             # Note that while $isa only checks on value at a time
119             # We can pass it additional parameters as we do here.
120             # These additional parameters are then used in the type definition
121             # For example, see InstanceOf
122 46         143 $isa->($_[0], @params);
123             }
124 38         233 };
125              
126 38 100       167 if (ref $type_definition->{inflate}) {
127 33         100 my $inflation = $type_definition->{inflate};
128 33     0   281 $Moo::HandleMoose::TYPE_MAP{$parameterized_isa} = sub { $inflation->(\@params) };
  0         0  
129             }
130              
131             # Remove old $isa, but return the rest of the arguments
132             # so any specs defined after 'isa' don't get lost
133 38         68 shift;
134 38         198 return ($parameterized_isa, @_);
135             }
136             else {
137 44         298 return $isa;
138             }
139             },
140 22     22   1903 is_type => sub { $test->(@_) },
        22      
141 257         3019 };
142             }
143              
144             sub exception_message {
145 81     81 0 143 my ($attribute_value, $type) = @_;
146 81 100       179 $attribute_value = defined $attribute_value ? $attribute_value : 'undef';
147 81         15973 return "${attribute_value} is not ${type}!";
148             }
149              
150             sub inflate_type {
151 0     0 0   my $coderef = shift;
152 0 0         if (my $inflator = $Moo::HandleMoose::TYPE_MAP{$coderef}) {
153 0           return $inflator->();
154             }
155             return Moose::Meta::TypeConstraint->new(
156 0     0     constraint => sub { eval { &$coderef; 1 } }
  0            
  0            
157 0           );
158             }
159              
160             1;
161             __END__