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