File Coverage

blib/lib/LINQ/Util.pm
Criterion Covered Total %
statement 15 15 100.0
branch n/a
condition n/a
subroutine 6 6 100.0
pod 2 2 100.0
total 23 23 100.0


line stmt bran cond sub pod time code
1 2     2   1473 use 5.006;
  2         7  
2 2     2   11 use strict;
  2         3  
  2         40  
3 2     2   9 use warnings;
  2         3  
  2         106  
4              
5             package LINQ::Util;
6              
7             our $AUTHORITY = 'cpan:TOBYINK';
8             our $VERSION = '0.001';
9              
10 2     2   11 use Exporter::Shiny qw( fields check_fields );
  2         3  
  2         9  
11              
12             sub fields {
13 13     13 1 12960 require LINQ::FieldSet::Selection;
14 13         58 'LINQ::FieldSet::Selection'->new( @_ );
15             }
16              
17             sub check_fields {
18 35     35 1 5934 require LINQ::FieldSet::Assertion;
19 35         162 'LINQ::FieldSet::Assertion'->new( @_ );
20             }
21              
22             1;
23              
24             __END__
25              
26             =pod
27              
28             =encoding utf-8
29              
30             =head1 NAME
31              
32             LINQ::Util - useful utilities to make working with LINQ collections easier
33              
34             =head1 SYNOPSIS
35              
36             use feature qw( say );
37             use LINQ qw( LINQ )';
38             use LINQ::Util qw( fields );
39            
40             my $collection = LINQ( [
41             { name => 'Alice', age => 30, dept => 'IT' },
42             { name => 'Bob' , age => 29, dept => 'IT' },
43             { name => 'Carol', age => 32, dept => 'Marketing' },
44             { name => 'Dave', age => 33, dept => 'Accounts' },
45             ] );
46            
47             my $name_and_dept = $collection->select( fields( 'name', 'dept' ) );
48            
49             for ( $name_and_dept->to_list ) {
50             printf( "Hi, I'm %s from %s\n", $_->name, $_->dept );
51             }
52              
53             =head1 DESCRIPTION
54              
55             LINQ::Util provides a collection of auxiliary functions to make working with
56             LINQ collections a little more intuitive and perhaps avoid passing a bunch of
57             C<< sub { ... } >> arguments to C<select> and C<where>.
58              
59             =head1 FUNCTIONS
60              
61             =over
62              
63             =item C<< fields( SPEC ) >>
64              
65             Creates a coderef (actually a blessed object overloading C<< &{} >>) which
66             takes a hashref or object as input, selects just the fields/keys given in the
67             SPEC, and returns an object with those fields.
68              
69             A simple example would be:
70              
71             my $selector = fields( 'name' );
72             my $object = $selector->( { name => 'Bob', age => 29 } );
73              
74             In this example, C<< $object >> would be a blessed object with a C<name>
75             method which returns "Bob".
76              
77             Fields can be renamed:
78              
79             my $selector = fields( 'name', -as => 'moniker' );
80             my $object = $selector->( { name => 'Bob', age => 29 } );
81             say $object->moniker; # ==> "Bob"
82              
83             A coderef can be used as a field:
84              
85             my $selector = fields(
86             sub { uc( $_->{'name'} ) }, -as => 'moniker',
87             );
88             my $object = $selector->( { name => 'Bob', age => 29 } );
89             say $object->moniker; # ==> "BOB"
90              
91             An asterisk field selects all the input fields:
92              
93             my $selector = fields(
94             sub { uc( $_->{'name'} ) }, -as => 'moniker',
95             '*',
96             );
97             my $object = $selector->( { name => 'Bob', age => 29 } );
98             say $object->moniker; # ==> "BOB"
99             say $object->name; # ==> "Bob"
100             say $object->age; # ==> 29
101              
102             The aim of the C<fields> function is to allow the LINQ C<select> method to
103             function more like an SQL SELECT, where you give a list of fields you wish
104             to select.
105              
106             =item C<< check_fields( SPEC ) >>
107              
108             If C<< fields() >> can be compared to SQL SELECT, then C<< check_fields() >>
109             can be compared to SQL WHERE. Like C<< fields() >> it assumes your data is
110             hashrefs or blessed objects with attributes.
111              
112             # Select people called Bob.
113             $people
114             ->where( check_fields( 'name', -is => 'Bob' ) )
115             ->select( fields( 'name', 'age', 'dept' ) );
116              
117             Different operators can be used. Whether performing string or numeric
118             comparison, ">", "<", ">=", "<=", "==", and "!=" are used. (And the C<< -is >>
119             parameter is used to provide the right hand side of the comparison, even
120             for comparisons like "!=".)
121              
122             $people
123             ->where( check_fields( 'name', -cmp => '>', -is => 'Bob' ) );
124              
125             C<< check_fields() >> will probably guess correctly whether you want numeric
126             or string comparison, but if you need to specify, you can:
127              
128             $people
129             ->where( check_fields( 'phone', -is => '012345679', -string );
130            
131             $people
132             ->where( check_fields( 'age', -is => '33', -numeric );
133              
134             String comparisons can be made case-insensitive:
135              
136             $people
137             ->where( check_fields( 'name', -is => 'Bob', -nocase ) );
138              
139             You can use C<< -in >> to find a value in an arrayref. These comparisons are
140             always stringy and case-sensitive.
141              
142             $people
143             ->where( check_fields( 'name', -in => ['Alice', 'Bob'] ) );
144              
145             You can invert any comparison using C<< -nix >>.
146              
147             $people
148             ->where( check_fields( 'name', -nix, -in => ['Alice', 'Bob'] ) );
149              
150             You can perform more complex matches using L<match::simple>:
151              
152             $people
153             ->where( check_fields( 'name', -match => qr/^[RB]ob(ert)?$/i ) );
154              
155             SQL LIKE is also supported:
156              
157             $people
158             ->where( check_fields( 'name', -like => 'Bob%', -nocase ) );
159              
160             You can check multiple fields at once. There's an implied "AND" between the
161             conditions.
162              
163             $people
164             ->where( check_fields(
165             'name', -is => 'Bob',
166             'age', -nix, -is => 33,
167             ) );
168              
169             You can compare one field to another field using C<< -to >>:
170              
171             # Says all the values which are between the min and max.
172             LINQ(
173             { min => 10, max => 100, value => 50 },
174             { min => 10, max => 100, value => 5 },
175             { min => 10, max => 20, value => 50 },
176             )->where( check_fields(
177             'value', -cmp => '>=', -to => 'min', -numeric,
178             'value', -cmp => '<=', -to => 'max', -numeric,
179             ) )->foreach( sub {
180             say $_->value;
181             } );
182              
183             You can invert a whole C<< check_fields() >> using the C<< not >> method:
184              
185             my $where_not_bob = check_fields( 'name', -is => 'Bob' )->not;
186            
187             $people->where( $where_not_bob );
188              
189             Generally, you can use C<< not >>, C<< and >>, and C<< or >> methods to compose
190             more complex conditions. The C<< ~ >>, C<< & >>, and C<< | >> bitwise operators
191             are also overloaded to compose conditions.
192              
193             my $where_alice = check_fields( 'name', -is => 'Alice' );
194             my $where_bob = check_fields( 'name', -is => 'Bob' );
195            
196             my $where_alice_or_bob = $where_alice->or( $where_bob );
197            
198             # Or...
199             my $where_alice_or_bob = $where_alice | $where_bob;
200            
201             # Or...
202             my $where_alice_or_bob =
203             check_fields( 'name', -is => 'Alice' )
204             ->or( 'name', -is => 'Bob' );
205              
206             Like with C<< fields() >>, fields can be a coderef.
207              
208             my $where_bob = check_fields(
209             sub { $_->get_name("givenName") }, -is => 'Bob'
210             );
211              
212             =back
213              
214             =head1 BUGS
215              
216             Please report any bugs to
217             L<http://rt.cpan.org/Dist/Display.html?Queue=LINQ>.
218              
219             =head1 SEE ALSO
220              
221             L<LINQ::Collection>, L<LINQ>.
222              
223             L<https://en.wikipedia.org/wiki/Language_Integrated_Query>
224              
225             =head1 AUTHOR
226              
227             Toby Inkster E<lt>tobyink@cpan.orgE<gt>.
228              
229             =head1 COPYRIGHT AND LICENCE
230              
231             This software is copyright (c) 2021 by Toby Inkster.
232              
233             This is free software; you can redistribute it and/or modify it under
234             the same terms as the Perl 5 programming language system itself.
235              
236             =head1 DISCLAIMER OF WARRANTIES
237              
238             THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
239             WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
240             MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.