File Coverage

blib/lib/LINQ.pm
Criterion Covered Total %
statement 51 51 100.0
branch 22 22 100.0
condition 3 3 100.0
subroutine 15 15 100.0
pod 3 3 100.0
total 94 94 100.0


line stmt bran cond sub pod time code
1 114     114   13847320 use 5.006;
  114         1109  
2 114     114   605 use strict;
  114         198  
  114         2721  
3 114     114   863 use warnings;
  114         206  
  114         7290  
4              
5             package LINQ;
6              
7             our $AUTHORITY = 'cpan:TOBYINK';
8             our $VERSION = '0.001';
9              
10 114     114   52649 use Exporter::Shiny qw( LINQ Range Repeat END );
  114         60568  
  114         705  
11              
12             our $FORCE_ITERATOR;
13             our $IN_LOOP;
14              
15             my $end = do {
16              
17             package LINQ::END;
18             our $AUTHORITY = 'cpan:TOBYINK';
19             our $VERSION = '0.001';
20             my $x = 42;
21             bless( \$x );
22             &Internals::SvREADONLY( \$x, !!1 );
23             \$x;
24             };
25              
26             my $last = do {
27              
28             package LINQ::LAST;
29             our $AUTHORITY = 'cpan:TOBYINK';
30             our $VERSION = '0.001';
31             my $x = 666;
32             bless( \$x );
33             &Internals::SvREADONLY( \$x, !!1 );
34             \$x;
35             };
36              
37             BEGIN {
38 114     114   28429 *LINQ::END = sub () { $end };
  1839     1839   17224  
39            
40             *LINQ::LAST = sub () {
41 6 100   6   2435 if ( $IN_LOOP ) {
42 3         26 die( $last );
43             }
44 3         1674 require LINQ::Exception;
45 3         30 'LINQ::Exception::CallerError'->throw(
46             message => "Cannot call LINQ::LAST outside foreach",
47             );
48 114         56073 };
49             } #/ BEGIN
50              
51             sub LINQ ($) {
52 760     760 1 129748 my $data = shift;
53 760         1597 my $ref = ref( $data );
54            
55 760 100       1940 if ( $ref eq 'ARRAY' ) {
56 453 100       1031 if ( $FORCE_ITERATOR ) {
57 216         927 my @data = @$data;
58 216         25062 require LINQ::Iterator;
59 216 100   1251   1297 return LINQ::Iterator::->new( sub { @data ? shift( @data ) : LINQ::END } );
  1251         2980  
60             }
61            
62 237         26808 require LINQ::Array;
63 237         857 return LINQ::Array::->new( $data );
64             } #/ if ( $ref eq 'ARRAY' )
65            
66 307 100       793 if ( $ref eq 'CODE' ) {
67 304         24851 require LINQ::Iterator;
68 304         1086 return LINQ::Iterator::->new( $data );
69             }
70            
71 3         12 require Scalar::Util;
72 3 100 100     28 if ( Scalar::Util::blessed( $data ) and $data->DOES( 'LINQ::Collection' ) ) {
73 1         22 return $data;
74             }
75            
76 2         364 require LINQ::Exception;
77 2         14 'LINQ::Exception::CallerError'->throw(
78             message => "Cannot create LINQ object from '$data'",
79             );
80             } #/ sub LINQ ($)
81              
82             sub Range {
83 7     7 1 265 my ( $min, $max ) = @_;
84            
85 7 100       20 my $value = defined( $min ) ? $min : 0;
86            
87 7 100       16 if ( not defined $max ) {
88 2     15   19 return LINQ sub { $value++ };
  15         28  
89             }
90            
91 5 100   35   26 return LINQ sub { return LINQ::END if $value > $max; $value++ };
  35         59  
  31         61  
92             } #/ sub Range
93              
94             sub Repeat {
95 6     6 1 255 my ( $value, $count ) = @_;
96            
97 6 100       20 if ( not defined $count ) {
98 5     35   48 return LINQ sub { $value };
  35         68  
99             }
100            
101 1 100   8   6 return LINQ sub { return LINQ::END if $count-- <= 0; $value };
  8         18  
  7         11  
102             }
103              
104             1;
105              
106             __END__
107              
108             =pod
109              
110             =encoding utf-8
111              
112             =head1 NAME
113              
114             LINQ - an interpretation of Microsoft's Language Integrated Query
115              
116             =head1 SYNOPSIS
117              
118             use feature qw( say );
119             use LINQ qw( LINQ )';
120            
121             my $double_even_numbers =
122             LINQ( [ 1 .. 100 ] )
123             ->where( sub { $_ % 2 == 0 } )
124             ->select( sub { $_ * 2 } );
125            
126             for my $n ( $double_even_numbers->to_list ) {
127             say $n;
128             }
129              
130             =head1 DESCRIPTION
131              
132             LINQ is basically an application of SQL concepts to arrays and iterators.
133             Hopefully this implementation will eventually cover other data types like
134             SQL tables, XML and JSON data, etc.
135              
136             Not much is documented yet, but the test suite includes numerous examples
137             of LINQ's usage.
138              
139             =head1 FUNCTIONS
140              
141             The C<LINQ>, C<Range>, and C<Repeat> functions return LINQ collections,
142             objects implementing the L<LINQ::Collection> interface.
143              
144             The C<< LINQ::END() >> and C<< LINQ::LAST() >> functions are used as
145             signals to control LINQ's iterators and loops.
146              
147             Additional utility functions can be found in L<LINQ::Util>.
148              
149             =over
150              
151             =item C<< LINQ( SOURCE ) >>
152              
153             Creates a LINQ collection from a source. The source may be an existing LINQ
154             collection, which will be returned as-is, an arrayref of items, or a coderef
155             which will be called in scalar context and expected to return a single item
156             each time it is called. It should return the special value C<< LINQ::END() >>
157             to indicate that the end of the collection has been reached.
158              
159             C<LINQ> may be exported, but is not exported by default.
160              
161             =item C<< Range( MIN, MAX ) >>
162              
163             Returns a LINQ collection containing the range of numbers from MIN to MAX.
164             If MIN is undef, it is treated as 0. If MAX is undef, it is treated as positive
165             infinity.
166              
167             If you want a range from 0 to negative infinity, use:
168              
169             my $below_zero = Range( 0, undef )->select( sub { -$_ } );
170              
171             C<Range> may be exported, but is not exported by default.
172              
173             =item C<< Repeat( VALUE, COUNT ) >>
174              
175             Returns a LINQ collection containing the same value multiple times. If COUNT
176             is undef, then it is treated as infinity.
177              
178             C<Repeat> may be exported, but is not exported by default.
179              
180             =item C<< END() >>
181              
182             Returns the special value C<< LINQ::END() >>.
183              
184             C<END> may be exported, but is not exported by default, and I recommend
185             calling it by its fully qualified name for clarity.
186              
187             =item C<< LAST() >>
188              
189             Used by the C<foreach> method of L<LINQ::Collection>. If called otherwise,
190             will die.
191              
192             =back
193              
194             =head1 HISTORY
195              
196             I wrote this back in 2014, but never released it. After a discussion
197             about how nice it would be to have a programming language which used SQL
198             concepts natively, eliminating the need to "map" between how your
199             application handled data and how your database handled data, I remembered
200             this. So I thought I'd push what I had so far onto CPAN and maybe think
201             about reviving it.
202              
203             =head1 BUGS
204              
205             Please report any bugs to
206             L<http://rt.cpan.org/Dist/Display.html?Queue=LINQ>.
207              
208             =head1 SEE ALSO
209              
210             L<LINQ::Collection>, L<LINQ::Util>, L<LINQ::Exception>.
211              
212             L<https://en.wikipedia.org/wiki/Language_Integrated_Query>
213              
214             =head1 AUTHOR
215              
216             Toby Inkster E<lt>tobyink@cpan.orgE<gt>.
217              
218             =head1 COPYRIGHT AND LICENCE
219              
220             This software is copyright (c) 2014, 2021 by Toby Inkster.
221              
222             This is free software; you can redistribute it and/or modify it under
223             the same terms as the Perl 5 programming language system itself.
224              
225             =head1 DISCLAIMER OF WARRANTIES
226              
227             THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
228             WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
229             MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.