diff options
Diffstat (limited to 'README.markdown')
-rw-r--r-- | README.markdown | 389 |
1 files changed, 336 insertions, 53 deletions
diff --git a/README.markdown b/README.markdown index b8b83c9..5473dec 100644 --- a/README.markdown +++ b/README.markdown @@ -1,66 +1,349 @@ -List::Breakdown -=============== +# NAME -Filter elements from a list non-uniquely into a specified hash -structure, which can be nested, that pass subroutines, match regular -expressions, or fall within intervals. +List::Breakdown - Build sublist structures matching conditions +# VERSION -Installation ------------- +Version 0.17 -To install this module, run the following commands: +# SYNOPSIS - perl Makefile.PL - make - make test - make install + use List::Breakdown 'breakdown'; + ... + my @words = qw(foo bar baz quux wibble florb); + my $cats = { + all => sub { 1 }, + has_b => sub { m/ b /msx }, + has_w => sub { m/ w /msx }, + length => { + 3 => sub { length == 3 }, + 4 => sub { length == 4 }, + long => sub { length > 4 }, + }, + has_ba => qr/ba/msx, + }; + my %filtered = breakdown $cats, @words; -Support and Documentation -------------------------- +This puts the following structure in `%filtered`: -After installing, you can find documentation for this module with the -perldoc command. + ( + all => ['foo', 'bar', 'baz', 'quux', 'wibble', 'florb'], + has_b => ['bar', 'baz', 'wibble', 'florb'], + has_w => ['wibble'], + length => { + 3 => ['foo', 'bar', 'baz'], + 4 => ['quux'], + long => ['wibble', 'florb'], + }, + has_ba => ['bar', 'baz'], + ) + +# DESCRIPTION + +This module assists you in making a _breakdown_ of a list, copying and +filtering its items into a structured bucket layout according to your +specifications. Think of it as a syntax for [`grep`](https://metacpan.org/pod/perlfunc#grep-BLOCK-LIST) that returns named and structured results from one list. + +It differs from the excellent [List::Categorize](https://metacpan.org/pod/List::Categorize) in the use +of references to define each category, and in not requiring only one final +category for any given item; an item can end up in the result set for more than +one filter. + +If you want to divide or _partition_ your list so that each item can only +appear in one category, you may want either +[List::MoreUtils](https://metacpan.org/pod/List::MoreUtils#Partitioning) or possibly +[Set::Partition](https://metacpan.org/pod/Set::Partition) instead. + +# SUBROUTINES/METHODS + +## `breakdown(\%spec, @items)` + +This is the only exportable subroutine. Given a hash reference structure and a +list of items, it applies each of the referenced values as tests, returning a +new hash in the same structure with the references replaced with the matching +items, in the same way as [`grep`](https://metacpan.org/pod/perlfunc#grep-BLOCK-LIST). + +There are two shortcut syntaxes for a value in the `\%spec` structure: + +- `ARRAY` + + If the referenced array has exactly two items, it will be interpreted as + defining numeric bounds `[lower,upper)` for its values. `undef` can be used + to denote negative or positive infinity. Any other number of items is a fatal + error. + +- `Regexp` + + This will be interpreted as a pattern for the list items to match. + +Additionally, if the value is a `HASH` reference, it can be used to make a +sub-part of the structure, as demonstrated in the `length` key of the example +`\%spec` given in [SYNOPSIS](#synopsis). + +# EXAMPLES + +## Collecting troublesome records + +Suppose you have a list of strings from a very legacy system that you need to +regularly check for problematic characters, alerting you to problems with an +imperfect Perl parser: + + my @records = ( + "NEW CUSTOMER John O''Connor\r 2017-01-01", + "RETURNING CUSTOMER\tXah Zhang 2016-01-01", + "CHECK ACCOUNT Pierre d'Alun 2016-12-01", + "RETURNING CUSTOMER Aaron Carter 2016-05-01" + ); + +You could have a bucket structure like this, using the **pattern syntax**, which +catches certain error types you've seen before for review: + + my %buckets = ( + bad_whitespace => qr/ [\r\t] /msx, + apostrophes => qr/ ' /msx, + double_apostrophes => qr/ '' /msx, + not_ascii => qr/ [^[:ascii:]] /msx + }; + +Applying the bucket structure like so: + + my %results = breakdown \%buckets, @records; + +The result set would look like this: + + my %expected = ( + bad_whitespace => [ + "NEW CUSTOMER John O''Connor\r 2017-01-01", + "RETURNING CUSTOMER\tXah Lee 2016-01-01" + ], + apostrophes => [ + "NEW CUSTOMER John O''Connor\r 2017-01-01", + 'CHECK ACCOUNT Pierre d\'Alun 2016-12-01' + ], + double_apostrophes => [ + "NEW CUSTOMER John O''Connor\r 2017-01-01" + ], + not_ascii => [ + ] + ); + +Notice that some of the lines appear in more than one list, and that the +`not_ascii` bucket is empty, because none of the items matched it. + +## Monitoring system check results + +Suppose you ran a list of checks with your monitoring system, and now you have +a list of `HASH` references with keys describing each check and its outcome: + + my @checks = ( + { + hostname => 'webserver1', + status => 'OK', + }, + { + hostname => 'webserver2', + status => 'CRITICAL', + }, + { + hostname => 'webserver3', + status => 'WARNING', + }, + { + hostname => 'webserver4', + status => 'OK', + } + ); + +You would like to break the list down by status. You would lay out your buckets +like so, using the **subroutine syntax**: + + my %buckets = ( + ok => sub { $_->{status} eq 'OK' }, + problem => { + warning => sub { $_->{status} eq 'WARNING' }, + critical => sub { $_->{status} eq 'CRITICAL' }, + unknown => sub { $_->{status} eq 'UNKNOWN' }, + }, + ); + +And apply them like so: + + my %results = breakdown \%buckets, @checks; + +For our sample data above, this would yield the following structure in +`%results`: + + ( + ok => [ + { + hostname => 'webserver1', + status => 'OK' + }, + { + hostname => 'webserver4', + status => 'OK' + } + ], + problem => { + warning => [ + { + hostname => 'webserver3', + status => 'WARNING' + } + ], + critical => [ + { + hostname => 'webserver2', + status => 'CRITICAL' + } + ], + unknown => [] + } + ) + +Note the extra level of `HASH` references beneath the `problem` key. + +## Grouping numbers by size + +Suppose you have a list of numbers from your volcanic activity reporting +system, some of which might be merely worrisome, and some others an emergency, +and they need to be filtered to know where to send them: + + my @numbers = ( 1, 32, 3718.4, 0x56, 0777, 3.14, -5, 1.2e5 ); + +You could filter them into buckets like this, using the **interval syntax**: an +`ARRAY` reference with exactly two elements: lower bound (inclusive) first, +upper bound (exclusive) second: + + my $filters = { + negative => [ undef, 0 ], + positive => { + small => [ 0, 10 ], + medium => [ 10, 100 ], + large => [ 100, undef ], + }, + }; + +Applying the bucket structure like so: + + my %filtered = breakdown $filters, @numbers; + +The result set would look like this: + + my %expected = ( + negative => [ -5 ] + positive => { + small => [ 1, 3.14 ], + medium => [ 32, 86 ], + large => [ 3_718.4, 511, 120_000 ] + }, + ); + +Notice that you can express infinity or negative infinity as `undef`. Note +also this is a numeric comparison only. + +# AUTHOR + +Tom Ryder `<tom@sanctum.geek.nz>` + +# DIAGNOSTICS + +- `HASH reference expected for first argument` + + The first argument that `breakdown()` saw wasn't the hash reference it expects. + That's the only format a spec is allowed to have. + +- `Reference expected for '%s'` + + The value for the named key in the spec was not a reference, and one was + expected. + +- `Unhandled ref type %s for '%s'` + + The value for the named key in the spec is of a type that makes no sense to + this module. Legal reference types are `ARRAY`, `CODE`, `HASH`, and + `Regexp`. + +# DEPENDENCIES + +- Perl 5.6.0 or newer +- [base](https://metacpan.org/pod/base) +- [Carp](https://metacpan.org/pod/Carp) +- [Exporter](https://metacpan.org/pod/Exporter) + +# CONFIGURATION AND ENVIRONMENT + +None required. + +# INCOMPATIBILITIES + +None known. + +# BUGS AND LIMITATIONS + +Definitely. This is a very early release. Please report any bugs or feature +requests to `tom@sanctum.geek.nz`. + +# SUPPORT + +You can find documentation for this module with the **perldoc** command. perldoc List::Breakdown -License and Copyright ---------------------- +You can also look for information at: + +- RT: CPAN's request tracker (report bugs here) + + [http://rt.cpan.org/NoAuth/Bugs.html?Dist=List-Breakdown](http://rt.cpan.org/NoAuth/Bugs.html?Dist=List-Breakdown) + +- AnnoCPAN: Annotated CPAN documentation + + [http://annocpan.org/dist/List-Breakdown](http://annocpan.org/dist/List-Breakdown) + +- CPAN Ratings + + [http://cpanratings.perl.org/d/List-Breakdown](http://cpanratings.perl.org/d/List-Breakdown) + +- Search CPAN + + [http://search.cpan.org/dist/List-Breakdown/](http://search.cpan.org/dist/List-Breakdown/) + +# LICENSE AND COPYRIGHT Copyright (C) 2017 Tom Ryder -This program is free software; you can redistribute it and/or modify it -under the terms of the the Artistic License (2.0). You may obtain a -copy of the full license at: - -<http://www.perlfoundation.org/artistic_license_2_0> - -Any use, modification, and distribution of the Standard or Modified -Versions is governed by this Artistic License. By using, modifying or -distributing the Package, you accept this license. Do not use, modify, -or distribute the Package, if you do not accept this license. - -If your Modified Version has been derived from a Modified Version made -by someone other than you, you are nevertheless required to ensure that -your Modified Version complies with the requirements of this license. - -This license does not grant you the right to use any trademark, service -mark, tradename, or logo of the Copyright Holder. - -This license includes the non-exclusive, worldwide, free-of-charge -patent license to make, have made, use, offer to sell, sell, import and -otherwise transfer the Package with respect to any patent claims -licensable by the Copyright Holder that are necessarily infringed by the -Package. If you institute patent litigation (including a cross-claim or -counterclaim) against any party alleging that the Package constitutes -direct or contributory patent infringement, then this Artistic License -to you shall terminate on the date that such litigation is filed. - -Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER -AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. -THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY -YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR -CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR -CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, -EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +This program is free software; you can redistribute it and/or modify it under +the terms of the Artistic License (2.0). You may obtain a copy of the full +license at: + +[http://www.perlfoundation.org/artistic\_license\_2\_0](http://www.perlfoundation.org/artistic_license_2_0) + +Any use, modification, and distribution of the Standard or Modified Versions is +governed by this Artistic License. By using, modifying or distributing the +Package, you accept this license. Do not use, modify, or distribute the +Package, if you do not accept this license. + +If your Modified Version has been derived from a Modified Version made by +someone other than you, you are nevertheless required to ensure that your +Modified Version complies with the requirements of this license. + +This license does not grant you the right to use any trademark, service mark, +tradename, or logo of the Copyright Holder. + +This license includes the non-exclusive, worldwide, free-of-charge patent +license to make, have made, use, offer to sell, sell, import and otherwise +transfer the Package with respect to any patent claims licensable by the +Copyright Holder that are necessarily infringed by the Package. If you +institute patent litigation (including a cross-claim or counterclaim) against +any party alleging that the Package constitutes direct or contributory patent +infringement, then this Artistic License to you shall terminate on the date +that such litigation is filed. + +Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND +CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR +NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL LAW. +UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY +OUT OF THE USE OF THE PACKAGE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. |