File Coverage

blib/lib/Devel/Events/Match.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3             package Devel::Events::Match;
4 1     1   22013 use Moose;
  0            
  0            
5              
6             use Carp qw/croak/;
7             use Scalar::Util qw/reftype/;
8              
9             sub match {
10             my ( $self, $cond, @event ) = @_;
11             $self->compile_cond($cond)->(@event);
12             }
13              
14             sub grep {
15             my ( $self, %args ) = @_;
16              
17             my $events = $args{events} or croak "'events' is a required parameter";;
18             my $match = $args{match} or croak "'match' is a required parameter";;
19              
20             my $compiled_cond = $self->compile_cond($match);
21              
22             grep { $compiled_cond->(@$_) } @$events;
23             }
24              
25             sub first {
26             my ( $self, %args ) = @_;
27              
28             my $events = $args{events} or croak "'events' is a required parameter";;
29             my $match = $args{match} or croak "'match' is a required parameter";;
30              
31             my $compiled_cond = $self->compile_cond($match);
32              
33             foreach my $event ( @$events ) {
34             return wantarray ? @$event : $event if $compiled_cond->(@$event);
35             }
36              
37             return;
38             }
39              
40             sub take_while {
41             my ( $self, %args ) = @_;
42              
43             my $match = $args{match} or croak "'match' is a required parameter";;
44              
45             my $compiled_cond = $self->compile_cond($match);
46              
47             $self->limit( %args, to => sub { not $compiled_cond->(@_) }, to_inclusive => 0 );
48             }
49              
50             sub take_until {
51             my ( $self, %args ) = @_;
52              
53             my $match = delete $args{match} or croak "'match' is a required parameter";
54              
55             $self->limit( %args, to => $match, to_inclusive => 0 );
56             }
57              
58              
59             sub drop_while {
60             my ( $self, %args ) = @_;
61              
62             my $match = $args{match} or croak "'match' is a required parameter";;
63              
64             my $compiled_cond = $self->compile_cond($match);
65              
66             $self->limit( %args, from => sub { not $compiled_cond->(@_) });
67             }
68              
69             sub drop_until {
70             my ( $self, %args ) = @_;
71              
72             my $match = delete $args{match} or croak "'match' is a required parameter";;
73              
74             $self->limit( %args, from => $match );
75             }
76              
77             sub limit {
78             my ( $self, %args ) = @_;
79              
80             my ( $events, $from, $to ) = @args{qw/events from to/};
81              
82             croak "'events' is a required parameter" unless $events;
83              
84             $_ = $self->compile_cond($_) for $from, $to;
85              
86             my $to_inclusive = exists $args{to_inclusive} ? $args{to_inclusive} : 1;
87             my $from_inclusive = exists $args{from_inclusive} ? $args{from_inclusive} : 1;
88              
89             my @matches;
90             my @events = @$events;
91              
92             if ( $from ) {
93             before: while ( my $event = shift @events ) {
94             if ( $from->(@$event) ) {
95             push @matches, $event if $from_inclusive;
96             last before;
97             }
98             }
99             }
100              
101             if ( $to ) {
102             match: while ( my $event = shift @events ) {
103             if ( $to->(@$event) ) {
104             push @matches, $event if $to_inclusive;
105             last match;
106             } else {
107             push @matches, $event;
108             }
109             }
110              
111             return @matches;
112             } else {
113             return ( @matches, @events );
114             }
115             }
116              
117             sub chunk {
118             my ( $self, %args ) = @_;
119              
120             my $events = $args{events} or croak "'events' is a required parameter";;
121             my $marker = $args{marker} || $args{match} or croak "'marker' is a required parameter";;
122            
123             my $compiled_cond = $self->compile_cond($marker);
124              
125             my @chunks = ( [ ] );
126              
127             foreach my $event ( @$events ) {
128             push @chunks, [ ] if $compiled_cond->( @$event );
129             push @{ $chunks[-1] }, $event;
130             }
131              
132             shift @chunks if exists $args{first} and not $args{first};
133             pop @chunks if exists $args{last} and not $args{last};
134              
135             return @chunks;
136             }
137              
138             sub compile_cond {
139             my ( $self, $cond ) = @_;
140              
141             if ( ref $cond ) {
142             if ( reftype $cond eq 'CODE' ) {
143             return $cond;
144             } elsif ( reftype $cond eq 'HASH' ) {
145              
146             my %cond = %$cond;
147              
148             foreach my $subcond ( values %cond ) {
149             $subcond = $self->compile_cond($subcond);
150             }
151              
152             return sub {
153             my ( @data ) = @_;
154              
155             if ( @data == 1 and ref $data[0]) {
156             if ( reftype($data[0]) eq 'ARRAY' ) {
157             @data = @{ $data[0] };
158             } elsif ( reftype($data[0]) eq 'HASH' ) {
159             @data = %{ $data[0] };
160             }
161             }
162              
163             my $type = shift @data if @data % 2 == 1;
164              
165             my %data = @data;
166              
167             $data{type} = $type if defined $type;
168              
169             foreach my $key ( keys %cond ) {
170             my $subcond = $cond{$key};
171             return unless $subcond->($data{$key});
172             }
173              
174             return 1;
175             }
176             }
177             } elsif ( defined $cond ) {
178             return sub {
179             my ( $type ) = @_;
180             defined $type and $type eq $cond;
181             }
182             }
183            
184             croak "unknown condition format: $cond";
185             }
186              
187              
188             __PACKAGE__;
189              
190             __END__
191              
192             =pod
193              
194             =head1 NAME
195              
196             Devel::Events::Match - Event matching, splicing and dicing.
197              
198             =head1 SYNOPSIS
199              
200             use Devel::Events::Match;
201              
202             my $matcher = Devel::Events::Match->new;
203              
204             my @matching = $matcher->grep( match => $cond, events => \@events );
205              
206             =head1 DESCRIPTION
207              
208             This class provides event list filtering, chunking etc based on a simple match
209             format.
210              
211             This class is used by L<Devel::Events::Handler::Log::Memory> in order to ease
212             access into the event log.
213              
214             =head1 METHODS
215              
216             =item compile_cond
217              
218             Used to compile condition values into code references.
219              
220             Scalars become equality tests on the first element (event type/name matches this).
221              
222             Hashes become recursive conditions, where each key is matched on the field. The
223             'type' pseudofield is the first element of the event. Every value in the hash
224             gets C<compile_cond> called on it recursively.
225              
226             Code references are returned verbatim.
227              
228             The output is a code reference that can be used to match events.
229              
230             =item first %args
231              
232             Return the first event that matches a certain condition.
233              
234             Requires the C<match> and C<events> parameters.
235              
236             =item grep %args
237              
238             Return the list of events that match a certain condition.
239              
240             Requires the C<match> and C<events> parameters.
241              
242             =item limit from => $cond, to => $cond, %args
243              
244             Return events between two events. If C<from> or C<to> is omitted then it
245             returns all the events up to or from the other filter (C<from> defaults to
246             C<sub { 1 }> and C<to> defaults to C<sub { 0 }>).
247              
248             If either the C<from_inclusive> and C<to_inclusive> parameters are provided and
249             set to false then the range will only begin on the event after the C<from>
250             match and end on the event before the C<to> match respectively.
251              
252             Requires the C<events> parameter.
253              
254             =item chunk %args
255              
256             Cuts the event log into chunks. When C<$marker> matches a new chunk is opened.
257              
258             Requires the C<marker> and C<events> parameters.
259              
260             The C<first> and C<last> parameters, when provided and false will cause the
261             first and last chunks to be dropped, respectively.
262              
263             The first chunk contains all the events up to the first matching one.
264              
265             =item take_while %args
266              
267             =item take_until %args
268              
269             =item drop_while %args
270              
271             =item drop_until %args
272              
273             Require the C<match> and C<events> parameters.
274              
275             =cut
276