File Coverage

blib/lib/WWW/Ohloh/API.pm
Criterion Covered Total %
statement 19 21 90.4
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 26 28 92.8


line stmt bran cond sub pod time code
1             package WWW::Ohloh::API;
2              
3 15     15   3093948 use warnings;
  15         35  
  15         1199  
4 15     15   80 use strict;
  15         27  
  15         470  
5 15     15   67 use Carp;
  15         35  
  15         1221  
6              
7 15     15   8701 use Object::InsideOut;
  15         837063  
  15         84  
8              
9 15     15   1617193 use LWP::UserAgent;
  15         6394581  
  15         563  
10 15     15   14232 use Readonly;
  15         46273  
  15         968  
11 15     15   10649 use XML::LibXML;
  0            
  0            
12             use Params::Validate qw(:all);
13              
14             use WWW::Ohloh::API::Account;
15             use WWW::Ohloh::API::Analysis;
16             use WWW::Ohloh::API::Project;
17             use WWW::Ohloh::API::Projects;
18             use WWW::Ohloh::API::Languages;
19             use WWW::Ohloh::API::ActivityFact;
20             use WWW::Ohloh::API::ActivityFacts;
21             use WWW::Ohloh::API::Kudos;
22             use WWW::Ohloh::API::ContributorLanguageFact;
23             use WWW::Ohloh::API::Enlistments;
24             use WWW::Ohloh::API::Factoid;
25             use WWW::Ohloh::API::SizeFact;
26             use WWW::Ohloh::API::Stack;
27              
28             use Digest::MD5 qw/ md5_hex /;
29              
30             our $VERSION = '0.3.2';
31              
32             Readonly our $OHLOH_URL => 'http://www.ohloh.net/';
33              
34             our $useragent_signature = "WWW-Ohloh-API/$VERSION";
35              
36             my @api_key_of : Field : Std(api_key) : Arg(api_key);
37             my @api_version_of : Field : Default(1); # for now, there's only v1
38              
39             my @user_agent_of : Field;
40              
41             my @debugging : Field : Arg(debug) : Default(0) : Std(debug);
42              
43             my @parser_of : Field;
44              
45             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
46              
47             sub fetch_messages {
48             my $self = shift;
49              
50             require WWW::Ohloh::API::Messages;
51              
52             return WWW::Ohloh::API::Messages->new( ohloh => $self, @_ );
53             }
54              
55             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
56              
57             sub get_account_stack {
58             my $self = shift;
59              
60             my $id = shift;
61              
62             $id = md5_hex($id) if -1 < index $id, '@'; # it's an email
63              
64             my ( $url, $xml ) =
65             $self->_query_server("accounts/$id/stacks/default.xml");
66              
67             return WWW::Ohloh::API::Stack->new(
68             ohloh => $self,
69             request_url => $url,
70             xml => $xml->findnodes('stack[1]'),
71             );
72             }
73              
74             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
75              
76             sub get_project_stacks {
77             my $self = shift;
78              
79             my $project = shift;
80              
81             my ( $url, $xml ) = $self->_query_server("projects/$project/stacks.xml");
82              
83             return map {
84             WWW::Ohloh::API::Stack->new(
85             ohloh => $self,
86             request_url => $url,
87             xml => $_,
88             )
89             } $xml->findnodes('//result/stack');
90             }
91              
92             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
93              
94             sub get_size_facts {
95             my $self = shift;
96              
97             my ( $project_id, $analysis_id ) =
98             validate_pos( @_, 1, { default => 'latest' }, );
99              
100             my ( $url, $xml ) = $self->_query_server(
101             "projects/$project_id/analyses/$analysis_id/size_facts.xml");
102              
103             return map {
104             WWW::Ohloh::API::SizeFact->new(
105             ohloh => $self,
106             request_url => $url,
107             xml => $_
108             )
109             } $xml->findnodes('//size_fact');
110              
111             }
112              
113             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
114              
115             sub get_account {
116             my $self = shift;
117              
118             my ( $type, $id ) = @_;
119              
120             $type eq 'id'
121             or $type eq 'email'
122             or croak "first argument must be 'id' or 'email'";
123              
124             $id = md5_hex($id) if $type eq 'email';
125              
126             my ( $url, $xml ) = $self->_query_server("accounts/$id.xml");
127              
128             return WWW::Ohloh::API::Account->new(
129             ohloh => $self,
130             request_url => $url,
131             xml => $xml->findnodes('account[1]'),
132             );
133             }
134              
135             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
136              
137             sub get_enlistments {
138             my $self = shift;
139             my %arg = @_;
140              
141             return WWW::Ohloh::API::Enlistments->new(
142             ohloh => $self,
143             project_id => $arg{project_id},
144             ( sort => $arg{sort} ) x !!$arg{sort},
145             );
146             }
147              
148             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
149              
150             sub get_factoids {
151             my $self = shift;
152              
153             my $project_id = shift;
154              
155             my ( $url, $xml ) =
156             $self->_query_server("projects/$project_id/factoids.xml");
157              
158             return map {
159             WWW::Ohloh::API::Factoid->new(
160             ohloh => $self,
161             request_url => $url,
162             xml => $_
163             )
164             } $xml->findnodes('//factoid');
165             }
166              
167             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
168              
169             sub get_contributor_language_facts {
170             my $self = shift;
171              
172             my %param = validate(
173             @_,
174             { project_id => 1,
175             contributor_id => 1,
176             } );
177              
178             my ( $url, $xml ) = $self->_query_server(
179             "projects/$param{project_id}/contributors/$param{contributor_id}.xml"
180             );
181              
182             return map {
183             WWW::Ohloh::API::ContributorLanguageFact->new(
184             ohloh => $self,
185             request_url => $url,
186             xml => $_
187             )
188             } $xml->findnodes('//contributor_language_fact');
189              
190             }
191              
192             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
193              
194             sub get_kudos {
195             my $self = shift;
196             my ( $type, $id ) = @_;
197              
198             $type eq 'id'
199             or $type eq 'email'
200             or croak "first argument must be 'id' or 'email'";
201              
202             $id = md5_hex($id) if $type eq 'email';
203              
204             return WWW::Ohloh::API::Kudos->new(
205             ohloh => $self,
206             id => $id,
207             );
208             }
209              
210             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
211              
212             sub get_project {
213             my $self = shift;
214             my $id = shift;
215              
216             my ( $url, $xml ) = $self->_query_server("projects/$id.xml");
217              
218             return WWW::Ohloh::API::Project->new(
219             ohloh => $self,
220             request_url => $url,
221             xml => $xml->findnodes('project[1]'),
222             );
223             }
224              
225             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
226              
227             sub get_projects {
228             my $self = shift;
229             my %arg = validate( @_, { query => 0, sort => 0, max => 0 } );
230              
231             return WWW::Ohloh::API::Projects->new(
232             ohloh => $self,
233             query => $arg{query},
234             sort => $arg{sort},
235             max => $arg{max},
236             );
237             }
238              
239             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
240              
241             sub get_analysis {
242             my $self = shift;
243             my $project = shift;
244              
245             $_[0] ||= 'latest';
246              
247             my ( $url, $xml ) =
248             $self->_query_server("projects/$project/analyses/$_[0].xml");
249              
250             my $analysis = WWW::Ohloh::API::Analysis->new(
251             request_url => $url,
252             xml => $xml->findnodes('analysis[1]'),
253             );
254              
255             unless ( $analysis->project_id == $project ) {
256             croak "analysis $_[0] doesn't apply to project $project";
257             }
258              
259             return $analysis;
260             }
261              
262             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
263              
264             sub get_languages {
265             my $self = shift;
266             my %arg = @_;
267              
268             return WWW::Ohloh::API::Languages->new(
269             ohloh => $self,
270             ( sort => $arg{sort} ) x !!$arg{sort},
271             );
272             }
273              
274             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
275              
276             sub get_language {
277             my $self = shift;
278             my $id = shift;
279              
280             my ( $url, $xml ) = $self->_query_server("languages/$id.xml");
281              
282             return WWW::Ohloh::API::Language->new(
283             request_url => $url,
284             xml => $xml->findnodes('language[1]'),
285             );
286              
287             }
288              
289             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
290              
291             sub get_activity_facts {
292             my $self = shift;
293             my ( $project, $analysis ) =
294             validate_pos( @_, 1, { default => 'latest' }, );
295              
296             return WWW::Ohloh::API::ActivityFacts->new(
297             ohloh => $self,
298             project => $project,
299             analysis => $analysis,
300             );
301             }
302              
303             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
304              
305             sub _ua {
306             my $self = shift;
307             my $ua;
308             unless ( $ua = $user_agent_of[$$self] ) {
309             $ua = $user_agent_of[$$self] = LWP::UserAgent->new;
310             $ua->agent($useragent_signature);
311             }
312             return $ua;
313             }
314              
315             sub _parser {
316             my $self = shift;
317             return $parser_of[$$self] ||= XML::LibXML->new;
318             }
319              
320             sub _query_server {
321             my $self = shift;
322             my $url = shift;
323             my %param = $_[0] ? %{ $_[0] } : ();
324              
325             $param{api_key} = $self->get_api_key
326             or croak "api key not configured";
327              
328             $param{v} = $api_version_of[$$self];
329              
330             $url = $OHLOH_URL . $url;
331              
332             $url .= '?' . join '&', map { "$_=$param{$_}" } keys %param;
333              
334             warn "querying ohloh server with $url" if $debugging[$$self];
335              
336             # TODO: beef up here for failures
337             my $request = HTTP::Request->new( GET => $url );
338             my $response = $self->_ua->request($request);
339              
340             unless ( $response->is_success ) {
341             croak "http query to Ohloh server failed: " . $response->status_line;
342             }
343              
344             my $result = $response->content;
345              
346             my $dom = eval { $self->_parser->parse_string($result) }
347             or croak "server didn't feed back valid xml: $@";
348              
349             if ( $dom->findvalue('/response/status/text()') ne 'success' ) {
350             croak "query to Ohloh server failed: ",
351             $dom->findvalue('/response/status/text()');
352             }
353              
354             return $url, $dom->findnodes('/response/result[1]');
355             }
356              
357             1; # Magic true value required at end of module
358             __END__
359              
360             =head1 NAME
361              
362             WWW::Ohloh::API - Ohloh API implementation
363              
364             =head1 SYNOPSIS
365              
366             use WWW::Ohloh::API;
367              
368             my $ohloh = WWW::Ohloh::API->new( api_key => $my_api_key );
369             my $account $ohloh->get_account( id => 12933 );
370              
371             print $account->name;
372              
373             =head1 DESCRIPTION
374              
375             This module is a Perl interface to the Ohloh API as defined at
376             http://www.ohloh.net/api/getting_started.
377              
378             =head1 METHODS
379              
380             =head2 new( [ api_key => $api_key ] )
381              
382             Create a new WWW::Ohloh::API object. To be able to retrieve information
383             from the Ohloh server, an api key must be either passed to the constructor
384             or set via the L<set_api_key> method.
385              
386             my $ohloh = WWW::Ohloh::API->new( api_key => $your_key );
387              
388             =head2 get_account( [ id | email ] => $account_id )
389              
390             Return the account associated with the $account_id as a
391             L<WWW::Ohloh::API::Account>
392             object. If no such account exists, an error is thrown.
393             The $accound_id can either be specified as the Ohloh id number,
394             or the email address associated with the account.
395              
396             my $account = $ohloh->get_account( id => 12933 );
397             my $other_accound = $ohloh->get_account( email => 'foo@bar.com' );
398              
399              
400             =head2 get_project( $id )
401              
402             Return the project having the Ohloh id I<$id> as a
403             L<WWW::Ohloh::API::Project>. If no such project exists,
404             an error is thrown.
405              
406             my $project = $ohloh->get_project( 1234) ;
407             print $project->name;
408              
409             =head2 get_projects( query => $query, sort => $sorting_order, max => $nbr )
410              
411             Return a set of projects as a L<WWW::Ohloh::API::Projects> object.
412              
413             =head3 Parameters
414              
415             =over
416              
417             =item query
418              
419             If provided, only the projects matching the query string are returned.
420             A project matches the query string is any of its name, description
421             or tags does.
422              
423             =item sort
424              
425             If provided, the projects will be returned according to the specified
426             sorting order. Valid values are
427             'created_at', 'description', 'id', 'name', 'stack_count',
428             'updated_at', 'created_at_reverse',
429             'description_reverse', 'id_reverse', 'name_reverse',
430             'stack_count_reverse' or 'updated_at_reverse'. If no sorting order
431             is explicitly given, 'id' is the default.
432              
433             =item max
434              
435             If given, the project set will returns at most I<$nbr> projects.
436              
437             # get top ten stacked projects
438             my @top = $ohloh->get_projects( max => 10, sort => 'stack_count' )->all;
439              
440             =back
441              
442             =head2 get_languages( sort => $order )
443              
444             Return the languages known to Ohloh a set of L<WWW::Ohloh::API::Language>
445             objects.
446              
447             An optional I<sort> parameter can be passed to the method. The valid
448             I<$order>s it accepts are
449             C<total>, C<code>, C<projects>, C<comment_ratio>,
450             C<contributors>, C<commits> and C<name>. If I<sort> is not explicitly called,
451             projects are returned in alphabetical order of C<name>s.
452              
453             =head2 get_activity_facts( $project_id, $analysis )
454              
455             Return a set of activity facts computed out of the project associated
456             with the I<$project_id> as a L<WWW::Ohloh::API::ActivityFacts> object.
457              
458             The optional argument I<$analysis> can be either an Ohloh analysis id
459             (which must be an analysis associated to the project) or the keyword
460             'latest'. By default the latest analysis will be queried.
461              
462             =head2 get_contributor_language_facts( project_id => $p_id, contributor_id => $c_id )
463              
464             my @facts = $ohloh->get_contributor_language_facts(
465             project_id => 1234,
466             contributor_id => 5678
467             );
468              
469             Return the list of contributor language facts associated to the
470             contributor I<$c_id> for the project I<$p_id>.
471              
472             =head2 get_enlistments( project_id => $id )
473              
474             Returns the list of enlistements pertaining to the
475             given project as an L<WWW::Ohloh::API::Enlistment> object.
476              
477             my $enlistments = $ohloh->get_enlistments( project_id => 1234 );
478              
479             while ( my $enlistment = $enlistments->next ) {
480             # do stuff with $enlistment...
481             }
482              
483             =head2 get_size_facts( $project_id, $analysis_id )
484              
485             Return the list of L<WWW::Ohloh::API::SizeFact> objects pertaining to the
486             given project and analysis. If I<$analysis_id> is not provided, it defaults
487             to the latest analysis done on the project.
488              
489             =head2 get_project_stacks( $project_id )
490              
491             Return the list of stacks containing the project as
492             L<WWW::Ohloh::API::Stack>
493             objects.
494              
495             =head2 get_account_stack( $account_id )
496              
497             Return the stack associated with the account as an
498             L<WWW::Ohloh::API::Stack> object.
499              
500             =head2 fetch_messages( [ account | project ] => I<$id> )
501              
502             Returns the messages associated to the given account or project
503             as a L<WWW::Ohloh::API::Messages> object.
504              
505             =head1 SEE ALSO
506              
507             =over
508              
509             =item *
510              
511             L<WWW::Ohloh::API::Project>,
512             L<WWW::Ohloh::API::Projects>,
513             L<WWW::Ohloh::API::Account>,
514             L<WWW::Ohloh::API::KudoScore>,
515             L<WWW::Ohloh::API::Languages>,
516             L<WWW::Ohloh::API::Language>.
517              
518             =item *
519              
520             Ohloh API reference: http://www.ohloh.net/api/getting_started
521              
522             =item *
523              
524             How to obtain an Ohloh API key: http://www.ohloh.net/api_keys/new
525              
526             =back
527              
528             =head1 VERSION
529              
530             This document describes WWW::Ohloh::API version 0.3.2
531              
532             =head1 BUGS AND LIMITATIONS
533              
534             WWW::Ohloh::API is very extremely alpha quality. It'll improve,
535             but till then: I<Caveat emptor>.
536              
537             Please report any bugs or feature requests to
538             C<bug-www-ohloh-api@rt.cpan.org>, or through the web interface at
539             L<http://rt.cpan.org>.
540              
541              
542             =head1 AUTHOR
543              
544             Yanick Champoux C<< <yanick@cpan.org> >>
545              
546             =head1 LICENCE AND COPYRIGHT
547              
548             Copyright (c) 2008, Yanick Champoux C<< <yanick@cpan.org> >>. All rights reserved.
549              
550             This module is free software; you can redistribute it and/or
551             modify it under the same terms as Perl itself. See L<perlartistic>.
552