File Coverage

blib/lib/WebService/Soundcloud.pm
Criterion Covered Total %
statement 120 261 45.9
branch 28 94 29.7
condition 2 18 11.1
subroutine 25 38 65.7
pod 21 21 100.0
total 196 432 45.3


line stmt bran cond sub pod time code
1             package WebService::Soundcloud;
2            
3 2     2   43972 use 5.006;
  2         9  
  2         88  
4            
5 2     2   11 use strict;
  2         4  
  2         64  
6 2     2   9 use warnings;
  2         8  
  2         58  
7            
8 2     2   10 use Carp;
  2         4  
  2         199  
9 2     2   2069 use LWP::UserAgent;
  2         114931  
  2         68  
10 2     2   22 use URI;
  2         3  
  2         57  
11 2     2   2523 use JSON qw(decode_json);
  2         30183  
  2         14  
12 2     2   2711 use Data::Dumper;
  2         16345  
  2         168  
13 2     2   16 use HTTP::Headers;
  2         6  
  2         65  
14 2     2   22 use Scalar::Util qw(reftype);
  2         5  
  2         7077  
15            
16             # declare domains
17             our %domain_for = (
18             'prod' => 'https://api.soundcloud.com/',
19             'production' => 'https://api.soundcloud.com/',
20             'development' => 'https://api.sandbox-soundcloud.com/',
21             'dev' => 'https://api.sandbox-soundcloud.com/',
22             'sandbox' => 'https://api.sandbox-soundcloud.com/'
23             );
24            
25             our $DEBUG = 0;
26             our %path_for = (
27             'authorize' => 'connect',
28             'access_token' => 'oauth2/token'
29             );
30            
31             our %formats = (
32             '*' => '*/*',
33             'json' => 'application/json',
34             'xml' => 'application/xml'
35             );
36            
37             our $VERSION = '0.04';
38            
39             =pod
40            
41             =head1 NAME
42            
43             WebService::Soundcloud - Thin wrapper around Soundcloud RESTful API!
44            
45             =head1 VERSION
46            
47             Version 0.02
48            
49             =head1 SYNOPSIS
50            
51             #!/usr/bin/perl
52             use WebService::Soundcloud;
53            
54             my $scloud = WebService::Soundcloud->new($client_id, $client_secret,
55             { redirect_uri => 'http://mydomain.com/callback' }
56             );
57            
58             # Now get authorization url
59             my $authorization_url = $scloud->get_authorization_url();
60            
61             # Redirect the user to authorization url
62             use CGI;
63             my $q = new CGI;
64             $q->redirect($authorization_url);
65            
66             # In your '/callback' handler capture code params
67             # Check for error
68             if ($q->param(error)) {
69             die "Authorization Failed: ". $q->param('error');
70             }
71             # Get authorization code
72             my $code = $q->param('code');
73            
74             # Get Access Token
75             my $access_token = $scloud->get_access_token($code);
76            
77             # Save access_token and refresh_token, expires_in, scope for future use
78             my $oauth_token = $access_token->{access_token};
79            
80             # OAuth Dance is completed :-) Have fun now.
81            
82             # Default request and response formats are 'json'
83            
84             # a GET request '/me' - gets users details
85             my $user = $scloud->get('/me');
86            
87             # a PUT request '/me' - updated users details
88             my $user = $scloud->put('/me', encode_json(
89             { 'user' => {
90             'description' => 'Have fun with Perl wrapper to Soundcloud API'
91             } } ) );
92            
93             # Comment on a Track POSt request usage
94             my $comment = $scloud->post('/tracks//comments',
95             { body => 'I love this hip-hop track' } );
96            
97             # Delete a track
98             my $track = $scloud->delete('/tracks/{id}');
99            
100             # Download a track
101             my $file_path = $scloud->download('', $dest_file);
102            
103            
104             =head1 DESCRIPTION
105            
106             This module provides a wrapper around Soundcloud RESTful API to work with
107             different kinds of soundcloud resources. It contains many functions for
108             convenient use rather than standard Soundcloud RESTful API.
109            
110             The complete API is documented at http://developers.soundcloud.com/docs.
111            
112             In order to use this module you will need to register your application
113             with Soundcloud at http://soundcloud.com/you/apps : your application will
114             be given a client ID and a client secret which you will need to use to
115             connect.
116            
117             =head2 METHODS
118            
119             =over 4
120            
121             =item new
122            
123             Returns a newly created C object. The first
124             argument is $client_id, the second argument is $client_secret - these
125             are required and will have been provided when you registered your
126             application with Soundcloud The third optional argument is a
127             HASHREF that contains additional parameters that may be required:
128            
129             =over 4
130            
131             =item redirect_uri
132            
133             This is the URI of your application to which the user will be redirected
134             after they have authorised the connection with Soundcloud. This should
135             be the same as the one provided when you registered your application and
136             will be required for most applications.
137            
138             =back
139            
140             =cut
141            
142             sub new
143             {
144 1     1 1 717 my ($class, $client_id, $client_secret, $options ) = @_;
145            
146 1 0 33     4 if(!defined $client_id && !defined $client_secret )
147             {
148 0         0 croak "Client ID and Secret required";
149             }
150            
151 1 50       3 $options = {} unless defined $options;
152            
153 1         2 my $self = bless $options, $class;
154            
155 1         4 $self->client_id($client_id);
156 1         3 $self->client_secret($client_secret);
157            
158 1 50       3 $options->{debug} = $DEBUG unless ( $options->{debug} );
159            
160            
161 1         3 return $self;
162             }
163            
164             =item client_id
165            
166             Accessor for the Client ID that was provided when you registered your
167             application.
168            
169             =cut
170            
171             sub client_id
172             {
173 2     2 1 3 my ( $self, $client_id ) = @_;
174            
175 2 100       8 if ( defined $client_id )
176             {
177 1         7 $self->{client_id} = $client_id;
178             }
179            
180 2         6 return $self->{client_id};
181             }
182            
183             =item client_secret
184            
185             Accessor for the Client Secret that was provided when you registered
186             your application.
187            
188             =cut
189            
190             sub client_secret
191             {
192 2     2 1 3 my ( $self, $client_secret ) = @_;
193            
194 2 100       5 if ( defined $client_secret )
195             {
196 1         2 $self->{client_secret} = $client_secret;
197             }
198            
199 2         6 return $self->{client_secret};
200             }
201            
202             =item redirect_uri
203            
204             Accessor for the redirect_uri this can be passed as an option to the
205             constructor or supplied later (before any connect call.) This should
206             match to that provided when you registered your application.
207            
208             It is the URI of your application that the user will be redirected
209             (with the authorization code as a parameter,) after they have clicked
210             "Connect" on the soundcloud connect page. This will not be used if
211             you are using the credential based authentication to obtain the OAuth token
212             (e.g if you are an application with no UI that is operating for a single
213             user.)
214            
215             =cut
216            
217             sub redirect_uri
218             {
219 2     2 1 3 my ( $self, $redirect_uri ) = @_;
220            
221 2 50       5 if ( defined $redirect_uri )
222             {
223 0         0 $self->{redirect_uri} = $redirect_uri;
224             }
225            
226 2         6 return $self->{redirect_uri};
227             }
228            
229             =item basic_params
230            
231             This returns a HASHREF that is suitable to be used as the basic parameters
232             in most places, containing the application credentials (ID and Secret) and
233             redirect_uri
234            
235             =cut
236            
237             sub basic_params
238             {
239 1     1 1 2 my ( $self ) = @_;
240            
241 1         3 my $params = {
242             client_id => $self->client_id(),
243             client_secret => $self->client_secret(),
244             };
245            
246 1 50       4 if ( defined $self->redirect_uri() )
247             {
248 1         2 $params->{redirect_uri} = $self->redirect_uri();
249             }
250            
251 1         3 return $params;
252            
253             }
254            
255             =item ua
256            
257             Returns the L object that will be used to connect to the
258             API host
259            
260             =cut
261            
262             sub ua
263             {
264 3     3 1 5 my ( $self ) = @_;
265            
266 3 100       20 if (!defined $self->{user_agent} )
267             {
268 1         9 $self->{user_agent} = LWP::UserAgent->new();
269             }
270            
271 3         2851 return $self->{user_agent};
272             }
273            
274             =item get_authorization_url
275            
276             This method is used to get authorization url, user should be redirected
277             for authenticate from soundcloud. This will return URL to which user
278             should be redirected.
279            
280             =cut
281            
282             sub get_authorization_url
283             {
284 1     1 1 3 my ( $self, $args ) = @_;
285 1         2 my $call = 'get_authorization_url';
286 1         5 my $params = $self->basic_params();
287            
288 1         2 $params->{response_type} = 'code';
289            
290 1 50       4 $params = { %{$params}, %{$args} } if ref($args) eq 'HASH';
  1         3  
  1         4  
291 1         5 my $authorize_url = $self->_build_url( $path_for{'authorize'}, $params );
292 1         6 return $authorize_url;
293             }
294            
295             =item get_access_token
296            
297             This method is used to receive access_token, refresh_token,
298             scope, expires_in details from soundcloud once user is
299             authenticated. access_token, refresh_token should be stored as it should
300             be sent along with every request to access private resources on the
301             user behalf.
302            
303             The argument C<$code> is required unless you are using credential based
304             authentication, and will have been supplied to your C after
305             the user pressed "Connect" on the soundcloud connect page.
306            
307             =cut
308            
309             sub get_access_token
310             {
311 0     0 1 0 my ( $self, $code, $args ) = @_;
312 0         0 my $request;
313 0         0 my $call = 'get_access_token';
314 0         0 my $params = $self->_access_token_params($code);
315            
316 0 0       0 $params = { %{$params}, %{$args} } if ref($args) eq 'HASH';
  0         0  
  0         0  
317 0         0 return $self->_access_token($params);
318             }
319            
320             =item _access_token_params
321            
322             =cut
323            
324             sub _access_token_params
325             {
326 0     0   0 my ( $self, $code ) = @_;
327            
328 0         0 my $params = $self->basic_params();
329            
330 0 0       0 if ( $self->{scope} )
331             {
332 0         0 $params->{scope} = $self->{scope};
333             }
334 0 0 0     0 if ( $self->{username} && $self->{password} )
    0          
335             {
336 0         0 $params->{username} = $self->{username};
337 0         0 $params->{password} = $self->{password};
338 0         0 $params->{grant_type} = 'password';
339             }
340             elsif ( defined $code )
341             {
342 0         0 $params->{code} = $code;
343 0         0 $params->{grant_type} = 'authorization_code';
344             }
345             else
346             {
347 0         0 die "neither credentials or auth code provided";
348             }
349            
350 0         0 return $params;
351             }
352            
353             =item get_access_token_refresh
354            
355             This method is used to get new access_token by exchanging refresh_token
356             before the earlier access_token is expired. You will receive new
357             access_token, refresh_token, scope and expires_in details from
358             soundcloud. access_token, refresh_token should be stored as it should
359             be sent along with every request to access private resources on the
360             user behalf.
361            
362             If a C of 'non-expiring' was supplied at the time the initial tokem
363             was obtained then this should not be necessary.
364            
365             =cut
366            
367             sub get_access_token_refresh
368             {
369 0     0 1 0 my ( $self, $refresh_token, $args ) = @_;
370            
371 0         0 my $params = $self->basic_params();
372            
373 0         0 $params->{refresh_token} = $refresh_token;
374 0         0 $params->{grant_type} = 'refresh_token';
375            
376 0 0       0 $params = { %{$params}, %{$args} } if ref($args) eq 'HASH';
  0         0  
  0         0  
377 0         0 return $self->_access_token($params);
378             }
379            
380             =item request
381            
382             This performs an HTTP request with the $method supplied to the supplied
383             $url. The third argument $headers can be supplied to insert any required
384             headers into the request, if $content is supplied it will be processed
385             appropriately and inserted into the request.
386            
387             An L will be returned and this should be checked to
388             determine the status of the request.
389            
390             =cut
391            
392             sub request
393             {
394 3     3 1 6 my ( $self, $method, $url, $headers, $content ) = @_;
395 3         25 my $req = HTTP::Request->new( $method, $url, $headers );
396            
397 3 50       568 if ( defined $content )
398             {
399 0         0 my $u = URI->new();
400 0         0 $u->query_form($content);
401 0         0 my $query = $u->query();
402 0         0 $req->content($query);
403             }
404 3         46 $self->log($req->as_string());
405 3         14 return $self->ua()->request($req);
406             }
407            
408             =item get_object
409            
410             This returns a decoded object corresponding to the URI given
411            
412             It will for the response_format to 'json' for the request as
413             parsing the XML is tricky given no schema.
414            
415             =cut
416            
417             sub get_object
418             {
419 0     0 1 0 my ( $self, $url, $params, $headers ) = @_;
420            
421 0         0 my $obj;
422            
423 0         0 my $save_response_format = $self->response_format();
424 0         0 $self->response_format('json');
425            
426 0         0 my $res = $self->get( $url, $params, $headers );
427            
428 0 0       0 if ( $res->is_success() )
429             {
430 0         0 $obj = decode_json( $res->decoded_content() );
431             }
432            
433 0         0 $self->response_format($save_response_format);
434            
435 0         0 return $obj;
436             }
437            
438             =item get_list
439            
440             This returns a decoded LIST REF of the list method specified by URI
441            
442             Currently this will force response_format to 'json' as parsin the XML
443             is tricky without a schema.
444            
445             =cut
446            
447             sub get_list
448             {
449 1     1 1 150652 my ( $self, $url, $params, $headers ) = @_;
450            
451 1         3 my $ret = [];
452 1         2 my $continue = 1;
453 1         3 my $offset = 0;
454 1         2 my $limit = 50;
455            
456 1         5 my $save_response_format = $self->response_format();
457 1         4 $self->response_format('json');
458            
459 1 50       8 if ( !defined $params )
460             {
461 1         3 $params = {};
462             }
463 1         5 while ($continue)
464             {
465 1         2 $params->{limit} = $limit;
466 1         4 $params->{offset} = $offset;
467            
468 1         6 my $res = $self->get( $url, $params, $headers );
469            
470 1 50       170749 if ( $res->is_success() )
471             {
472 0 0       0 if (defined(my $obj = $self->parse_content( $res->decoded_content())))
473             {
474 0 0       0 if (defined (my $type = reftype($obj) ) )
475             {
476 0 0       0 if ( $type eq 'ARRAY' )
    0          
477             {
478 0         0 $offset += $limit;
479 0         0 $continue = scalar @{$obj};
  0         0  
480             }
481             elsif ( $type eq 'HASH' )
482             {
483 0 0       0 if ( exists $obj->{collection} )
484             {
485 0 0       0 if(!defined($url = $obj->{next_href}))
486             {
487 0         0 $continue = 0;
488             }
489 0         0 $obj = $obj->{collection};
490             }
491             else
492             {
493 0         0 croak "not a collection";
494             }
495             }
496             else
497             {
498 0         0 croak "Unexpected $type reference instead of list";
499             }
500 0         0 push @{$ret}, @{$obj};
  0         0  
  0         0  
501             }
502             }
503             else
504             {
505 0         0 $continue = 0;
506             }
507             }
508             else
509             {
510 1         19 warn $res->request()->uri();
511 1         198 die $res->status_line();
512             }
513             }
514            
515 0         0 $self->response_format($save_response_format);
516            
517 0         0 return $ret;
518             }
519            
520             =item get(, , )
521            
522             This method is used to dispatch GET request on the give URL(first argument).
523             second argument is an anonymous hash request parameters to be send along with GET request.
524             The third optional argument() is used to send headers.
525             This method will return HTTP::Response object
526            
527             =cut
528            
529             sub get
530             {
531 3     3 1 517854 my ( $self, $path, $params, $extra_headers ) = @_;
532 3         13 my $url = $self->_build_url( $path, $params );
533 3         14 my $headers = $self->_build_headers($extra_headers);
534 3         16 return $self->request( 'GET', $url, $headers );
535             }
536            
537             =item I<$OBJ>->post(, , )
538            
539             This method is used to dispatch POST request on the give URL(first argument).
540             second argument is the content to be posted to URL.
541             The third optional argument() is used to send headers.
542             This method will return HTTP::Response object
543            
544             =cut
545            
546             sub post
547             {
548 0     0 1 0 my ( $self, $path, $content, $extra_headers ) = @_;
549 0         0 my $url = $self->_build_url($path);
550 0         0 my $headers = $self->_build_headers($extra_headers);
551 0         0 return $self->request( 'POST', $url, $headers, $content );
552             }
553            
554             =item I<$OBJ>->put(, , )
555            
556             This method is used to dispatch PUT request on the give URL(first argument).
557             second argument is the content to be sent to URL.
558             The third optional argument() is used to send headers.
559             This method will return HTTP::Response object
560            
561             =cut
562            
563             sub put
564             {
565 0     0 1 0 my ( $self, $path, $content, $extra_headers ) = @_;
566 0         0 my $url = $self->_build_url($path);
567            
568             # Set Content-Length Header as well otherwise nginx will throw 411 Length Required ERROR
569 0 0       0 $extra_headers->{'Content-Length'} = 0
570             unless $extra_headers->{'Content-Length'};
571 0         0 my $headers = $self->_build_headers($extra_headers);
572 0         0 return $self->request( 'PUT', $url, $headers, $content );
573             }
574            
575             =item I<$OBJ>->delete(, , )
576            
577             This method is used to dispatch DELETE request on the give URL(first argument).
578             second optional argument is an anonymous hash request parameters to be send
579             along with DELETE request. The third optional argument() is used to
580             send headers. This method will return HTTP::Response object
581            
582             =cut
583            
584             sub delete
585             {
586 0     0 1 0 my ( $self, $path, $params, $extra_headers ) = @_;
587 0         0 my $url = $self->_build_url( $path, $params );
588 0         0 my $headers = $self->_build_headers($extra_headers);
589 0         0 return $self->request( 'DELETE', $url, $headers );
590             }
591            
592             =item I<$OBJ>->download(, )
593            
594             This method is used to download a particular track id given as first argument.
595             second argument is name of the destination path where the downloaded track will
596             be saved to. This method will return the file path of downloaded track.
597            
598             =cut
599            
600             sub download
601             {
602 0     0 1 0 my ( $self, $trackid, $file ) = @_;
603 0         0 my $url = $self->_build_url( "/tracks/$trackid/download", {});
604 0         0 $self->log($url);
605            
606 0         0 my $rc = 0;
607             # Set Response format to */*
608             # Memorize old response format
609 0         0 my $old_response_format = $self->{response_format};
610 0         0 $self->response_format('*');
611 0         0 my $headers = $self->_build_headers();
612 0         0 $self->ua()->add_handler('response_redirect',\&_our_redirect);
613 0         0 my $response = $self->request( 'GET', $url, $headers );
614            
615 0         0 $self->ua()->remove_handler('response_redirect');
616            
617 0 0       0 if (!($rc = $response->is_success()))
618             {
619            
620 0         0 $self->log($response->request()->as_string());
621 0         0 $self->log($response->as_string());
622 0         0 foreach my $red ( $response->redirects() )
623             {
624 0         0 $self->log($red->request()->as_string());
625 0         0 $self->log($red->as_string());
626             }
627             }
628             # Reset response format
629 0         0 $self->{response_format} = $formats{$old_response_format};
630 0         0 return $rc;
631             }
632            
633             =item _our_redirect
634            
635             This subroutime is intended to be used as a callback on 'response_redirect'
636             It processes the response to make a new request for the redirect with the
637             Authorization header removed so that EC3 doesn't get confused.
638            
639             =cut
640            
641             sub _our_redirect
642             {
643 0     0   0 my ( $response, $ua, $h ) = @_;
644            
645 0         0 my $code = $response->code();
646            
647 0         0 my $req;
648            
649 0 0       0 if (_is_redirect($code) )
650             {
651 0         0 my $referal = $response->request()->clone();
652 0         0 $referal->remove_header('Host','Cookie','Referer','Authorization');
653            
654 0 0       0 if (my $ref_uri = $response->header('Location'))
655             {
656 0         0 my $uri = URI->new($ref_uri);
657 0         0 $referal->header('Host' => $uri->host());
658 0         0 $referal->uri($uri);
659 0 0       0 if ( $ua->redirect_ok($referal, $response) )
660             {
661 0         0 $req = $referal;
662             }
663             }
664             }
665            
666 0         0 return $req;
667             }
668            
669             =item _is_redirect
670            
671             Helper subroutine to determine if the code indicates a redirect.
672            
673             =cut
674            
675             sub _is_redirect
676             {
677 0     0   0 my ($code) = @_;
678            
679 0         0 my $rc = 0;
680            
681 0 0       0 if ( defined $code )
682             {
683 0 0 0     0 if ( $code == &HTTP::Status::RC_MOVED_PERMANENTLY
      0        
      0        
684             or $code == &HTTP::Status::RC_FOUND
685             or $code == &HTTP::Status::RC_SEE_OTHER
686             or $code == &HTTP::Status::RC_TEMPORARY_REDIRECT )
687             {
688 0         0 $rc = 1;
689             }
690             }
691 0         0 return $rc;
692             }
693            
694             =item request_format
695            
696             Accessor for the request format to be used. Acceptable values are 'json' and
697             'xml'. The default is 'json'.
698             =cut
699            
700             sub request_format
701             {
702 3     3 1 6 my ( $self, $format ) = @_;
703            
704 3 100       25 if ($format)
    100          
705             {
706 1         2 $self->{request_format} = $format;
707             }
708             elsif(!defined $self->{request_format})
709             {
710 1         3 $self->{request_format} = 'json';
711             }
712            
713 3         11 return $self->{request_format};
714             }
715            
716             =item response_format
717            
718             Accessor for the response format to be used. The allowed values are 'json'
719             and 'xml'. The default is 'json'. This will cause the appropriate setting
720             of the Accept header in requests.
721            
722             =cut
723            
724             sub response_format
725             {
726 5     5 1 488 my ( $self, $format ) = @_;
727 5 100       21 if ($format)
    100          
728             {
729 2         5 $self->{response_format} = $format;
730             }
731             elsif (!defined $self->{response_format})
732             {
733 1         3 $self->{response_format} = 'json';
734             }
735 5         17 return $self->{response_format};
736             }
737            
738             =item parse_content
739            
740             This will return the parsed object corresponding to the response content
741             passed as asn argument. It will select the appropriate parser based on the
742             value of 'response_format'.
743            
744             It will return undef if there is a problem with the parsing.
745            
746             =cut
747            
748             sub parse_content
749             {
750 0     0 1 0 my ( $self, $content ) = @_;
751            
752 0         0 my $object;
753            
754 0 0       0 if ( defined $content )
755             {
756            
757             eval
758 0         0 {
759 0 0       0 if ( $self->response_format() eq 'json' )
    0          
760             {
761 0         0 $object = decode_json($content);
762             }
763             elsif ( $self->response_format() eq 'xml' )
764             {
765 0         0 require XML::Simple;
766 0         0 my $xs = XML::Simple->new();
767 0         0 $object = $xs->XMLin($content);
768             }
769             };
770 0 0       0 if ( $@ )
771             {
772 0         0 warn $@;
773             }
774             }
775 0         0 return $object;
776             }
777            
778             =back
779            
780             =head1 INTERNAL SUBROUTINES/METHODS
781            
782             Please do not use these internal methods directly. They are internal to
783             WebService::Soundcloud module itself. These can be renamed/deleted/updated at any point
784             of time in future.
785            
786             =over 4
787            
788             =item I<$OBJ>->_access_token()
789            
790             This method is used to get access_token from soundcloud. This will be called
791             from get_access_token and get_access_token_refresh methods.
792            
793             =cut
794            
795             sub _access_token
796             {
797 0     0   0 my ( $self, $params ) = @_;
798 0         0 my $call = '_access_token';
799 0         0 my $url = $self->_access_token_url();
800 0         0 my $headers = $self->_build_headers();
801 0         0 my $response = $self->request( 'POST', $url, $headers, $params );
802 0 0       0 die "Failed to fetch "
803             . $url . " "
804             . $response->content() . " ("
805             . $response->status_line() . ")"
806             unless $response->is_success;
807 0         0 my $uri = URI->new;
808 0         0 my $access_token = decode_json( $response->decoded_content );
809            
810             # store access_token, refresh_token
811 0         0 foreach (qw(access_token refresh_token expire expires_in))
812             {
813 0         0 $self->{$_} = $access_token->{$_};
814             }
815            
816             # set access_token, refresh_token
817 0         0 return $access_token;
818             }
819            
820             =item I<$OBJ>->_access_token_url()
821            
822             This method is used to get access_token_url of soundcloud RESTful API.
823             This will be called from _access_token method.
824            
825             =cut
826            
827             sub _access_token_url
828             {
829 0     0   0 my ( $self, $params ) = @_;
830 0         0 my $url = $self->_build_url( $path_for{'access_token'}, $params );
831 0         0 return $url;
832             }
833            
834             =item I<$OBJ>->_build_url(, PARAMS>)
835            
836             This method is used to prepare absolute URL for a given path and request parameters.
837            
838             =cut
839            
840             sub _build_url
841             {
842 4     4   10 my ( $self, $path, $params ) = (@_);
843 4         8 my $call = '_build_url';
844            
845             # get base URL
846 4 50       19 my $base_url =
847             $self->{development} ? $domain_for{development} : $domain_for{production};
848            
849             #$params->{client_id} = $self->client_id();
850             # Prepare URI Object
851 4         29 my $uri = URI->new_abs( $path, $base_url );
852            
853 4 50       10935 if ( $uri->query() )
854             {
855 0 0       0 $params = { %{$params || {}}, $uri->query_form() };
  0         0  
856             }
857 4         41 $uri->query_form( %{$params} );
  4         30  
858 4         246 return $uri;
859             }
860            
861             =item I<$OBJ>->_build_headers()
862            
863             This method is used to set extra headers to the current HTTP Request.
864            
865             =cut
866            
867             sub _build_headers
868             {
869 3     3   6 my ( $self, $extra ) = @_;
870 3         25 my $headers = HTTP::Headers->new;
871            
872 3 50       45 $headers->header( 'Accept' => $formats{ $self->{response_format} } )
873             if ( $self->{response_format} );
874 3 50       245 $headers->header( 'Content-Type' => $formats{ $self->{request_format} } . '; charset=utf-8' )
875             if ( $self->{request_format} );
876 3 50 33     127 $headers->header( 'Authorization' => "OAuth " . $self->{access_token} )
877             if ( $self->{access_token} && !$extra->{no_auth});
878 3         137 foreach my $key ( %{$extra} )
  3         13  
879             {
880 0         0 $headers->header( $key => $extra->{$key} );
881             }
882 3         8 return $headers;
883             }
884            
885             =item I<$OBJ>->log()
886            
887             This method is used to write some text to STDERR.
888            
889             =cut
890            
891             sub log
892             {
893 3     3 1 379 my ( $self, $msg ) = @_;
894 3 50       12 if ( $self->{debug} )
895             {
896 0           print STDERR "$msg\n";
897             }
898             }
899            
900             =back
901            
902             =head1 AUTHOR
903            
904             Mohan Prasad Gutta, C<< >>
905            
906             =head1 CONTRIBUTORS
907            
908             Jonathan Stowe C
909            
910             =head1 BUGS
911            
912             Parts of this are extremely difficult to test properly so there almost
913             certainly will be bugs, please feel free to fix and send me a patch if
914             you find one.
915            
916             =head1 SUPPORT
917            
918             You can find documentation for this module with the perldoc command.
919             perldoc WebService::Soundcloud
920             You can also look for information at:
921            
922             =over 4
923            
924             =item * RT: CPAN's request tracker (report bugs here)
925             L
926            
927             =item * AnnoCPAN: Annotated CPAN documentation
928             L
929            
930             =item * CPAN Ratings
931             L
932            
933             =item * Search CPAN
934             L
935            
936             =back
937            
938             =head1 LICENSE AND COPYRIGHT
939            
940             Copyright 2013 Mohan Prasad Gutta.
941             This program is free software; you can redistribute it and/or modify it
942             under the terms of either: the GNU General Public License as published
943             by the Free Software Foundation; or the Artistic License.
944             See http://dev.perl.org/licenses/ for more information.
945            
946             =cut
947            
948             1;