File Coverage

blib/lib/Hustle/Table.pm
Criterion Covered Total %
statement 64 66 96.9
branch 13 16 81.2
condition 4 6 66.6
subroutine 14 14 100.0
pod 0 4 0.0
total 95 106 89.6


line stmt bran cond sub pod time code
1             package Hustle::Table;
2 4     4   3028 use version; our $VERSION=version->declare("v0.6.0");
  4         6858  
  4         20  
3              
4 4     4   320 use strict;
  4         9  
  4         62  
5 4     4   15 use warnings;
  4         6  
  4         73  
6              
7 4     4   1585 use Template::Plex;
  4         60117  
  4         110  
8              
9             #use feature "refaliasing";
10 4     4   29 no warnings "experimental";
  4         8  
  4         135  
11              
12 4     4   18 use Carp qw;
  4         9  
  4         176  
13              
14              
15              
16 4     4   19 use constant DEBUG=>0;
  4         5  
  4         208  
17              
18             #constants for entry fields
19 4     4   19 use enum (qw);
  4         7  
  4         23  
20              
21             #Public API
22             #
23             sub new {
24 4   50 4 0 2683 my $class=shift//__PACKAGE__;
25 4   50     22 my $default=shift//undef;
26 4         19 bless [[undef,$default, "exact",1]],$class; #Prefill with default handler
27             }
28              
29             sub add {
30 16     16 0 1579 my ($self,@list)=@_;
31 16         19 my $entry;
32             my $rem;
33 16         31 for my $item (@list){
34 20         39 for(ref $item){
35 20 100       56 if(/ARRAY/){
    100          
36             #warn $item->$*;
37 5         7 $entry=$item;
38 5 50       14 croak "Incorrect number of items in dispatch vector. Should be 3" unless $entry->@* == 3;
39             }
40              
41             elsif(/HASH/){
42 12         32 $entry=[$item->@{qw}];
43             }
44              
45             else{
46 3 100       9 if(@list>=4){ #Flat hash/list key pairs passed in sub call
    50          
47 2         5 my %item=@list;
48 2         5 $entry=[@item{qw}];
49 2         4 $rem =1;
50             }
51             elsif(@list==2){ #Flat list of matcher and sub. Assume regex
52             # matcher=>sub
53 1         3 $entry=[$list[0],$list[1],undef];
54 1         2 $rem=1;
55             }
56             else{
57            
58             }
59             }
60              
61             }
62              
63             #croak "matcher not specified" unless defined $entry->[matcher_];
64              
65 20 100       34 if(defined $entry->[matcher_]){
66             #Append to the end of the normal matching list
67 19         66 splice @$self, @$self-1,0, $entry;
68             }
69             else {
70             #No matcher, thus this used as the default
71 1         4 $self->set_default($entry->[value_]);
72             #$self->[$self->@*-1]=$entry;
73             }
74 20 100       85 last if $rem;
75             }
76             }
77              
78              
79             #overwrites the default handler.
80             sub set_default {
81 2     2 0 9 my ($self,$sub)=@_;
82 2         5 my $entry=[undef,$sub,"exact",1];
83 2         7 $self->[@$self-1]=$entry;
84             }
85              
86              
87              
88             sub prepare_dispatcher{
89 3     3 0 254 my $self=shift;
90 3         7 my %options=@_;
91 3   100     16 my $cache=$options{cache}//{};
92 3         9 $self->_prepare_online_cached($cache);
93             }
94              
95             #
96             #Private API
97             #
98              
99             sub _prepare_online_cached {
100 3     3   7 my $table=shift; #self
101 3         4 my $cache=shift;
102 3 50       11 if(ref $cache ne "HASH"){
103 0         0 carp "Cache provided isn't a hash. Using internal cache with no size limits";
104 0         0 $cache={};
105             }
106              
107 3         6 my $sub_template=
108             '
109             @{[do {
110             my $do_capture;
111             my $d="";
112              
113             my $pack=ref $item->[Hustle::Table::matcher_];
114             my $is_regex;
115              
116             if($pack){
117             $is_regex= ($pack eq "Regexp" or grep /^Regexp$/, @{$pack."::ISA"});
118             }
119              
120              
121             for($item->[Hustle::Table::type_]){
122             if($is_regex){
123             $d.=\'($input=~$table->[\'. $index .\'][Hustle::Table::matcher_] )\';
124             $do_capture=1;
125             }
126             elsif(ref($item->[Hustle::Table::matcher_]) eq "CODE"){
127              
128             $d.=\'($table->[\'.$index.\'][Hustle::Table::matcher_]->($input, ($table->[\'.$index.\'][1])))\';
129             }
130             elsif(/exact/){
131             $d.=\'($input eq "\'. $item->[Hustle::Table::matcher_]. \'")\';
132             }
133             elsif(/begin/){
134             $d.=\'(index($input, "\' . $item->[Hustle::Table::matcher_]. \'")==0)\';
135             }
136             elsif(/end/){
137             $d.=\'(index(reverse($input), reverse("\'. $item->[Hustle::Table::matcher_].\'"))==0)\';
138             }
139             elsif(/numeric/){
140             $d.=\'(\' . $item->[Hustle::Table::matcher_] . \'== $input)\';
141             }
142             else{
143             #assume a regex - even if a basic string
144             $item->[Hustle::Table::matcher_]=qr{$item->[Hustle::Table::matcher_]};
145             $item->[Hustle::Table::type_]=undef;
146             $do_capture=1;
147             $d.=\'($input=~m{\' . $item->[Hustle::Table::matcher_].\'})\';
148             }
149             }
150              
151              
152             if($do_capture){
153             $d.=\' and return ($cache->{$input}=$table->[\'.$index.\'], [@{^CAPTURE}]);\';
154             }
155             else {
156             $d.=\' and return ($cache->{$input}=$table->[\'.$index.\']);\';
157              
158             }
159             $d;
160             }]}
161             ';
162              
163 3         6 my $template=
164             '
165             my \$input;
166             my \$entry;
167             no warnings "numeric";
168             sub {
169             \$input=shift;
170             \$entry=\$cache->{\$input};
171             #Locate cached regex types, perform capture, and return
172             #Locate cached non regex types and return
173             #\$entry
174             \$entry->[Hustle::Table::type_]
175             ? return \$entry
176             : (\$input=~ \$entry->[Hustle::Table::matcher_])
177             and return (\$entry, [\@{^CAPTURE}])
178              
179              
180             if \$entry;
181              
182              
183             #Build the logic for matcher entry in order of listing
184             @{[do {
185             my $index=0;
186             my $base={index=>0, item=>undef};
187             my $sub=$self->load([$sub], $base);
188             map {
189             $base->{index}=$_;
190             $base->{item}=$table->[$_];
191             my $s=$sub->render;
192             $s;
193             } 0..$table->@*-2;
194             }]}
195              
196              
197             #If we get here we cache and return the default matcher
198             \$cache->{\$input}=\$table->[\@\$table-1];
199             } ';
200              
201 3         29 my $top_level=Template::Plex->load([$template],{table=>$table, cache=>$cache, sub=>$sub_template});
202 3         4529 my $s=$top_level->render;
203             #say STDERR $s;
204             #my $line=1;
205             #print map $_."\n", split "\n", $s;
206 3     3   20 my $ss=eval $s;
  3         6  
  3         503  
  3         7091  
207             #print $@;
208 3         16 $ss;
209             }
210              
211             1;
212             __END__