File Coverage

blib/lib/Hustle/Table.pm
Criterion Covered Total %
statement 61 63 96.8
branch 13 16 81.2
condition 4 6 66.6
subroutine 13 13 100.0
pod 0 4 0.0
total 91 102 89.2


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