File Coverage

blib/lib/DDC/Any.pm
Criterion Covered Total %
statement 83 93 89.2
branch 34 54 62.9
condition 11 26 42.3
subroutine 12 12 100.0
pod 0 4 0.0
total 140 189 74.0


line stmt bran cond sub pod time code
1             #-*- Mode: CPerl -*-
2              
3             ## File: DDC::Any.pm
4             ## Author: Bryan Jurish
5             ## Description:
6             ## + DDC Query utilities: wrap DDC::XS or DDC::PP
7             ##======================================================================
8              
9             package DDC::Any;
10 17     17   1680681 use DDC::Concordance;
  17         56  
  17         648  
11 17     17   119 use Carp qw(carp confess);
  17         37  
  17         816  
12 17     17   98 use strict;
  17         39  
  17         8353  
13              
14             our @ISA = qw();
15             our $VERSION = $DDC::Concordance::VERSION;
16              
17             ##======================================================================
18             ## Globals
19              
20             our $WHICH = undef;
21             our ($COMPILER);
22              
23             ##======================================================================
24             ## Overrides
25              
26             ## $CQuery = DDC::Any->parse($qstr)
27             ## + convenience wrapper, re-implemented here b/c it uses the __PACKAGE__ keyword
28             sub parse {
29 103 50   103 0 46462 shift if (UNIVERSAL::isa($_[0],__PACKAGE__));
30 103 100       309 $COMPILER = DDC::Any::CQueryCompiler->new() if (!$COMPILER);
31 103         331 return $COMPILER->ParseQuery(@_);
32             }
33              
34             ## $version = DDC::Any->library_version()
35             ## + returns extended version string
36             sub library_version {
37 2 50   2 0 1713 return undef if (!defined($WHICH));
38 2         31 return "$WHICH / " . $WHICH->can('library_version')->();
39             }
40              
41              
42             ## $obj = DDC::Any::Object->new(@args)
43             ## + override calls "real" subclass new() method
44             package DDC::Any::Object;
45             sub new {
46 31     31   2665 my $that = shift;
47 31   33     156 my $class = ref($that)||$that;
48 31         225 $class =~ s/^DDC::Any::/${DDC::Any::WHICH}::/;
49 31         232 return $class->new(@_);
50             };
51              
52             ##======================================================================
53             ## Import
54             package DDC::Any;
55              
56             ##--------------------------------------------------------------
57             ## $bool = PACKAGE->have_xs()
58             ## + attempts to load DDC::XS, and returns true if it is available in a suitable version
59             our $MIN_XS_VERSION = 0.21;
60             sub have_xs {
61 12 50   12 0 693 shift if (UNIVERSAL::isa($_[0],__PACKAGE__));
62 12 50   12   2353 eval "use DDC::XS;" if (!$INC{'DDC/XS.pm'});
  0         0  
  0         0  
  12         776  
63 12 50       114 return 0 if (!$INC{'DDC/XS.pm'});
64 0   0     0 (my $xs_version = ($DDC::XS::VERSION||0)) =~ s/[^0-9\.]//g;
65 0   0     0 return ($xs_version && $xs_version >= $MIN_XS_VERSION);
66             }
67              
68             ##--------------------------------------------------------------
69             ## \%dst_stash = mapstash($src,$dst,%opts)
70             ## + %opts:
71             ## inherit => $which ##-- tweak inheritance; (0:don't, >0:$dst ISA $src, <0:$src ISA $dst)
72             ## deep => $bool, ##-- walk package tree? (default: true)
73             ## ignore => $re, ##-- ignore fully qualified source-symbols matching $re (default:none)
74             sub mapstash {
75 11     11 0 71 my ($src0,$dst0,%opts) = @_;
76 11   50     60 my $inherit = $opts{inherit} || 0;
77 11 50       50 my $deep = exists($opts{deep}) ? $opts{deep} : 1;
78 11         29 my $ignore = $opts{ignore};
79 11 50       43 $ignore = qr{$ignore} if (!ref($ignore));
80 11         46 my @queue = ([$src0,$dst0]);
81 17     17   136 no strict 'refs';
  17         37  
  17         13149  
82 11         54 while (@queue) {
83 748         1519 my ($src,$dst) = @{shift @queue};
  748         1739  
84             #print STDERR "mapping $src -> $dst\n";
85 748         1227 my $src_stash = \%{"${src}::"};
  748         2174  
86 748         1018 my $dst_stash = \%{"${dst}::"};
  748         1565  
87 748         4529 while (my ($src_sym,$src_glob)=each %$src_stash) {
88 8668 100 66     54337 if ($ignore && "${src}::${src_sym}" =~ $ignore) {
89             ##-- ignored
90 649         3453 next;
91             }
92 8019 100 66     26115 if ($deep && $src_sym =~ /::$/) {
    100          
93             ##-- sub-package
94 737         2053 $src_sym =~ s/::$//;
95 737         1026 $dst_stash->{"${src_sym}::"} = *{"${dst}::${src_sym}::"};
  737         4782  
96 737         4550 push(@queue, ["${src}::${src_sym}","${dst}::${src_sym}"]);
97             }
98             elsif ($src_sym eq 'ISA') {
99             ##-- copy inheritance
100 726         1021 @{"${dst}::ISA"} = map {(my $isa=$_)=~s/^\Q${src0}\E\b/${dst0}/; $isa} @{"${src}::ISA"};
  726         28283  
  704         4391  
  704         2367  
  726         2519  
101             }
102             else {
103             ##-- anything else: copy
104 6556         51813 $dst_stash->{$src_sym} = $src_glob;
105             }
106             }
107              
108 748 50       2353 if ($inherit > 0) {
    50          
109 0         0 push(@{"${dst}::ISA"}, $src); ##-- tweak inheritance: $dst ISA $src
  0         0  
110             } elsif ($inherit < 0) {
111 748         1061 push(@{"${src}::ISA"}, $dst); ##-- tweak inheritance: $src ISA $dst
  748         33181  
112             }
113             }
114              
115 11         36 return \%{"${dst0}::"};
  11         63  
116             }
117              
118              
119             ##--------------------------------------------------------------
120             ## import guts
121              
122             ## $WHICH = PACKAGE->import(@requests)
123             sub import {
124 18     18   278 my $that = shift;
125              
126             ##-- parse user request
127 18         36 my $which = $WHICH;
128 18         99 my %alias = ('xs'=>'DDC::XS', pp=>'DDC::PP', any=>'', default=>'');
129 18         51 foreach (@_) {
130 11 50       171 if (/^:(\S+)$/i) {
131 11         54 $which = lc($1);
132 11 100       49 $which = $alias{$which} if (exists($alias{$which}));
133             }
134             }
135              
136             ##-- sanity check(s)
137 18 100       78 if ($which) {
138 11 100       2134 return $WHICH if ($which eq 'none'); ##-- don't map back-end (yet)
139 4 50       16 if ($WHICH) {
140 0 0       0 carp(__PACKAGE__ . "::import() cannot override current back-end '$WHICH' -- ignoring user request '$which'")
141             if ($WHICH ne $which);
142 0         0 return $WHICH;
143             }
144             }
145              
146             ##-- be safe anyways
147 11         25 undef $WHICH;
148 11         21 undef $COMPILER;
149              
150             ##-- load back-end
151 11 100 66     65 if (!$which || $which eq 'DDC::XS') {
152 7 50       27 if (!$that->have_xs()) {
153 7 50 50     82 die("DDC::Any::import(): failed to load DDC::XS back-end: $@") if (($which||'') eq 'DDC::XS');
154             } else {
155 0         0 $which = 'DDC::XS';
156             }
157             }
158 11 50 66     85 if (!$which || $which eq 'DDC::PP') {
159 11 100   10   980 eval "use DDC::PP;" if (!$INC{'DDC/PP.pm'});
  10         4823  
  10         55  
  10         401  
160 11 50       76 if (!$INC{'DDC/PP.pm'}) {
161 0 0 0     0 die("DDC::Any::import(): failed to load DDC::PP back-end: $@") if (($which||'') eq 'DDC::PP');
162             } else {
163 11         35 $which = 'DDC::PP';
164             }
165             }
166 11 50       45 die("DDC::Any::import(): failed to load any back-end") if (!$which);
167              
168             ##-- map back-end
169 11         29 $WHICH = $which;
170 11         628 mapstash($WHICH=>'DDC::Any', deep=>1, inherit=>-1, ignore=>qr{${WHICH}::(?:VERSION|COMPILER|parse|import|library_version|.*::new)$});
171 11         15951 return $WHICH;
172             }
173              
174              
175             1; ##-- be happy
176              
177             __END__