| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | # | 
| 2 |  |  |  |  |  |  | # This file is part of WWW-GitHub-PostReceiveHook | 
| 3 |  |  |  |  |  |  | # | 
| 4 |  |  |  |  |  |  | # This software is copyright (c) 2011 by Matt Phillips. | 
| 5 |  |  |  |  |  |  | # | 
| 6 |  |  |  |  |  |  | # This is free software; you can redistribute it and/or modify it under | 
| 7 |  |  |  |  |  |  | # the same terms as the Perl 5 programming language system itself. | 
| 8 |  |  |  |  |  |  | # | 
| 9 | 1 |  |  | 1 |  | 1811 | use Web::Simple 'WWW::GitHub::PostReceiveHook'; | 
|  | 1 |  |  |  |  | 64524 |  | 
|  | 1 |  |  |  |  | 7 |  | 
| 10 |  |  |  |  |  |  | package WWW::GitHub::PostReceiveHook; | 
| 11 |  |  |  |  |  |  | # ABSTRACT: A simple means of receiving GitHub's web hooks | 
| 12 |  |  |  |  |  |  | $WWW::GitHub::PostReceiveHook::VERSION = '0.004'; | 
| 13 | 1 |  |  | 1 |  | 4898 | use Try::Tiny; | 
|  | 1 |  |  |  |  | 1583 |  | 
|  | 1 |  |  |  |  | 49 |  | 
| 14 | 1 |  |  | 1 |  | 1045 | use JSON; | 
|  | 1 |  |  |  |  | 18936 |  | 
|  | 1 |  |  |  |  | 6 |  | 
| 15 | 1 |  |  | 1 |  | 1018 | use Encode; | 
|  | 1 |  |  |  |  | 12232 |  | 
|  | 1 |  |  |  |  | 431 |  | 
| 16 |  |  |  |  |  |  |  | 
| 17 |  |  |  |  |  |  | has routes => ( | 
| 18 |  |  |  |  |  |  | is        => 'rw', | 
| 19 |  |  |  |  |  |  | predicate => 'has_routes', | 
| 20 |  |  |  |  |  |  | required  => 1, | 
| 21 |  |  |  |  |  |  | isa       => sub { | 
| 22 |  |  |  |  |  |  | # must be hash | 
| 23 |  |  |  |  |  |  | die 'Routes must be a HASH ref.' unless ref $_[0] eq 'HASH'; | 
| 24 |  |  |  |  |  |  |  | 
| 25 |  |  |  |  |  |  | # validate each route | 
| 26 |  |  |  |  |  |  | while (my ($key, $value) = each %{ $_[0] }) { | 
| 27 |  |  |  |  |  |  | # must match simple path | 
| 28 |  |  |  |  |  |  | die 'Routes must be of the form qr{^/\w+/?}' if $key !~ m{^/\w+/?$}; | 
| 29 |  |  |  |  |  |  | # must map to a coderef | 
| 30 |  |  |  |  |  |  | die 'route must map to CODE ref.' unless ref $value eq 'CODE'; | 
| 31 |  |  |  |  |  |  | } | 
| 32 |  |  |  |  |  |  | }, | 
| 33 |  |  |  |  |  |  | ); | 
| 34 |  |  |  |  |  |  |  | 
| 35 |  |  |  |  |  |  | sub dispatch_request { | 
| 36 |  |  |  |  |  |  |  | 
| 37 |  |  |  |  |  |  | sub (POST + /*) { | 
| 38 | 4 |  |  | 4 |  | 1002 | my ( $self, $path ) = @_; | 
| 39 |  |  |  |  |  |  |  | 
| 40 |  |  |  |  |  |  | # only pass along the request if it matches a given path | 
| 41 | 4 | 50 | 33 |  |  | 78 | return if ! $self->has_routes || ! $self->routes->{ "/$path" }; | 
| 42 |  |  |  |  |  |  |  | 
| 43 |  |  |  |  |  |  | # catch the payload | 
| 44 |  |  |  |  |  |  | sub (%payload=) { | 
| 45 | 3 |  |  |  |  | 834 | my ( $self, $payload ) = @_; | 
| 46 | 3 |  |  |  |  | 4 | my $response; | 
| 47 |  |  |  |  |  |  |  | 
| 48 |  |  |  |  |  |  | try { | 
| 49 |  |  |  |  |  |  | # encode multibyte | 
| 50 | 3 |  |  |  |  | 71 | $payload = encode_utf8 $payload; | 
| 51 |  |  |  |  |  |  |  | 
| 52 |  |  |  |  |  |  | # deserialize | 
| 53 | 3 |  |  |  |  | 42 | my $json = decode_json $payload; | 
| 54 |  |  |  |  |  |  |  | 
| 55 |  |  |  |  |  |  | # callback | 
| 56 | 2 |  |  |  |  | 39 | $self->routes->{ "/$path" }->( $json ); | 
| 57 |  |  |  |  |  |  | } | 
| 58 |  |  |  |  |  |  | catch { | 
| 59 |  |  |  |  |  |  | # malformed JSON string, neither array, object, number, string or atom, at character offset 0 ? | 
| 60 |  |  |  |  |  |  | # you are trying to POST non JSON data. don't do that. | 
| 61 | 1 |  |  |  |  | 23 | warn "Caught exception: /$path: attempted to trigger callback but failed:\n$_"; | 
| 62 |  |  |  |  |  |  |  | 
| 63 |  |  |  |  |  |  | # override the default 200 OK | 
| 64 | 1 |  |  |  |  | 54 | $response = [ 400, [ 'Content-type' => 'text/plain' ], ['Bad Request'] ]; | 
| 65 | 3 |  |  |  |  | 18 | }; | 
| 66 |  |  |  |  |  |  |  | 
| 67 |  |  |  |  |  |  | # return catch response if set | 
| 68 | 3 | 100 |  |  |  | 1162 | return $response if $response; | 
| 69 |  |  |  |  |  |  |  | 
| 70 | 2 |  |  |  |  | 11 | $response = [ 200, [ 'Content-type' => 'text/plain' ], ['OK'] ]; | 
| 71 |  |  |  |  |  |  | } | 
| 72 | 4 |  |  |  |  | 617 | }, | 
| 73 | 7 |  |  | 7 | 0 | 76960 | } | 
| 74 |  |  |  |  |  |  |  | 
| 75 |  |  |  |  |  |  | 1; | 
| 76 |  |  |  |  |  |  |  | 
| 77 |  |  |  |  |  |  | __END__ |