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 115     115   13757499 use 5.006;
  115         1186  
2 115     115   604 use strict;
  115         199  
  115         2494  
3 115     115   512 use warnings;
  115         211  
  115         7463  
4              
5             package LINQ;
6              
7             our $AUTHORITY = 'cpan:TOBYINK';
8             our $VERSION = '0.002';
9              
10 115     115   52239 use Exporter::Shiny qw( LINQ Range Repeat END );
  115         61719  
  115         707  
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.002';
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.002';
31             my $x = 666;
32             bless( \$x );
33             &Internals::SvREADONLY( \$x, !!1 );
34             \$x;
35             };
36              
37             BEGIN {
38 115     115   27875 *LINQ::END = sub () { $end };
  1899     1899   17253  
39            
40             *LINQ::LAST = sub () {
41 6 100   6   2558 if ( $IN_LOOP ) {
42 3         26 die( $last );
43             }
44 3         1684 require LINQ::Exception;
45 3         32 'LINQ::Exception::CallerError'->throw(
46             message => "Cannot call LINQ::LAST outside foreach",
47             );
48 115         55458 };
49             } #/ BEGIN
50              
51             sub LINQ ($) {
52 784     784 1 138654 my $data = shift;
53 784         1627 my $ref = ref( $data );
54            
55 784 100       2155 if ( $ref eq 'ARRAY' ) {
56 469 100       1078 if ( $FORCE_ITERATOR ) {
57 216         966 my @data = @$data;
58 216         25338 require LINQ::Iterator;
59 216 100   1251   1279 return LINQ::Iterator::->new( sub { @data ? shift( @data ) : LINQ::END } );
  1251         3169  
60             }
61            
62 253         25724 require LINQ::Array;
63 253         887 return LINQ::Array::->new( $data );
64             } #/ if ( $ref eq 'ARRAY' )
65            
66 315 100       799 if ( $ref eq 'CODE' ) {
67 312         24895 require LINQ::Iterator;
68 312         1139 return LINQ::Iterator::->new( $data );
69             }
70            
71 3         16 require Scalar::Util;
72 3 100 100     38 if ( Scalar::Util::blessed( $data ) and $data->DOES( 'LINQ::Collection' ) ) {
73 1         28 return $data;
74             }
75            
76 2         461 require LINQ::Exception;
77 2         17 'LINQ::Exception::CallerError'->throw(
78             message => "Cannot create LINQ object from '$data'",
79             );
80             } #/ sub LINQ ($)
81              
82             sub Range {
83 7     7 1 293 my ( $min, $max ) = @_;
84            
85 7 100       24 my $value = defined( $min ) ? $min : 0;
86            
87 7 100       22 if ( not defined $max ) {
88 2     15   15 return LINQ sub { $value++ };
  15         31  
89             }
90            
91 5 100   35   31 return LINQ sub { return LINQ::END if $value > $max; $value++ };
  35         69  
  31         66  
92             } #/ sub Range
93              
94             sub Repeat {
95 6     6 1 281 my ( $value, $count ) = @_;
96            
97 6 100       20 if ( not defined $count ) {
98 5     35   33 return LINQ sub { $value };
  35         71  
99             }
100            
101 1 100   8   9 return LINQ sub { return LINQ::END if $count-- <= 0; $value };
  8         19  
  7         13  
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.