File Coverage

blib/lib/KiokuDB/TypeMap.pm
Criterion Covered Total %
statement 2 4 50.0
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 4 6 66.6


line stmt bran cond sub pod time code
1             package KiokuDB::TypeMap;
2             BEGIN {
3 1     1   28847 $KiokuDB::TypeMap::AUTHORITY = 'cpan:NUFFIN';
4             }
5             $KiokuDB::TypeMap::VERSION = '0.57';
6 1     1   1543 use Moose;
  0            
  0            
7             # ABSTRACT: Class to collapsing/expanding logic.
8              
9             use Carp qw(croak);
10             use Try::Tiny;
11              
12             use KiokuDB::TypeMap::Entry;
13             use KiokuDB::TypeMap::Entry::Alias;
14              
15             use namespace::clean -except => 'meta';
16              
17             with qw(KiokuDB::Role::TypeMap);
18              
19             has [qw(entries isa_entries)] => (
20             #isa => "HashRef[KiokuDB::TypeMap::Entry|KiokuDB::TypeMap::Entry::Alias]", # dog slow regex
21             is => "ro",
22             lazy_build => 1,
23             );
24              
25             sub _build_entries { +{} }
26             sub _build_isa_entries { +{} }
27              
28             has [qw(all_entries all_isa_entries)] => (
29             #isa => "HashRef[KiokuDB::TypeMap::Entry|KiokuDB::TypeMap::Entry::Alias]", # dog slow regex
30             is => "ro",
31             lazy_build => 1,
32             );
33              
34             has all_isa_entry_classes => (
35             isa => "ArrayRef[Str]",
36             is => "ro",
37             lazy_build => 1,
38             );
39              
40             has includes => (
41             isa => "ArrayRef[KiokuDB::TypeMap]",
42             is => "ro",
43             lazy_build => 1,
44             );
45              
46             sub _build_includes { [] }
47              
48             my %loaded;
49              
50             sub resolve {
51             my ( $self, $class ) = @_;
52              
53             # if we're linking the class might not be loaded yet
54             unless ( $loaded{$class}++ ) {
55             ( my $pmfile = $class . ".pm" ) =~ s{::}{/}g;
56              
57             try {
58             require $pmfile;
59             } catch {
60             croak $_ unless /Can't locate \Q$pmfile\E in \@INC/;
61             };
62             }
63              
64             # if this is an anonymous class, redo the lookup using a single named
65             # ancestor
66             if ( my $meta = Class::MOP::get_metaclass_by_name($class) ) {
67             if ( $meta->is_anon_class ) {
68             my $ancestor = $meta;
69              
70             search: {
71             my @super = $ancestor->superclasses;
72              
73             if ( @super == 1 ) {
74             $ancestor = Class::MOP::get_metaclass_by_name($super[0]);
75             if ( $ancestor->is_anon_class ) {
76             redo search;
77             }
78             } else {
79             croak "Cannot resolve anonymous class with multiple inheritence: $class";
80             }
81             }
82              
83             return $self->resolve( $ancestor->name );
84             }
85             }
86              
87              
88             if ( my $entry = $self->all_entries->{$class} || $self->all_isa_entries->{$class} ) {
89             return $self->resolve_entry( $entry );
90             } else {
91             foreach my $superclass ( @{ $self->all_isa_entry_classes } ) {
92             if ( $class->isa($superclass) ) {
93             return $self->resolve_entry( $self->all_isa_entries->{$superclass} );
94             }
95             }
96             }
97              
98             return;
99             }
100              
101             sub resolve_entry {
102             my ( $self, $entry ) = @_;
103              
104             if ( $entry->isa("KiokuDB::TypeMap::Entry::Alias") ) {
105             return $self->resolve( $entry->to );
106             } else {
107             return $entry;
108             }
109             }
110              
111             sub BUILD {
112             my $self = shift;
113              
114             # verify that there are no conflicting internal definitions
115             my $reg = $self->entries;
116             foreach my $key ( keys %{ $self->isa_entries } ) {
117             if ( exists $reg->{$key} ) {
118             croak "isa entry $key already present in plain entries";
119             }
120             }
121              
122             # Verify that there are no conflicts between the includesd type maps
123             my %seen;
124             foreach my $map ( @{ $self->includes } ) {
125             foreach my $key ( keys %{ $map->all_entries } ) {
126             if ( $seen{$key} ) {
127             croak "entry $key found in $map conflicts with $seen{$key}";
128             }
129              
130             $seen{$key} = $map;
131             }
132              
133             foreach my $key ( keys %{ $map->all_isa_entries } ) {
134             if ( $seen{$key} ) {
135             croak "isa entry $key found in $map conflicts with $seen{$key}";
136             }
137              
138             $seen{$key} = $map;
139             }
140             }
141             }
142              
143             sub _build_all_entries {
144             my $self = shift;
145              
146             return {
147             map { %$_ } (
148             ( map { $_->all_entries } @{ $self->includes } ),
149             $self->entries,
150             ),
151             };
152             }
153              
154             sub _build_all_isa_entries {
155             my $self = shift;
156              
157             return {
158             map { %$_ } (
159             ( map { $_->all_isa_entries } @{ $self->includes } ),
160             $self->isa_entries,
161             ),
162             };
163             }
164              
165             sub _build_all_isa_entry_classes {
166             my $self = shift;
167              
168             return [
169             sort { !$a->isa($b) <=> !$b->isa($a) } # least derived first
170             keys %{ $self->all_isa_entries }
171             ];
172             }
173              
174             __PACKAGE__->meta->make_immutable;
175              
176             __PACKAGE__
177              
178             __END__
179              
180             =pod
181              
182             =encoding UTF-8
183              
184             =head1 NAME
185              
186             KiokuDB::TypeMap - Class to collapsing/expanding logic.
187              
188             =head1 VERSION
189              
190             version 0.57
191              
192             =head1 SYNOPSIS
193              
194             use KiokuDB::TypeMap;
195              
196             KiokuDB::TypeMap->new(
197             entries => {
198             'Foo' => KiokuDB::TypeMap::Entry::Naive->new,
199             },
200             isa_entries => {
201             'My::Class' => KiokuDB::TypeMap::Entry::Naive->new,
202             },
203             includes => [
204             $typemap_foo,
205             $typemap_bar,
206             ],
207             );
208              
209             =head1 DESCRIPTION
210              
211             The L<KiokuDB> typemap maps classes to L<KiokuDB::TypeMap::Entry> objects.
212              
213             The mapping is by class, and entries can be keyed normally (using
214             C<ref $object> equality) or by filtering on C<< $object->isa($class) >>
215             (C<isa_entries>).
216              
217             =head1 ATTRIBUTES
218              
219             =over 4
220              
221             =item entries
222              
223             A hash of normal entries.
224              
225             =item isa_entries
226              
227             A hash of C<< $object->isa >> based entries.
228              
229             =item includes
230              
231             A list of parent typemaps to inherit entries from.
232              
233             =back
234              
235             =head1 METHODS
236              
237             =over 4
238              
239             =item resolve $class
240              
241             Given a class returns the C<KiokuDB::TypeMap::Entry> object corresponding to
242             that class.
243              
244             Called by L<KiokuDB::TypeMap::Resolver>
245              
246             =item resolve_entry $entry
247              
248             If the entry is an alias, it will be resolved recursively, and simply returned
249             otherwise.
250              
251             =item all_entries
252              
253             Returns the merged C<entries> from this typemap and all the included typemaps.
254              
255             =item all_isa_entries
256              
257             Returns the merged C<isa_entries> from this typemap and all the included
258             typemaps.
259              
260             =item all_isa_entry_classes
261              
262             An array reference of all the classes in C<all_isa_entries>, sorted from least
263             derived to most derived.
264              
265             =back
266              
267             =head1 AUTHOR
268              
269             Yuval Kogman <nothingmuch@woobling.org>
270              
271             =head1 COPYRIGHT AND LICENSE
272              
273             This software is copyright (c) 2014 by Yuval Kogman, Infinity Interactive.
274              
275             This is free software; you can redistribute it and/or modify it under
276             the same terms as the Perl 5 programming language system itself.
277              
278             =cut