File Coverage

blib/lib/Hustle/Table.pm
Criterion Covered Total %
statement 67 70 95.7
branch 13 18 72.2
condition 4 6 66.6
subroutine 15 15 100.0
pod 0 4 0.0
total 99 113 87.6


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