File Coverage

blib/lib/Grep/Query.pm
Criterion Covered Total %
statement 94 95 98.9
branch 30 38 78.9
condition 4 5 80.0
subroutine 14 14 100.0
pod 3 3 100.0
total 145 155 93.5


line stmt bran cond sub pod time code
1             package Grep::Query;
2              
3 9     9   80499 use 5.010;
  9         77  
4              
5 9     9   45 use strict;
  9         17  
  9         167  
6 9     9   38 use warnings;
  9         18  
  9         511  
7              
8             our $VERSION = '1.009';
9             $VERSION = eval $VERSION;
10              
11 9     9   3770 use Grep::Query::Parser;
  9         28  
  9         330  
12 9     9   4220 use Grep::Query::FieldAccessor;
  9         20  
  9         430  
13              
14 9     9   56 use Scalar::Util qw(blessed);
  9         16  
  9         424  
15 9     9   82 use Carp;
  9         21  
  9         396  
16 9     9   45 use Digest::MD5;
  9         17  
  9         305  
17              
18             # allow importing the qgrep function/method
19             # to enable non-OO use
20             #
21 9     9   49 use Exporter qw(import);
  9         17  
  9         8217  
22             our @EXPORT_OK = qw(qgrep);
23              
24             ## CTOR
25             ##
26             sub new
27             {
28 437     437 1 495146 my $class = shift;
29 437         775 my $query = shift;
30            
31 437 50       1175 croak("No query provided") unless defined($query);
32            
33             # parse the query right now
34             #
35 437         1575 my ($parsedQuery, $fieldRefs) = Grep::Query::Parser::parsequery($query);
36 420         8519 my $self =
37             {
38             _query => $query,
39             _parsedquery => $parsedQuery,
40             _fieldrefs => $fieldRefs
41             };
42 420         1485 bless($self, $class);
43            
44 420         1349 return $self;
45             }
46              
47             ## METHODS
48             ##
49              
50             sub qgrep
51             {
52 324 50   324 1 831689 croak("missing parameters") unless @_;
53              
54 324         706 my $arg = shift;
55            
56 324 100 100     2416 my $obj =
57             (blessed($arg) // '') eq __PACKAGE__
58             ? $arg
59             : __PACKAGE__->new($arg);
60            
61 324         1226 return $obj->__qgrep(@_);
62             }
63              
64             sub getQuery
65             {
66 1     1 1 6 my $self = shift;
67            
68 1         6 return $self->{_query};
69             }
70              
71             # don't call this directly, use the above
72             #
73             sub __qgrep
74             {
75             # why even bother if you're not interested in the result?
76             #
77 324 100   324   994 return undef unless defined(wantarray());
78            
79 322         583 my $self = shift(@_);
80              
81             # first check if the first argument is/should be a field accessor
82             #
83 322         438 my $fieldAccessor;
84 322 100       514 if (@{$self->{_fieldrefs}})
  322         927  
85             {
86             # the query uses fields, so there must be a field accessor first
87             #
88 173         312 $fieldAccessor = shift(@_);
89            
90 173 100       374 if (defined($fieldAccessor))
91             {
92             # verify that the field accessor is of the right sort and has the known fields
93             #
94 144 100       504 croak("field names used in query; the argument before the list must be a field accessor") unless ref($fieldAccessor) eq 'Grep::Query::FieldAccessor';
95 143         249 $fieldAccessor->assertField($_) foreach (@{$self->{_fieldrefs}});
  143         761  
96             }
97             else
98             {
99             # for laziness, the caller passed undef and so we can assume the objects to be queried
100             # are in fact plain hashes so we manufacture a field accessor for that
101             #
102 29         51 $fieldAccessor = Grep::Query::FieldAccessor->newDefault(@{$self->{_fieldrefs}});
  29         210  
103             }
104             }
105             else
106             {
107             # it's weird if a field accessor is present, but the query uses no fields - flag that mistake
108             #
109 149 100       418 croak("no fields used in query, yet the first list argument is a field accessor?") if ref($_[0]) eq 'Grep::Query::FieldAccessor';
110             }
111              
112             # trim away undef values
113             #
114 320 50       727 my @list = map { defined($_) ? $_ : () } @_;
  6672         11242  
115            
116             # nothing to see here
117             #
118 320 50       1027 return(wantarray() ? () : 0) unless @list;
    100          
119            
120             # try to make sure all items in the list have the same structure...
121             #
122 312 100       857 if (@list > 1)
123             {
124 288         2696 my $fp = __fingerprint(Digest::MD5->new(), $list[0])->hexdigest();
125 288         1301 foreach my $entry (@list)
126             {
127 6648 50       17792 croak("layout of datastructures in query list are not the same") unless $fp eq __fingerprint(Digest::MD5->new(), $entry)->hexdigest();
128             }
129             }
130            
131             # a special case:
132             # if there is only one argument AND it is a hash ref, we can let loose a query on it
133             # assuming we restructure the incoming data as a list of individual key/value pairs
134             #
135             # for this, we must have a fieldaccessor
136             #
137 312         589 my $lonehash = 0;
138 312 100 66     962 if (scalar(@list) == 1 && ref($list[0]) eq 'HASH')
139             {
140 24 50       96 croak("a lone hash used in query; first argument must be a field accessor") unless $fieldAccessor;
141 24         29 my @eachList;
142 24         112 while (my @kv = each %{$list[0]})
  648         1447  
143             {
144 624         988 push(@eachList, \@kv);
145             }
146 24         85 @list = @eachList;
147 24         55 $lonehash = 1;
148             }
149            
150             # the list we were given needs to be made into a hash with unique keys so we
151             # identify 'rows' while evaluating the query
152             #
153             # that means we can return multiple identical hits and that we can sort the return list
154             # in the same order we got it
155             #
156             # keys are simply a number, and values are refs to the individual scalars/objects to avoid copying them
157             #
158 312         496 my $id = 0;
159 312         631 my %data = map { $id++ => \$_ } @list;
  7272         14543  
160            
161             # kick off the query
162             #
163 312         996 %data = %{ $self->{_parsedquery}->xeq($fieldAccessor, \%data) };
  312         1367  
164              
165             # only return the number of matches if the full list isn't desired
166             #
167 312 50       1300 return scalar(keys(%data)) unless wantarray();
168              
169             # fix up an array with the matches
170             #
171 312         504 my @matched;
172 312 100       648 if ($lonehash)
173             {
174             # we started with a hash, so that is what should be returned
175             #
176 24         32 my %h;
177 24         76 $h{${$data{$_}}->[0]} = ${$data{$_}}->[1] foreach (keys(%data));
  240         411  
  240         348  
178 24         61 push(@matched, \%h);
179             }
180             else
181             {
182             # keep the (relative) order they we're given to us by sorting on the artificial
183             # key index we gave them
184             #
185 288         1564 foreach my $k (sort { $a <=> $b } (keys(%data)))
  7074         9440  
186             {
187 2612         3254 push(@matched, ${$data{$k}});
  2612         4217  
188             }
189             }
190            
191             # now return the result list
192             #
193 312         6928 return @matched;
194             }
195              
196             sub __fingerprint
197             {
198 18276     18276   24032 my $digest = shift;
199 18276         22335 my $obj = shift;
200              
201 18276         23488 my $type = ref($obj);
202 18276         36985 $digest->add($type);
203 18276 50       32168 if ($type eq 'ARRAY') { __fingerprint($digest, $_) foreach (@$obj) }
  0 100       0  
204 2268         8031 elsif ($type eq 'HASH') { __fingerprint($digest, $obj->{$_}) foreach (sort(keys(%$obj))) }
205 16008         40650 else { $digest->add($digest->digest()) }
206            
207 18276         53155 return $digest;
208             }
209              
210             1;
211              
212             =head1 NAME
213              
214             Grep::Query - Query logic for lists of scalars/objects
215              
216             =head1 VERSION
217              
218             Version 1.009
219              
220             =head1 SYNOPSIS
221              
222             use Grep::Query qw(qgrep);
223            
224             my @data = ( 'a' .. 'z' );
225             my @result;
226              
227             # very simple query equal to a standard "grep(/[dkob]/, @data)"
228             #
229             @result = qgrep('REGEXP([dkob])', @data);
230             #
231             # @result contains ( 'd', 'k', 'o', 'b' )
232            
233             # go more wild
234             #
235             @result = qgrep('REGEXP([dkob]) AND ( REGEXP([yaxkz]) OR REGEXP([almn]) )', @data);
236             #
237             # @result contains ( 'k' )
238              
239             # or use it in OO fashion
240             #
241             my $gq = Grep::Query->new('REGEXP([dkob]) AND ( REGEXP([yaxkz]) OR REGEXP([almn]) )');
242             @result = $gq->qgrep(@data);
243            
244             # also query a list of objects, and use numerical comparisons too
245             #
246             my @persons = ...; # assume person objects can respond to '->getName()' and '->calculateAge()'
247            
248             # create a query object - note that the syntax now references 'field' names of name/age in the query
249             #
250             my $personQuery = Grep::Query->new('name.REGEXP(^A) AND age.>=(42)');
251            
252             # set up a field accessor to teach G::Q how to match field names to whatever's needed to get data from the objects
253             #
254             my $fieldAccessor = Grep::Query::FieldAccessor->new();
255             $fieldAccessor->add('name', sub { $_[0]->getName() });
256             $fieldAccessor->add('age', sub { $_[0]->calculateAge() });
257            
258             # now execute the query by passing the field accessor before the person list
259             #
260             @result = $personQuery->qgrep($fieldAccessor, @persons);
261             #
262             # @result contains a list of person objects that has a name starting with 'A' and an age greater than or equal to 42
263            
264             # If what you have is a single hash (rather than a list of them) and you wish to query it and pick out key/values
265             # that matches, the query is special cased for passing just a single hash.
266             # A field accessor is necessary, and it will receive individual key/value pairs as small lists.
267             #
268             # Assume a %videos hash, keyed by video name, and value is another hash with at least the key 'length' holding the video
269             # length in seconds...:
270             #
271             my $fieldAccessor = Grep::Query::FieldAccessor->new();
272             $fieldAccessor->add('key', sub { $_[0]->[0] });
273             $fieldAccessor->add('length', sub { $_[0]->[1]->{length} });
274             my $videoQuery = Grep::Query->new('key.REGEXP(^Alias) AND length.gt(2500)');
275             @result = $videoQuery->qgrep($fieldAccessor, \%videos);
276             #
277             # $result[0] contains a hash ref with all videos with name starting with 'Alias' and at least 2500 seconds long
278            
279             =head1 BACKGROUND
280              
281             Why use this module when you could easily write a grep BLOCK or plain regexp
282             EXPR to select things in a list using whatever criteria you desired?
283              
284             =head2 The original use-case was this:
285              
286             Given a number of commandline tools I provide to users in my workplace, quite
287             frequently I wanted the user to be able to express, with some flag(s), a
288             selection among a list of 'somethings' computed at runtime - the most common
289             probably a list of file/directory names. It was also common to have this type
290             of filtering defined in various configuration files and persistently apply them
291             every time a command was run.
292              
293             Example: the user gives the command:
294              
295             SomeCommand /some/path
296              
297             The 'SomeCommand' may, for example, scan the given path and for all files it finds it will
298             do something useful. So, I also wanted to provide flags for the command such
299             that they can say...
300              
301             SomeCommand -exclude 'some_regexp' /some/path
302              
303             ...in order to filter the list of files that should be worked on.
304              
305             Obviously not a problem, and I also provided the reverse if that was more
306             convenient:
307              
308             SomeCommand -include 'another_regexp' /some/path
309              
310             And the idea was extended so flags could be given multiple times and
311             interweaved:
312              
313             SomeCommand -include 'rx1' -exclude 'rx2' -include 'rx3' ... /some/path
314              
315             Thus, the original set was shrunk by first selecting only those matching the
316             regexp C and then shrink that by excluding those matching C etc. - I
317             think you get the idea.
318              
319             What I found however is that it becomes hard to string together regexps to find
320             the exact subset you want when the rules are a bit more complex. In fact, while
321             regexps are powerful, they're not that suited to easily mix multiple of them
322             (and some expressions are basically impossible, e.g. 'I want this but not this'),
323             especially when you try to provide a commandline interface to them...
324              
325             Thus, instead I'd wanted to provide a more capable way for a user to give a
326             more complex query, i.e. where it'd be possible to use AND/OR/NOT as well as
327             parenthesized groups, e.g. something like this (very contrived and structured
328             on several lines for readability):
329              
330             (
331             REGEXP/some_rx_1/ AND REGEXP/some_rx_2/
332             )
333             OR
334             (
335             REGEXP/some_rx_3/ AND NOT REGEXP/some_rx_4/
336             )
337             OR
338             NOT
339             (
340             REGEXP/some_rx_5/ OR NOT REGEXP/some_rx_6/
341             )
342              
343             Basically, feed 'something' the query and a list of scalars and get back a list
344             of the subset of scalars that fulfills the query. In short, behaving like a
345             grep, you might say, but where the normal BLOCK or EXPR is a query decided by
346             the user
347              
348             As it turned out, once the basics above was functioning I added some other
349             features, such as realizing that lists were not always just simple scalars, but
350             could just as well be "objects" and also that it then was useful to use
351             numerical comparisons rather than just regular expressions.
352              
353             Hence, this module to encapsulate the mechanism.
354              
355             =head3 Is it for you?
356              
357             It may be comparatively slow and very memory-intensive depending on the
358             complexity of the query and the size of the original data set.
359              
360             If your needs can be met by a regular grep call, utilizing a regular expression
361             directly, or using a block of code you can write beforehand, this module
362             probably isn't necessary, although it might be convenient if your block is
363             complex enough.
364              
365             =head1 DESCRIPTION
366              
367             The visible API is made to be simple but also compact - the single method/function
368             C, actually. For the slightly more complex scenarios a helper class is
369             required, but generally a very simple one giving high flexibility in how to structure
370             the query itself regardless of how the list itself is laid out.
371              
372             It has a behavior similar to C - give it a list and get back a list (or
373             in scalar context, the number of matches). The main difference is that the
374             matching stuff is a query expressed in a fairly simple language.
375              
376             It can be used in both non-OO and OO styles. The latter obviously useful when
377             the query will be used multiple times so as to avoid parsing the query every
378             time.
379              
380             The basic intent is to make it easy to do the easy stuff while still making it
381             easy to move up to something more complex, without having a wide or wordy API.
382             This is a two-edged sword - I hope this will not be confusing.
383              
384             =head2 QUERY LANGUAGE
385              
386             A query effectively have two slightly different "modes", depending on if the
387             query is aimed at a list of ordinary scalars or if the list consists of objects
388             (or plain hashes, which is regarded as a special case of objects). There is
389             also a special case when you pass only a single hash ref - it can be treated
390             as a list, and a new hash ref with matching key/value pairs passed back.
391              
392             =over
393              
394             =item Scalars
395              
396             In the first case, the query doesn't use "field" names - it is implicit that
397             the comparison should be made directly on scalars in the list.
398              
399             Note that is possible to use field names if desired - just make the accessors
400             so that it properly extracts parts of each scalar.
401              
402             =item Hashes/Objects
403              
404             In the second case, the query uses field names for the comparisons and
405             therefore a "field accessor" object is required when executing the query so as
406             to provide the query engine with the mapping between a field name and the data.
407              
408             A special case occurs when the list consists of hashes with keys being exactly
409             the field names - if so, the query engine can transparently create the
410             necessary field accessor if one is not passed in.
411              
412             The default field accessor also understands 'navigation paths', i.e. handling
413             a deep structure with lists-in-lists/hashes etc. This will work to any depth.
414              
415             =back
416              
417             It's important to note that either the query uses field names everywhere, or
418             not at all. Mixing comparisons with field names and others without is illegal.
419              
420             For hashes/objects it's necessary to use field names - otherwise you will match
421             against scalar representations of hashref values for example, e.g. 'HASH(0x12345678)'.
422             Hardly useful.
423              
424             =head3 SYNTAX
425              
426             The query language syntax is fairly straightforward and can be divided in two main
427             parts: the logical connectors and the comparison atoms.
428              
429             In the tables below, note that case is irrelevant, i.e. 'AND' is equal to 'and' which is
430             equal to 'And' and so on.
431              
432             =over
433              
434             =item Comments
435              
436             Comments can be used in the query using the begin/end style like '/* some comment */'.
437              
438             =item Logical connectors
439              
440             In this category we find the basic logic operators used to tie comparisons
441             together, i.e AND/OR/NOT and parentheses to enforce order.
442              
443             =over
444              
445             =item * B or B
446              
447             Used to negate the list generated by an expression.
448              
449             =item * B or B<&&>
450              
451             Used to select the intersection of two lists formed by expressions before and
452             after.
453              
454             =item * B or B<||>
455              
456             Used to select the union of two lists formed by expressions before and
457             after.
458              
459             =item * B<()>
460              
461             Used to enforce a grouping order.
462              
463             =back
464              
465             =item Comparison atoms
466              
467             A comparison atom is how to describe a match. It can be divided in string and
468             numeric matches. A complete atom can contain the following:
469              
470             IB<.>IBIB
471              
472             The I is optional. If given, it is terminated with a period (B<.>).
473             It cannot contain a period or a space, but otherwise it can be any text that
474             can be used as a hash key.
475              
476             The rest of the expression consists of an I and a I to be used
477             by that operator delimited by B and B. To
478             accommodate values happening to use characters normally used in a delimiter,
479             choice of character(s) is very flexible. The delimiters can be of two different
480             kinds. Either common start/stop pairs like parentheses: I<()>, braces: I<{}>,
481             brackets: I<[]> or angles: IE>. Or, it can be an arbitrary character except
482             space, and the same character again after the value, e.g. I.
483              
484             The Is are:
485              
486             =over
487              
488             =item * B or B
489              
490             These operators always evaluate to true and false respectively.
491              
492             =item * B or B<=~>
493              
494             This operator expects to use the I as a regular expression for use in
495             matching.
496              
497             =item * B, B, B, B, B, B
498              
499             These are B based matches, i.e. I, I, I,
500             I, I and I.
501              
502             Don't confuse these with the B comparisons - results will likely
503             be unexpected since using these means that "2" is greater than "19"...
504              
505             =item * B<==>, B, B>, B=>, B>, B=>
506              
507             These are B matches.
508              
509             =back
510              
511             =back
512              
513             =head3 EXAMPLES
514              
515             # in normal Perl code, we would for example write:
516             #
517             my $v = "abcdefgh";
518             if ($v =~ /abc/)
519             {
520             ...
521             }
522            
523             # equivalent ways to write the regexp in a query would be:
524             #
525             REGEXP(abc)
526             regexp(abc) # case doesn't matter
527             =~(abc) # in case you're more comfortable with the Perl operator
528             =~{abc} # braces as delimiters
529             =~[abc] # brackets as delimiters
530             =~ # angles as delimiters
531             =~/abc/ # Perlish
532             =~dabcd # works, but quite confusing
533            
534             # a compound query with fields
535             #
536             name.REGEXP(^A) AND age.>=(42) # field names before the operators
537              
538             =head1 METHODS/FUNCTIONS
539              
540             =head2 new( $query )
541              
542             Constructor for a Grep::Query object if using the OO interface.
543              
544             The argument query string is required.
545              
546             Croaks if a problem is discovered.
547              
548             =head3 EXAMPLE
549              
550             # create a G::Q object
551             #
552             my $gq = Grep::Query->new('==(42) OR >(100)');
553              
554             =head2 getQuery()
555              
556             Returns the original query text.
557              
558             =head2 qgrep
559              
560             Execute a query.
561              
562             This method can be called in a few different ways, depending on if it's used in
563             an OO fashion or not, or if the query contains field names or not.
564              
565             Croaks if something is wrong.
566              
567             Return value: Number of matches in the given data list if called in scalar
568             context, the matching list otherwise. The return list will keep the relative order as the
569             original data list. A notable exception: if called in void context, the query
570             is skipped altogether - seems to be no point in spending a lot of work when no
571             one's interested in the results, right?
572              
573             =over
574              
575             =item * Non-OO, no fields: qgrep( $query, @data )
576              
577             The given C<$query> string will be parsed on the fly and executed against the
578             C<@data>.
579              
580             =item * Non-OO, with fields: qgrep( $query, $fieldAccessor, @data )
581              
582             The given C<$query> string will be parsed on the fly and executed against the
583             data, using the C<$fieldAccessor> object to get values from C<@data> objects.
584              
585             Note: In a certain case, the C<$fieldAccessor> argument can be passed as
586             C and it will be auto-generated. See below for details.
587            
588              
589             =item * OO, no fields: $obj->qgrep( @data )
590              
591             The C<$obj> must first have been created using L and then it can be
592             executed against the C<@data>.
593              
594             =item * OO, with fields: $obj->qgrep( $fieldAccessor, @data )
595              
596             The C<$obj> must first have been created using L and then it can be
597             executed, using the C<$fieldAccessor> object to get values from C<@data>
598             objects.
599              
600             Note: In a certain case, the C<$fieldAccessor> argument can be passed as
601             C and it will be auto-generated. See below for details.
602              
603             =item * Passing a single hashref: qgrep($fieldAccessor, \%hash)
604              
605             In this case, the field accessor methods will be called with two-item
606             arrayrefs, e.g. the key is in the first (0) slot, and the value is in the
607             second (1) slot.
608              
609             =back
610              
611             =head3 Autogenerated field accessor
612              
613             If the C<@data> holds plain hashes with keys exactly corresponding to the field
614             names used in the query, the query engine can autogenerate a field accessor.
615              
616             This is only a convenience, a manually constructed field accessor will be used
617             if given. To take advantage of the convenience, simply pass C as the
618             C<$fieldAccessor> argument.
619              
620             If you have a deep structure, you may use 'field' names connected by '->' linkages,
621             where raw text are used as regular hash keys and array indexes are denoted using
622             []. When the end of the navigation path has been reached the object at that
623             location is returned.
624              
625             =head3 EXAMPLES
626              
627             # sample data
628             my @scalarData = ( 105, 3, 98, 100, 42, 101, 42 );
629              
630             # make sure to import the qgrep function
631             #
632             use Grep::Query qw(qgrep);
633            
634             # now call it directly
635             #
636             my $matches = qgrep('==(42) OR >(100)', @scalarData);
637             #
638             # $matches is now 4 (matching 105, 42, 101, 42)
639            
640             # or equivalently, create a G::E object and call the method on it
641             #
642             my $gq = Grep::Query->new('==(42) OR >(100)');
643             $matches = $gq->qgrep(@scalarData);
644             #
645             # $matches again 4
646            
647             # some sample fielded data in a hash
648             #
649             my @hashData =
650             (
651             { x => 52, y => 38 },
652             { x => 94, y => 42 },
653             { x => 25, y => 77 }
654             );
655            
656             # autogenerate a field accessor since the query matches the fields
657             #
658             $matches = qgrep('x.>(20) AND y.>(40)', undef, @hashData);
659             #
660             # $matches is now 2 (matching last two entries)
661            
662             # but using different field names (or if it was opaque objects used)
663             # we must provide an explicit field accessor
664             #
665             my $fieldAccessor = Grep::Query::FieldAccessor->new
666             (
667             {
668             fieldY => sub { $_[0]->{y} },
669             fieldX => sub { $_[0]->{x} },
670             }
671             );
672             $matches = qgrep('fieldX.>(20) AND fieldY.>(40)', $fieldAccessor, @hashData);
673             #
674             # $matches again 2
675            
676             # a hash with depth
677             #
678             my @hashData =
679             (
680             { x => { fee => 1, fie => 2, foo => 3 }, y => [ 2, 4, 6 ] },
681             { x => { fee => 10, fie => 20, foo => 30 }, y => [ 12, 14, 16 ] },
682             { x => { fee => 100, fie => 200, foo => 300 }, y => [ 22, 24, 26 ] },
683             );
684             $matches = qgrep('x->fie.>(30) AND y->[2].>(20)', undef, @hashData);
685             #
686             # $matches is now 1 (matching last entry)
687            
688             =head1 AUTHOR
689              
690             Kenneth Olwing, C<< >>
691              
692             =head1 BUGS
693              
694             Please report any bugs or feature requests to C,
695             or through the web interface at
696             L. I will be
697             notified, and then you'll automatically be notified of progress on your bug as
698             I make changes.
699              
700             =head1 SUPPORT
701              
702             You can find documentation for this module with the perldoc command.
703              
704             perldoc Grep::Query
705              
706             You can also look for information at:
707              
708             =over 4
709              
710             =item * RT: CPAN's request tracker (report bugs here)
711              
712             L
713              
714             =item * AnnoCPAN: Annotated CPAN documentation
715              
716             L
717              
718             =item * CPAN Ratings
719              
720             L
721              
722             =item * Search CPAN
723              
724             L
725              
726             =back
727              
728             =head1 ACKNOWLEDGEMENTS
729              
730             First and foremost, I thank my family for putting up with me!
731              
732             =over
733              
734             =item David Mertens, C<< >> for the name.
735              
736             =item Ron Savage, C<< >> for helping follow current best
737             practices for modules.
738              
739             =back
740              
741             =head1 REPOSITORY
742              
743             L.
744              
745             =head1 LICENSE AND COPYRIGHT
746              
747             Copyright 2016 Kenneth Olwing.
748              
749             This program is free software; you can redistribute it and/or modify it
750             under the terms of the the Artistic License (2.0). You may obtain a
751             copy of the full license at:
752              
753             L
754              
755             Any use, modification, and distribution of the Standard or Modified
756             Versions is governed by this Artistic License. By using, modifying or
757             distributing the Package, you accept this license. Do not use, modify,
758             or distribute the Package, if you do not accept this license.
759              
760             If your Modified Version has been derived from a Modified Version made
761             by someone other than you, you are nevertheless required to ensure that
762             your Modified Version complies with the requirements of this license.
763              
764             This license does not grant you the right to use any trademark, service
765             mark, tradename, or logo of the Copyright Holder.
766              
767             This license includes the non-exclusive, worldwide, free-of-charge
768             patent license to make, have made, use, offer to sell, sell, import and
769             otherwise transfer the Package with respect to any patent claims
770             licensable by the Copyright Holder that are necessarily infringed by the
771             Package. If you institute patent litigation (including a cross-claim or
772             counterclaim) against any party alleging that the Package constitutes
773             direct or contributory patent infringement, then this Artistic License
774             to you shall terminate on the date that such litigation is filed.
775              
776             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
777             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
778             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
779             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
780             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
781             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
782             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
783             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
784              
785             =cut