File Coverage

lib/Net/API/Stripe/List.pm
Criterion Covered Total %
statement 19 136 13.9
branch 0 70 0.0
condition 0 56 0.0
subroutine 7 28 25.0
pod 10 15 66.6
total 36 305 11.8


line stmt bran cond sub pod time code
1             ##----------------------------------------------------------------------------
2             ## Stripe API - ~/lib/Net/API/Stripe/List.pm
3             ## Version v0.200.3
4             ## Copyright(c) 2020 DEGUEST Pte. Ltd.
5             ## Author: Jacques Deguest <jack@deguest.jp>
6             ## Created 2019/11/02
7             ## Modified 2022/10/25
8             ## All rights reserved.
9             ##
10             ##
11             ## This program is free software; you can redistribute it and/or modify it
12             ## under the same terms as Perl itself.
13             ##----------------------------------------------------------------------------
14             package Net::API::Stripe::List;
15             # To be inherited
16             BEGIN
17             {
18 1     1   463 use strict;
  1         3  
  1         31  
19 1     1   5 use warnings;
  1         2  
  1         29  
20 1     1   5 use parent qw( Net::API::Stripe::Generic );
  1         3  
  1         6  
21 1     1   64 use vars qw( $VERSION );
  1         35  
  1         58  
22             # use B;
23             # use B::Deparse;
24 1     1   22 our( $VERSION ) = 'v0.200.3';
25             };
26              
27 1     1   7 use strict;
  1         2  
  1         21  
28 1     1   4 use warnings;
  1         2  
  1         1614  
