aboutsummaryrefslogtreecommitdiff
path: root/README.markdown
diff options
context:
space:
mode:
Diffstat (limited to 'README.markdown')
-rw-r--r--README.markdown389
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.