| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package WWW::GoDaddy::REST::Resource; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 1 |  |  | 1 |  | 1031 | use Carp; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 69 |  | 
| 4 | 1 |  |  | 1 |  | 211 | use List::MoreUtils qw( natatime ); | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 5 |  |  |  |  |  |  | use Moose; | 
| 6 |  |  |  |  |  |  | use URI; | 
| 7 |  |  |  |  |  |  | use URI::QueryParam; | 
| 8 |  |  |  |  |  |  | use WWW::GoDaddy::REST::Util qw( abs_url json_instance json_encode json_decode is_json ); | 
| 9 |  |  |  |  |  |  |  | 
| 10 |  |  |  |  |  |  | use constant DEFAULT_IMPL_CLASS => 'WWW::GoDaddy::REST::Resource'; | 
| 11 |  |  |  |  |  |  | use overload '""'               => \&to_string; | 
| 12 |  |  |  |  |  |  |  | 
| 13 |  |  |  |  |  |  | has 'client' => ( | 
| 14 |  |  |  |  |  |  | is       => 'rw', | 
| 15 |  |  |  |  |  |  | isa      => 'WWW::GoDaddy::REST', | 
| 16 |  |  |  |  |  |  | required => 1 | 
| 17 |  |  |  |  |  |  | ); | 
| 18 |  |  |  |  |  |  |  | 
| 19 |  |  |  |  |  |  | has 'fields' => ( | 
| 20 |  |  |  |  |  |  | is       => 'ro', | 
| 21 |  |  |  |  |  |  | isa      => 'HashRef', | 
| 22 |  |  |  |  |  |  | required => 1, | 
| 23 |  |  |  |  |  |  | ); | 
| 24 |  |  |  |  |  |  |  | 
| 25 |  |  |  |  |  |  | has 'http_response' => ( | 
| 26 |  |  |  |  |  |  | is       => 'ro', | 
| 27 |  |  |  |  |  |  | isa      => 'Maybe[HTTP::Response]', | 
| 28 |  |  |  |  |  |  | required => 0 | 
| 29 |  |  |  |  |  |  | ); | 
| 30 |  |  |  |  |  |  |  | 
| 31 |  |  |  |  |  |  | sub save { | 
| 32 |  |  |  |  |  |  | my $self = shift; | 
| 33 |  |  |  |  |  |  | my $url = $self->link('self') || $self->client->schema( $self->type )->query_url( $self->id ); | 
| 34 |  |  |  |  |  |  | return $self->client->http_request_as_resource( 'PUT', $url, $self ); | 
| 35 |  |  |  |  |  |  | } | 
| 36 |  |  |  |  |  |  |  | 
| 37 |  |  |  |  |  |  | sub delete { | 
| 38 |  |  |  |  |  |  | my $self = shift; | 
| 39 |  |  |  |  |  |  | my $url = $self->link('self') || $self->client->schema( $self->type )->query_url( $self->id ); | 
| 40 |  |  |  |  |  |  | return $self->client->http_request_as_resource( 'DELETE', $url, $self ); | 
| 41 |  |  |  |  |  |  | } | 
| 42 |  |  |  |  |  |  |  | 
| 43 |  |  |  |  |  |  | sub follow_link { | 
| 44 |  |  |  |  |  |  | my $self      = shift; | 
| 45 |  |  |  |  |  |  | my $link_name = shift; | 
| 46 |  |  |  |  |  |  |  | 
| 47 |  |  |  |  |  |  | my $link_url = $self->link($link_name); | 
| 48 |  |  |  |  |  |  | if ( !$link_url ) { | 
| 49 |  |  |  |  |  |  | my @valid_links = keys %{ $self->links() }; | 
| 50 |  |  |  |  |  |  | croak("$link_name is not a valid link name. Did you mean one of these? @valid_links"); | 
| 51 |  |  |  |  |  |  | } | 
| 52 |  |  |  |  |  |  |  | 
| 53 |  |  |  |  |  |  | return $self->client->http_request_as_resource( 'GET', $link_url ); | 
| 54 |  |  |  |  |  |  | } | 
| 55 |  |  |  |  |  |  |  | 
| 56 |  |  |  |  |  |  | sub do_action { | 
| 57 |  |  |  |  |  |  | my $self   = shift; | 
| 58 |  |  |  |  |  |  | my $action = shift; | 
| 59 |  |  |  |  |  |  | my $params = shift; | 
| 60 |  |  |  |  |  |  |  | 
| 61 |  |  |  |  |  |  | my $action_url = $self->action($action); | 
| 62 |  |  |  |  |  |  | if ( !$action_url ) { | 
| 63 |  |  |  |  |  |  | if($self->id) { | 
| 64 |  |  |  |  |  |  | # try and find an action in the schema as fallback | 
| 65 |  |  |  |  |  |  | my $schema = $self->schema(); | 
| 66 |  |  |  |  |  |  | my $resource_actions = $schema->f('resourceActions') || {}; | 
| 67 |  |  |  |  |  |  | if( exists $resource_actions->{$action} ) { | 
| 68 |  |  |  |  |  |  | my $self_uri = URI->new($self->link('self') || $schema->query_url( $self->id )); | 
| 69 |  |  |  |  |  |  | $self_uri->query("$action"); | 
| 70 |  |  |  |  |  |  | $action_url = "$self_uri"; | 
| 71 |  |  |  |  |  |  | } | 
| 72 |  |  |  |  |  |  | } | 
| 73 |  |  |  |  |  |  | if( !$action_url ) { | 
| 74 |  |  |  |  |  |  | croak("$action is not a valid action name."); | 
| 75 |  |  |  |  |  |  | } | 
| 76 |  |  |  |  |  |  | } | 
| 77 |  |  |  |  |  |  |  | 
| 78 |  |  |  |  |  |  | return $self->client->http_request_as_resource( 'POST', $action_url, $params ); | 
| 79 |  |  |  |  |  |  |  | 
| 80 |  |  |  |  |  |  | } | 
| 81 |  |  |  |  |  |  |  | 
| 82 |  |  |  |  |  |  | sub items { | 
| 83 |  |  |  |  |  |  | my $self = shift; | 
| 84 |  |  |  |  |  |  | return ($self); | 
| 85 |  |  |  |  |  |  | } | 
| 86 |  |  |  |  |  |  |  | 
| 87 |  |  |  |  |  |  | sub id { | 
| 88 |  |  |  |  |  |  | return shift->f('id'); | 
| 89 |  |  |  |  |  |  | } | 
| 90 |  |  |  |  |  |  |  | 
| 91 |  |  |  |  |  |  | sub type { | 
| 92 |  |  |  |  |  |  | return shift->f('type'); | 
| 93 |  |  |  |  |  |  | } | 
| 94 |  |  |  |  |  |  |  | 
| 95 |  |  |  |  |  |  | sub resource_type { | 
| 96 |  |  |  |  |  |  | return shift->f('resourceType'); | 
| 97 |  |  |  |  |  |  | } | 
| 98 |  |  |  |  |  |  |  | 
| 99 |  |  |  |  |  |  | sub type_fq { | 
| 100 |  |  |  |  |  |  | my $self = shift; | 
| 101 |  |  |  |  |  |  |  | 
| 102 |  |  |  |  |  |  | my $base_url = $self->schemas_url; | 
| 103 |  |  |  |  |  |  |  | 
| 104 |  |  |  |  |  |  | return abs_url( $base_url, $self->type ); | 
| 105 |  |  |  |  |  |  | } | 
| 106 |  |  |  |  |  |  |  | 
| 107 |  |  |  |  |  |  | sub resource_type_fq { | 
| 108 |  |  |  |  |  |  | my $self = shift; | 
| 109 |  |  |  |  |  |  |  | 
| 110 |  |  |  |  |  |  | return unless $self->resource_type; | 
| 111 |  |  |  |  |  |  |  | 
| 112 |  |  |  |  |  |  | return abs_url( $self->schemas_url, $self->resource_type ); | 
| 113 |  |  |  |  |  |  | } | 
| 114 |  |  |  |  |  |  |  | 
| 115 |  |  |  |  |  |  | sub schema { | 
| 116 |  |  |  |  |  |  | my $self = shift; | 
| 117 |  |  |  |  |  |  | my $schema | 
| 118 |  |  |  |  |  |  | = $self->client->schema( $self->resource_type_fq ) | 
| 119 |  |  |  |  |  |  | || $self->client->schema( $self->type_fq ) | 
| 120 |  |  |  |  |  |  | || $self->client->schema( $self->type ); | 
| 121 |  |  |  |  |  |  | return $schema; | 
| 122 |  |  |  |  |  |  | } | 
| 123 |  |  |  |  |  |  |  | 
| 124 |  |  |  |  |  |  | sub link { | 
| 125 |  |  |  |  |  |  | my $self = shift; | 
| 126 |  |  |  |  |  |  | my $name = shift; | 
| 127 |  |  |  |  |  |  |  | 
| 128 |  |  |  |  |  |  | my $links = $self->links(); | 
| 129 |  |  |  |  |  |  | if ( exists $links->{$name} ) { | 
| 130 |  |  |  |  |  |  | return $links->{$name}; | 
| 131 |  |  |  |  |  |  | } | 
| 132 |  |  |  |  |  |  | return undef; | 
| 133 |  |  |  |  |  |  | } | 
| 134 |  |  |  |  |  |  |  | 
| 135 |  |  |  |  |  |  | sub links { | 
| 136 |  |  |  |  |  |  | return shift->f('links'); | 
| 137 |  |  |  |  |  |  | } | 
| 138 |  |  |  |  |  |  |  | 
| 139 |  |  |  |  |  |  | sub action { | 
| 140 |  |  |  |  |  |  | my $self = shift; | 
| 141 |  |  |  |  |  |  | my $name = shift; | 
| 142 |  |  |  |  |  |  |  | 
| 143 |  |  |  |  |  |  | my $actions = $self->actions(); | 
| 144 |  |  |  |  |  |  | if ( exists $actions->{$name} ) { | 
| 145 |  |  |  |  |  |  | return $actions->{$name}; | 
| 146 |  |  |  |  |  |  | } | 
| 147 |  |  |  |  |  |  | return undef; | 
| 148 |  |  |  |  |  |  | } | 
| 149 |  |  |  |  |  |  |  | 
| 150 |  |  |  |  |  |  | sub actions { | 
| 151 |  |  |  |  |  |  | return shift->f('actions') || {}; | 
| 152 |  |  |  |  |  |  | } | 
| 153 |  |  |  |  |  |  |  | 
| 154 |  |  |  |  |  |  | sub f { | 
| 155 |  |  |  |  |  |  | return shift->field(@_); | 
| 156 |  |  |  |  |  |  | } | 
| 157 |  |  |  |  |  |  |  | 
| 158 |  |  |  |  |  |  | sub f_as_resources { | 
| 159 |  |  |  |  |  |  | my $self     = shift; | 
| 160 |  |  |  |  |  |  | my $field    = shift; | 
| 161 |  |  |  |  |  |  | my $raw_data = $self->f($field); | 
| 162 |  |  |  |  |  |  |  | 
| 163 |  |  |  |  |  |  | my ( $container, $type ); | 
| 164 |  |  |  |  |  |  |  | 
| 165 |  |  |  |  |  |  | # if the 'field' is data, skip detection and use the resource type | 
| 166 |  |  |  |  |  |  | if ( $field eq 'data' ) { | 
| 167 |  |  |  |  |  |  | ( $container, $type ) = ( 'array', $self->resource_type ); | 
| 168 |  |  |  |  |  |  | } | 
| 169 |  |  |  |  |  |  | else { | 
| 170 |  |  |  |  |  |  | ( $container, $type ) = $self->schema->resource_field_type($field); | 
| 171 |  |  |  |  |  |  | } | 
| 172 |  |  |  |  |  |  | my %defaults = ( | 
| 173 |  |  |  |  |  |  | client        => $self->client, | 
| 174 |  |  |  |  |  |  | http_response => $self->http_response | 
| 175 |  |  |  |  |  |  | ); | 
| 176 |  |  |  |  |  |  |  | 
| 177 |  |  |  |  |  |  | my $type_schema = $self->client->schema($type); | 
| 178 |  |  |  |  |  |  | if ($type_schema) { | 
| 179 |  |  |  |  |  |  | if ( $container && $container eq 'map' ) { | 
| 180 |  |  |  |  |  |  | my %ret; | 
| 181 |  |  |  |  |  |  | foreach ( my ( $k, $v ) = each %{$raw_data} ) { | 
| 182 |  |  |  |  |  |  | $v->{type} ||= $type_schema->id; | 
| 183 |  |  |  |  |  |  | $ret{$k} = $self->new_subclassed( { %defaults, fields => $v } ); | 
| 184 |  |  |  |  |  |  | } | 
| 185 |  |  |  |  |  |  | return \%ret; | 
| 186 |  |  |  |  |  |  | } | 
| 187 |  |  |  |  |  |  | elsif ( $container && $container eq 'array' ) { | 
| 188 |  |  |  |  |  |  | my @ret; | 
| 189 |  |  |  |  |  |  | foreach my $v ( @{$raw_data} ) { | 
| 190 |  |  |  |  |  |  | $v->{type} ||= $type_schema->id; | 
| 191 |  |  |  |  |  |  | push @ret, $self->new_subclassed( { %defaults, fields => $v } ); | 
| 192 |  |  |  |  |  |  | } | 
| 193 |  |  |  |  |  |  | return \@ret; | 
| 194 |  |  |  |  |  |  | } | 
| 195 |  |  |  |  |  |  | elsif ( ref($raw_data) eq 'HASH' ) { | 
| 196 |  |  |  |  |  |  | my %ret = %{$raw_data}; | 
| 197 |  |  |  |  |  |  | $ret{type} ||= $type_schema->id; | 
| 198 |  |  |  |  |  |  | return $self->new_subclassed( { %defaults, fields => \%ret } ); | 
| 199 |  |  |  |  |  |  | } | 
| 200 |  |  |  |  |  |  | } | 
| 201 |  |  |  |  |  |  |  | 
| 202 |  |  |  |  |  |  | # just return the raw data if not returned otherwise | 
| 203 |  |  |  |  |  |  | return $raw_data; | 
| 204 |  |  |  |  |  |  |  | 
| 205 |  |  |  |  |  |  | } | 
| 206 |  |  |  |  |  |  |  | 
| 207 |  |  |  |  |  |  | sub field { | 
| 208 |  |  |  |  |  |  | my $self = shift; | 
| 209 |  |  |  |  |  |  | if ( @_ <= 1 ) { | 
| 210 |  |  |  |  |  |  | return $self->_get_field(@_); | 
| 211 |  |  |  |  |  |  | } | 
| 212 |  |  |  |  |  |  | else { | 
| 213 |  |  |  |  |  |  | return $self->_set_field(@_); | 
| 214 |  |  |  |  |  |  | } | 
| 215 |  |  |  |  |  |  | } | 
| 216 |  |  |  |  |  |  |  | 
| 217 |  |  |  |  |  |  | sub schemas_url { | 
| 218 |  |  |  |  |  |  | my $self = shift; | 
| 219 |  |  |  |  |  |  |  | 
| 220 |  |  |  |  |  |  | my $found; | 
| 221 |  |  |  |  |  |  |  | 
| 222 |  |  |  |  |  |  | my $http_resp = $self->http_response; | 
| 223 |  |  |  |  |  |  | my $link      = $self->link('schemas'); | 
| 224 |  |  |  |  |  |  |  | 
| 225 |  |  |  |  |  |  | if ( $http_resp && $http_resp->header('X-API-Schemas') ) { | 
| 226 |  |  |  |  |  |  | return $http_resp->header('X-API-Schemas'); | 
| 227 |  |  |  |  |  |  | } | 
| 228 |  |  |  |  |  |  |  | 
| 229 |  |  |  |  |  |  | return $self->client->schemas_url; | 
| 230 |  |  |  |  |  |  | } | 
| 231 |  |  |  |  |  |  |  | 
| 232 |  |  |  |  |  |  | sub _get_field { | 
| 233 |  |  |  |  |  |  | my $self = shift; | 
| 234 |  |  |  |  |  |  | my $name = shift; | 
| 235 |  |  |  |  |  |  |  | 
| 236 |  |  |  |  |  |  | if ( !exists $self->fields->{$name} ) { | 
| 237 |  |  |  |  |  |  | return undef; | 
| 238 |  |  |  |  |  |  | } | 
| 239 |  |  |  |  |  |  | return $self->fields->{$name}; | 
| 240 |  |  |  |  |  |  |  | 
| 241 |  |  |  |  |  |  | } | 
| 242 |  |  |  |  |  |  |  | 
| 243 |  |  |  |  |  |  | sub _set_field { | 
| 244 |  |  |  |  |  |  | my $self  = shift; | 
| 245 |  |  |  |  |  |  | my $name  = shift; | 
| 246 |  |  |  |  |  |  | my $value = shift; | 
| 247 |  |  |  |  |  |  | $self->fields->{$name} = $value; | 
| 248 |  |  |  |  |  |  | return $self->fields->{$name}; | 
| 249 |  |  |  |  |  |  | } | 
| 250 |  |  |  |  |  |  |  | 
| 251 |  |  |  |  |  |  | sub new_subclassed { | 
| 252 |  |  |  |  |  |  | my $class  = shift; | 
| 253 |  |  |  |  |  |  | my $params = shift; | 
| 254 |  |  |  |  |  |  |  | 
| 255 |  |  |  |  |  |  | my $data = $params->{fields}; | 
| 256 |  |  |  |  |  |  |  | 
| 257 |  |  |  |  |  |  | my $impl = DEFAULT_IMPL_CLASS; | 
| 258 |  |  |  |  |  |  | if ( ref($data) eq "HASH" ) { | 
| 259 |  |  |  |  |  |  | my $type_short = $params->{fields}->{type} || ''; | 
| 260 |  |  |  |  |  |  | my $type_long = $type_short ? $params->{client}->schemas_url($type_short) : ''; | 
| 261 |  |  |  |  |  |  |  | 
| 262 |  |  |  |  |  |  | $impl = $class->find_implementation( ( $type_long, $type_short ) ) || DEFAULT_IMPL_CLASS; | 
| 263 |  |  |  |  |  |  | eval "require $impl;"; | 
| 264 |  |  |  |  |  |  | } | 
| 265 |  |  |  |  |  |  | else { | 
| 266 |  |  |  |  |  |  |  | 
| 267 |  |  |  |  |  |  | # hmm, the json response didn't seem to be a hashref... | 
| 268 |  |  |  |  |  |  | # well in this case, there are not really any fields | 
| 269 |  |  |  |  |  |  | # so the caller will have to check the http_response | 
| 270 |  |  |  |  |  |  | # content | 
| 271 |  |  |  |  |  |  | $params->{fields} = {}; | 
| 272 |  |  |  |  |  |  | } | 
| 273 |  |  |  |  |  |  |  | 
| 274 |  |  |  |  |  |  | return $impl->new($params); | 
| 275 |  |  |  |  |  |  |  | 
| 276 |  |  |  |  |  |  | } | 
| 277 |  |  |  |  |  |  |  | 
| 278 |  |  |  |  |  |  | sub TO_JSON { | 
| 279 |  |  |  |  |  |  | my $self = shift; | 
| 280 |  |  |  |  |  |  | return $self->data; | 
| 281 |  |  |  |  |  |  | } | 
| 282 |  |  |  |  |  |  |  | 
| 283 |  |  |  |  |  |  | sub data { | 
| 284 |  |  |  |  |  |  | my $self = shift; | 
| 285 |  |  |  |  |  |  |  | 
| 286 |  |  |  |  |  |  | my %fields = %{ $self->fields || {} }; | 
| 287 |  |  |  |  |  |  | if (%fields) { | 
| 288 |  |  |  |  |  |  | return \%fields; | 
| 289 |  |  |  |  |  |  | } | 
| 290 |  |  |  |  |  |  | elsif ( $self->http_response ) { | 
| 291 |  |  |  |  |  |  | my $content = $self->http_response->decoded_content; | 
| 292 |  |  |  |  |  |  | return is_json($content) ? json_decode($content) : $content; | 
| 293 |  |  |  |  |  |  | } | 
| 294 |  |  |  |  |  |  | return {}; | 
| 295 |  |  |  |  |  |  | } | 
| 296 |  |  |  |  |  |  |  | 
| 297 |  |  |  |  |  |  | sub to_string { | 
| 298 |  |  |  |  |  |  | my $self   = shift; | 
| 299 |  |  |  |  |  |  | my $pretty = shift; | 
| 300 |  |  |  |  |  |  |  | 
| 301 |  |  |  |  |  |  | my $JSON = json_instance(); | 
| 302 |  |  |  |  |  |  | if ($pretty) { | 
| 303 |  |  |  |  |  |  | $JSON->pretty(1); | 
| 304 |  |  |  |  |  |  | } | 
| 305 |  |  |  |  |  |  | return json_encode( $self, $JSON ); | 
| 306 |  |  |  |  |  |  | } | 
| 307 |  |  |  |  |  |  |  | 
| 308 |  |  |  |  |  |  | my %SCHEMA_TO_IMPL = ( | 
| 309 |  |  |  |  |  |  | 'collection' => 'WWW::GoDaddy::REST::Collection', | 
| 310 |  |  |  |  |  |  | 'schema'     => 'WWW::GoDaddy::REST::Schema', | 
| 311 |  |  |  |  |  |  | ); | 
| 312 |  |  |  |  |  |  |  | 
| 313 |  |  |  |  |  |  | sub find_implementation { | 
| 314 |  |  |  |  |  |  | my $class    = shift; | 
| 315 |  |  |  |  |  |  | my @look_for = @_; | 
| 316 |  |  |  |  |  |  |  | 
| 317 |  |  |  |  |  |  | foreach (@look_for) { | 
| 318 |  |  |  |  |  |  | if ( exists $SCHEMA_TO_IMPL{$_} ) { | 
| 319 |  |  |  |  |  |  | return $SCHEMA_TO_IMPL{$_}; | 
| 320 |  |  |  |  |  |  | } | 
| 321 |  |  |  |  |  |  | } | 
| 322 |  |  |  |  |  |  | return; | 
| 323 |  |  |  |  |  |  | } | 
| 324 |  |  |  |  |  |  |  | 
| 325 |  |  |  |  |  |  | sub register_implementation { | 
| 326 |  |  |  |  |  |  | my $class = shift; | 
| 327 |  |  |  |  |  |  |  | 
| 328 |  |  |  |  |  |  | if ( @_ % 2 != 0 ) { | 
| 329 |  |  |  |  |  |  | croak("Expecting even number of parameters"); | 
| 330 |  |  |  |  |  |  | } | 
| 331 |  |  |  |  |  |  |  | 
| 332 |  |  |  |  |  |  | my $iterator = natatime 2, @_; | 
| 333 |  |  |  |  |  |  | while ( my ( $schema, $subclass ) = $iterator->() ) { | 
| 334 |  |  |  |  |  |  | $SCHEMA_TO_IMPL{$schema} = $subclass; | 
| 335 |  |  |  |  |  |  | } | 
| 336 |  |  |  |  |  |  | return; | 
| 337 |  |  |  |  |  |  | } | 
| 338 |  |  |  |  |  |  |  | 
| 339 |  |  |  |  |  |  | 1; | 
| 340 |  |  |  |  |  |  |  | 
| 341 |  |  |  |  |  |  | =head1 NAME | 
| 342 |  |  |  |  |  |  |  | 
| 343 |  |  |  |  |  |  | WWW::GoDaddy::REST::Resource - Represent a REST resource | 
| 344 |  |  |  |  |  |  |  | 
| 345 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 346 |  |  |  |  |  |  |  | 
| 347 |  |  |  |  |  |  | $client = WWW::GoDaddy::REST->new(...); | 
| 348 |  |  |  |  |  |  |  | 
| 349 |  |  |  |  |  |  | $resource = WWW::GoDaddy::REST::Resource->new({ | 
| 350 |  |  |  |  |  |  | client => $client, | 
| 351 |  |  |  |  |  |  | fields => { | 
| 352 |  |  |  |  |  |  | 'type'  => 'automobile', | 
| 353 |  |  |  |  |  |  | 'id'    => '1001', | 
| 354 |  |  |  |  |  |  | 'make'  => 'Tesla', | 
| 355 |  |  |  |  |  |  | 'model' => 'S' | 
| 356 |  |  |  |  |  |  | 'links' => { | 
| 357 |  |  |  |  |  |  | 'self'    => 'https://example.com/v1/automobiles/1001', | 
| 358 |  |  |  |  |  |  | 'schemas' => 'https://example.com/v1/schemas' | 
| 359 |  |  |  |  |  |  | }, | 
| 360 |  |  |  |  |  |  | 'actions' => { | 
| 361 |  |  |  |  |  |  | 'charge' => 'https://example.com/v1/automobiles/1001?charge' | 
| 362 |  |  |  |  |  |  | } | 
| 363 |  |  |  |  |  |  | # ... | 
| 364 |  |  |  |  |  |  | # see: https://github.com/godaddy/gdapi/blob/master/specification.md | 
| 365 |  |  |  |  |  |  | }, | 
| 366 |  |  |  |  |  |  | }); | 
| 367 |  |  |  |  |  |  |  | 
| 368 |  |  |  |  |  |  | $resource->f('id'); # get 1001 | 
| 369 |  |  |  |  |  |  | $resource->f('id','2000'); # set to 2000 and return 2000 | 
| 370 |  |  |  |  |  |  |  | 
| 371 |  |  |  |  |  |  | # follow a link in links section | 
| 372 |  |  |  |  |  |  | $schemas_resource = $resource->follow_link('schemas'); | 
| 373 |  |  |  |  |  |  |  | 
| 374 |  |  |  |  |  |  | # perform an action in the actions section | 
| 375 |  |  |  |  |  |  | $result_resource  = $resource->do_action('charge',{ 'with' => 'quick_charger' }); | 
| 376 |  |  |  |  |  |  |  | 
| 377 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 378 |  |  |  |  |  |  |  | 
| 379 |  |  |  |  |  |  | Base class used to represent a REST resource. | 
| 380 |  |  |  |  |  |  |  | 
| 381 |  |  |  |  |  |  | =head1 CLASS METHODS | 
| 382 |  |  |  |  |  |  |  | 
| 383 |  |  |  |  |  |  | =over 4 | 
| 384 |  |  |  |  |  |  |  | 
| 385 |  |  |  |  |  |  | =item new | 
| 386 |  |  |  |  |  |  |  | 
| 387 |  |  |  |  |  |  | Given a hash reference of L<"ATTRIBUTES"> and values, return a new instance | 
| 388 |  |  |  |  |  |  | of this object. | 
| 389 |  |  |  |  |  |  |  | 
| 390 |  |  |  |  |  |  | It is likely more important that you use the C<new_subclassed> class method. | 
| 391 |  |  |  |  |  |  |  | 
| 392 |  |  |  |  |  |  | Example: | 
| 393 |  |  |  |  |  |  |  | 
| 394 |  |  |  |  |  |  | my $resource = WWW::GoDaddy::REST::Resource->new({ | 
| 395 |  |  |  |  |  |  | client => WWW::GoDaddy::REST->new(...), | 
| 396 |  |  |  |  |  |  | fields => { | 
| 397 |  |  |  |  |  |  | id => '...', | 
| 398 |  |  |  |  |  |  | ... | 
| 399 |  |  |  |  |  |  | }, | 
| 400 |  |  |  |  |  |  | }); | 
| 401 |  |  |  |  |  |  |  | 
| 402 |  |  |  |  |  |  | =item new_subclassed | 
| 403 |  |  |  |  |  |  |  | 
| 404 |  |  |  |  |  |  | This takes the same paramegers as C<new> and is the preferred construction | 
| 405 |  |  |  |  |  |  | method.  This tries to find the appropriate subclass of | 
| 406 |  |  |  |  |  |  | C<WWW::GoDaddy::REST::Resource> and passes along the paramegers to the C<new> | 
| 407 |  |  |  |  |  |  | method of that subclass instead. | 
| 408 |  |  |  |  |  |  |  | 
| 409 |  |  |  |  |  |  | See also: C<new> | 
| 410 |  |  |  |  |  |  |  | 
| 411 |  |  |  |  |  |  | =item find_implementation | 
| 412 |  |  |  |  |  |  |  | 
| 413 |  |  |  |  |  |  | Given a list of schema type names, find the best implementation sub class. | 
| 414 |  |  |  |  |  |  |  | 
| 415 |  |  |  |  |  |  | Returns the string of the class name. If no good subclass candidate is found, | 
| 416 |  |  |  |  |  |  | returns undef. | 
| 417 |  |  |  |  |  |  |  | 
| 418 |  |  |  |  |  |  | Example: | 
| 419 |  |  |  |  |  |  |  | 
| 420 |  |  |  |  |  |  | find_implementation( 'schema' ); | 
| 421 |  |  |  |  |  |  | # WWW::GoDaddy::REST::Schema | 
| 422 |  |  |  |  |  |  |  | 
| 423 |  |  |  |  |  |  | =item register_implementation | 
| 424 |  |  |  |  |  |  |  | 
| 425 |  |  |  |  |  |  | Register a subclass handler for a schema type given a schema name and the | 
| 426 |  |  |  |  |  |  | name of a L<WWW::GoDaddy::REST::Resource> subclass. | 
| 427 |  |  |  |  |  |  |  | 
| 428 |  |  |  |  |  |  | This can take as many schema => resource class pairs as you want. | 
| 429 |  |  |  |  |  |  |  | 
| 430 |  |  |  |  |  |  | Example: | 
| 431 |  |  |  |  |  |  |  | 
| 432 |  |  |  |  |  |  | WWW::GoDaddy::REST::Resource->register_subclass( 'account' => 'My::AccountRes' ); | 
| 433 |  |  |  |  |  |  | WWW::GoDaddy::REST::Resource->register_subclass( 'foo' => 'Bar', 'baz' => 'Buzz' ); | 
| 434 |  |  |  |  |  |  |  | 
| 435 |  |  |  |  |  |  | =back | 
| 436 |  |  |  |  |  |  |  | 
| 437 |  |  |  |  |  |  | =head1 ATTRIBUTES | 
| 438 |  |  |  |  |  |  |  | 
| 439 |  |  |  |  |  |  | =over 4 | 
| 440 |  |  |  |  |  |  |  | 
| 441 |  |  |  |  |  |  | =item client | 
| 442 |  |  |  |  |  |  |  | 
| 443 |  |  |  |  |  |  | Instance of L<WWW::GoDaddy::REST> associated with the resource. | 
| 444 |  |  |  |  |  |  |  | 
| 445 |  |  |  |  |  |  | =item fields | 
| 446 |  |  |  |  |  |  |  | 
| 447 |  |  |  |  |  |  | Hash reference containing the raw data for the underlying resource. | 
| 448 |  |  |  |  |  |  |  | 
| 449 |  |  |  |  |  |  | Several methods delegate to this underlying structure such as C<f>, | 
| 450 |  |  |  |  |  |  | and C<field>. | 
| 451 |  |  |  |  |  |  |  | 
| 452 |  |  |  |  |  |  | =item http_response | 
| 453 |  |  |  |  |  |  |  | 
| 454 |  |  |  |  |  |  | Optionally present instance of an L<HTTP::Response> object so that | 
| 455 |  |  |  |  |  |  | you can inspect the HTTP information related to the resource. | 
| 456 |  |  |  |  |  |  |  | 
| 457 |  |  |  |  |  |  | =back | 
| 458 |  |  |  |  |  |  |  | 
| 459 |  |  |  |  |  |  | =head1 METHODS | 
| 460 |  |  |  |  |  |  |  | 
| 461 |  |  |  |  |  |  | =over 4 | 
| 462 |  |  |  |  |  |  |  | 
| 463 |  |  |  |  |  |  | =item f | 
| 464 |  |  |  |  |  |  |  | 
| 465 |  |  |  |  |  |  | Get or set a field by name. You may also use the longer name C<field>. | 
| 466 |  |  |  |  |  |  |  | 
| 467 |  |  |  |  |  |  | When performing a set, it also returns the new value that was set. | 
| 468 |  |  |  |  |  |  |  | 
| 469 |  |  |  |  |  |  | Example: | 
| 470 |  |  |  |  |  |  |  | 
| 471 |  |  |  |  |  |  | $res->f('field_name');       # get | 
| 472 |  |  |  |  |  |  | $res->f('field_name','new'); # set | 
| 473 |  |  |  |  |  |  |  | 
| 474 |  |  |  |  |  |  | =item f_as_resources | 
| 475 |  |  |  |  |  |  |  | 
| 476 |  |  |  |  |  |  | Get a field by name.  If it is a resource, this will turn it into an | 
| 477 |  |  |  |  |  |  | object instead of giving you the raw hash reference as the return value. | 
| 478 |  |  |  |  |  |  |  | 
| 479 |  |  |  |  |  |  | Note, if the field is a 'map' or 'array' of resources, every item in | 
| 480 |  |  |  |  |  |  | those lists will be 'resourcified'. | 
| 481 |  |  |  |  |  |  |  | 
| 482 |  |  |  |  |  |  | If this is not a resource, then it does return the raw value. | 
| 483 |  |  |  |  |  |  |  | 
| 484 |  |  |  |  |  |  | Example: | 
| 485 |  |  |  |  |  |  |  | 
| 486 |  |  |  |  |  |  | # return value is a WWW::GoDaddy::REST::Resource, not a hash ref | 
| 487 |  |  |  |  |  |  | $driver = $car->f('driver'); | 
| 488 |  |  |  |  |  |  |  | 
| 489 |  |  |  |  |  |  | See C<f> if you want the raw value.  This will return the raw value, if | 
| 490 |  |  |  |  |  |  | the value does not look like a resource. | 
| 491 |  |  |  |  |  |  |  | 
| 492 |  |  |  |  |  |  | =item field | 
| 493 |  |  |  |  |  |  |  | 
| 494 |  |  |  |  |  |  | Get or set a field by name.  You may also use the shorter name C<f>. | 
| 495 |  |  |  |  |  |  |  | 
| 496 |  |  |  |  |  |  | When performing a set, it also returns the new value that was set. | 
| 497 |  |  |  |  |  |  |  | 
| 498 |  |  |  |  |  |  | Example: | 
| 499 |  |  |  |  |  |  |  | 
| 500 |  |  |  |  |  |  | $res->field('field_name');       # get | 
| 501 |  |  |  |  |  |  | $res->fieldf('field_name','new'); # set | 
| 502 |  |  |  |  |  |  |  | 
| 503 |  |  |  |  |  |  | =item save | 
| 504 |  |  |  |  |  |  |  | 
| 505 |  |  |  |  |  |  | Does a PUT at this resources URI.  Returns a new resource object. | 
| 506 |  |  |  |  |  |  |  | 
| 507 |  |  |  |  |  |  | Example: | 
| 508 |  |  |  |  |  |  |  | 
| 509 |  |  |  |  |  |  | $r2 = $r1->save(); | 
| 510 |  |  |  |  |  |  |  | 
| 511 |  |  |  |  |  |  | =item delete | 
| 512 |  |  |  |  |  |  |  | 
| 513 |  |  |  |  |  |  | Does a DELETE on this resource.  Returns a new resource object.  This | 
| 514 |  |  |  |  |  |  | return value likely is only useful to get at the C<http_response> attribute. | 
| 515 |  |  |  |  |  |  |  | 
| 516 |  |  |  |  |  |  | =item do_action | 
| 517 |  |  |  |  |  |  |  | 
| 518 |  |  |  |  |  |  | Does a POST with the supplied data on the action URL with the given name. | 
| 519 |  |  |  |  |  |  |  | 
| 520 |  |  |  |  |  |  | If the action with the provided name does not exist, this method will | 
| 521 |  |  |  |  |  |  | die.  See also: C<action> and C<actions> | 
| 522 |  |  |  |  |  |  |  | 
| 523 |  |  |  |  |  |  | Example: | 
| 524 |  |  |  |  |  |  |  | 
| 525 |  |  |  |  |  |  | $r2 = $r1->do_action('some_action',{ a => 'a_v' }); | 
| 526 |  |  |  |  |  |  |  | 
| 527 |  |  |  |  |  |  | =item follow_link | 
| 528 |  |  |  |  |  |  |  | 
| 529 |  |  |  |  |  |  | Gets the resource by following the link URL with the provided name. | 
| 530 |  |  |  |  |  |  |  | 
| 531 |  |  |  |  |  |  | If the link with the provided name does not exist, this method will | 
| 532 |  |  |  |  |  |  | die.  See also: C<link> and C<link> | 
| 533 |  |  |  |  |  |  |  | 
| 534 |  |  |  |  |  |  | Example: | 
| 535 |  |  |  |  |  |  |  | 
| 536 |  |  |  |  |  |  | $r2 = $r1->follow_link('some_link'); | 
| 537 |  |  |  |  |  |  |  | 
| 538 |  |  |  |  |  |  | =item id | 
| 539 |  |  |  |  |  |  |  | 
| 540 |  |  |  |  |  |  | Return the id of this instance | 
| 541 |  |  |  |  |  |  |  | 
| 542 |  |  |  |  |  |  | =item type | 
| 543 |  |  |  |  |  |  |  | 
| 544 |  |  |  |  |  |  | Return the name of the schema type that this object belongs to. | 
| 545 |  |  |  |  |  |  |  | 
| 546 |  |  |  |  |  |  | =item type_fq | 
| 547 |  |  |  |  |  |  |  | 
| 548 |  |  |  |  |  |  | Return the full URI to the schema type that this object belongs to. | 
| 549 |  |  |  |  |  |  |  | 
| 550 |  |  |  |  |  |  | =item resource_type | 
| 551 |  |  |  |  |  |  |  | 
| 552 |  |  |  |  |  |  | Return the name of the schema type that this collection's objects belong to. | 
| 553 |  |  |  |  |  |  |  | 
| 554 |  |  |  |  |  |  | =item resource_type_fq | 
| 555 |  |  |  |  |  |  |  | 
| 556 |  |  |  |  |  |  | Return the full URI to the schema type that this collection's objects belong to. | 
| 557 |  |  |  |  |  |  |  | 
| 558 |  |  |  |  |  |  | =item schemas_url | 
| 559 |  |  |  |  |  |  |  | 
| 560 |  |  |  |  |  |  | Returns the URL for the schema collection.  This differs from the | 
| 561 |  |  |  |  |  |  | client C<schemas_url> method since it has more places to look for hints | 
| 562 |  |  |  |  |  |  | of the schemas collection url (headers, json response etc). | 
| 563 |  |  |  |  |  |  |  | 
| 564 |  |  |  |  |  |  | Example: | 
| 565 |  |  |  |  |  |  |  | 
| 566 |  |  |  |  |  |  | $r->schemas_url(); | 
| 567 |  |  |  |  |  |  |  | 
| 568 |  |  |  |  |  |  | =item schema | 
| 569 |  |  |  |  |  |  |  | 
| 570 |  |  |  |  |  |  | Find and return the L<WWW::GoDaddy::REST::Schema> object that this is. | 
| 571 |  |  |  |  |  |  |  | 
| 572 |  |  |  |  |  |  | =item link | 
| 573 |  |  |  |  |  |  |  | 
| 574 |  |  |  |  |  |  | Return the link URL for the given name or undef if it does not exist. | 
| 575 |  |  |  |  |  |  |  | 
| 576 |  |  |  |  |  |  | Example: | 
| 577 |  |  |  |  |  |  |  | 
| 578 |  |  |  |  |  |  | # https://example.com/v1/thing/... | 
| 579 |  |  |  |  |  |  | $r->link('self'); | 
| 580 |  |  |  |  |  |  | # 'https://example.com/v1/me/1' | 
| 581 |  |  |  |  |  |  |  | 
| 582 |  |  |  |  |  |  | =item links | 
| 583 |  |  |  |  |  |  |  | 
| 584 |  |  |  |  |  |  | Return the hashref that contains the link => url information | 
| 585 |  |  |  |  |  |  |  | 
| 586 |  |  |  |  |  |  | Example: | 
| 587 |  |  |  |  |  |  |  | 
| 588 |  |  |  |  |  |  | $r->links(); | 
| 589 |  |  |  |  |  |  | # { | 
| 590 |  |  |  |  |  |  | #     'self'      => 'https://example.com/v1/me/1', | 
| 591 |  |  |  |  |  |  | #     'some_link' => 'https://example.com/v1/me/1/some_link' | 
| 592 |  |  |  |  |  |  | # } | 
| 593 |  |  |  |  |  |  |  | 
| 594 |  |  |  |  |  |  | =item action | 
| 595 |  |  |  |  |  |  |  | 
| 596 |  |  |  |  |  |  | Return the action URL for the given name. | 
| 597 |  |  |  |  |  |  |  | 
| 598 |  |  |  |  |  |  | Example: | 
| 599 |  |  |  |  |  |  |  | 
| 600 |  |  |  |  |  |  | $r->action('custom_action'); | 
| 601 |  |  |  |  |  |  | # https://example.com/v1/thing/1001?some_action | 
| 602 |  |  |  |  |  |  |  | 
| 603 |  |  |  |  |  |  | =item actions | 
| 604 |  |  |  |  |  |  |  | 
| 605 |  |  |  |  |  |  | Return the hashref that contains the action => url information | 
| 606 |  |  |  |  |  |  |  | 
| 607 |  |  |  |  |  |  | Example: | 
| 608 |  |  |  |  |  |  |  | 
| 609 |  |  |  |  |  |  | $r->actions(); | 
| 610 |  |  |  |  |  |  | # { | 
| 611 |  |  |  |  |  |  | #     'custom_action' => 'https://example.com/v1/thing/1001?some_action' | 
| 612 |  |  |  |  |  |  | # } | 
| 613 |  |  |  |  |  |  |  | 
| 614 |  |  |  |  |  |  | =item items | 
| 615 |  |  |  |  |  |  |  | 
| 616 |  |  |  |  |  |  | Returns a list of resources that this resource contains.  This implementation | 
| 617 |  |  |  |  |  |  | simply returns a list of 'self'.  It is here to be consistent with the | 
| 618 |  |  |  |  |  |  | implementation found in L<WWW::GoDaddy::REST::Collection>. | 
| 619 |  |  |  |  |  |  |  | 
| 620 |  |  |  |  |  |  | Example: | 
| 621 |  |  |  |  |  |  |  | 
| 622 |  |  |  |  |  |  | @items = $resource->items(); | 
| 623 |  |  |  |  |  |  |  | 
| 624 |  |  |  |  |  |  | =item TO_JSON | 
| 625 |  |  |  |  |  |  |  | 
| 626 |  |  |  |  |  |  | Returns a hashref that represents this object.  This exists to make using the | 
| 627 |  |  |  |  |  |  | L<JSON> module more convenient.  This does NOT return a JSON STRING, just a | 
| 628 |  |  |  |  |  |  | perl data structure. | 
| 629 |  |  |  |  |  |  |  | 
| 630 |  |  |  |  |  |  | See C<to_string>. | 
| 631 |  |  |  |  |  |  |  | 
| 632 |  |  |  |  |  |  | =item to_string | 
| 633 |  |  |  |  |  |  |  | 
| 634 |  |  |  |  |  |  | Returns a JSON string that represents this object.  This takes an optional | 
| 635 |  |  |  |  |  |  | parameter, "pretty".  If true, the json output will be prettified. This defaults | 
| 636 |  |  |  |  |  |  | to false. | 
| 637 |  |  |  |  |  |  |  | 
| 638 |  |  |  |  |  |  | =item data | 
| 639 |  |  |  |  |  |  |  | 
| 640 |  |  |  |  |  |  | The resource is returned as a perl data structure.  Note, if there are no | 
| 641 |  |  |  |  |  |  | C<fields>, then the http_respons is consulted, if json data is found in | 
| 642 |  |  |  |  |  |  | the content, that is returned (for instance, a plane old string or integer). | 
| 643 |  |  |  |  |  |  |  | 
| 644 |  |  |  |  |  |  | =back | 
| 645 |  |  |  |  |  |  |  | 
| 646 |  |  |  |  |  |  | =head1 AUTHOR | 
| 647 |  |  |  |  |  |  |  | 
| 648 |  |  |  |  |  |  | David Bartle, C<< <davidb@mediatemple.net> >> | 
| 649 |  |  |  |  |  |  |  | 
| 650 |  |  |  |  |  |  | =head1 COPYRIGHT & LICENSE | 
| 651 |  |  |  |  |  |  |  | 
| 652 |  |  |  |  |  |  | Copyright (c) 2014 Go Daddy Operating Company, LLC | 
| 653 |  |  |  |  |  |  |  | 
| 654 |  |  |  |  |  |  | Permission is hereby granted, free of charge, to any person obtaining a | 
| 655 |  |  |  |  |  |  | copy of this software and associated documentation files (the "Software"), | 
| 656 |  |  |  |  |  |  | to deal in the Software without restriction, including without limitation | 
| 657 |  |  |  |  |  |  | the rights to use, copy, modify, merge, publish, distribute, sublicense, | 
| 658 |  |  |  |  |  |  | and/or sell copies of the Software, and to permit persons to whom the | 
| 659 |  |  |  |  |  |  | Software is furnished to do so, subject to the following conditions: | 
| 660 |  |  |  |  |  |  |  | 
| 661 |  |  |  |  |  |  | The above copyright notice and this permission notice shall be included in | 
| 662 |  |  |  |  |  |  | all copies or substantial portions of the Software. | 
| 663 |  |  |  |  |  |  |  | 
| 664 |  |  |  |  |  |  | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
| 665 |  |  |  |  |  |  | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
| 666 |  |  |  |  |  |  | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | 
| 667 |  |  |  |  |  |  | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
| 668 |  |  |  |  |  |  | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | 
| 669 |  |  |  |  |  |  | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | 
| 670 |  |  |  |  |  |  | DEALINGS IN THE SOFTWARE. | 
| 671 |  |  |  |  |  |  |  | 
| 672 |  |  |  |  |  |  |  | 
| 673 |  |  |  |  |  |  | =cut |