| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Net::Marathon; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 4 |  |  | 4 |  | 46493 | use 5.006; | 
|  | 4 |  |  |  |  | 12 |  | 
| 4 | 4 |  |  | 4 |  | 18 | use strict; | 
|  | 4 |  |  |  |  | 5 |  | 
|  | 4 |  |  |  |  | 92 |  | 
| 5 | 4 |  |  | 4 |  | 16 | use warnings; | 
|  | 4 |  |  |  |  | 13 |  | 
|  | 4 |  |  |  |  | 124 |  | 
| 6 | 4 |  |  | 4 |  | 2572 | use LWP::UserAgent; | 
|  | 4 |  |  |  |  | 157876 |  | 
|  | 4 |  |  |  |  | 156 |  | 
| 7 | 4 |  |  | 4 |  | 2246 | use JSON::XS; | 
|  | 4 |  |  |  |  | 14673 |  | 
|  | 4 |  |  |  |  | 253 |  | 
| 8 | 4 |  |  | 4 |  | 2055 | use Net::Marathon::App; | 
|  | 4 |  |  |  |  | 8 |  | 
|  | 4 |  |  |  |  | 192 |  | 
| 9 | 4 |  |  | 4 |  | 1571 | use Net::Marathon::Group; | 
|  | 4 |  |  |  |  | 7 |  | 
|  | 4 |  |  |  |  | 117 |  | 
| 10 | 4 |  |  | 4 |  | 1667 | use Net::Marathon::Events; | 
|  | 4 |  |  |  |  | 13 |  | 
|  | 4 |  |  |  |  | 148 |  | 
| 11 | 4 |  |  | 4 |  | 2860 | use Net::Marathon::Deployment; | 
|  | 4 |  |  |  |  | 7 |  | 
|  | 4 |  |  |  |  | 4709 |  | 
| 12 |  |  |  |  |  |  |  | 
| 13 |  |  |  |  |  |  | =head1 NAME | 
| 14 |  |  |  |  |  |  |  | 
| 15 |  |  |  |  |  |  | Net::Marathon - An object-oriented Mapper for the Marathon REST API, fork of Marathon module | 
| 16 |  |  |  |  |  |  |  | 
| 17 |  |  |  |  |  |  | =cut | 
| 18 |  |  |  |  |  |  |  | 
| 19 |  |  |  |  |  |  | our $VERSION = '0.1.0'; | 
| 20 |  |  |  |  |  |  | our $verbose = 0; | 
| 21 |  |  |  |  |  |  |  | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 24 |  |  |  |  |  |  |  | 
| 25 |  |  |  |  |  |  | Net::Marathon 0.1.0 is a fork of Marathon 0.9 with a fix on Events API (applied this patch https://github.com/geidies/perl-Marathon/pull/1). | 
| 26 |  |  |  |  |  |  | Otherwise it is the same, more differences may come in future versions. | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | This module is a wrapper around the [Marathon REST API](http://mesosphere.github.io/marathon/docs/rest-api.html), so it can be used without having to write JSON by hand. | 
| 29 |  |  |  |  |  |  |  | 
| 30 |  |  |  |  |  |  | For the most common tasks, there is a helper method in the main module. Some additional methods are found in the Net::Marathon::App etc. submodules. | 
| 31 |  |  |  |  |  |  |  | 
| 32 |  |  |  |  |  |  | To start, create a marathon object: | 
| 33 |  |  |  |  |  |  |  | 
| 34 |  |  |  |  |  |  | my $m = Net::Marathon->new( url => 'http://my.marathon.here:8080' ); | 
| 35 |  |  |  |  |  |  |  | 
| 36 |  |  |  |  |  |  | my $app = $m->get_app('hello-marathon'); | 
| 37 |  |  |  |  |  |  |  | 
| 38 |  |  |  |  |  |  | $app->instances( 23 ); | 
| 39 |  |  |  |  |  |  | $app->update(); | 
| 40 |  |  |  |  |  |  | print STDERR Dumper( $app->deployments ); | 
| 41 |  |  |  |  |  |  |  | 
| 42 |  |  |  |  |  |  | sleep 10; | 
| 43 |  |  |  |  |  |  |  | 
| 44 |  |  |  |  |  |  | $app->instances( 1 ); | 
| 45 |  |  |  |  |  |  | $app->update( {force => 'true'} ); # should work even if the scaling up is not done yet. | 
| 46 |  |  |  |  |  |  |  | 
| 47 |  |  |  |  |  |  |  | 
| 48 |  |  |  |  |  |  | =head1 SUBROUTINES/METHODS | 
| 49 |  |  |  |  |  |  |  | 
| 50 |  |  |  |  |  |  | =head2 new | 
| 51 |  |  |  |  |  |  |  | 
| 52 |  |  |  |  |  |  | Creates a Marathon object. You can pass in the URL to the marathon REST interface: | 
| 53 |  |  |  |  |  |  |  | 
| 54 |  |  |  |  |  |  | use Net::Marathon; | 
| 55 |  |  |  |  |  |  | my $marathon = Net::Marathon->new( url => 'http://169.254.47.11:8080', verbose => 0 ); | 
| 56 |  |  |  |  |  |  |  | 
| 57 |  |  |  |  |  |  | The "verbose" parameter makes the module more chatty on STDERR. | 
| 58 |  |  |  |  |  |  |  | 
| 59 |  |  |  |  |  |  | =cut | 
| 60 |  |  |  |  |  |  |  | 
| 61 |  |  |  |  |  |  | sub new { | 
| 62 | 4 |  |  | 4 | 1 | 1528 | my ($class, %conf) = @_; | 
| 63 | 4 |  | 100 |  |  | 19 | my $url = delete $conf{url} || 'http://localhost:8080/'; | 
| 64 | 4 |  | 50 |  |  | 21 | $Net::Marathon::verbose = delete $conf{verbose} || 0; | 
| 65 | 4 |  |  |  |  | 24 | my $ua = LWP::UserAgent->new; | 
| 66 | 4 |  |  |  |  | 6181 | my $self = bless { | 
| 67 |  |  |  |  |  |  | _ua     => $ua, | 
| 68 |  |  |  |  |  |  | }; | 
| 69 | 4 |  |  |  |  | 15 | $self->_set_url($url); | 
| 70 | 4 |  |  |  |  | 13 | return $self; | 
| 71 |  |  |  |  |  |  | } | 
| 72 |  |  |  |  |  |  |  | 
| 73 |  |  |  |  |  |  | sub _set_url { # void | 
| 74 | 4 |  |  | 4 |  | 7 | my ($self, $url) = @_; | 
| 75 | 4 | 100 |  |  |  | 33 | unless ( $url =~ m,^https?\://, ) { | 
| 76 | 1 |  |  |  |  | 3 | $url = 'http://' . $url; | 
| 77 |  |  |  |  |  |  | } | 
| 78 | 4 | 100 |  |  |  | 18 | unless ( $url =~ m,/$, ) { | 
| 79 | 1 |  |  |  |  | 2 | $url .= '/'; | 
| 80 |  |  |  |  |  |  | } | 
| 81 | 4 |  |  |  |  | 16 | $self->{_url} = $url; | 
| 82 |  |  |  |  |  |  | } | 
| 83 |  |  |  |  |  |  |  | 
| 84 |  |  |  |  |  |  | =head2 get_app( $id ) | 
| 85 |  |  |  |  |  |  |  | 
| 86 |  |  |  |  |  |  | Returns a Net::Marathon::App as identified by the single argument "id". In case there is no such app, will return undef. | 
| 87 |  |  |  |  |  |  |  | 
| 88 |  |  |  |  |  |  | my $app = $marathon->get_app('such-1'); | 
| 89 |  |  |  |  |  |  | print $app->id . "\n"; | 
| 90 |  |  |  |  |  |  |  | 
| 91 |  |  |  |  |  |  | =cut | 
| 92 |  |  |  |  |  |  |  | 
| 93 |  |  |  |  |  |  | sub get_app { # Net::Marathon::App | 
| 94 | 0 |  |  | 0 | 1 | 0 | my ( $self, $id ) = @_; | 
| 95 | 0 |  |  |  |  | 0 | my $api_response = $self->_get_obj('/v2/apps/' . $id); | 
| 96 | 0 | 0 |  |  |  | 0 | return undef unless defined $api_response; | 
| 97 | 0 |  |  |  |  | 0 | return Net::Marathon::App->new( $api_response->{app}, $self ); | 
| 98 |  |  |  |  |  |  | } | 
| 99 |  |  |  |  |  |  |  | 
| 100 |  |  |  |  |  |  | =head2 new_app( $config ) | 
| 101 |  |  |  |  |  |  |  | 
| 102 |  |  |  |  |  |  | Returns a new Net::Marathon::App as described in the $config hash. Example: | 
| 103 |  |  |  |  |  |  |  | 
| 104 |  |  |  |  |  |  | my $app = $marathon->new_app({ id => 'very-1', mem => 4, cpus => 0.1, cmd => "while [ 1 ]; do echo 'wow.'; done" }); | 
| 105 |  |  |  |  |  |  |  | 
| 106 |  |  |  |  |  |  | This will not (!) start the app in marathon. To do so, call create() on the returned object: | 
| 107 |  |  |  |  |  |  |  | 
| 108 |  |  |  |  |  |  | $app->create(); | 
| 109 |  |  |  |  |  |  |  | 
| 110 |  |  |  |  |  |  | =cut | 
| 111 |  |  |  |  |  |  |  | 
| 112 |  |  |  |  |  |  | sub new_app { | 
| 113 | 0 |  |  | 0 | 1 | 0 | my ($self, $config) = @_; | 
| 114 | 0 |  |  |  |  | 0 | return Net::Marathon::App->new( $config, $self ); | 
| 115 |  |  |  |  |  |  | } | 
| 116 |  |  |  |  |  |  |  | 
| 117 |  |  |  |  |  |  | =head2 get_group( $id ) | 
| 118 |  |  |  |  |  |  |  | 
| 119 |  |  |  |  |  |  | Works like get_app, just for groups. | 
| 120 |  |  |  |  |  |  |  | 
| 121 |  |  |  |  |  |  | =cut | 
| 122 |  |  |  |  |  |  |  | 
| 123 |  |  |  |  |  |  | sub get_group { # Net::Marathon::App | 
| 124 | 0 |  |  | 0 | 1 | 0 | my ( $self, $id ) = @_; | 
| 125 | 0 |  |  |  |  | 0 | return Net::Marathon::Group->get( $id, $self ); | 
| 126 |  |  |  |  |  |  | } | 
| 127 |  |  |  |  |  |  |  | 
| 128 |  |  |  |  |  |  | =head2 new_group( $config ) | 
| 129 |  |  |  |  |  |  |  | 
| 130 |  |  |  |  |  |  | Creates a new group. You can either specify the apps in-line: | 
| 131 |  |  |  |  |  |  |  | 
| 132 |  |  |  |  |  |  | my $group = $marathon->new_group( { id => 'very-1', apps: [{ id => "such-2", cmd => ... }, { id => "such-3", cmd => ... }] } ); | 
| 133 |  |  |  |  |  |  |  | 
| 134 |  |  |  |  |  |  | Or add them to the created group later: | 
| 135 |  |  |  |  |  |  |  | 
| 136 |  |  |  |  |  |  | my $group = $marathon->new_group( { id => 'very-1' } ); | 
| 137 |  |  |  |  |  |  | $group->add( $marathon->new_app( { id => "such-2", cmd => ... } ); | 
| 138 |  |  |  |  |  |  | $group->add( $marathon->new_app( { id => "such-3", cmd => ... } ); | 
| 139 |  |  |  |  |  |  |  | 
| 140 |  |  |  |  |  |  | In any case, new_group will just return a Net::Marathon::Group object, it will not commit to marathon until you call create() on the returned object: | 
| 141 |  |  |  |  |  |  |  | 
| 142 |  |  |  |  |  |  | $group->create(); | 
| 143 |  |  |  |  |  |  |  | 
| 144 |  |  |  |  |  |  | =cut | 
| 145 |  |  |  |  |  |  |  | 
| 146 |  |  |  |  |  |  | sub new_group { | 
| 147 | 0 |  |  | 0 | 1 | 0 | my ($self, $config) = @_; | 
| 148 | 0 |  |  |  |  | 0 | return Net::Marathon::Group->new( $config, $self ); | 
| 149 |  |  |  |  |  |  | } | 
| 150 |  |  |  |  |  |  |  | 
| 151 |  |  |  |  |  |  | =head2 events() | 
| 152 |  |  |  |  |  |  |  | 
| 153 |  |  |  |  |  |  | Returns a Net::Marathon::Events objects. You can register callbacks on it and start listening to the events stream. | 
| 154 |  |  |  |  |  |  |  | 
| 155 |  |  |  |  |  |  | =cut | 
| 156 |  |  |  |  |  |  |  | 
| 157 |  |  |  |  |  |  | sub events { | 
| 158 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 159 | 0 |  |  |  |  | 0 | return Net::Marathon::Events->new( $self ); | 
| 160 |  |  |  |  |  |  | } | 
| 161 |  |  |  |  |  |  |  | 
| 162 |  |  |  |  |  |  | =head2 get_tasks( $status ) | 
| 163 |  |  |  |  |  |  |  | 
| 164 |  |  |  |  |  |  | Returns an array of currently running tasks. If $status is "running" or "staging", will filter and return only those tasks. | 
| 165 |  |  |  |  |  |  |  | 
| 166 |  |  |  |  |  |  | =cut | 
| 167 |  |  |  |  |  |  |  | 
| 168 |  |  |  |  |  |  | sub get_tasks { | 
| 169 | 0 |  |  | 0 | 1 | 0 | my ($self, $status) = @_; | 
| 170 | 0 | 0 | 0 |  |  | 0 | $status = '' unless $status && $status =~ m/^running|staging$/; | 
| 171 | 0 | 0 |  |  |  | 0 | if ( $status ) { | 
| 172 | 0 |  |  |  |  | 0 | $status = '?status='.$status; | 
| 173 |  |  |  |  |  |  | } | 
| 174 | 0 |  |  |  |  | 0 | my $task_obj = $self->_get_obj_from_json('/v2/tasks'.$status); | 
| 175 | 0 |  | 0 |  |  | 0 | my $task_arrayref = ( defined $task_obj && exists $task_obj->{tasks} && $task_obj->{tasks} ) || []; | 
| 176 | 0 | 0 |  |  |  | 0 | return wantarray ? @{$task_arrayref} : $task_arrayref; | 
|  | 0 |  |  |  |  | 0 |  | 
| 177 |  |  |  |  |  |  | } | 
| 178 |  |  |  |  |  |  |  | 
| 179 |  |  |  |  |  |  | =head2 kill_tasks({ tasks => $@ids, scale => bool }) | 
| 180 |  |  |  |  |  |  |  | 
| 181 |  |  |  |  |  |  | Kills the tasks with the given @ids. Scales if the scale param is true. | 
| 182 |  |  |  |  |  |  |  | 
| 183 |  |  |  |  |  |  | =cut | 
| 184 |  |  |  |  |  |  |  | 
| 185 |  |  |  |  |  |  | sub kill_tasks { | 
| 186 | 0 |  |  | 0 | 1 | 0 | my ($self, $args) = @_; | 
| 187 | 0 | 0 | 0 |  |  | 0 | my $param = $args && $args->{scale} && $args->{scale} && $args->{scale} !~ /false/i ? '?scale=true' : ''; #default is false | 
| 188 | 0 |  |  |  |  | 0 | return $self->_put_post_delete( 'POST', '/v2/tasks/delete'.$param, { ids => $args->{tasks} } ); | 
| 189 |  |  |  |  |  |  | } | 
| 190 |  |  |  |  |  |  |  | 
| 191 |  |  |  |  |  |  | =head2 get_deployments | 
| 192 |  |  |  |  |  |  |  | 
| 193 |  |  |  |  |  |  | Returns a list of Net::Marathon::Deployment objects with the currently running deployments. | 
| 194 |  |  |  |  |  |  |  | 
| 195 |  |  |  |  |  |  | =cut | 
| 196 |  |  |  |  |  |  |  | 
| 197 |  |  |  |  |  |  | sub get_deployments { | 
| 198 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 199 | 0 |  |  |  |  | 0 | my $deployments = $self->_get_obj('/v2/deployments'); | 
| 200 | 0 |  |  |  |  | 0 | my @depl_objs = (); | 
| 201 | 0 |  |  |  |  | 0 | foreach ( @{$deployments} ) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 202 | 0 |  |  |  |  | 0 | push @depl_objs, Net::Marathon::Deployment->new( $_, $self ); | 
| 203 |  |  |  |  |  |  | } | 
| 204 | 0 | 0 |  |  |  | 0 | return wantarray ? @depl_objs : \@depl_objs; | 
| 205 |  |  |  |  |  |  | } | 
| 206 |  |  |  |  |  |  |  | 
| 207 |  |  |  |  |  |  | =head2 kill_deployment( $id, { force => bool } ) | 
| 208 |  |  |  |  |  |  |  | 
| 209 |  |  |  |  |  |  | Stop the deployment with given id. | 
| 210 |  |  |  |  |  |  |  | 
| 211 |  |  |  |  |  |  | =cut | 
| 212 |  |  |  |  |  |  |  | 
| 213 |  |  |  |  |  |  | sub kill_deployment { | 
| 214 | 0 |  |  | 0 | 1 | 0 | my ($self, $id, $args) = @_; | 
| 215 | 0 | 0 | 0 |  |  | 0 | my $param = $args && $args->{force} && $args->{force} && $args->{force} !~ /false/i ? '?force=true' : ''; #default is false | 
| 216 | 0 |  |  |  |  | 0 | return $self->_put_post_delete( 'DELETE', '/v2/deployments/' . $id . $param ); | 
| 217 |  |  |  |  |  |  | } | 
| 218 |  |  |  |  |  |  |  | 
| 219 |  |  |  |  |  |  | sub get_endpoint { | 
| 220 | 1 |  |  | 1 | 0 | 5 | my ( $self, $path ) = @_; | 
| 221 | 1 |  |  |  |  | 6 | my $url = $self->{_url} . $path; | 
| 222 | 1 |  |  |  |  | 11 | $url =~ s,/+,/,g; | 
| 223 | 1 |  |  |  |  | 6 | $url =~ s,^http:/,http://,; | 
| 224 | 1 |  |  |  |  | 4 | return $url; | 
| 225 |  |  |  |  |  |  | } | 
| 226 |  |  |  |  |  |  |  | 
| 227 |  |  |  |  |  |  | =head2 metrics | 
| 228 |  |  |  |  |  |  |  | 
| 229 |  |  |  |  |  |  | returns the metrics returned by the /metrics endpoint, converted from json to perl. | 
| 230 |  |  |  |  |  |  |  | 
| 231 |  |  |  |  |  |  | =cut | 
| 232 |  |  |  |  |  |  |  | 
| 233 |  |  |  |  |  |  | sub metrics { | 
| 234 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 235 | 0 |  |  |  |  | 0 | return $self->_get_obj('/metrics'); | 
| 236 |  |  |  |  |  |  | } | 
| 237 |  |  |  |  |  |  |  | 
| 238 |  |  |  |  |  |  | =head2 help | 
| 239 |  |  |  |  |  |  |  | 
| 240 |  |  |  |  |  |  | returns the HTML returned by the /help endpoint. | 
| 241 |  |  |  |  |  |  |  | 
| 242 |  |  |  |  |  |  | =cut | 
| 243 |  |  |  |  |  |  |  | 
| 244 |  |  |  |  |  |  | sub help { # string (html) | 
| 245 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 246 | 0 |  |  |  |  | 0 | return $self->_get_html('/help'); | 
| 247 |  |  |  |  |  |  | } | 
| 248 |  |  |  |  |  |  |  | 
| 249 |  |  |  |  |  |  | =head2 logging | 
| 250 |  |  |  |  |  |  |  | 
| 251 |  |  |  |  |  |  | returns the HTML returned by the /logging endpoint. | 
| 252 |  |  |  |  |  |  |  | 
| 253 |  |  |  |  |  |  | =cut | 
| 254 |  |  |  |  |  |  |  | 
| 255 |  |  |  |  |  |  | sub logging { # string (html) | 
| 256 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 257 | 0 |  |  |  |  | 0 | return $self->_get_html('/logging'); | 
| 258 |  |  |  |  |  |  | } | 
| 259 |  |  |  |  |  |  |  | 
| 260 |  |  |  |  |  |  | =head2 ping | 
| 261 |  |  |  |  |  |  |  | 
| 262 |  |  |  |  |  |  | returns 1 if the master responds to a ping request. | 
| 263 |  |  |  |  |  |  |  | 
| 264 |  |  |  |  |  |  | =cut | 
| 265 |  |  |  |  |  |  |  | 
| 266 |  |  |  |  |  |  | sub ping { # string (plaintext) | 
| 267 | 1 |  |  | 1 | 1 | 14 | my $self = shift; | 
| 268 | 1 | 50 |  |  |  | 6 | return $self->_get_html('/ping') =~ m,pong, ? 'pong' : undef; | 
| 269 |  |  |  |  |  |  | } | 
| 270 |  |  |  |  |  |  |  | 
| 271 |  |  |  |  |  |  | sub _get { # HTTP::Response | 
| 272 | 1 |  |  | 1 |  | 3 | my ( $self, $path ) = @_; | 
| 273 | 1 |  |  |  |  | 7 | my $url = $self->get_endpoint( $path ); | 
| 274 | 1 |  |  |  |  | 9 | my $response = $self->{_ua}->get( $url ); | 
| 275 | 1 |  |  |  |  | 64592 | $self->_response_handler( 'GET', $response ); | 
| 276 | 1 |  |  |  |  | 3 | return $response; | 
| 277 |  |  |  |  |  |  | } | 
| 278 |  |  |  |  |  |  |  | 
| 279 |  |  |  |  |  |  | sub _get_html { # string (html) or undef on error | 
| 280 | 1 |  |  | 1 |  | 3 | my ( $self, $path ) = @_; | 
| 281 | 1 |  |  |  |  | 5 | my $response = $self->_get($path); | 
| 282 | 1 | 50 |  |  |  | 5 | if ( $response->is_success ) { | 
| 283 | 0 |  |  |  |  | 0 | return $response->decoded_content; | 
| 284 |  |  |  |  |  |  | } | 
| 285 | 1 |  |  |  |  | 29 | return ''; | 
| 286 |  |  |  |  |  |  | } | 
| 287 |  |  |  |  |  |  |  | 
| 288 |  |  |  |  |  |  | sub _get_obj { # hashref | 
| 289 | 0 |  |  | 0 |  | 0 | my ( $self, $path ) = @_; | 
| 290 | 0 |  |  |  |  | 0 | my $response = $self->_get_html($path); | 
| 291 | 0 | 0 |  |  |  | 0 | if ($response) { | 
| 292 | 0 |  |  |  |  | 0 | return decode_json $response; | 
| 293 |  |  |  |  |  |  | } | 
| 294 | 0 |  |  |  |  | 0 | return undef; | 
| 295 |  |  |  |  |  |  | } | 
| 296 |  |  |  |  |  |  |  | 
| 297 |  |  |  |  |  |  | sub _get_obj_from_json { # hashref | 
| 298 | 0 |  |  | 0 |  | 0 | my ( $self, $path ) = @_; | 
| 299 | 0 |  |  |  |  | 0 | my $response = $self->_put_post_delete('GET', $path); | 
| 300 | 0 | 0 |  |  |  | 0 | if ($response) { | 
| 301 | 0 |  |  |  |  | 0 | return decode_json $response; | 
| 302 |  |  |  |  |  |  | } | 
| 303 | 0 |  |  |  |  | 0 | return undef; | 
| 304 |  |  |  |  |  |  | } | 
| 305 |  |  |  |  |  |  |  | 
| 306 |  |  |  |  |  |  | sub _post { | 
| 307 | 0 |  |  | 0 |  | 0 | my ($self, $path, $payload) = @_; | 
| 308 | 0 |  |  |  |  | 0 | return $self->_put_post_delete( 'POST', $path, $payload ); | 
| 309 |  |  |  |  |  |  | } | 
| 310 |  |  |  |  |  |  |  | 
| 311 |  |  |  |  |  |  | sub _put { | 
| 312 | 0 |  |  | 0 |  | 0 | my ($self, $path, $payload) = @_; | 
| 313 | 0 |  |  |  |  | 0 | return $self->_put_post_delete( 'PUT', $path, $payload ); | 
| 314 |  |  |  |  |  |  | } | 
| 315 |  |  |  |  |  |  |  | 
| 316 |  |  |  |  |  |  | sub _delete { | 
| 317 | 0 |  |  | 0 |  | 0 | my ($self, $path, $payload) = @_; | 
| 318 | 0 |  |  |  |  | 0 | return $self->_put_post_delete( 'DELETE', $path, $payload ); | 
| 319 |  |  |  |  |  |  | } | 
| 320 |  |  |  |  |  |  |  | 
| 321 |  |  |  |  |  |  | sub _put_post_delete { | 
| 322 | 0 |  |  | 0 |  | 0 | my ($self, $method, $path, $payload) = @_; | 
| 323 | 0 |  |  |  |  | 0 | my $req = HTTP::Request->new( $method, $self->get_endpoint($path) ); | 
| 324 | 0 |  |  |  |  | 0 | $req->header( 'Accept' => 'application/json' ); | 
| 325 | 0 | 0 |  |  |  | 0 | if ( $payload ) { | 
| 326 | 0 |  |  |  |  | 0 | $req->header( 'Content-Type' => 'application/json' ); | 
| 327 | 0 |  |  |  |  | 0 | $req->content( encode_json $payload ); | 
| 328 |  |  |  |  |  |  | } | 
| 329 | 0 |  |  |  |  | 0 | my $response = $self->{_ua}->request( $req ); | 
| 330 | 0 |  |  |  |  | 0 | $self->_response_handler( $method, $response ); | 
| 331 | 0 | 0 |  |  |  | 0 | return $response->is_success ? $response->decoded_content : undef; | 
| 332 |  |  |  |  |  |  | } | 
| 333 |  |  |  |  |  |  |  | 
| 334 |  |  |  |  |  |  | sub _response_handler { | 
| 335 | 1 |  |  | 1 |  | 3 | my ( $self, $method, $response ) = @_; | 
| 336 | 1 | 50 |  |  |  | 6 | if ( $verbose ) { | 
| 337 | 0 | 0 |  |  |  | 0 | unless ( $response->is_success ) { | 
| 338 | 0 |  |  |  |  | 0 | print STDERR 'Error doing '.$method.' against '. $response->base.': ' . $response->status_line . "\n"; | 
| 339 | 0 |  |  |  |  | 0 | print STDERR $response->decoded_content ."\n"; | 
| 340 |  |  |  |  |  |  | } else { | 
| 341 | 0 |  |  |  |  | 0 | print STDERR $response->status_line . "\n" | 
| 342 |  |  |  |  |  |  | } | 
| 343 |  |  |  |  |  |  | } | 
| 344 | 1 |  |  |  |  | 3 | return $response; | 
| 345 |  |  |  |  |  |  | } | 
| 346 |  |  |  |  |  |  |  | 
| 347 |  |  |  |  |  |  | =head1 AUTHOR | 
| 348 |  |  |  |  |  |  |  | 
| 349 |  |  |  |  |  |  | Sebastian Geidies C<<  >> (original Marathon module) | 
| 350 |  |  |  |  |  |  |  | 
| 351 |  |  |  |  |  |  | Miroslav Tynovsky | 
| 352 |  |  |  |  |  |  |  | 
| 353 |  |  |  |  |  |  | =cut | 
| 354 |  |  |  |  |  |  |  | 
| 355 |  |  |  |  |  |  | 1; |