File Coverage

lib/JIRA/REST/Class/Iterator.pm
Criterion Covered Total %
statement 14 50 28.0
branch 0 8 0.0
condition 0 12 0.0
subroutine 5 10 50.0
pod 4 4 100.0
total 23 84 27.3


line stmt bran cond sub pod time code
1             package JIRA::REST::Class::Iterator;
2 4     4   1626 use parent qw( JIRA::REST::Class::Abstract );
  4         4  
  4         16  
3 4     4   206 use strict;
  4         4  
  4         55  
4 4     4   12 use warnings;
  4         4  
  4         80  
5 4     4   58 use 5.010;
  4         8  
6              
7             our $VERSION = '0.10';
8             our $SOURCE = 'CPAN';
9             ## $SOURCE = 'GitHub'; # COMMENT
10             # the line above will be commented out by Dist::Zilla
11              
12             # ABSTRACT: A helper class for L<JIRA::REST::Class|JIRA::REST::Class> that represents a JIRA query as an object. Allows the user to iterate over the results and retrieve them one by one. Wraps L<JIRA::REST|JIRA::REST>'s L<set_search_iterator|JIRA::REST/"set_search_iterator PARAMS"> and L<next_issue|JIRA::REST/next_issue> methods to make them a bit more object-like.
13              
14 4     4   12 use Readonly 2.04;
  4         41  
  4         2819  