29              
30             # Provide our own version of as_hash to avoid our helper methods from being called by Module::Generic::as_hash
31             sub as_hash
32             {
33 0     0 1   my $self = CORE::shift( @_ );
34 0           my $p = {};
35 0 0 0       $p = CORE::shift( @_ ) if( scalar( @_ ) == 1 && ref( $_[0] ) eq 'HASH' );
36 0           my $data = $self->_data;
37 0           my $res = [];
38 0           foreach my $this ( @$data )
39             {
40 0 0 0       if( $self->_is_object( $this ) && $this->can( 'as_hash' ) )
41             {
42 0           my $v = $this->as_hash( $p );
43 0           CORE::push( @$res, $v );
44             }
45             }
46 0           return( { data => $res } );
47             }
48              
49 0     0 1   sub object { return( CORE::shift->_set_get_scalar( 'object', @_ ) ); }
50              
51 0     0 0   sub count { return( CORE::shift->total_count( @_ ) ); }
52              
53 0     0 1   sub data { return( CORE::shift->_data( @_ ) ); }
54              
55 0     0 1   sub has_more { return( CORE::shift->_set_get_boolean( 'has_more', @_ ) ); }
56              
57 0     0 1   sub url { return( CORE::shift->_set_get_uri( 'url', @_ ) ); }
58              
59             sub total_count
60             {
61 0     0 1   my $self = CORE::shift( @_ );
62 0 0         if( @_ )
63             {
64 0           $self->_set_get_scalar( 'total_count', @_ );
65             }
66 0           my $total = $self->_set_get_scalar( 'total_count' );
67 0 0 0       if( !defined( $total ) || !CORE::length( $total ) )
68             {
69 0           return( $self->_data->size );
70             }
71             else
72             {
73 0           return( $total );
74             }
75             }
76              
77             # Additional methods for navigation to be used like $list->next or $list->prev
78             sub get
79             {
80 0     0 1   my $self = CORE::shift( @_ );
81 0 0 0       my $pos = @_ ? int( CORE::shift( @_ ) ) : ( $self->{_pos} || 0 );
82 0           my $data = $self->_data;
83 0           return( $data->[ $pos ] );
84             }
85              
86             sub length
87             {
88 0     0 1   my $self = CORE::shift( @_ );
89 0           my $data = $self->_data;
90 0           return( scalar( @$data ) );
91             }
92              
93             sub next
94             {
95 0     0 1   my $self = CORE::shift( @_ );
96 0 0         $self->{_pos} = -1 if( !exists( $self->{_pos} ) );
97 0           my $data = $self->_data;
98 0 0 0       if( $self->{_pos} + 1 < scalar( @$data ) )
    0 0        
99             {
100 0           return( $data->[ ++$self->{_pos} ] );
101             }
102             elsif( $self->has_more && scalar( @$data ) && $self->_is_object( $data->[-1] ) )
103             {
104 0           my $last_id = $data->[-1]->id;
105 0 0         $self->{_limit} = scalar( @$data ) if( !$self->{_limit} );
106             my $opts =
107             {
108             starting_after => $last_id,
109 0   0       limit => ( $self->{_limit} || 10 ),
110             };
111 0   0       my $hash = $self->parent->get( $self->url, $opts ) || return;
112 0 0   0     return( $self->error( "Cannot find property 'object' in this hash reference: ", sub{ $self->dumper( $hash ) } ) ) if( !CORE::exists( $hash->{object} ) );
  0            
113 0   0       my $class = $self->_object_type_to_class( $hash->{object} ) || return;
114 0   0       my $list = $self->parent->_response_to_object( $class, $hash ) || return;
115 0           $data = $list->_data;
116 0           $self->{data} = $data;
117 0           $self->{_pos} = 0;
118 0 0         return( '' ) if( !scalar( @$data ) );
119 0           return( $data->[ $self->{_pos} ] );
120             }
121             else
122             {
123             # We do not return undef(), which we use to signal errors
124 0           return( '' );
125             }
126             }
127              
128             sub prev
129             {
130 0     0 1   my $self = CORE::shift( @_ );
131 0 0         $self->{_pos} = -1 if( !exists( $self->{_pos} ) );
132 0           my $data = $self->_data;
133 0 0 0       if( $self->{_pos} - 1 >= 0 )
    0          
134             {
135 0           return( $data->[ --$self->{_pos} ] );
136             }
137             elsif( scalar( @$data ) && $self->_is_object( $data->[0] ) )
138             {
139 0           my $first_id = $data->[0]->id;
140 0 0         $self->{_limit} = scalar( @$data ) if( !$self->{_limit} );
141             my $opts =
142             {
143             ending_before => $first_id,
144 0   0       limit => ( $self->{_limit} || 10 ),
145             };
146 0   0       my $hash = $self->parent->get( $self->url, $opts ) || return;
147 0 0   0     return( $self->error( "Cannot find property 'object' in this hash reference: ", sub{ $self->dumper( $hash ) } ) ) if( !CORE::exists( $hash->{object} ) );
  0            
148 0   0       my $class = $self->_object_type_to_class( $hash->{object} ) || return;
149 0   0       my $list = $self->parent->_response_to_object( $class, $hash ) || return;
150 0           $data = $list->_data;
151 0           $self->{data} = $data;
152             # $self->_data( $data );
153 0           $self->{_pos} = $#$data;
154 0 0         return( '' ) if( !scalar( @$data ) );
155 0           return( $data->[ $self->{_pos} ] );
156             }
157             else
158             {
159             # We do not return undef(), which we use to signal errors
160 0           return( '' );
161             }
162             }
163              
164             sub pop
165             {
166 0     0 0   my $self = CORE::shift( @_ );
167 0           return( $self->_data->pop );
168             }
169              
170             sub push
171             {
172 0     0 0   my $self = CORE::shift( @_ );
173 0   0       my $this = CORE::shift( @_ ) || return( $self->error( "Nothing was provided to add to the list of object." ) );
174 0 0         $self->_check( $this ) || return;
175 0           $self->_data->push( $this );
176 0           return( $self );
177             }
178              
179             sub shift
180             {
181 0     0 0   my $self = CORE::shift( @_ );
182 0           return( $self->_data->shift );
183             }
184              
185             sub unshift
186             {
187 0     0 0   my $self = CORE::shift( @_ );
188 0   0       my $this = CORE::shift( @_ ) || return( $self->error( "Nothing was provided to add to the list of object." ) );
189 0 0         $self->_check( $this ) || return;
190 0           $self->_data->unshift( $this );
191 0           return( $self );
192             }
193              
194             sub _check
195             {
196 0     0     my $self = CORE::shift( @_ );
197 0   0       my $this = CORE::shift( @_ ) || return( $self->error( "No data was provided to check." ) );
198 0 0         return( $self->error( "Data provided is not an object." ) ) if( !$self->_is_object( $this ) );
199             # Check if there is any data and if there is find out what kind of object we are holding so we can maintain consistency
200 0           my $data = $self->_data;
201 0           my $obj_name;
202 0 0 0       if( scalar( @$data ) && $self->_is_object( $data->[0] ) )
203             {
204 0 0         $obj_name = $data->[0]->object if( $data->[0]->can( 'object' ) );
205             }
206 0 0         if( $this->can( 'object' ) )
207             {
208 0 0         return( $self->error( "Object provided ($this) has an object type (", $this->object, ") different from the ones currently in our stack ($obj_name)." ) ) if( $this->object ne $obj_name );
209             }
210 0           return( $this );
211             }
212              
213             sub _data
214             {
215 0     0     my $self = CORE::shift( @_ );
216 0           my $field = 'data';
217 0 0         if( @_ )
218             {
219 0           my $ref = CORE::shift( @_ );
220 0 0         return( $self->error( "I was expecting an array ref, but instead got '$ref'. _is_array returned: '", $self->_is_array( $ref ), "'" ) ) if( !$self->_is_array( $ref ) );
221 0           my $arr = [];
222             # Are we provided with an array of existing objects? No need to do anything then
223 0 0         if( $self->_is_object( $ref->[0] ) )
224             {
225             # Unless it is already an array object, we make it one
226 0 0         $arr = $self->_is_object( $ref ) ? $ref : $self->new_array( $ref );
227             }
228             else
229             {
230 0 0         if( scalar( @$ref ) )
231             {
232 0           my $type;
233 0 0         if( $self->_is_object( $ref->[0] ) )
234             {
235 0 0         return( $self->error( "I found an array of objects, but they do not have the method \"object\"." ) ) if( !$ref->[0]->can( 'object' ) );
236 0   0       $type = $ref->[0]->object || return( $self->error( "Somehow, the object property for this object (", $ref->[0], ") is empty." ) );
237             }
238             else
239             {
240 0 0         return( $self->error( "I was expecting an array of hash reference, but instead of hash I found $ref->[0]" ) ) if( ref( $ref->[0] ) ne 'HASH' );
241 0 0   0     return( $self->error( "Found an hash reference in this array, but it is empty: ", sub{ $self->dumper( $ref ) } ) ) if( !scalar( keys( %{$ref->[0]} ) ) );
  0            
  0            
242 0   0 0     $type = $ref->[0]->{object} || return( $self->error( "I was expecting a string in property 'object', but found nothing: ", sub{ $self->dumper( $ref ) } ) );
  0            
243             }
244 0   0       my $class = $self->_object_type_to_class( $type ) || return( $self->error( "Could not find corresponding class for ojbect type \"$type\"." ) );
245 0           $arr = $self->_set_get_object_array_object( $field, $class, $ref );
246             # Store this value used by next() and prev() to replicate the query with the right limit
247             # If initial query made by the user was 10 this array would be 10 or less if there is no more data
248             }
249             }
250 0           $self->{ $field } = $arr;
251             }
252 0 0 0       if( !$self->{ $field } || !$self->_is_object( $self->{ $field } ) )
253             {
254 0 0 0       my $o = $self->new_array( ( defined( $self->{ $field } ) && CORE::length( $self->{ $field } ) ) ? $self->{ $field } : [] );
255 0           $self->{ $field } = $o;
256             }
257 0           return( $self->{ $field } );
258             }
259              
260             1;
261             # NOTE: POD
262             __END__
263              
264             =encoding utf8
265              
266             =head1 NAME
267              
268             Net::API::Stripe::List - Stripe List Object
269              
270             =head1 SYNOPSIS
271              
272             my $stripe = Net::API::Stripe->new( conf_file => 'settings.json' ) || die( Net::API::Stripe->error );
273             my $list = $stripe->customers( 'list' ) || die( $stripe->error );
274             printf( "%d total customer(s) found\n", $list->count );
275             while( my $cust = $list->next )
276             {
277             printf( "Customer %s with e-mail has a balance of %s\n", $cust->name, $cust->email, $cust->balance->format_money( 0, '¥' ) );
278             }
279              
280             =head1 VERSION
281              
282             v0.200.3
283              
284             =head1 DESCRIPTION
285              
286             This is a package with a set of useful methods to be inherited by various Stripe package, such as bellow packages. It can also be used directly in a generic way and this will find out which list of objects this is. This is the case for example when getting the list of customer tax ids in B<Net::API::Stripe::tax_id_list>().
287              
288             =over 4
289              
290             =item L<Net::API::Stripe::Billing::Invoice::Lines>
291              
292             =item L<Net::API::Stripe::Billing::Subscription::Items>
293              
294             =item L<Net::API::Stripe::Charge::Refunds>
295              
296             =item L<Net::API::Stripe::Connect::Account::ExternalAccounts>
297              
298             =item L<Net::API::Stripe::Connect::ApplicationFee::Refunds>
299              
300             =item L<Net::API::Stripe::Connect::Transfer::Reversals>
301              
302             =item L<Net::API::Stripe::Customer::List>
303              
304             =item L<Net::API::Stripe::Customer::Sources>
305              
306             =item L<Net::API::Stripe::Customer::Subscription>
307              
308             =item L<Net::API::Stripe::Customer::TaxIds>
309              
310             =item L<Net::API::Stripe::File::Links>
311              
312             =item L<Net::API::Stripe::Order::Return>
313              
314             =item L<Net::API::Stripe::Payment::Intent::Charges>
315              
316             =item L<Net::API::Stripe::Sigma::ScheduledQueryRun::File::Links>
317              
318             =back
319              
320             =head1 CONSTRUCTOR
321              
322             =head2 new( %ARG )
323              
324             Creates a new L<Net::API::Stripe::List> object.
325             It may also take an hash like arguments, that also are method of the same name.
326              
327             =head1 METHODS
328              
329             =head2 object string
330              
331             This is the string identifier of the type of data. Usually it is "list"
332              
333             =head2 data array
334              
335             This is an array of data, usually objects, but it could vary, which is why this method should be overriden by package inheriting from this one.
336              
337             =head2 has_more boolean
338              
339             This is a boolean value to indicate whether the data is buffered
340              
341             =head2 url URI
342              
343             This is uri to be used to access the next or previous set of data
344              
345             =head2 total_count integer
346              
347             Total size of the array i.e. number of elements
348              
349             =head2 get offset
350              
351             Retrieves the data at the offset specified
352              
353             =head2 length integer
354              
355             The size of the array
356              
357             =head2 next
358              
359             Moves to the next entry in the array
360              
361             =head2 prev
362              
363             Moves to the previous entry in the array
364              
365             =head1 API SAMPLE
366              
367             {
368             "object": "list",
369             "url": "/v1/refunds",
370             "has_more": false,
371             "data": [
372             {
373             "id": "re_fake123456789",
374             "object": "refund",
375             "amount": 30200,
376             "balance_transaction": "txn_fake123456789",
377             "charge": "ch_fake123456789",
378             "created": 1540736617,
379             "currency": "jpy",
380             "metadata": {},
381             "reason": null,
382             "receipt_number": null,
383             "source_transfer_reversal": null,
384             "status": "succeeded",
385             "transfer_reversal": null
386             },
387             {...},
388             {...}
389             ]
390             }
391              
392             =head1 HISTORY
393              
394             =head2 v0.1
395              
396             Initial version
397              
398             =head2 v0.200
399              
400             Change in version numbering
401              
402             =head1 AUTHOR
403              
404             Jacques Deguest E<lt>F<jack@deguest.jp>E<gt>
405              
406             =head1 SEE ALSO
407              
408             Stripe API documentation:
409              
410             L<https://stripe.com/docs/api>
411              
412             =head1 COPYRIGHT & LICENSE
413              
414             Copyright (c) 2020-2020 DEGUEST Pte. Ltd.
415              
416             You can use, copy, modify and redistribute this package and associated
417             files under the same terms as Perl itself.
418              
419             =cut