File Coverage

blib/lib/KiokuDB/TypeMap.pm
Criterion Covered Total %
statement 80 82 97.5
branch 21 24 87.5
condition 3 3 100.0
subroutine 17 17 100.0
pod 2 3 66.6
total 123 129 95.3


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3             package KiokuDB::TypeMap;
4 28     28   413736 use Moose;
  28         1079688  
  28         175  
5              
6 28     28   139279 use Carp qw(croak);
  28         50  
  28         1548  
7 28     28   131 use Try::Tiny;
  28         37  
  28         1281  
8              
9 28     28   8301 use KiokuDB::TypeMap::Entry;
  28         56  
  28         817  
10 28     28   10406 use KiokuDB::TypeMap::Entry::Alias;
  28         77  
  28         1103  
11              
12 28     28   192 use namespace::clean -except => 'meta';
  28         45  
  28         168  
13              
14             with qw(KiokuDB::Role::TypeMap);
15              
16             has [qw(entries isa_entries)] => (
17             #isa => "HashRef[KiokuDB::TypeMap::Entry|KiokuDB::TypeMap::Entry::Alias]", # dog slow regex
18             is => "ro",
19             lazy_build => 1,
20             );
21              
22 367     367   8515 sub _build_entries { +{} }
23 133     133   3239 sub _build_isa_entries { +{} }
24              
25             has [qw(all_entries all_isa_entries)] => (
26             #isa => "HashRef[KiokuDB::TypeMap::Entry|KiokuDB::TypeMap::Entry::Alias]", # dog slow regex
27             is => "ro",
28             lazy_build => 1,
29             );
30              
31             has all_isa_entry_classes => (
32             isa => "ArrayRef[Str]",
33             is => "ro",
34             lazy_build => 1,
35             );
36              
37             has includes => (
38             isa => "ArrayRef[KiokuDB::TypeMap]",
39             is => "ro",
40             lazy_build => 1,
41             );
42              
43 521     521   11964 sub _build_includes { [] }
44              
45             my %loaded;
46              
47             sub resolve {
48 908     908 1 16386 my ( $self, $class ) = @_;
49              
50             # if we're linking the class might not be loaded yet
51 908 100       2800 unless ( $loaded{$class}++ ) {
52 101         428 ( my $pmfile = $class . ".pm" ) =~ s{::}{/}g;
53              
54             try {
55 101     101   15456 require $pmfile;
56             } catch {
57 40 50   40   1252 croak $_ unless /Can't locate \Q$pmfile\E in \@INC/;
58 101         902 };
59             }
60              
61             # if this is an anonymous class, redo the lookup using a single named
62             # ancestor
63 908 100       45925 if ( my $meta = Class::MOP::get_metaclass_by_name($class) ) {
64 349 100       3468 if ( $meta->is_anon_class ) {
65 35         1126 my $ancestor = $meta;
66              
67 35         188 search: {
68 35         65 my @super = $ancestor->superclasses;
69              
70 35 50       2728 if ( @super == 1 ) {
71 35         127 $ancestor = Class::MOP::get_metaclass_by_name($super[0]);
72 35 50       370 if ( $ancestor->is_anon_class ) {
73 0         0 redo search;
74             }
75             } else {
76 0         0 croak "Cannot resolve anonymous class with multiple inheritence: $class";
77             }
78             }
79              
80 35         872 return $self->resolve( $ancestor->name );
81             }
82             }
83              
84              
85 873 100 100     31637 if ( my $entry = $self->all_entries->{$class} || $self->all_isa_entries->{$class} ) {
86 363         1230 return $self->resolve_entry( $entry );
87             } else {
88 510         677 foreach my $superclass ( @{ $self->all_isa_entry_classes } ) {
  510         13884  
89 2342 100       13373 if ( $class->isa($superclass) ) {
90 94         2443 return $self->resolve_entry( $self->all_isa_entries->{$superclass} );
91             }
92             }
93             }
94              
95 416         1795 return;
96             }
97              
98             sub resolve_entry {
99 457     457 1 706 my ( $self, $entry ) = @_;
100              
101 457 100       3874 if ( $entry->isa("KiokuDB::TypeMap::Entry::Alias") ) {
102 4         106 return $self->resolve( $entry->to );
103             } else {
104 453         1647 return $entry;
105             }
106             }
107              
108             sub BUILD {
109 610     610 0 760 my $self = shift;
110              
111             # verify that there are no conflicting internal definitions
112 610         15916 my $reg = $self->entries;
113 610         690 foreach my $key ( keys %{ $self->isa_entries } ) {
  610         14947  
114 558 100       1464 if ( exists $reg->{$key} ) {
115 1         36 croak "isa entry $key already present in plain entries";
116             }
117             }
118              
119             # Verify that there are no conflicts between the includesd type maps
120 609         864 my %seen;
121 609         601 foreach my $map ( @{ $self->includes } ) {
  609         15195  
122 460         453 foreach my $key ( keys %{ $map->all_entries } ) {
  460         11218  
123 519 100       895 if ( $seen{$key} ) {
124 2         37 croak "entry $key found in $map conflicts with $seen{$key}";
125             }
126              
127 517         768 $seen{$key} = $map;
128             }
129              
130 458         583 foreach my $key ( keys %{ $map->all_isa_entries } ) {
  458         10944  
131 517 100       954 if ( $seen{$key} ) {
132 1         16 croak "isa entry $key found in $map conflicts with $seen{$key}";
133             }
134              
135 516         3684 $seen{$key} = $map;
136             }
137             }
138             }
139              
140             sub _build_all_entries {
141 605     605   740 my $self = shift;
142              
143             return {
144 1052         15833 map { %$_ } (
  447         9651  
145 605         609 ( map { $_->all_entries } @{ $self->includes } ),
  605         13812  
146             $self->entries,
147             ),
148             };
149             }
150              
151             sub _build_all_isa_entries {
152 601     601   658 my $self = shift;
153              
154             return {
155 1048         15993 map { %$_ } (
  447         9911  
156 601         619 ( map { $_->all_isa_entries } @{ $self->includes } ),
  601         13503  
157             $self->isa_entries,
158             ),
159             };
160             }
161              
162             sub _build_all_isa_entry_classes {
163 143     143   213 my $self = shift;
164              
165             return [
166 790         7499 sort { !$a->isa($b) <=> !$b->isa($a) } # least derived first
  143         3480  
167 143         185 keys %{ $self->all_isa_entries }
168             ];
169             }
170              
171             __PACKAGE__->meta->make_immutable;
172              
173             __PACKAGE__
174              
175             __END__
176              
177             =pod
178              
179             =head1 NAME
180              
181             KiokuDB::TypeMap - Class to collapsing/expanding logic.
182              
183             =head1 SYNOPSIS
184              
185             use KiokuDB::TypeMap;
186              
187             KiokuDB::TypeMap->new(
188             entries => {
189             'Foo' => KiokuDB::TypeMap::Entry::Naive->new,
190             },
191             isa_entries => {
192             'My::Class' => KiokuDB::TypeMap::Entry::Naive->new,
193             },
194             includes => [
195             $typemap_foo,
196             $typemap_bar,
197             ],
198             );
199              
200             =head1 DESCRIPTION
201              
202             The L<KiokuDB> typemap maps classes to L<KiokuDB::TypeMap::Entry> objects.
203              
204             The mapping is by class, and entries can be keyed normally (using
205             C<ref $object> equality) or by filtering on C<< $object->isa($class) >>
206             (C<isa_entries>).
207              
208             =head1 ATTRIBUTES
209              
210             =over 4
211              
212             =item entries
213              
214             A hash of normal entries.
215              
216             =item isa_entries
217              
218             A hash of C<< $object->isa >> based entries.
219              
220             =item includes
221              
222             A list of parent typemaps to inherit entries from.
223              
224             =back
225              
226             =head1 METHODS
227              
228             =over 4
229              
230             =item resolve $class
231              
232             Given a class returns the C<KiokuDB::TypeMap::Entry> object corresponding tot
233             hat class.
234              
235             Called by L<KiokuDB::TypeMap::Resover>
236              
237             =item resolve_entry $entry
238              
239             If the entry is an alias, it will be resolved recursively, and simply returned
240             otherwise.
241              
242             =item all_entries
243              
244             Returns the merged C<entries> from this typemap and all the included typemaps.
245              
246             =item all_isa_entries
247              
248             Returns the merged C<isa_entries> from this typemap and all the included
249             typemaps.
250              
251             =item all_isa_entry_classes
252              
253             An array reference of all the classes in C<all_isa_entries>, sorted from least
254             derived to most derived.
255              
256             =back