File Coverage

blib/lib/Data/Object/Library.pm
Criterion Covered Total %
statement 57 65 87.6
branch 8 22 36.3
condition 0 21 0.0
subroutine 9 12 75.0
pod 0 1 0.0
total 74 121 61.1


line stmt bran cond sub pod time code
1             # ABSTRACT: Data::Object Type Library for Perl 5
2             package Data::Object::Library;
3              
4 12     12   405028 use 5.10.0;
  12         39  
  12         556  
5              
6 12     12   60 use strict;
  12         17  
  12         399  
7 12     12   60 use warnings;
  12         17  
  12         459  
8              
9 12     12   7089 use Type::Library -base;
  12         235147  
  12         152  
10 12     12   12647 use Type::Utils -all;
  12         55023  
  12         163  
11              
12 12     12   35332 use Data::Object;
  12         1880  
  12         5718  
13              
14             our $VERSION = '0.03'; # VERSION
15              
16             extends 'Types::Standard';
17             extends 'Types::Common::Numeric';
18             extends 'Types::Common::String';
19              
20             sub DECLARE {
21 492     492 0 1319 my ($name, %opts) = @_;
22              
23 492 100       1681 return map +(DECLARE($_, %opts)), @$name if ref $name;
24              
25 360         911 ($opts{name} = $name) =~ s/:://g;
26              
27 360         963 my $registry = __PACKAGE__->meta;
28              
29 360 50 0     3417 my @cans = ref($opts{can}) eq 'ARRAY' ? @{$opts{can}} : $opts{can} // ();
  360         898  
30 360 50 0     704 my @isas = ref($opts{isa}) eq 'ARRAY' ? @{$opts{isa}} : $opts{isa} // ();
  360         639  
31 360 50 0     656 my @does = ref($opts{does}) eq 'ARRAY' ? @{$opts{does}} : $opts{does} // ();
  360         633  
32              
33 360         445 my $code = $opts{constraint};
34 360         356 my $text = $opts{inlined};
35              
36             $opts{constraint} = sub {
37 0     0   0 my @args = @_;
38 0 0 0     0 return if @isas and grep(not($args[0]->isa($_)), @isas);
39 0 0 0     0 return if @cans and grep(not($args[0]->can($_)), @cans);
40 0 0 0     0 return if @does and grep(not($args[0]->does($_)), @does);
41 0 0 0     0 return if $code and not $code->(@args);
42 0         0 return 1;
43 360         1676 };
44             $opts{inlined} = sub {
45 360     360   193128 my $blessed = "Scalar::Util::blessed($_[1])";
46 360 50       6825 return join(' && ', map "($_)",
47             join(' && ', map "($blessed and $_[1]->isa('$_'))", @isas),
48             join(' && ', map "($blessed and $_[1]->does('$_'))", @does),
49             join(' && ', map "($blessed and $_[1]->can('$_'))", @cans),
50             $text ? $text : (),
51             );
52 360         1659 };
53              
54 360         524 $opts{bless} = "Type::Tiny";
55 360 50       838 $opts{parent} = "Object" unless $opts{parent};
56 360         461 $opts{coerion} = 1;
57              
58 12     12   83 { no warnings "numeric"; $opts{_caller_level}++ }
  12         19  
  12         15435  
  360         324  
  360         525  
59              
60 360         577 my $coerce = delete $opts{coerce};
61 360         1492 my $type = declare(%opts);
62              
63 360         133816 my $functions = {
64             'Data::Object::Array' => 'data_array',
65             'Data::Object::Code' => 'data_code',
66             'Data::Object::Float' => 'data_float',
67             'Data::Object::Hash' => 'data_hash',
68             'Data::Object::Integer' => 'data_integer',
69             'Data::Object::Number' => 'data_number',
70             'Data::Object::Regexp' => 'data_regexp',
71             'Data::Object::Scalar' => 'data_scalar',
72             'Data::Object::String' => 'data_string',
73             'Data::Object::Undef' => 'data_undef',
74             'Data::Object::Universal' => 'data_universal',
75             };
76              
77 360         633 my ($key) = grep { $functions->{$_} } @isas;
  360         906  
78              
79 360 50       1189 for my $coercive ('ARRAY' eq ref $coerce ? @$coerce : $coerce) {
80 744         4264 my $object = $registry->get_type($coercive);
81 744         7866 my $function = $$functions{$key};
82              
83 744         2967 my $forward = Data::Object->can($function);
84 744     0   3592 coerce $opts{name}, from $coercive, via { $forward->($_) };
  0         0  
85              
86 744         104251 $object->coercion->i_really_want_to_unfreeze;
87              
88 744         7290 my $reverse = Data::Object->can('deduce_deep');
89 744     0   3573 coerce $coercive, from $opts{name}, via { $reverse->($_) };
  0         0  
90              
91 744         70207 $object->coercion->freeze;
92             }
93              
94 360         5656 return $type;
95             }
96              
97             DECLARE ["ArrayObj", "ArrayObject"] => (
98             isa => ["Data::Object::Array"],
99             does => ["Data::Object::Role::Array"],
100             can => ["data", "dump"],
101             coerce => ["ArrayRef"],
102             constraint_generator => sub {
103             return Data::Object::Library::ArrayObject() unless @_;
104              
105             my $param = Types::TypeTiny::to_TypeTiny(shift);
106              
107             Types::TypeTiny::TypeTiny->check($param)
108             or Types::Standard::_croak(
109             "Parameter to ArrayObject[`a] expected ".
110             "to be a type constraint; got $param"
111             );
112              
113             return sub {
114             my $arrayobj = shift;
115             $param->check($_) || return for @$arrayobj;
116             return !!1;
117             }
118             },
119             deep_explanation => sub {
120             my ($type, $value, $varname) = @_;
121             my $param = $type->parameters->[0];
122              
123             for my $i (0 .. $#$value) {
124             my $item = $value->[$i];
125             next if $param->check($item);
126             my $message = '"%s" constrains each value in the array with "%s"';
127             my @criteria = @{ $param->validate_explain($item, sprintf('%s->[%d]', $varname, $i)) };
128             return [sprintf($message, $type, $param), @criteria]
129             }
130              
131             return;
132             },
133             );
134              
135             DECLARE ["CodeObj", "CodeObject"] => (
136             isa => ["Data::Object::Code"],
137             does => ["Data::Object::Role::Code"],
138             can => ["data", "dump"],
139             coerce => ["CodeRef"],
140             );
141              
142             DECLARE ["FloatObj", "FloatObject"] => (
143             isa => ["Data::Object::Float"],
144             does => ["Data::Object::Role::Float"],
145             can => ["data", "dump"],
146             coerce => ["Str", "Num", "LaxNum"],
147             );
148              
149             DECLARE ["HashObj", "HashObject"] => (
150             isa => ["Data::Object::Hash"],
151             does => ["Data::Object::Role::Hash"],
152             can => ["data", "dump"],
153             coerce => ["HashRef"],
154             constraint_generator => sub {
155             return Data::Object::Library::HashObject() unless @_;
156              
157             my $param = Types::TypeTiny::to_TypeTiny(shift);
158              
159             Types::TypeTiny::TypeTiny->check($param)
160             or Types::Standard::_croak(
161             "Parameter to HashObject[`a] expected ".
162             "to be a type constraint; got $param"
163             );
164              
165             return sub {
166             my $hashobj = shift;
167             $param->check($_) || return for values %$hashobj;
168             return !!1;
169             }
170             },
171             deep_explanation => sub {
172             my ($type, $value, $varname) = @_;
173             my $param = $type->parameters->[0];
174              
175             for my $k (sort keys %$value) {
176             my $item = $value->{$k};
177             next if $param->check($item);
178             my $message = '"%s" constrains each value in the hash object with "%s"';
179             my @criteria = @{ $param->validate_explain($item, sprintf('%s->{%s}', $varname, B::perlstring($k))) };
180             return [sprintf($message, $type, $param), @criteria]
181             }
182              
183             return;
184             },
185             my_methods => {
186             hashref_allows_key => sub {
187             my $self = shift;
188             Data::Object::Library::Str()->check($_[0]);
189             },
190             hashref_allows_value => sub {
191             my $self = shift;
192             my ($key, $value) = @_;
193              
194             return !!0 unless $self->my_hashref_allows_key($key);
195             return !!1 if $self == Data::Object::Library::HashRef();
196              
197             my $href = $self->find_parent(sub {
198             $_->has_parent && $_->parent == Data::Object::Library::HashRef()
199             });
200              
201             my $param = $href->type_parameter;
202              
203             Data::Object::Library::Str()->check($key) and $param->check($value);
204             },
205             },
206             );
207              
208             DECLARE ["IntObj", "IntObject", "IntegerObj", "IntegerObject"] => (
209             isa => ["Data::Object::Integer"],
210             does => ["Data::Object::Role::Integer"],
211             can => ["data", "dump"],
212             coerce => ["Str", "Num", "LaxNum", "StrictNum", "Int"],
213             );
214              
215             DECLARE ["NumObj", "NumObject", "NumberObj", "NumberObject"] => (
216             isa => ["Data::Object::Number"],
217             does => ["Data::Object::Role::Number"],
218             can => ["data", "dump"],
219             coerce => ["Str", "Num", "LaxNum", "StrictNum"],
220             );
221              
222             DECLARE ["RegexpObj", "RegexpObject"] => (
223             isa => ["Data::Object::Regexp"],
224             does => ["Data::Object::Role::Regexp"],
225             can => ["data", "dump"],
226             coerce => ["RegexpRef"],
227             );
228              
229             DECLARE ["ScalarObj", "ScalarObject"] => (
230             isa => ["Data::Object::Scalar"],
231             does => ["Data::Object::Role::Scalar"],
232             can => ["data", "dump"],
233             coerce => ["ScalarRef"],
234             );
235              
236             DECLARE ["StrObj", "StrObject", "StringObj", "StringObject"] => (
237             isa => ["Data::Object::String"],
238             does => ["Data::Object::Role::String"],
239             can => ["data", "dump"],
240             coerce => ["Str"],
241             );
242              
243             DECLARE ["UndefObj", "UndefObject"] => (
244             isa => ["Data::Object::Undef"],
245             does => ["Data::Object::Role::Undef"],
246             can => ["data", "dump"],
247             coerce => ["Undef"],
248             );
249              
250             DECLARE ["AnyObj", "AnyObject", "UniversalObj", "UniversalObject"] => (
251             isa => ["Data::Object::Universal"],
252             does => ["Data::Object::Role::Universal"],
253             can => ["data", "dump"],
254             coerce => ["Any"],
255             );
256              
257             1;
258              
259             __END__