File Coverage

blib/lib/Pithub/Result.pm
Criterion Covered Total %
statement 107 109 98.1
branch 34 38 89.4
condition 7 9 77.7
subroutine 29 30 96.6
pod 11 11 100.0
total 188 197 95.4


line stmt bran cond sub pod time code
1             package Pithub::Result;
2             our $AUTHORITY = 'cpan:PLU';
3              
4             # ABSTRACT: Github v3 result object
5              
6 24     24   191 use Moo;
  24         75  
  24         217  
7              
8             our $VERSION = '0.01041';
9              
10 24     24   19284 use Pithub::ResultSet ();
  24         69  
  24         574  
11 24     24   150 use JSON::MaybeXS qw( JSON );
  24         56  
  24         1223  
12 24     24   149 use URI ();
  24         69  
  24         447  
13 24     24   116 use Carp qw( confess croak );
  24         53  
  24         42507  
14              
15             sub _isa_isa_maker {
16 48     48   195 my $class = shift;
17             return sub {
18 580 50   580   11791 confess "must be an instance of $class but isn't a reference"
19             if !ref $_[0];
20             confess "must be an instance of $class, but is a " . ref $_[0]
21 580 50       859 unless eval { $_[0]->isa($class) };
  580         11642  
22 48         316 };
23             }
24              
25              
26             has 'auto_pagination' => (
27             default => sub { 0 },
28             is => 'rw',
29             );
30              
31              
32             has 'content' => (
33             builder => '_build_content',
34             clearer => 'clear_content',
35             is => 'ro',
36             lazy => 1,
37             );
38              
39              
40             has 'first_page_uri' => (
41             builder => '_build_first_page_uri',
42             clearer => 'clear_first_page_uri',
43             is => 'ro',
44             lazy => 1,
45             );
46              
47              
48             has 'last_page_uri' => (
49             builder => '_build_last_page_uri',
50             clearer => 'clear_last_page_uri',
51             is => 'ro',
52             lazy => 1,
53             );
54              
55              
56             has 'next_page_uri' => (
57             builder => '_build_next_page_uri',
58             clearer => 'clear_next_page_uri',
59             is => 'ro',
60             lazy => 1,
61             );
62              
63              
64             has 'prev_page_uri' => (
65             builder => '_build_prev_page_uri',
66             clearer => 'clear_prev_page_uri',
67             is => 'ro',
68             lazy => 1,
69             );
70              
71              
72             has 'response' => (
73             handles => {
74             code => 'code',
75             raw_content => 'content',
76             request => 'request',
77             success => 'is_success',
78             },
79             is => 'ro',
80             isa => _isa_isa_maker('HTTP::Response'),
81             required => 1,
82             );
83              
84              
85             # required for next_page etc
86             has '_request' => (
87             is => 'ro',
88             isa => sub {
89             croak 'must be a coderef, but is ' . ref $_[0]
90             unless ref $_[0] eq 'CODE';
91             },
92             required => 1,
93             );
94              
95             # required for next
96             has '_iterator' => (
97             builder => '_build__iterator',
98             clearer => '_clear_iterator',
99             is => 'ro',
100             isa => _isa_isa_maker(Pithub::ResultSet::),
101             lazy => 1,
102             );
103              
104              
105             has 'utf8' => (
106             is => 'ro',
107             default => 1,
108             );
109              
110             has '_json' => (
111             builder => '_build__json',
112             is => 'ro',
113             isa => sub {
114             confess "$_[0] is not a suitable JSON object"
115             unless eval { $_[0]->can('decode') };
116             },
117             lazy => 1,
118             );
119              
120              
121             sub count {
122 4     4 1 3536 my ($self) = @_;
123 4 100       92 return 0 unless $self->success;
124 3         169 my $content = $self->content;
125 3 100 100     305 if ( ref $content eq 'HASH' && scalar keys %$content == 0 ) {
126 1         5 return 0;
127             }
128 2         51 return $self->_iterator->getLength;
129             }
130              
131              
132             sub first {
133 2     2 1 580 my ($self) = @_;
134 2         40 my $content = $self->content;
135 2 100       162 if ( ref $content eq 'ARRAY' ) {
136 1         7 return $content->[0];
137             }
138 1         10 return $content;
139             }
140              
141              
142             sub first_page {
143 5     5 1 45 my ($self) = @_;
144 5 100       105 return unless $self->first_page_uri;
145 2         43 return $self->_paginate( $self->first_page_uri );
146             }
147              
148              
149             sub get_page {
150 6     6 1 1574 my ( $self, $page ) = @_;
151              
152             # First we need to get an URI we can work with and replace
153             # the page GET parameter properly with the given value. If
154             # we cannot get the first or last page URI, then there is
155             # only one page.
156 6   100     146 my $uri_str = $self->first_page_uri || $self->last_page_uri;
157 6 100       118 return unless $uri_str;
158              
159 5         20 my $uri = URI->new($uri_str);
160 5         358 my %query = $uri->query_form;
161              
162 5         238 $query{page} = $page;
163              
164             my $options = {
165             prepare_request => sub {
166 5     5   12 my ($request) = @_;
167 5         16 %query = ( $request->uri->query_form, %query );
168 5         286 $request->uri->query_form(%query);
169             },
170 5         29 };
171              
172 5         18 return $self->_request->(
173             method => 'GET',
174             path => $uri->path,
175             options => $options,
176             );
177             }
178              
179              
180             sub last_page {
181 5     5 1 4632 my ($self) = @_;
182 5 100       128 return unless $self->last_page_uri;
183 3         74 return $self->_paginate( $self->last_page_uri );
184             }
185              
186              
187             ## no critic (Subroutines::ProhibitBuiltinHomonyms)
188             sub next {
189 118     118 1 3220 my ($self) = @_;
190 118         1941 my $row = $self->_iterator->getNext;
191 118 100       284 return $row if $row;
192 10 100       39 if ( $self->auto_pagination ) {
193 8         16 my $result = $self->next_page;
194 8 100       102 return unless $result;
195 6         19 $self->_reset;
196 6         16 $self->{response} = $result->response;
197 6         94 return $self->_iterator->getNext;
198             }
199 2         7 return;
200             }
201             ## use critic
202              
203              
204             sub next_page {
205 13     13 1 3048 my ($self) = @_;
206 13 100       242 return unless $self->next_page_uri;
207 9         229 return $self->_paginate( $self->next_page_uri );
208             }
209              
210              
211             sub prev_page {
212 5     5 1 4268 my ($self) = @_;
213 5 100       119 return unless $self->prev_page_uri;
214 2         43 return $self->_paginate( $self->prev_page_uri );
215             }
216              
217              
218             sub etag {
219 0     0 1 0 my ($self) = @_;
220 0         0 return $self->response->header('ETag');
221             }
222              
223              
224             sub ratelimit {
225 1     1 1 1210 my ($self) = @_;
226 1         8 return $self->response->header('X-RateLimit-Limit');
227             }
228              
229              
230             sub ratelimit_remaining {
231 1     1 1 651 my ($self) = @_;
232 1         7 return $self->response->header('X-RateLimit-Remaining');
233             }
234              
235             sub _build_content {
236 20     20   10903 my ($self) = @_;
237 20 100       320 if ( $self->raw_content ) {
238 19         1052 return $self->_json->decode( $self->raw_content );
239             }
240 1         45 return {};
241             }
242              
243             sub _build_first_page_uri {
244 7     7   98 return shift->_get_link_header('first');
245             }
246              
247             sub _build__iterator {
248 12     12   150 my ($self) = @_;
249 12         190 my $content = $self->content;
250 12 100       793 $content = [$content] unless ref $content eq 'ARRAY';
251 12         57 return Pithub::ResultSet->new($content);
252             }
253              
254             sub _build_last_page_uri {
255 7     7   75 return shift->_get_link_header('last');
256             }
257              
258             sub _build_next_page_uri {
259 13     13   120 return shift->_get_link_header('next');
260             }
261              
262             sub _build_prev_page_uri {
263 6     6   1925 return shift->_get_link_header('prev');
264             }
265              
266             sub _build__json {
267 13     13   145 my ($self) = @_;
268 13         49 return JSON->new->utf8( $self->utf8 );
269             }
270              
271             sub _get_link_header {
272 33     33   69 my ( $self, $type ) = @_;
273             return $self->{_get_link_header}{$type}
274 33 100       129 if $self->{_get_link_header}{$type};
275 24         97 my $link = $self->response->header('Link');
276 24 100       1215 return unless $link;
277 19 50       131 return unless $link =~ /(next|first|last|prev)/;
278 19         83 foreach my $item ( split /,/, $link ) {
279 48         238 my @result = $item =~ /<([^>]+)>; rel="([^"]+)"/g;
280 48 50 33     157 next if !$result[1] || !$result[0];
281 48         134 $self->{_get_link_header}{ $result[1] } = $result[0];
282             }
283 19         149 return $self->{_get_link_header}{$type};
284             }
285              
286             sub _paginate {
287 16     16   133 my ( $self, $uri_str ) = @_;
288 16         50 my $uri = URI->new($uri_str);
289             my $options = {
290             prepare_request => sub {
291 16     16   30 my ($request) = @_;
292 16         44 my %query = ( $request->uri->query_form, $uri->query_form );
293 16         1566 $request->uri->query_form(%query);
294             },
295 16         1170 };
296 16         51 return $self->_request->(
297             method => 'GET',
298             path => $uri->path,
299             options => $options,
300             );
301             }
302              
303             sub _reset {
304 6     6   11 my ($self) = @_;
305 6         108 $self->clear_content;
306 6         122 $self->clear_first_page_uri;
307 6         115 $self->clear_last_page_uri;
308 6         112 $self->clear_next_page_uri;
309 6         122 $self->clear_prev_page_uri;
310 6         153 $self->_clear_iterator;
311 6         48 delete $self->{_get_link_header};
312             }
313              
314             1;
315              
316             __END__