File Coverage

blib/lib/Twitter/API/Role/RequestArgs.pm
Criterion Covered Total %
statement 52 52 100.0
branch 26 28 92.8
condition 5 6 83.3
subroutine 10 10 100.0
pod 4 4 100.0
total 97 100 97.0


line stmt bran cond sub pod time code
1             package Twitter::API::Role::RequestArgs;
2             # ABSTRACT: API request method helpers
3             $Twitter::API::Role::RequestArgs::VERSION = '1.0006';
4 5     5   12229 use 5.14.1;
  5         18  
5 5     5   30 use warnings;
  5         10  
  5         156  
6 5     5   25 use Carp;
  5         11  
  5         291  
7 5     5   25 use Moo::Role;
  5         9  
  5         38  
8 5     5   1840 use Ref::Util qw/is_arrayref is_hashref/;
  5         12  
  5         240  
9 5     5   481 use namespace::clean;
  5         9069  
  5         39  
10              
11             requires 'request';
12              
13             #pod =method request_with_id
14             #pod
15             #pod Transforms an argument list with a required C<screen_name> or C<user_id>,
16             #pod optionally passed as a leading, positional argument, a hashref argument.
17             #pod
18             #pod If a hashref follows the optional plain scalar, the user_id or screen_name is
19             #pod added to it. Otherwise a new hashref is created and inserted into C<@_>.
20             #pod
21             #pod If the optional plain scalar argument is missing, and there is hashref of
22             #pod arguments, or if the hashref does not contain the key C<screen_name> or
23             #pod C<user_id>, request_with_id croaks.
24             #pod
25             #pod Examples:
26             #pod
27             #pod $self->request_with_id(get => 'some/endpoint', 'foo');
28             #pod # is transformed to:
29             #pod $self->request(get => 'some/endpoint', { screen_name => 'foo' });
30             #pod
31             #pod $self->request_with_id(get => 'some/endpoint', 8575429);
32             #pod # is transformed to:
33             #pod $self->request(get => 'some/endpoint', { user_id => 8675429 });
34             #pod
35             #pod $self->request_with_id(get => 'some/endpoint', {
36             #pod screen_name => 'semifor',
37             #pod });
38             #pod # is transformed to:
39             #pod $self->request(get => 'some/endpoint', { screen_name => 'semifor' });
40             #pod
41             #pod $self->request_with_id(get => 'some/endpoint', {
42             #pod foo => 'bar',
43             #pod }); ### croaks ###
44             #pod
45             #pod =cut
46              
47             # if there is a positional arg, it's an :ID (screen_name or user_id)
48             sub request_with_id {
49 2     2 1 2009 splice @_, 1, 0, [];
50 2 100 66     12 push @{$_[1]}, ':ID' if @_ > 4 && !is_hashref($_[4]);
  1         3  
51 2         12 goto $_[0]->can('request_with_pos_args');
52             }
53              
54             #pod =method request_with_pos_args
55             #pod
56             #pod Transforms a list of required arguments, optionally provided positionally in a
57             #pod determined order, into a hashref of named arguments. If a hashref follows the
58             #pod positional arguments, the named arguments are added to it. Otherwise, a new
59             #pod hashref in inserted into C<@_>.
60             #pod
61             #pod Zero or more of the required arguments may be provided positionally, as long as
62             #pod the appear in the specified order. I any of the required arguments are not
63             #pod provided positionally, they must be provided in the hashref or
64             #pod request_with_pos_args croaks.
65             #pod
66             #pod The positional name C<:ID> is treated specially. It is transformed to
67             #pod C<user_id> if the value it represents contains only digits. Otherwise, it is
68             #pod transformed to C<screen_name>.
69             #pod
70             #pod Examples:
71             #pod
72             #pod $self->request_with_pos_args(
73             #pod [ 'id', 'name' ], get => 'some/endpoint',
74             #pod '007', 'Bond'
75             #pod );
76             #pod # is transformed to:
77             #pod $self->request(get => 'some/endpoint', {
78             #pod id => '007',
79             #pod name => 'Bond',
80             #pod });
81             #pod
82             #pod $self->request_with_pos_args(
83             #pod [ 'id', 'name' ], get => 'some/endpoint',
84             #pod '007', { name => 'Bond' }
85             #pod );
86             #pod # is also transformed to:
87             #pod $self->request(get => 'some/endpoint', {
88             #pod id => '007',
89             #pod name => 'Bond',
90             #pod });
91             #pod
92             #pod $self->request_with_pos_args(
93             #pod [ ':ID', 'status' ], get => 'some/endpoint',
94             #pod 'alice', 'down the rabbit hole'
95             #pod );
96             #pod # is transformed to:
97             #pod $self->request(get => 'some/endpoint', {
98             #pod sreen_name => 'alice',
99             #pod status => 'down the rabbit hole',
100             #pod });
101             #pod
102             #pod =cut
103              
104             sub request_with_pos_args {
105 14     14 1 27139 my $self = shift;
106              
107 14         55 $self->request($self->normalize_pos_args(@_));
108             }
109              
110             #pod =method normalize_pos_args
111             #pod
112             #pod Helper method for C<request_with_pos_args>. Takes the same arguments described in
113             #pod C<request_with_pos_args> above, and returns a list of arguments ready for a
114             #pod call to C<request>.
115             #pod
116             #pod Individual methods in L<Twitter::API::Trait::ApiMethods> use
117             #pod C<normalize_pos_args> if they need to do further processing on the args hashref
118             #pod before calling C<request>.
119             #pod
120             #pod =cut
121              
122             sub normalize_pos_args {
123 15     15 1 19 my $self = shift;
124 15         25 my @pos_names = shift;
125 15         23 my $http_method = shift;
126 15         21 my $path = shift;
127 15         20 my %args;
128              
129             # names can be a single value or an arrayref
130 15 100       42 @pos_names = @{ $pos_names[0] } if is_arrayref($pos_names[0]);
  12         24  
131              
132             # gather positional arguments and name them
133 15         34 while ( @pos_names ) {
134 15 100 100     74 last if @_ == 0 || is_hashref($_[0]);
135 10         32 $args{shift @pos_names} = shift;
136             }
137              
138             # get the optional, following args hashref and expand it
139 15 100       21 my %args_hash; %args_hash = %{ shift() } if is_hashref($_[0]);
  15         50  
  9         28  
140              
141             # extract any required args if we still have names
142 15         40 while ( my $name = shift @pos_names ) {
143 6 100       14 if ( $name eq ':ID' ) {
144 3 100       26 $name = exists $args_hash{screen_name} ? 'screen_name' : 'user_id';
145             croak 'missing required screen_name or user_id'
146 3 100       306 unless exists $args_hash{$name};
147             }
148 4 100       86 croak "missing required '$name' arg" unless exists $args_hash{$name};
149 3         10 $args{$name} = delete $args_hash{$name};
150             }
151              
152             # name the :ID value (if any) based on its value
153 12 100       46 if ( my $id = delete $args{':ID'} ) {
154 5 100       31 $args{$id =~/\D/ ? 'screen_name' : 'user_id'} = $id;
155             }
156              
157             # merge in the remaining optional values
158 12         39 for my $name ( keys %args_hash ) {
159             croak "'$name' specified in both positional and named args"
160 7 100       203 if exists $args{$name};
161 5         12 $args{$name} = $args_hash{$name};
162             }
163              
164 10         47 return ($http_method, $path, \%args, @_);
165             }
166              
167             #pod =method flatten_list_args([ $key | \@keys ], \%args)
168             #pod
169             #pod Some Twitter API arguments take a list of values as a string of comma separated
170             #pod items. To allow callers to pass an array reference of items instead, this
171             #pod method is used to flatten array references to strings. The key or keys identify
172             #pod which values to flatten in the C<\%args> hash reference, if they exist.
173             #pod
174             #pod =cut
175              
176             sub flatten_list_args {
177 9     9 1 31 my ( $self, $keys, $args ) = @_;
178              
179 9 50       49 for my $key ( is_arrayref($keys) ? @$keys : $keys ) {
180 9 100       47 if ( my $value = $args->{$key} ) {
181 2 50       19 $args->{$key} = join ',' => @$value if is_arrayref($value);
182             }
183             }
184             }
185              
186             1;
187              
188             __END__
189              
190             =pod
191              
192             =encoding UTF-8
193              
194             =head1 NAME
195              
196             Twitter::API::Role::RequestArgs - API request method helpers
197              
198             =head1 VERSION
199              
200             version 1.0006
201              
202             =head1 SYNOPSIS
203              
204             package MyApiMethods;
205             use Moo::Role;
206              
207             sub timeline {
208             shift->request_with_id(get => 'statuses/user_timeline, @_);
209             }
210              
211             Then, in your application code:
212              
213             use Twitter::API;
214              
215             my $client = Twitter::API->new_with_traits(
216             traits => '+MyApiMethods',
217             %othe_new_options,
218             );
219              
220             my $statuses = $client->timeline('semifor');
221              
222             # equivalent to:
223             my $statuses = $client->get('statuses/user_timeline', {
224             screen_name => 'semifor',
225             });
226              
227             =head1 DESCRIPTION
228              
229             Helper methods for implementers of custom traits for creating concise Twitter
230             API methods. Used in L<Twitter::API::Trait::ApiMethods>.
231              
232             =head1 METHODS
233              
234             =head2 request_with_id
235              
236             Transforms an argument list with a required C<screen_name> or C<user_id>,
237             optionally passed as a leading, positional argument, a hashref argument.
238              
239             If a hashref follows the optional plain scalar, the user_id or screen_name is
240             added to it. Otherwise a new hashref is created and inserted into C<@_>.
241              
242             If the optional plain scalar argument is missing, and there is hashref of
243             arguments, or if the hashref does not contain the key C<screen_name> or
244             C<user_id>, request_with_id croaks.
245              
246             Examples:
247              
248             $self->request_with_id(get => 'some/endpoint', 'foo');
249             # is transformed to:
250             $self->request(get => 'some/endpoint', { screen_name => 'foo' });
251              
252             $self->request_with_id(get => 'some/endpoint', 8575429);
253             # is transformed to:
254             $self->request(get => 'some/endpoint', { user_id => 8675429 });
255              
256             $self->request_with_id(get => 'some/endpoint', {
257             screen_name => 'semifor',
258             });
259             # is transformed to:
260             $self->request(get => 'some/endpoint', { screen_name => 'semifor' });
261              
262             $self->request_with_id(get => 'some/endpoint', {
263             foo => 'bar',
264             }); ### croaks ###
265              
266             =head2 request_with_pos_args
267              
268             Transforms a list of required arguments, optionally provided positionally in a
269             determined order, into a hashref of named arguments. If a hashref follows the
270             positional arguments, the named arguments are added to it. Otherwise, a new
271             hashref in inserted into C<@_>.
272              
273             Zero or more of the required arguments may be provided positionally, as long as
274             the appear in the specified order. I any of the required arguments are not
275             provided positionally, they must be provided in the hashref or
276             request_with_pos_args croaks.
277              
278             The positional name C<:ID> is treated specially. It is transformed to
279             C<user_id> if the value it represents contains only digits. Otherwise, it is
280             transformed to C<screen_name>.
281              
282             Examples:
283              
284             $self->request_with_pos_args(
285             [ 'id', 'name' ], get => 'some/endpoint',
286             '007', 'Bond'
287             );
288             # is transformed to:
289             $self->request(get => 'some/endpoint', {
290             id => '007',
291             name => 'Bond',
292             });
293              
294             $self->request_with_pos_args(
295             [ 'id', 'name' ], get => 'some/endpoint',
296             '007', { name => 'Bond' }
297             );
298             # is also transformed to:
299             $self->request(get => 'some/endpoint', {
300             id => '007',
301             name => 'Bond',
302             });
303              
304             $self->request_with_pos_args(
305             [ ':ID', 'status' ], get => 'some/endpoint',
306             'alice', 'down the rabbit hole'
307             );
308             # is transformed to:
309             $self->request(get => 'some/endpoint', {
310             sreen_name => 'alice',
311             status => 'down the rabbit hole',
312             });
313              
314             =head2 normalize_pos_args
315              
316             Helper method for C<request_with_pos_args>. Takes the same arguments described in
317             C<request_with_pos_args> above, and returns a list of arguments ready for a
318             call to C<request>.
319              
320             Individual methods in L<Twitter::API::Trait::ApiMethods> use
321             C<normalize_pos_args> if they need to do further processing on the args hashref
322             before calling C<request>.
323              
324             =head2 flatten_list_args([ $key | \@keys ], \%args)
325              
326             Some Twitter API arguments take a list of values as a string of comma separated
327             items. To allow callers to pass an array reference of items instead, this
328             method is used to flatten array references to strings. The key or keys identify
329             which values to flatten in the C<\%args> hash reference, if they exist.
330              
331             =head1 AUTHOR
332              
333             Marc Mims <marc@questright.com>
334              
335             =head1 COPYRIGHT AND LICENSE
336              
337             This software is copyright (c) 2015-2021 by Marc Mims.
338              
339             This is free software; you can redistribute it and/or modify it under
340             the same terms as the Perl 5 programming language system itself.
341              
342             =cut