File Coverage

blib/lib/M3/ServerView/View.pm
Criterion Covered Total %
statement 118 122 96.7
branch 39 42 92.8
condition 10 12 83.3
subroutine 22 24 91.6
pod 6 6 100.0
total 195 206 94.6


line stmt bran cond sub pod time code
1             package M3::ServerView::View;
2              
3 7     7   74968 use strict;
  7         14  
  7         332  
4 7     7   41 use warnings;
  7         12  
  7         269  
5              
6 7     7   37 use Carp qw(croak);
  7         11  
  7         582  
7 7     7   7215 use POSIX qw(strftime);
  7         59051  
  7         53  
8 7     7   9832 use Scalar::Util qw(refaddr looks_like_number);
  7         16  
  7         716  
9              
10 7     7   8659 use M3::ServerView::Parser;
  7         30  
  7         282  
11 7     7   5195 use M3::ServerView::ResultSet;
  7         49  
  7         12183  
12              
13             my %Entries;
14             my %Connection;
15             my %Response_time;
16             my %Request_time;
17             my %Url;
18              
19             # Constructor
20             sub new {
21 7     7 1 3459 my ($pkg, $connection, $url) = @_;
22              
23 7         16 my $self = bless \do { my $v; }, $pkg;
  7         36  
24              
25 7         53 $Url{refaddr $self} = $url;
26 7 100       37 $Connection{refaddr $self} = $connection if $connection;
27              
28 7         49 $self->reload();
29            
30 7         32 return $self;
31             }
32              
33             # Deconstructor
34             sub DESTROY {
35 7     7   22346 my ($self) = @_;
36 7         50 delete $Connection{refaddr $self};
37 7         247 delete $Entries{refaddr $self};
38 7         49 delete $Response_time{refaddr $self};
39 7         58 delete $Request_time{refaddr $self};
40 7         662 delete $Url{refaddr $self};
41             }
42              
43             sub connection {
44 52     52 1 95 my ($self) = @_;
45 52         344 return $Connection{refaddr $self};
46             }
47              
48             # Page loading
49             sub reload {
50 7     7 1 16 my ($self) = @_;
51 7         41 $self->_load();
52             }
53              
54             sub _load {
55 7     7   19 my ($self) = @_;
56              
57             # Clear entries
58 7         37 $Entries{refaddr $self} = [];
59              
60 7 100       50 return unless $Url{refaddr $self};
61            
62             # Fetch page
63 3         35 $Request_time{refaddr $self} = CORE::time;
64 3         30 my ($content, $response_time) = $self->connection->_get_page_contents($Url{refaddr $self});
65 3         313 $Response_time{refaddr $self} = sprintf("%.6f", $response_time);
66            
67             # Parser contents
68 3         41 $self->_parse($content);
69            
70 3         14 1;
71             }
72              
73             # Generic fallback parser that uses M3::ServerView::Parser;
74             sub _entry_class {
75 0     0   0 my ($self) = @_;
76 0         0 croak "View class '", ref $self, "' doesn't override _entry_class()";
77             }
78              
79             sub _entry_columns {
80 0     0   0 my ($self) = @_;
81 0         0 croak "View class '", ref $self, "' doesn't override _entry_columns()";
82             }
83              
84             sub _parse {
85 3     3   9 my ($self, $content) = @_;
86 3         32 my $parser = M3::ServerView::Parser->new($self);
87 3         18 $parser->parse($content);
88             }
89              
90              
91             sub _entries {
92 16     16   36 my ($self) = @_;
93 16         85 return $Entries{refaddr $self};
94             }
95              
96             sub _add_entry {
97 75     75   379 my ($self, $entry) = @_;
98 75         79 push @{$Entries{refaddr $self}}, $entry;
  75         439  
99             }
100              
101             sub response_time {
102 1     1 1 12 my ($self) = @_;
103 1         14 return $Response_time{refaddr $self};
104             }
105              
106             sub request_time {
107 2     2 1 5 my ($self, $format) = @_;
108 2         9 my $time = $Request_time{refaddr $self};
109              
110 2 100 66     13 if ($format && $format eq "timestamp") {
111 1         7 return $time;
112             }
113            
114 1         86 return strftime("%Y-%m-%d %H:%M:%S", localtime($time));
115             }
116              
117             sub search {
118 15     15 1 2262 my ($self, $query, $options) = @_;
119            
120 15 100       73 $options = {} unless ref $options eq "HASH";
121            
122 15         22 my @matches;
123            
124             # Build rules
125 15         60 my $case_sensitive = $options->{case_sensitive};
126 15 100       120 $case_sensitive = 0 unless defined $case_sensitive;
127 15         68 my @matchers = _build_matchers($query, $case_sensitive);
128            
129 15         85 CHECK_ENTRIES: for my $entry (@{$self->_entries}) {
  15         52  
130             # Check if entry matches all matchers and
131             # break at the first that doens't
132 156   100     261 !($_->($entry)) && next CHECK_ENTRIES for @matchers;
133            
134 113         174 push @matches, $entry;
135             }
136            
137             # Sort results
138 15 100       52 if (exists $options->{order_by}) {
139 7     7   49 no warnings;
  7         14  
  7         8300  
140 5         11 my $key = $options->{order_by};
141 5   100     25 my $sort_as = $options->{sort_as} || "";
142 5   100     24 my $sort_order = lc($options->{sort_order}) || "asc";
143 5 100       89 croak q{Sort order must be either 'asc' or 'desc'} unless $sort_order =~ /^asc|desc$/;
144 4 100       14 if ($sort_order eq "asc") {
145 2 100       9 if ($sort_as eq "text") {
146 1         7 @matches = sort { $a->{$key} cmp $b->{$key} } @matches;
  7         18  
147             }
148             else {
149 1         8 @matches = sort { $a->{$key} <=> $b->{$key} } @matches;
  7         18  
150             }
151             }
152             else {
153 2 100       7 if ($sort_as eq "text") {
154 1         4 @matches = sort { $b->{$key} cmp $a->{$key} } @matches;
  9         18  
155             }
156             else {
157 1         11 @matches = sort { $b->{$key} <=> $a->{$key} } @matches;
  9         18  
158             }
159             }
160             }
161            
162 14         88 my $rs = M3::ServerView::ResultSet->new(\@matches);
163 14         79 return $rs;
164             }
165              
166             my %Op = (
167             "==" => sub { $_[0] == $_[1]; },
168             "!=" => sub { $_[0] != $_[1]; },
169             "<" => sub { $_[0] < $_[1]; },
170             ">" => sub { $_[0] > $_[1]; },
171             "<=" => sub { $_[0] <= $_[1]; },
172             ">=" => sub { $_[0] >= $_[1]; },
173              
174             "eq" => sub { $_[0] eq $_[1]; },
175             "ne" => sub { $_[0] ne $_[1]; },
176             "lt" => sub { $_[0] lt $_[1]; },
177             "gt" => sub { $_[0] gt $_[1]; },
178             "le" => sub { $_[0] le $_[1]; },
179             "ge" => sub { $_[0] ge $_[1]; },
180            
181             "like" => sub { $_[0] =~ m{ $_[1] }xi; },
182             );
183              
184             my %Op_transform = (
185             "==" => "eq",
186             "!=" => "ne",
187             ">" => "gt",
188             "<" => "lt",
189             ">=" => "ge",
190             "<=" => "le",
191             );
192              
193             sub _build_matchers {
194 20     20   841 my ($rules, $case_sensitive) = @_;
195              
196 20 100       58 $rules = {} unless $rules;
197            
198 20         32 my @matchers;
199              
200 20         88 while (my ($key, $rule) = each %$rules) {
201 10         18 my ($op, $value) = ("==", $rule);
202            
203 10 100       32 if (ref $rule eq "ARRAY") {
204 2         6 ($op, $value) = @$rule;
205             }
206              
207 10 50       30 croak "Don't know how to handle op '${op}'" if !exists $Op{$op};
208            
209 10 100 66     65 if (!looks_like_number($value) && exists $Op_transform{$op}) {
210 4         10 $op = $Op_transform{$op};
211             }
212              
213 10         12 my $matcher;
214            
215             # Basicly the same subroutine except we lowercase the contents if we
216             # perform a case-insensitive match
217 10 100       25 if ($case_sensitive) {
218             $matcher = sub {
219 6     6   7 my $entry = shift;
220 6 100       22 return 0 unless exists $entry->{$key};
221 2 50       10 return 0 unless defined $entry->{$key};
222 2 100       7 return 0 unless $Op{$op}->($entry->{$key}, $value);
223 1         5 return 1;
224 1         7 };
225             }
226             else {
227             $matcher = sub {
228 56     56   1103 my $entry = shift;
229 56 100       159 return 0 unless exists $entry->{$key};
230 45 50       100 return 0 unless defined $entry->{$key};
231 45 100       155 return 0 unless $Op{$op}->(lc($entry->{$key}), lc($value));
232 13         57 return 1;
233 9         43 };
234             }
235            
236 10         40 push @matchers, $matcher;
237             }
238            
239 20         66 return @matchers;
240             }
241              
242             1;
243             __END__