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 3 5 60.0
total 140 167 83.8


line stmt bran cond sub pod time code
1             package MooX::Types::MooseLike;
2 14     14   64921 use strict;
  14         20  
  14         530  
3 14     14   63 use warnings FATAL => 'all';
  14         24  
  14         581  
4 14     14   63 use Exporter 5.57 'import';
  14         325  
  14         874  
5             our @EXPORT_OK;
6             push @EXPORT_OK, qw( exception_message inflate_type );
7 14     14   3290 use Module::Runtime qw(require_module);
  14         8961  
  14         84  
8 14     14   673 use Carp qw(confess croak);
  14         26  
  14         880  
9 14     14   64 use List::Util qw(first);
  14         18  
  14         3203  
10              
11             our $VERSION = '0.29';
12              
13             sub register_types {
14 17     17 1 542 my ($type_definitions, $into, $moose_namespace) = @_;
15 17         25 foreach my $type_def (@{$type_definitions}) {
  17         64  
16 257         359 my $coderefs = make_type($type_def, $moose_namespace);
17 257         465 install_type($type_def->{name}, $coderefs, $into);
18             }
19 17         84 return;
20             }
21              
22             sub install_type {
23 257     257 0 283 my ($type_name, $coderefs, $namespace) = @_;
24 257         352 my $is_type_name = 'is_' . $type_name;
25 257         291 my $type_full_name = $namespace . '::' . $type_name;
26 257         273 my $is_type_full_name = $namespace . '::' . $is_type_name;
27              
28             {
29 14     14   69 no strict 'refs'; ## no critic qw(TestingAndDebugging::ProhibitNoStrict)
  14         28  
  14         2277  
  257         217  
30 257         257 *{$type_full_name} = $coderefs->{type};
  257         999  
31 257         296 *{$is_type_full_name} = $coderefs->{is_type};
  257         916  
32 257         310 push @{"${namespace}::EXPORT_OK"}, $type_name, $is_type_name;
  257         738  
33             }
34 257         556 return;
35             }
36              
37             sub make_type {
38 257     257 0 257 my ($type_definition, $moose_namespace) = @_;
39 257         305 my $test = $type_definition->{test};
40              
41 257 100       470 if (my $subtype_of = $type_definition->{subtype_of}) {
42 3 100       9 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         1 $subtype_of = do {
46 14     14   66 no strict 'refs';
  14         18  
  14         10134  
47 1         2 &{$from . '::' . $subtype_of}();
  1         3  
48             };
49             }
50             # Assume a (base) test always exists even if you must write: test => sub {1}
51 3         4 my $base_test = $test;
52             $test = sub {
53 9     9   19 my $value = shift;
54 9         9 local $@;
55 9 100       11 eval { $subtype_of->($value); 1 } or return;
  9         15  
  7         37  
56             # TODO implement: eval { $base_test->($value); 1 } paradigm
57 7 50       15 if ($base_test) {
58 7 100       21 $base_test->($value) or return;
59             }
60 5         101 return 1;
61 3         14 };
62             }
63              
64             my $isa = sub {
65 251 100   251   45265 return if $test->(@_);
66 103         869 local $Carp::Internal{"MooX::Types::MooseLike"} = 1; ## no critic qw(Variables::ProhibitPackageVars)
67 103         331 confess $type_definition->{message}->(@_) ; ## no critic qw(ErrorHandling::RequireUseOfExceptions)
68 257         790 };
69              
70 257 100 66     772 if (ref $type_definition->{inflate}) {
    100          
71 81         180 $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       277 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         665 };
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   85885 if (ref($_[0]) eq 'ARRAY') {
93 38         47 my @params = @{$_[0]};
  38         77  
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   103276 if (my $parameterizer = $type_definition->{parameterizable}) {
99              
100             # Can we assume @params is a list of coderefs?
101 54 50       254 if(my $culprit = first { (ref($_) ne 'CODE') } @params) {
  61         167  
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         169 $isa->($_[0]);
109              
110             # Run the nested type coderefs on each value
111 49         68 foreach my $coderef (@params) {
112 52         114 foreach my $value ($parameterizer->($_[0])) {
113 59         76 $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         141 $isa->($_[0], @params);
123             }
124 38         173 };
125              
126 38 100       137 if (ref $type_definition->{inflate}) {
127 33         46 my $inflation = $type_definition->{inflate};
128 33     0   197 $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         43 shift;
134 38         165 return ($parameterized_isa, @_);
135             }
136             else {
137 44         174 return $isa;
138             }
139             },
140 22     22   1036 is_type => sub { $test->(@_) },
        22      
141 257         1478 };
142             }
143              
144             sub exception_message {
145 83     83 1 113 my ($attribute_value, $type) = @_;
146 83 100       150 $attribute_value = defined $attribute_value ? $attribute_value : 'undef';
147 83         11515 return "${attribute_value} is not ${type}!";
148             }
149              
150             sub inflate_type {
151 0     0 1   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__