File Coverage

blib/lib/WWW/Challonge.pm
Criterion Covered Total %
statement 87 97 89.6
branch 25 38 65.7
condition 9 13 69.2
subroutine 13 13 100.0
pod 4 4 100.0
total 138 165 83.6


line stmt bran cond sub pod time code
1             package WWW::Challonge;
2 6     6   99304 use WWW::Challonge::Tournament;
  6         18  
  6         432  
3 6     6   3976 use LWP::UserAgent;
  6         98888  
  6         225  
4 6     6   53 use Carp qw/carp croak/;
  6         11  
  6         457  
5 6     6   32 use JSON qw/to_json from_json/;
  6         8  
  6         49  
6              
7 6     6   1022 use 5.010;
  6         20  
  6         216  
8 6     6   31 use strict;
  6         10  
  6         214  
9 6     6   34 use warnings;
  6         9  
  6         8006  
10              
11             sub __handle_error;
12             sub __json_request;
13              
14             =head1 NAME
15              
16             WWW::Challonge - Perl wrapper for the Challonge API
17              
18             =head1 VERSION
19              
20             Version 1.01
21              
22             =cut
23              
24             our $VERSION = '1.01';
25             our $HOST = "https://api.challonge.com/v1";
26              
27             =head1 SYNOPSIS
28              
29             Access the Challonge API within Perl. Contains all the functions within the API,
30             as documented L.
31              
32             use WWW::Challonge;
33              
34             my $c = WWW::Challonge->new($api_key)
35             ...
36              
37             =head1 SUBROUTINES/METHODS
38              
39             =head2 new
40              
41             Creates a new C object. Takes in an API key, which is required,
42             and optionally a preconfigured L object, which is mostly used for
43             testing.
44              
45             my $c = WWW::Challonge->new($api_key);
46             my $c2 = WWW::Challonge->new({ key => $api_key, client => $my_client });
47              
48             =cut
49              
50             sub new
51             {
52             # Get the arguments:
53 10     10 1 44179 my $class = shift;
54 10         20 my $args = shift;
55              
56 10         16 my $key;
57             my $client;
58              
59             # If the argument is a scalar, use it as the API key:
60 10 50 66     75 if((! ref $args) && (defined $args))
    100          
61             {
62 0         0 $key = $args;
63              
64             # Create an LWP useragent to interface Challonge:
65 0         0 $client = LWP::UserAgent->new;
66             }
67             elsif(ref $args eq "HASH")
68             {
69 8 100 66     190 croak "'client' must be a LWP::UserAgent object"
70             unless((defined $args->{client}) &&
71             (UNIVERSAL::isa($args->{client}, "LWP::UserAgent")));
72              
73 7         17 $client = $args->{client};
74 7   50     28 $key = $args->{key} // "";
75             }
76             else
77             {
78 2         209 croak "Expected scalar or hashref";
79             }
80              
81             # Try to get some content and check the response code:
82 7         86 my $response = $client->get("$HOST/tournaments.json?api_key=$key");
83              
84             # Check for any errors:
85 7 100       47581 WWW::Challonge::__handle_error $response if($response->is_error);
86              
87             # Create and return the object:
88 6         92 my $c = { key => $key, client => $client };
89 6         42 bless $c, $class;
90             }
91              
92             =head2 tournaments
93              
94             Returns an arrayref of all C objects owned by the
95             user authenticated with in the 'new' request (the logged in user, so to speak).
96             Takes a number of optional arguments:
97              
98             =over 4
99              
100             =item state
101              
102             Get tournaments based on their progress:
103              
104             =over 4
105              
106             =item all
107              
108             Gets all tournaments regardless of state.
109              
110             =item pending
111              
112             Gets all tournaments that have yet to start.
113              
114             =item in_progress
115              
116             Gets all tournaments that have started but have not finished.
117              
118             =item ended
119              
120             Gets all tournaments that have finished.
121              
122             =back
123              
124             =item type
125              
126             Gets all tournaments of the given type:
127              
128             =over 4
129              
130             =item single_elimination
131              
132             =item double_elimination
133              
134             =item round_robin
135              
136             =item swiss
137              
138             =back
139              
140             =item created_after
141              
142             Gets all the tournaments created after the given date. Can be given as an
143             ISO8601 date string or as a L object.
144              
145             =item created_before
146              
147             Gets all the tournaments created before the given date. Can be given as an
148             ISO8601 string or as a L object.
149              
150             =item subdomain
151              
152             Gets all tournaments created under the given subdomian.
153              
154             =back
155              
156             my $tournies = $c->tournaments;
157             my $tournies2 = $c->tournaments({
158             type => "double_elimination",
159             created_after => "2015-03-18",
160             });
161              
162             =cut
163              
164             sub tournaments
165             {
166 9     9 1 3449 my $self = shift;
167 9   100     33 my $options = shift // {};
168              
169             # Get the key and the client:
170 9         25 my $key = $self->{key};
171 9         11 my $client = $self->{client};
172              
173             # The intial request URL:
174 9         23 my $req = "$HOST/tournaments.json?api_key=$key";
175              
176             # Loop through the options (if any) and add them on:
177 9         10 for my $option(keys %{$options})
  9         33  
178             {
179             # Validate the input:
180 6 100       31 if($option =~ /^state$/)
    50          
    50          
    50          
181             {
182 5 100       22 if($options->{$option} !~ /^all|pending|in_progress|ended$/)
183             {
184 1         129 croak "Argument '" . $options->{$option} .
185             "' for option '$option' is invalid";
186             }
187             }
188             elsif($option =~ /^type$/)
189             {
190 0 0       0 if($options->{$option} !~ /^(single|double)_elimination|round_robin|swiss$/)
191             {
192 0         0 croak "Argument '" . $options->{$option} .
193             "' for option '$option' is invalid";
194             }
195             }
196             elsif($option =~ /^created_(before|after)$/)
197             {
198 0 0       0 if($options->{$option} !~ /^\d{4}-\d{2}-\d{2}$/)
199             {
200 0         0 croak "Argument '" . $options->{$option} .
201             "' for option '$option' is invalid";
202             }
203             }
204             elsif($option =~ /^subdomain$/)
205             {
206 0 0       0 if($options->{$option} !~ /^[a-zA-Z0-9_]*$/)
207             {
208 0         0 croak "Argument '" . $options->{$option} .
209             "' for option '$option' is invalid";
210             }
211             }
212             else
213             {
214 1         77 carp "Unknown option '$option'";
215 0         0 return undef;
216             }
217              
218 4         13 $req .= "&" . $option . "=" . $options->{$option};
219             }
220              
221             # Make the request:
222 7         28 my $response = $client->get($req);
223              
224             # Check for any errors:
225 7 50       5681 WWW::Challonge::__handle_error $response if($response->is_error);
226              
227             # Make a new tournament object for every tourney returned:
228 7         51 my @tournaments;
229 7         11 for my $tournament(@{from_json($response->decoded_content)})
  7         16  
230             {
231 10         1223 push @tournaments, WWW::Challonge::Tournament->new($tournament,
232             $key, $client);
233             }
234              
235             # Return the array of tournaments:
236 7         51 return \@tournaments;
237             }
238              
239             =head2 tournament
240              
241             Gets a single C object by the given id or URL:
242              
243             my $tourney = $c->tournament("sample_tournament_1");
244              
245             If the tournament has a subdomain (e.g. test.challonge.com/mytourney), simply
246             specify like so:
247              
248             my $tourney = $c->tournament("test-mytourney")
249              
250             =cut
251              
252             sub tournament
253             {
254 14     14 1 14891 my $self = shift;
255 14         30 my $url = shift;
256              
257             # Give an error if no tournament is given:
258 14 100       184 croak "No tournament specified" unless(defined $url);
259              
260             # Get the key and client:
261 13         53 my $key = $self->{key};
262 13         27 my $client = $self->{client};
263              
264             # Try to get the tournament:
265 13         95 my $response = $client->get("$HOST/tournaments/$url.json?api_key=$key");
266              
267             # Check for any errors:
268 13 100       15278 WWW::Challonge::__handle_error $response if($response->is_error);
269              
270             # Otherwise create a tourney with the object and return it:
271 12         158 my $tourney = WWW::Challonge::Tournament->new(
272             from_json($response->decoded_content), $key, $client);
273 12         59 return $tourney;
274             }
275              
276             =head2 new_tournament
277              
278             Creates a new tournament, and returns it as a C
279             object. It takes an hashref of arguments. The name and URL are required, all
280             others are optional.
281              
282             =over 4
283              
284             =item name
285              
286             A string containing the name of the tournament.
287              
288             =item tournament_type
289              
290             A string containing one of the following, detailing the type of tournament.
291              
292             =over 4
293              
294             =item single elimination (default)
295              
296             =item double elimination
297              
298             =item round robin
299              
300             =item swiss
301              
302             =back
303              
304             =item url
305              
306             The url of the tournament, containing only letters, numbers and underscores.
307              
308             =item subdomain
309              
310             The subdomain of the tournament (requires write access to the given subdomain).
311              
312             =item description
313              
314             The description of the tournament to be displayed above the bracket.
315              
316             =item game_name
317              
318             The name of the game or sport being played.
319              
320             =item open_signup
321              
322             True/false. Have Challonge host a sign-up page (otherwise, manually add
323             participants).
324              
325             =item hold_third_place_match
326              
327             True/false. Single elimination only. Hold a match for semifinals losers to
328             determine third place? Default is false.
329              
330             =item pts_for_match_win
331              
332             Decimal (to the nearest tenth). Number of points gained on winning a match.
333             Swiss only. Default is 1.0.
334              
335             =item pts_for_match_tie
336              
337             Decimal (to the nearest tenth). Number of points gained on drawing a match.
338             Swiss only. Default is 0.5.
339              
340             =item pts_for_game_win
341              
342             Decimal (to the nearest tenth). Number of points gained on winning a single
343             game within a match. Swiss only. Default is 0.0.
344              
345             =item pts_for_game_tie
346              
347             Decimal (to the nearest tenth). Number of points gained on drawing a single
348             game within a match. Swiss only. Default is 0.0.
349              
350             =item pts_for_bye
351              
352             Decimal (to the nearest tenth). Number of points gained on getting a bye.
353             Swiss only. Default is 1.0.
354              
355             =item swiss_rounds
356              
357             Integer. Number of swiss rounds to play. Swiss only. It is recommended that
358             the number of rounds is limited to no more than two thirds of the number of
359             players, otherwise an impossible pairing situation may occur and the
360             tournament may end prematurely.
361              
362             =item ranked_by
363              
364             How the tournament is ranked. Can be one of the following.
365              
366             =over 4
367              
368             =item match wins
369              
370             =item game wins
371              
372             =item points scored
373              
374             =item points difference
375              
376             =item custom
377              
378             =back
379              
380             =item rr_pts_for_match_win
381              
382             Decimal (to the nearest tenth). Number of points gained by winning a match.
383             Round Robin 'custom' only. Default is 1.0.
384              
385             =item rr_pts_for_match_tie
386              
387             Decimal (to the nearest tenth). Number of points gained by drawing a match.
388             Round Robin 'custom' only. Default is 0.5.
389              
390             =item rr_pts_for_game_win
391              
392             Decimal (to the nearest tenth). Number of points gained by winning a single
393             game within a match. Round Robin 'custom' only. Default is 0.0.
394              
395             =item rr_pts_for_game_tie
396              
397             Decimal (to the nearest tenth). Number of points gained by drawing a single
398             game within a match. Round Robin 'custom' only. Default is 0.0.
399              
400             =item accept_attachments
401              
402             True/false. Allow match attachment uploads. Default is false.
403              
404             =item hide_forum
405              
406             True/false. Hide the forum tab on your Challonge page. Default is false.
407              
408             =item show_rounds
409              
410             True/false. Label each round about the bracket. Single and double elimination
411             only. Default is false.
412              
413             =item private
414              
415             True/false. Hide this tournament from the public browsable index and your
416             profile. Default is false.
417              
418             =item notify_users_when_matches_open
419             r
420              
421             True/false. Send registered Challonge users an email when matches open up
422             for them. Default is false.
423              
424             =item nofity_users_when_the_tournament_ends
425              
426             True/false. Send registered Challonge users an email with the results when
427             the tournament ends. Default is false.
428              
429             =item sequential_pairings
430              
431             True/false. Instead of following traditional seeding rules, make the pairings
432             go straight down the list of participants. For example, the first match will
433             be the first seed versus the second. Default is false.
434              
435             =item signup_cap
436              
437             Integer. The maximum number of participants. Any additional participants will
438             go on a waiting list.
439              
440             =item start_at
441              
442             DateTime. The planned time to start the tournament. Timezone defaults to
443             Eastern (EST).
444              
445             =item check_in_duration
446              
447             Integer. The length of the check-in window in minutes.
448              
449             =back
450              
451             my $tournament = $c->new_tournament({
452             name => "sample tournament",
453             url => "sample_tournament_1",
454             type => "double elimination"
455             });
456              
457             =cut
458              
459             sub new_tournament
460             {
461 13     13 1 21096 my $self = shift;
462 13         26 my $args = shift;
463              
464             # Get the key and client:
465 13         32 my $key = $self->{key};
466 13         23 my $client = $self->{client};
467              
468             # Fail if name and URL aren't given:
469 13 100 66     88 if((! defined $args->{name}) || (! defined $args->{url}))
470             {
471 2         246 croak "Name and URL are required to create a tournament";
472             }
473              
474             # Check the arguments and values are valid:
475 11 50       54 return undef unless(WWW::Challonge::Tournament::__args_are_valid($args));
476              
477             # Encapsulate the API key and the arguments in a single hashref:
478 11         43 my $params = { api_key => $key, tournament => $args };
479              
480             # Now we have all the arguments validated, send the POST request:
481 11         56 my $response = $client->request(
482             __json_request("$HOST/tournaments.json", "POST", $params));
483              
484             # Check for any errors:
485 11 100       10595 WWW::Challonge::__handle_error $response if($response->is_error);
486              
487             # Otherwise, make a tournament object and return it:
488 9         131 my $t = WWW::Challonge::Tournament->new(
489             from_json($response->decoded_content), $key, $client);
490 9         56 return $t;
491             }
492              
493             =head2 __handle_error
494              
495             Used throughout the module to deal with when the Challonge API returns an
496             error of some kind. Takes a L object.
497              
498             =cut
499              
500             sub __handle_error
501             {
502 14     14   187 my $response = shift;
503              
504             # Determine if the response is JSON or not:
505 14         30 eval { from_json($response->decoded_content) };
  14         52  
506 14 50       1790 unless($@)
507             {
508 14         21 for my $error(@{from_json($response->decoded_content)->{errors}})
  14         41  
509             {
510 14         1498 croak "(" . $response->code . ") " . $error;
511             }
512             }
513             else
514             {
515 0         0 croak "(" . $response->code . ") " . $response->decoded_content;
516             }
517             }
518              
519             =head2 __json_request
520              
521             Transforms a URI, verb and hashref to a L object to use for a
522             POST/PUT request with the hashref transformed to JSON.
523              
524             =cut
525              
526             sub __json_request
527             {
528 38     38   100 my $uri = shift;
529 38         74 my $action = shift;
530 38         46 my $params = shift;
531              
532 38         167 my $req = HTTP::Request->new($action, $uri);
533 38         4818 $req->header('Content-Type' => 'application/json');
534 38         2138 $req->content(to_json($params));
535              
536 38         1720 return $req;
537             }
538              
539             =head1 AUTHOR
540              
541             Alex Kerr, C<< >>
542              
543             =head1 BUGS
544              
545             Please report any bugs or feature requests to C, or through
546             the web interface at L. I will be notified, and then you'll
547             automatically be notified of progress on your bug as I make changes.
548              
549             =head1 SUPPORT
550              
551             You can find documentation for this module with the perldoc command.
552              
553             perldoc WWW::Challonge
554              
555             You can also look for information at:
556              
557             =over 4
558              
559             =item * RT: CPAN's request tracker (report bugs here)
560              
561             L
562              
563             =item * AnnoCPAN: Annotated CPAN documentation
564              
565             L
566              
567             =item * CPAN Ratings
568              
569             L
570              
571             =item * Search CPAN
572              
573             L
574              
575             =back
576              
577             =head1 SEE ALSO
578              
579             =over 4
580              
581             =item L
582              
583             =item L
584              
585             =item L
586              
587             =item L
588              
589             =back
590              
591             =head1 ACKNOWLEDGEMENTS
592              
593             Everyone on the L team for making such a great
594             service.
595              
596             =head1 LICENSE AND COPYRIGHT
597              
598             Copyright 2015 Alex Kerr.
599              
600             This program is free software; you can redistribute it and/or modify it
601             under the terms of the the Artistic License (2.0). You may obtain a
602             copy of the full license at:
603              
604             L
605              
606             Any use, modification, and distribution of the Standard or Modified
607             Versions is governed by this Artistic License. By using, modifying or
608             distributing the Package, you accept this license. Do not use, modify,
609             or distribute the Package, if you do not accept this license.
610              
611             If your Modified Version has been derived from a Modified Version made
612             by someone other than you, you are nevertheless required to ensure that
613             your Modified Version complies with the requirements of this license.
614              
615             This license does not grant you the right to use any trademark, service
616             mark, tradename, or logo of the Copyright Holder.
617              
618             This license includes the non-exclusive, worldwide, free-of-charge
619             patent license to make, have made, use, offer to sell, sell, import and
620             otherwise transfer the Package with respect to any patent claims
621             licensable by the Copyright Holder that are necessarily infringed by the
622             Package. If you institute patent litigation (including a cross-claim or
623             counterclaim) against any party alleging that the Package constitutes
624             direct or contributory patent infringement, then this Artistic License
625             to you shall terminate on the date that such litigation is filed.
626              
627             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
628             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
629             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
630             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
631             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
632             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
633             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
634             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
635              
636             =cut
637              
638             1; # End of WWW::Challonge