File Coverage

blib/lib/Text/Index.pm
Criterion Covered Total %
statement 76 82 92.6
branch 8 12 66.6
condition 1 3 33.3
subroutine 15 15 100.0
pod 9 9 100.0
total 109 121 90.0


line stmt bran cond sub pod time code
1             package Text::Index;
2              
3 2     2   94854 use 5.006;
  2         10  
  2         84  
4 2     2   13 use strict;
  2         4  
  2         85  
5 2     2   10 use warnings;
  2         4  
  2         88  
6 2     2   13 use Carp qw/croak/;
  2         2  
  2         177  
7 2     2   1175 use Params::Util qw/_INSTANCE _ARRAY/;
  2         6805  
  2         1800  
8              
9             our $VERSION = '0.01';
10              
11             =head1 NAME
12              
13             Text::Index - Create indices of a set of pages using a set of keywords
14              
15             =head1 SYNOPSIS
16              
17             use Text::Index;
18             my $index = Text::Index->new;
19            
20             $index->add_page($content);
21             $index->add_pages(@strings);
22             my @pages = $index->pages;
23            
24             # Add keyword with equivalent derivates
25             $index->add_keyword('Hamilton function', 'Hamiltonian');
26             $index->add_keywords([$keyword, @derivates], ...);
27             my @keywords = $i->keywords;
28             # ->keywords returns an array reference for each keyword
29             # (see ->add_keywords syntax)
30            
31             my $index = $i->generate_index;
32            
33             # Or for a single keyword:
34             my @page_list = $i->find_keyword($keyword);
35             my @page_list2 = $i->find_keyword($keyword, @derivates);
36              
37             =head1 DESCRIPTION
38              
39             This (simple) module searches for keywords in a set of pages and creates
40             an index.
41              
42             =head2 EXPORT
43              
44             None.
45              
46             =head2 METHODS
47              
48             This is a list of public methods.
49              
50             =over 2
51              
52             =item new
53              
54             Returns a new Text::Index object. When called on an
55             existing object, C clones that object (deeply).
56              
57             =cut
58              
59             sub new {
60 1     1 1 13 my $proto = shift;
61 1   33     9 my $class = ref($proto)||$proto;
62            
63 1         4 my $self = {
64             keywords => {},
65             pages => [],
66             };
67            
68 1 50       8 if (_INSTANCE($proto, __PACKAGE__)) {
69 0         0 @{$self->{pages}} = $proto->pages;
  0         0  
70 0         0 foreach ($proto->keywords) {
71 0         0 my $clone = {
72             key => $_->[0],
73 0         0 deriv => [ @{ $_->[1] } ],
74             };
75 0         0 $self->{keywords}{$_->[0]} = $clone;
76             }
77             }
78              
79 1         4 return bless $self => $class;
80             }
81              
82             =item add_page
83              
84             Adds a page to the index object. The page is expected to be
85             a string of text passed in as first argument.
86              
87             Returns the Text::Index object for convenience of
88             method chaining.
89              
90             =cut
91              
92             sub add_page {
93 1     1 1 560 my $self = shift;
94 1         3 my $page = shift;
95 1         2 push @{$self->{pages}}, $page;
  1         6  
96 1         4 return $self;
97             }
98              
99             =item add_pages
100              
101             Adds a number of pages to the index object.
102              
103             All arguments are treated as pages. See C.
104              
105             =cut
106              
107             sub add_pages {
108 1     1 1 3 my $self = shift;
109 1         2 push @{$self->{pages}}, @_;
  1         4  
110 1         4 return $self;
111             }
112              
113             =item pages
114              
115             Returns all registered pages as a list.
116              
117             =cut
118              
119             sub pages {
120 1     1 1 3 my $self = shift;
121 1         1 return @{$self->{pages}};
  1         8  
122             }
123              
124             =item add_keyword
125              
126             Adds a new keyword to the index. First argument must be the
127             keyword to add. Following the keyword may be any number of
128             alternative names / string which should be treated to be equal
129             to the keyword.
130              
131             Returns the Text::Index object for convenience.
132              
133             =cut
134              
135             sub add_keyword {
136 4     4 1 501 my $self = shift;
137 4         5 my $keyword = shift;
138 4         7 my @deriv = @_;
139              
140 4 50       13 croak("add_keyword requires a keyword as first argument.")
141             if not defined $keyword;
142            
143 4         33 $self->{keywords}{$keyword} = {
144             key => $keyword, deriv => \@deriv,
145             };
146            
147 4         15 return $self;
148             }
149              
150             =item add_keywords
151              
152             Works like C except that its arguments must be
153             a number of array references each referencing an array containing
154             a keyword and its associated derivates.
155              
156             Returns the Text::Index object for convenience.
157              
158             =cut
159              
160             sub add_keywords {
161 1     1 1 3 my $self = shift;
162 2         10 croak("add_keywords takes only array references as arguments")
163 1 50       3 if grep {!_ARRAY($_)} @_;
164 1         4 $self->add_keyword(@$_) for @_;
165 1         11 return $self;
166             }
167              
168              
169             =item keywords
170              
171             Returns all registered keywords as a list of array references.
172             Each of those references an array containing the keyword followed
173             by any possible derivates.
174              
175             =cut
176              
177             sub keywords {
178 1     1 1 3 my $self = shift;
179 1         2 return( map {[$_->{key}, @{$_->{deriv}}]} values(%{$self->{keywords}}) );
  4         7  
  4         558  
  1         4  
180             }
181              
182              
183             sub _search {
184 6     6   6 my $self = shift;
185 6         6 my $key = shift;
186 6         7 my $pages = $self->{pages};
187              
188 13         25 my @regexes = map {
189 6         8 my @w = map {quotemeta($_)} split /\s+/, $_;
  9         18  
190 9         115 my $str = join '\s+', @w;
191 9         89 qr/$str/i
192             } @$key;
193            
194 6         8 my @onpage;
195            
196 6         12 foreach my $page_no (1..@$pages) {
197 18         24 my $page = $pages->[$page_no-1];
198 18 100       30 study($page) if @regexes > 1;
199 18         21 foreach my $regex (@regexes) {
200 22 100       97 push(@onpage, $page_no), last if $page =~ $regex;
201             }
202             }
203              
204 6         30 return @onpage;
205             }
206              
207             =item generate_index
208              
209             Generates an index from the registered keywords and pages.
210             It returns an index of the form:
211              
212             {
213             'keyword' => [ @pages_containing_keyword ],
214             ...
215             }
216              
217             The search for the keywords is performed case and whitespace insensitively.
218              
219             =cut
220              
221             sub generate_index {
222 1     1 1 423 my $self = shift;
223              
224 1         2 my $index = {};
225 1         2 foreach my $key (values %{$self->{keywords}}) {
  1         5  
226 4         11 $index->{$key->{key}} = [
227 4         7 $self->_search( [ $key->{key}, @{$key->{deriv}} ] )
228             ];
229             }
230 1         2 return $index;
231             }
232              
233              
234              
235             =item find_keyword
236              
237             This method works like C only that it searches for
238             just one keyword which is provided as argument in the style of
239             C. It ignores any registered keywords and searches just
240             for the one given as argument.
241              
242             Returns a list of page number on which the keyword was found. The
243             list will be the empty list if the keyword wasn't found at all.
244              
245             =cut
246              
247             sub find_keyword {
248 2     2 1 465 my $self = shift;
249 2         3 my $key = shift;
250 2         3 my @deriv = @_;
251            
252 2 50       20 croak("keyword requires a keyword as first argument.")
253             if not defined $key;
254            
255 2         3 push @deriv, $key;
256              
257 2         6 return $self->_search(\@deriv);
258             }
259              
260             1;
261              
262             __END__