File Coverage

blib/lib/DDG/Request.pm
Criterion Covered Total %
statement 70 80 87.5
branch 20 20 100.0
condition 4 8 50.0
subroutine 15 19 78.9
pod 2 4 50.0
total 111 131 84.7


line stmt bran cond sub pod time code
1             package DDG::Request;
2             our $AUTHORITY = 'cpan:DDG';
3             # ABSTRACT: A request to DuckDuckGo itself, so the query itself and parameter around the query defining him
4             $DDG::Request::VERSION = '1017';
5 10     10   36266 use Moo;
  10         63025  
  10         45  
6 10     10   9844 use utf8;
  10         29  
  10         59  
7              
8              
9             #
10             # QUERY
11             #
12             ###############################
13              
14              
15             has query_raw => (
16             is => 'ro',
17             required => 1,
18             );
19              
20             my $whitespaces = qr{\s+};
21             my $whitespaces_matches = qr{($whitespaces)};
22             my $whitespaces_dashes = qr{[\s\-]+};
23             my $non_alphanumeric_ascii = qr{[\x00-\x1f\x21-\x2f\x3a-\x40\x5b-\x60\x7b-\x81\x{a7}]+};
24              
25              
26             has query_raw_parts => (
27             is => 'ro',
28             lazy => 1,
29             builder => '_build_query_raw_parts',
30             );
31             sub _build_query_raw_parts {
32             [
33 54     54   826 split(/$whitespaces_matches/,shift->query_raw)
34             ]
35             }
36              
37              
38             has query_parts => (
39             is => 'ro',
40             lazy => 1,
41             builder => '_build_query_parts',
42             );
43             sub _build_query_parts {
44 18     18   132 my $x;
45             [
46 52         198 grep { ( $x += length ) < 500 } # 500 matches the internal query max
47 100         275 grep { ! /$whitespaces/ }
48 107         198 grep { length }
49 18         34 @{shift->query_raw_parts}
  18         244  
50             ]
51             }
52              
53              
54             has query_parts_lc => (
55             is => 'ro',
56             lazy => 1,
57             builder => '_build_query_parts_lc',
58             );
59             sub _build_query_parts_lc {
60             [
61 0         0 map { lc }
62 0     0   0 @{shift->query_parts}
  0         0  
63             ]
64             }
65              
66              
67             has triggers => (
68             is => 'ro',
69             lazy => 1,
70             builder => '_build_triggers',
71             );
72             sub _build_triggers {
73 49     49   11084 my ( $self ) = @_;
74 49         78 my @parts = @{$self->query_raw_parts};
  49         652  
75 49 100       192 return {} if not scalar @parts;
76 48 100       128 my $x = $parts[0] eq '' ? 2 : 0;
77 48         82 my %triggers;
78 48         137 for ($x..(scalar @parts-1)) {
79 261 100       560 unless ($_ % 2) {
80 149         301 $triggers{$_} = $self->generate_triggers($parts[$_]);
81             }
82             }
83 48         295 return \%triggers;
84             }
85              
86              
87             sub generate_triggers {
88 149     149 1 254 my $part = lc $_[1];
89 149         204 my %parts;
90 149         287 ++$parts{$part};
91 149 100       357 $part =~ s/^!//go && ++$parts{$part};
92 149 100       323 $part =~ s/\?$//go && ++$parts{$part};
93             # Look for non-word characters, except single quotes, e.g. can't, John's
94 149 100       361 if ($part =~ /[^\w']/o) {
95             # The split could be part of the if but it would leave single quotes
96             # in the resulting terms.
97 10         36 my @boundary_words = split /\W+/o, $part ;
98 10         39 ++$parts{$_} for @boundary_words;
99 10         27 ++$parts{join('', @boundary_words)};
100 10         29 ++$parts{join(' ', @boundary_words)};
101             }
102 149         603 return [keys %parts];
103             }
104              
105              
106             has remainder => (
107             is => 'rwp',
108             lazy => 1
109             );
110              
111             sub generate_remainder {
112 24     24 1 54 my ( $self, $from_pos, $to_pos ) = @_;
113 24 100       61 $to_pos = $from_pos unless defined $to_pos;
114 24         39 my @query_raw_parts = @{$self->query_raw_parts};
  24         401  
115 24         213 my $max = scalar @query_raw_parts-1;
116 24         39 my $remainder = '';
117 24 100 66     135 if ( $to_pos < $max && ( $from_pos == 0 || ( $from_pos == 2 && $query_raw_parts[0] eq '' ) ) ) {
    100 66        
    100          
118 10         37 $remainder = join('',@query_raw_parts[$to_pos+1..$max]);
119 10         39 $remainder =~ s/^\s//;
120             } elsif ( $max % 2 ? $to_pos == $max-1 : $to_pos == $max ) {
121 11         43 $remainder = join('',@query_raw_parts[0..$from_pos-1]);
122 11         45 $remainder =~ s/\s$//;
123             } else {
124 3         11 my $left_remainder = join('',@query_raw_parts[0..$from_pos-1]);
125 3         10 my $right_remainder = join('',@query_raw_parts[$to_pos+1..$max]);
126 3         10 $left_remainder =~ s/\s$//;
127 3         10 $right_remainder =~ s/^\s//;
128 3         9 $remainder = $left_remainder.' '.$right_remainder;
129             }
130 24         69 $self->_set_remainder($remainder);
131 24         77 return $remainder;
132             }
133              
134              
135             has matched_trigger => (
136             is => 'ro',
137             lazy => 1,
138             builder => 1
139             );
140              
141             sub _build_matched_trigger {
142 0     0   0 my $self = shift;
143              
144 0   0     0 my $r = $self->remainder || '';
145 0         0 my $qr = $self->query_raw;
146 0         0 $qr =~ s/\s*\Q$r\E\s*//i;
147 0         0 return $qr;
148             }
149              
150              
151             has query => (
152             is => 'ro',
153             lazy => 1,
154             builder => '_build_query',
155             );
156             sub _build_query {
157 12     12   35955 join(' ',@{shift->query_parts})
  12         173  
158             }
159              
160              
161             has query_lc => (
162             is => 'ro',
163             lazy => 1,
164             builder => '_build_query_lc',
165             );
166             sub _build_query_lc {
167             lc(shift->query)
168 11     11   5620 }
169              
170              
171             has query_nowhitespace => (
172             is => 'ro',
173             lazy => 1,
174             builder => '_build_query_nowhitespace',
175             );
176             sub _build_query_nowhitespace {
177 11     11   5781 for (shift->query) {
178 11         125 s/$whitespaces//g;
179 11         69 return $_;
180             }
181             }
182              
183              
184             has query_nowhitespace_nodash => (
185             is => 'ro',
186             lazy => 1,
187             builder => '_build_query_nowhitespace_nodash',
188             );
189             sub _build_query_nowhitespace_nodash {
190 11     11   5664 for (shift->query) {
191 11         146 s/$whitespaces_dashes//g;
192 11         68 return $_;
193             }
194             }
195              
196              
197             has query_clean => (
198             is => 'ro',
199             lazy => 1,
200             builder => '_build_query_clean',
201             );
202             sub _build_query_clean {
203 11     11   5692 for (shift->query_lc) {
204 11         121 s/$non_alphanumeric_ascii//g;
205 11         52 s/$whitespaces/ /g;
206 11         72 return $_;
207             }
208             }
209              
210              
211             has words => (
212             is => 'ro',
213             lazy => 1,
214             builder => '_build_words',
215             );
216             sub _build_words {
217             [
218 11     11   212 grep { length }
  29         183  
219             split(/$whitespaces/,shift->query_clean)
220             ]
221             }
222              
223              
224             has wordcount => (
225             is => 'ro',
226             lazy => 1,
227             builder => '_build_wordcount',
228             );
229 11     11   5649 sub _build_wordcount { scalar @{shift->words} }
  11         160  
230              
231              
232             has seen_plugins => (
233             is => 'rw',
234             lazy => 1,
235             builder => '_build_seen_plugins',
236             );
237 41     41   428 sub _build_seen_plugins {[]}
238              
239             #
240             # LANGUAGE / LOCATION / IP
241             #
242             ###############################
243              
244             # DDG::Language TODO
245             has language => (
246             #isa => 'DDG::Language',
247             is => 'ro',
248             predicate => 'has_language',
249             );
250 0     0 0   sub lang { shift->language }
251              
252             has location => (
253             #isa => 'DDG::Location',
254             is => 'ro',
255             predicate => 'has_location',
256             );
257 0     0 0   sub loc { shift->location }
258              
259             1;
260              
261             __END__