15              
16             Readonly my @ACCESSORS => qw( total fetched iterator_args
17             restart_if_lt_total seen_cache );
18              
19             __PACKAGE__->mk_accessors( @ACCESSORS );
20              
21             sub init {
22 0     0 1   my $self = shift;
23 0           $self->SUPER::init( @_ );
24              
25 0           my $args = $self->iterator_args;
26              
27             # if we weren't passed a maxResults, use the default for the class object
28 0 0         unless ( exists $args->{maxResults} ) {
29 0           $args->{maxResults} = $self->jira->maxResults;
30             }
31              
32 0 0         if ( exists $args->{restart_if_lt_total} ) {
33 0           $self->restart_if_lt_total( $args->{restart_if_lt_total} );
34 0           delete $args->{restart_if_lt_total};
35             }
36              
37 0           $self->seen_cache( {} );
38 0           $self->fetched( 0 );
39 0           $self->set_search_iterator; # fetch the first bunch of issues
40 0           return;
41             }
42              
43             #pod =method B<issue_count>
44             #pod
45             #pod A count of the number of issues matched by the query.
46             #pod
47             #pod =cut
48              
49 0     0 1   sub issue_count { return shift->total }
50              
51             #pod =method B<next>
52             #pod
53             #pod The next issue returned by the query, as a
54             #pod L<JIRA::REST::Class::Issue|JIRA::REST::Class::Issue> object. If there are
55             #pod no more issues matched by the query, this method returns an undefined value.
56             #pod
57             #pod If the L</restart_if_lt_total> method is set to true and the number of
58             #pod issues fetched is less than the total number of issues matched by the query
59             #pod (see the L</issue_count> method), this method will rerun the query and keep
60             #pod returning issues. This is particularly useful if you are transforming a
61             #pod number of issues through an iterator, and the transformation causes the
62             #pod issues to no longer match the query.
63             #pod
64             #pod =cut
65              
66             sub next { ## no critic (ProhibitBuiltinHomonyms)
67 0     0 1   my $self = shift;
68              
69 0           my $issue = $self->_get_next_unseen_issue;
70              
71 0 0 0       if ( !$issue
      0        
72             && $self->fetched < $self->total
73             && $self->restart_if_lt_total ) {
74              
75             # ok, we didn't get as many results as we were promised,
76             # so let's try the search again
77 0           $self->set_search_iterator;
78 0           $issue = $self->_get_next_unseen_issue;
79             }
80              
81 0 0         if ( $issue ) {
82 0           $self->fetched( $self->fetched + 1 );
83 0           return $self->factory->make_object( 'issue', { data => $issue } );
84             }
85              
86 0           return;
87             }
88              
89             #pod =internal_method B<_get_next_unseen_issue>
90             #pod
91             #pod Method to consolidate code that fetches issues without duplication
92             #pod
93             #pod =cut
94              
95             sub _get_next_unseen_issue {
96 0     0     my $self = shift;
97 0           my $issue = $self->JIRA_REST->next_issue;
98              
99             # loop if we got an issue but we've seen it already
100 0   0       while ( $issue && $self->seen_cache->{ $issue->{id} }++ ) {
101 0           $issue = $self->JIRA_REST->next_issue;
102             }
103              
104 0           return $issue;
105             }
106              
107             #pod =method B<restart_if_lt_total>
108             #pod
109             #pod This getter/setter method tells the iterator whether to restart the search
110             #pod if the number of issues found is less than the issue count returned by the
111             #pod initial search.
112             #pod
113             #pod =cut
114              
115             #pod =internal_method B<set_search_iterator>
116             #pod
117             #pod Method that is used to restart a query that has run out of results prematurely.
118             #pod
119             #pod =cut
120              
121             sub set_search_iterator {
122 0     0 1   my $self = shift;
123 0   0       my $args = shift || $self->iterator_args;
124              
125 0           $self->JIRA_REST->set_search_iterator( $args );
126              
127             # fetch results using code borrowed from JIRA::REST
128 0           my $iter = $self->JIRA_REST->{iter};
129 0           $iter->{params}{startAt} = $iter->{offset};
130             $iter->{results}
131 0           = $self->JIRA_REST->POST( '/search', undef, $iter->{params} );
132              
133             # since there seems to be a problem with getting all the results in one
134             # search, let's track how many we get so we can re-search if necessary
135 0           $self->{total} = $self->JIRA_REST->{iter}->{results}->{total};
136 0           $self->{fetched} = 0;
137 0           return;
138             }
139              
140             1;
141              
142             __END__
143              
144             =pod
145              
146             =encoding UTF-8
147              
148             =for :stopwords Packy Anderson Alexey Melezhik
149              
150             =head1 NAME
151              
152             JIRA::REST::Class::Iterator - A helper class for L<JIRA::REST::Class|JIRA::REST::Class> that represents a JIRA query as an object. Allows the user to iterate over the results and retrieve them one by one. Wraps L<JIRA::REST|JIRA::REST>'s L<set_search_iterator|JIRA::REST/"set_search_iterator PARAMS"> and L<next_issue|JIRA::REST/next_issue> methods to make them a bit more object-like.
153              
154             =head1 VERSION
155              
156             version 0.10
157              
158             =head1 METHODS
159              
160             =head2 B<issue_count>
161              
162             A count of the number of issues matched by the query.
163              
164             =head2 B<next>
165              
166             The next issue returned by the query, as a
167             L<JIRA::REST::Class::Issue|JIRA::REST::Class::Issue> object. If there are
168             no more issues matched by the query, this method returns an undefined value.
169              
170             If the L</restart_if_lt_total> method is set to true and the number of
171             issues fetched is less than the total number of issues matched by the query
172             (see the L</issue_count> method), this method will rerun the query and keep
173             returning issues. This is particularly useful if you are transforming a
174             number of issues through an iterator, and the transformation causes the
175             issues to no longer match the query.
176              
177             =head2 B<restart_if_lt_total>
178              
179             This getter/setter method tells the iterator whether to restart the search
180             if the number of issues found is less than the issue count returned by the
181             initial search.
182              
183             =head1 INTERNAL METHODS
184              
185             =head2 B<_get_next_unseen_issue>
186              
187             Method to consolidate code that fetches issues without duplication
188              
189             =head2 B<set_search_iterator>
190              
191             Method that is used to restart a query that has run out of results prematurely.
192              
193             =head1 RELATED CLASSES
194              
195             =over 2
196              
197             =item * L<JIRA::REST::Class|JIRA::REST::Class>
198              
199             =item * L<JIRA::REST::Class::Abstract|JIRA::REST::Class::Abstract>
200              
201             =item * L<JIRA::REST::Class::Issue|JIRA::REST::Class::Issue>
202              
203             =back
204              
205             =head1 AUTHOR
206              
207             Packy Anderson <packy@cpan.org>
208              
209             =head1 COPYRIGHT AND LICENSE
210              
211             This software is Copyright (c) 2017 by Packy Anderson.
212              
213             This is free software, licensed under:
214              
215             The Artistic License 2.0 (GPL Compatible)
216              
217             =cut