| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package WebService::Rackspace::CloudFiles; | 
| 2 | 4 |  |  | 4 |  | 391868 | use Moo; | 
|  | 4 |  |  |  |  | 28016 |  | 
|  | 4 |  |  |  |  | 17 |  | 
| 3 | 4 |  |  | 4 |  | 5814 | use MooX::StrictConstructor; | 
|  | 4 |  |  |  |  | 39999 |  | 
|  | 4 |  |  |  |  | 17 |  | 
| 4 | 4 |  |  | 4 |  | 72813 | use Types::Standard qw(Bool Str Num Int HashRef InstanceOf ClassName); | 
|  | 4 |  |  |  |  | 341607 |  | 
|  | 4 |  |  |  |  | 52 |  | 
| 5 | 4 |  |  | 4 |  | 6069 | use DateTime::Format::HTTP; | 
|  | 4 |  |  |  |  | 1280975 |  | 
|  | 4 |  |  |  |  | 153 |  | 
| 6 | 4 |  |  | 4 |  | 2004 | use WebService::Rackspace::CloudFiles::Container; | 
|  | 4 |  |  |  |  | 11 |  | 
|  | 4 |  |  |  |  | 158 |  | 
| 7 | 4 |  |  | 4 |  | 1697 | use WebService::Rackspace::CloudFiles::Object; | 
|  | 4 |  |  |  |  | 11 |  | 
|  | 4 |  |  |  |  | 139 |  | 
| 8 | 4 |  |  | 4 |  | 28 | use WebService::Rackspace::CloudFiles::Object::Iterator; | 
|  | 4 |  |  |  |  | 5 |  | 
|  | 4 |  |  |  |  | 96 |  | 
| 9 | 4 |  |  | 4 |  | 1853 | use WebService::Rackspace::CloudFiles::ConnCache; | 
|  | 4 |  |  |  |  | 10 |  | 
|  | 4 |  |  |  |  | 96 |  | 
| 10 | 4 |  |  | 4 |  | 1824 | use LWP::UserAgent::Determined; | 
|  | 4 |  |  |  |  | 1722 |  | 
|  | 4 |  |  |  |  | 94 |  | 
| 11 | 4 |  |  | 4 |  | 1464 | use URI::QueryParam; | 
|  | 4 |  |  |  |  | 2353 |  | 
|  | 4 |  |  |  |  | 127 |  | 
| 12 | 4 |  |  | 4 |  | 21 | use JSON::Any; | 
|  | 4 |  |  |  |  | 5 |  | 
|  | 4 |  |  |  |  | 35 |  | 
| 13 | 4 |  |  | 4 |  | 439 | use Carp qw(confess); | 
|  | 4 |  |  |  |  | 5 |  | 
|  | 4 |  |  |  |  | 3978 |  | 
| 14 |  |  |  |  |  |  | our $VERSION = '2.01'; | 
| 15 |  |  |  |  |  |  |  | 
| 16 |  |  |  |  |  |  | my $DEBUG = 0; | 
| 17 |  |  |  |  |  |  |  | 
| 18 |  |  |  |  |  |  | has 'user'    => ( is => 'ro', isa => Str, required => 1 ); | 
| 19 |  |  |  |  |  |  | has 'key'     => ( is => 'ro', isa => Str, required => 1 ); | 
| 20 |  |  |  |  |  |  | has 'location'=> ( is => 'ro', isa => Str, required => 0, default => 'usa'); | 
| 21 |  |  |  |  |  |  | has 'timeout' => ( is => 'ro', isa => Num, required => 0, default => 30 ); | 
| 22 |  |  |  |  |  |  | has 'retries' => ( is => 'ro', isa => Str, required => 0, default => '1,2,4,8,16,32' ); | 
| 23 |  |  |  |  |  |  |  | 
| 24 |  |  |  |  |  |  | has 'connection_cache_class' => ( | 
| 25 |  |  |  |  |  |  | is => 'ro', | 
| 26 |  |  |  |  |  |  | isa => ClassName, | 
| 27 |  |  |  |  |  |  | default => 'WebService::Rackspace::CloudFiles::ConnCache' | 
| 28 |  |  |  |  |  |  | ); | 
| 29 |  |  |  |  |  |  |  | 
| 30 |  |  |  |  |  |  | has 'iterator_callback_class' => ( | 
| 31 |  |  |  |  |  |  | is => 'ro', | 
| 32 |  |  |  |  |  |  | isa => ClassName, | 
| 33 |  |  |  |  |  |  | default => 'WebService::Rackspace::CloudFiles::Object::Iterator' | 
| 34 |  |  |  |  |  |  | ); | 
| 35 |  |  |  |  |  |  |  | 
| 36 |  |  |  |  |  |  | has locations => ( | 
| 37 |  |  |  |  |  |  | traits => [ 'Hash' ], | 
| 38 |  |  |  |  |  |  | isa => HashRef, | 
| 39 |  |  |  |  |  |  | is => 'ro', | 
| 40 |  |  |  |  |  |  | default => sub { | 
| 41 |  |  |  |  |  |  | return { | 
| 42 |  |  |  |  |  |  | uk  => 'https://lon.auth.api.rackspacecloud.com/v1.0', | 
| 43 |  |  |  |  |  |  | usa => 'https://auth.api.rackspacecloud.com/v1.0', | 
| 44 |  |  |  |  |  |  | }, | 
| 45 |  |  |  |  |  |  | }, | 
| 46 |  |  |  |  |  |  | handles => { | 
| 47 |  |  |  |  |  |  | location_names => 'keys', | 
| 48 |  |  |  |  |  |  | }, | 
| 49 |  |  |  |  |  |  | ); | 
| 50 |  |  |  |  |  |  |  | 
| 51 |  |  |  |  |  |  | has location_url => ( | 
| 52 |  |  |  |  |  |  | is       => 'ro', | 
| 53 |  |  |  |  |  |  | isa      => Str, | 
| 54 |  |  |  |  |  |  | lazy     => 1, | 
| 55 |  |  |  |  |  |  | required => 0, | 
| 56 |  |  |  |  |  |  | default  => sub { | 
| 57 |  |  |  |  |  |  | my $self = shift; | 
| 58 |  |  |  |  |  |  |  | 
| 59 |  |  |  |  |  |  | return $self->locations->{$self->location} || | 
| 60 |  |  |  |  |  |  | confess "location $self->{location} unknown: valid locations are " . | 
| 61 |  |  |  |  |  |  | join ', ', $self->location_names ; | 
| 62 |  |  |  |  |  |  | }, | 
| 63 |  |  |  |  |  |  | ); | 
| 64 |  |  |  |  |  |  |  | 
| 65 |  |  |  |  |  |  | has 'ua' => ( | 
| 66 |  |  |  |  |  |  | is          => 'ro', | 
| 67 |  |  |  |  |  |  | isa         => InstanceOf['LWP::UserAgent'], | 
| 68 |  |  |  |  |  |  | required    => 0, | 
| 69 |  |  |  |  |  |  | lazy        => 1, | 
| 70 |  |  |  |  |  |  | builder     => '_build_ua', | 
| 71 |  |  |  |  |  |  | ); | 
| 72 |  |  |  |  |  |  |  | 
| 73 |  |  |  |  |  |  | has storage_url => ( | 
| 74 |  |  |  |  |  |  | is       => 'rw', | 
| 75 |  |  |  |  |  |  | isa      => Str, | 
| 76 |  |  |  |  |  |  | required => 0, | 
| 77 |  |  |  |  |  |  | lazy     => 1, | 
| 78 |  |  |  |  |  |  | default  => sub { | 
| 79 |  |  |  |  |  |  | my $self = shift; | 
| 80 |  |  |  |  |  |  | $self->_authenticate; | 
| 81 |  |  |  |  |  |  | $self->storage_url; | 
| 82 |  |  |  |  |  |  | }, | 
| 83 |  |  |  |  |  |  | ); | 
| 84 |  |  |  |  |  |  |  | 
| 85 |  |  |  |  |  |  | has cdn_management_url => ( | 
| 86 |  |  |  |  |  |  | is => 'rw', | 
| 87 |  |  |  |  |  |  | isa => Str, | 
| 88 |  |  |  |  |  |  | required => 0, | 
| 89 |  |  |  |  |  |  | lazy => 1, | 
| 90 |  |  |  |  |  |  | default => sub { | 
| 91 |  |  |  |  |  |  | my $self = shift; | 
| 92 |  |  |  |  |  |  | $self->_authenticate; | 
| 93 |  |  |  |  |  |  | $self->cdn_management_url; | 
| 94 |  |  |  |  |  |  | }, | 
| 95 |  |  |  |  |  |  | ); | 
| 96 |  |  |  |  |  |  |  | 
| 97 |  |  |  |  |  |  | has token => ( | 
| 98 |  |  |  |  |  |  | is       => 'rw', | 
| 99 |  |  |  |  |  |  | isa      => Str, | 
| 100 |  |  |  |  |  |  | required => 0, | 
| 101 |  |  |  |  |  |  | lazy     => 1, | 
| 102 |  |  |  |  |  |  | default  => sub { | 
| 103 |  |  |  |  |  |  | my $self = shift; | 
| 104 |  |  |  |  |  |  | $self->_authenticate; | 
| 105 |  |  |  |  |  |  | $self->token; | 
| 106 |  |  |  |  |  |  | }, | 
| 107 |  |  |  |  |  |  | ); | 
| 108 |  |  |  |  |  |  |  | 
| 109 |  |  |  |  |  |  | has is_authenticated => ( | 
| 110 |  |  |  |  |  |  | is       => 'rw', | 
| 111 |  |  |  |  |  |  | isa      => Bool, | 
| 112 |  |  |  |  |  |  | required => 0, | 
| 113 |  |  |  |  |  |  | default  => 0, | 
| 114 |  |  |  |  |  |  | ); | 
| 115 |  |  |  |  |  |  |  | 
| 116 |  |  |  |  |  |  | sub _build_ua { | 
| 117 | 0 |  |  | 0 |  | 0 | my $self = shift; | 
| 118 |  |  |  |  |  |  |  | 
| 119 | 0 |  |  |  |  | 0 | my $ua   = LWP::UserAgent::Determined->new( | 
| 120 |  |  |  |  |  |  | keep_alive            => 10, | 
| 121 |  |  |  |  |  |  | requests_redirectable => [qw(GET HEAD DELETE PUT)], | 
| 122 |  |  |  |  |  |  | ); | 
| 123 | 0 |  |  |  |  | 0 | $ua->timing($self->retries); | 
| 124 | 0 |  |  |  |  | 0 | $ua->conn_cache( | 
| 125 |  |  |  |  |  |  | $self->connection_cache_class->new( | 
| 126 |  |  |  |  |  |  | total_capacity          => 10, | 
| 127 |  |  |  |  |  |  | max_keep_alive_requests => 990, | 
| 128 |  |  |  |  |  |  | ) | 
| 129 |  |  |  |  |  |  | ); | 
| 130 | 0 |  |  |  |  | 0 | my $http_codes_hr = $ua->codes_to_determinate(); | 
| 131 | 0 |  |  |  |  | 0 | $http_codes_hr->{422} = 1; # used by cloudfiles for upload data corruption | 
| 132 | 0 |  |  |  |  | 0 | $ua->timeout( $self->timeout ); | 
| 133 | 0 |  |  |  |  | 0 | $ua->env_proxy; | 
| 134 |  |  |  |  |  |  |  | 
| 135 | 0 |  |  |  |  | 0 | return $ua; | 
| 136 |  |  |  |  |  |  | } | 
| 137 |  |  |  |  |  |  |  | 
| 138 |  |  |  |  |  |  | sub _authenticate { | 
| 139 | 3 |  |  | 3 |  | 207 | my $self = shift; | 
| 140 |  |  |  |  |  |  |  | 
| 141 | 3 |  |  |  |  | 70 | my $request = HTTP::Request->new( | 
| 142 |  |  |  |  |  |  | 'GET', | 
| 143 |  |  |  |  |  |  | $self->location_url, | 
| 144 |  |  |  |  |  |  | [   'X-Auth-User' => $self->user, | 
| 145 |  |  |  |  |  |  | 'X-Auth-Key'  => $self->key, | 
| 146 |  |  |  |  |  |  | ] | 
| 147 |  |  |  |  |  |  | ); | 
| 148 | 3 |  |  |  |  | 13980 | $self->is_authenticated(1); # needed to prevent infinite recursion on auth requests | 
| 149 | 3 |  |  |  |  | 146 | my $response = $self->_request($request); | 
| 150 | 3 |  |  |  |  | 63 | $self->is_authenticated(0); | 
| 151 |  |  |  |  |  |  |  | 
| 152 | 3 | 50 |  |  |  | 49 | confess 'Unauthorized'  if $response->code == 401; | 
| 153 | 3 | 50 |  |  |  | 23 | confess 'Unknown error' if $response->code != 204; | 
| 154 |  |  |  |  |  |  |  | 
| 155 | 3 |  | 33 |  |  | 25 | my $storage_url = $response->header('X-Storage-Url') | 
| 156 |  |  |  |  |  |  | || confess 'Missing storage url'; | 
| 157 | 3 |  | 33 |  |  | 85 | my $token = $response->header('X-Auth-Token') | 
| 158 |  |  |  |  |  |  | || confess 'Missing auth token'; | 
| 159 | 3 |  | 33 |  |  | 68 | my $cdn_management_url = $response->header('X-CDN-Management-Url') | 
| 160 |  |  |  |  |  |  | || confess 'Missing CDN management url'; | 
| 161 |  |  |  |  |  |  |  | 
| 162 | 3 |  |  |  |  | 142 | $self->storage_url($storage_url); | 
| 163 | 3 |  |  |  |  | 108 | $self->token($token); | 
| 164 | 3 |  |  |  |  | 95 | $self->cdn_management_url($cdn_management_url); | 
| 165 |  |  |  |  |  |  |  | 
| 166 | 3 |  |  |  |  | 90 | $self->is_authenticated(1); | 
| 167 |  |  |  |  |  |  | } | 
| 168 |  |  |  |  |  |  |  | 
| 169 |  |  |  |  |  |  | before _request => sub { | 
| 170 |  |  |  |  |  |  | my $self = shift; | 
| 171 |  |  |  |  |  |  |  | 
| 172 |  |  |  |  |  |  | $self->_authenticate unless $self->is_authenticated; | 
| 173 |  |  |  |  |  |  | }; | 
| 174 |  |  |  |  |  |  |  | 
| 175 |  |  |  |  |  |  | sub _request { | 
| 176 |  |  |  |  |  |  | my ( $self, $request, $filename ) = @_; | 
| 177 |  |  |  |  |  |  | warn $request->as_string if $DEBUG; | 
| 178 |  |  |  |  |  |  | my $response = $self->ua->request( $request, $filename ); | 
| 179 |  |  |  |  |  |  | warn $response->as_string if $DEBUG; | 
| 180 |  |  |  |  |  |  | if ( $response->code == 401 && $request->header('X-Auth-Token') ) { | 
| 181 |  |  |  |  |  |  |  | 
| 182 |  |  |  |  |  |  | # http://trac.cyberduck.ch/ticket/2876 | 
| 183 |  |  |  |  |  |  | # Be warned that the token will expire over time (possibly as short | 
| 184 |  |  |  |  |  |  | # as an hour). The application should trap a 401 (Unauthorized) | 
| 185 |  |  |  |  |  |  | # response on a given request (to either storage or cdn system) | 
| 186 |  |  |  |  |  |  | # and then re-authenticate to obtain an updated token. | 
| 187 |  |  |  |  |  |  | $self->is_authenticated(0); | 
| 188 |  |  |  |  |  |  | $self->_authenticate; | 
| 189 |  |  |  |  |  |  | $request->header( 'X-Auth-Token', $self->token ); | 
| 190 |  |  |  |  |  |  | warn $request->as_string if $DEBUG; | 
| 191 |  |  |  |  |  |  | $response = $self->ua->request( $request, $filename ); | 
| 192 |  |  |  |  |  |  | warn $response->as_string if $DEBUG; | 
| 193 |  |  |  |  |  |  | } | 
| 194 |  |  |  |  |  |  | return $response; | 
| 195 |  |  |  |  |  |  | } | 
| 196 |  |  |  |  |  |  |  | 
| 197 |  |  |  |  |  |  | sub containers { | 
| 198 | 1 |  |  | 1 | 1 | 109 | my $self    = shift; | 
| 199 | 1 |  |  |  |  | 20 | my $request = HTTP::Request->new( 'GET', | 
| 200 |  |  |  |  |  |  | $self->storage_url . '?format=json', | 
| 201 |  |  |  |  |  |  | [ 'X-Auth-Token' => $self->token ] ); | 
| 202 | 1 |  |  |  |  | 160 | my $response = $self->_request($request); | 
| 203 | 1 | 50 |  |  |  | 4 | return if $response->code == 204; | 
| 204 | 1 | 50 |  |  |  | 11 | confess 'Unknown error' if $response->code != 200; | 
| 205 | 1 |  |  |  |  | 10 | my @containers; | 
| 206 |  |  |  |  |  |  |  | 
| 207 | 1 |  |  |  |  | 2 | foreach my $container_data ( @{JSON::Any->from_json($response->content)} ) { | 
|  | 1 |  |  |  |  | 5 |  | 
| 208 | 1 |  |  |  |  | 81 | push @containers, $self->container(%{$container_data}); | 
|  | 1 |  |  |  |  | 6 |  | 
| 209 |  |  |  |  |  |  | } | 
| 210 | 1 |  |  |  |  | 4467 | return @containers; | 
| 211 |  |  |  |  |  |  | } | 
| 212 |  |  |  |  |  |  |  | 
| 213 |  |  |  |  |  |  | sub total_bytes_used { | 
| 214 | 0 |  |  | 0 | 1 | 0 | my $self    = shift; | 
| 215 | 0 |  |  |  |  | 0 | my $request = HTTP::Request->new( 'HEAD', $self->storage_url, | 
| 216 |  |  |  |  |  |  | [ 'X-Auth-Token' => $self->token ] ); | 
| 217 | 0 |  |  |  |  | 0 | my $response = $self->_request($request); | 
| 218 | 0 | 0 |  |  |  | 0 | confess 'Unknown error' unless $response->is_success; | 
| 219 | 0 |  |  |  |  | 0 | my $total_bytes_used = $response->header('X-Account-Bytes-Used'); | 
| 220 | 0 | 0 |  |  |  | 0 | $total_bytes_used = 0 if $total_bytes_used eq 'None'; | 
| 221 | 0 |  |  |  |  | 0 | return $total_bytes_used; | 
| 222 |  |  |  |  |  |  | } | 
| 223 |  |  |  |  |  |  |  | 
| 224 |  |  |  |  |  |  | sub container { | 
| 225 | 1 |  |  | 1 | 1 | 4 | my ( $self, %conf ) = @_; | 
| 226 | 1 | 50 |  |  |  | 3 | confess 'Missing name' unless $conf{name}; | 
| 227 | 1 |  |  |  |  | 2 | $conf{cloudfiles} = $self; | 
| 228 | 1 |  |  |  |  | 3 | for (keys %conf){ | 
| 229 | 2 | 50 |  |  |  | 28 | if (ref $conf{$_} eq ref JSON::Any->true){ | 
| 230 | 0 | 0 |  |  |  | 0 | $conf{$_} = ($conf{$_} ? 'true' : 'false'); | 
| 231 |  |  |  |  |  |  | } | 
| 232 |  |  |  |  |  |  | } | 
| 233 |  |  |  |  |  |  |  | 
| 234 | 1 |  |  |  |  | 20 | return WebService::Rackspace::CloudFiles::Container->new(%conf); | 
| 235 |  |  |  |  |  |  | } | 
| 236 |  |  |  |  |  |  |  | 
| 237 |  |  |  |  |  |  | sub create_container { | 
| 238 | 0 |  |  | 0 | 1 |  | my ( $self, %conf ) = @_; | 
| 239 | 0 |  |  |  |  |  | my $name = $conf{name}; | 
| 240 | 0 | 0 |  |  |  |  | confess 'Missing name' unless $name; | 
| 241 |  |  |  |  |  |  |  | 
| 242 | 0 |  |  |  |  |  | my $request = HTTP::Request->new( | 
| 243 |  |  |  |  |  |  | 'PUT', | 
| 244 |  |  |  |  |  |  | $self->storage_url . '/' . $name, | 
| 245 |  |  |  |  |  |  | [ 'X-Auth-Token' => $self->token ] | 
| 246 |  |  |  |  |  |  | ); | 
| 247 | 0 |  |  |  |  |  | my $response = $self->_request($request); | 
| 248 | 0 | 0 | 0 |  |  |  | confess 'Unknown error' | 
| 249 |  |  |  |  |  |  | if $response->code != 201 && $response->code != 202; | 
| 250 | 0 |  |  |  |  |  | return WebService::Rackspace::CloudFiles::Container->new( | 
| 251 |  |  |  |  |  |  | cloudfiles => $self, | 
| 252 |  |  |  |  |  |  | name       => $name, | 
| 253 |  |  |  |  |  |  | ); | 
| 254 |  |  |  |  |  |  | } | 
| 255 |  |  |  |  |  |  |  | 
| 256 |  |  |  |  |  |  | __PACKAGE__->meta->make_immutable; | 
| 257 |  |  |  |  |  |  |  | 
| 258 |  |  |  |  |  |  | 1; | 
| 259 |  |  |  |  |  |  |  | 
| 260 |  |  |  |  |  |  | __END__ | 
| 261 |  |  |  |  |  |  |  | 
| 262 |  |  |  |  |  |  | =head1 NAME | 
| 263 |  |  |  |  |  |  |  | 
| 264 |  |  |  |  |  |  | WebService::Rackspace::CloudFiles - Interface to Rackspace CloudFiles service | 
| 265 |  |  |  |  |  |  |  | 
| 266 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 267 |  |  |  |  |  |  |  | 
| 268 |  |  |  |  |  |  | use WebService::Rackspace::CloudFiles; | 
| 269 |  |  |  |  |  |  | use Perl6::Say; | 
| 270 |  |  |  |  |  |  |  | 
| 271 |  |  |  |  |  |  | my $cloudfiles = WebService::Rackspace::CloudFiles->new( | 
| 272 |  |  |  |  |  |  | user => 'myusername', | 
| 273 |  |  |  |  |  |  | key  => 'mysecretkey', | 
| 274 |  |  |  |  |  |  | ); | 
| 275 |  |  |  |  |  |  |  | 
| 276 |  |  |  |  |  |  | # list all containers | 
| 277 |  |  |  |  |  |  | my @containers = $cloudfiles->containers; | 
| 278 |  |  |  |  |  |  | foreach my $container (@containers) { | 
| 279 |  |  |  |  |  |  | say 'have container ' . $container->name; | 
| 280 |  |  |  |  |  |  | } | 
| 281 |  |  |  |  |  |  |  | 
| 282 |  |  |  |  |  |  | # create a new container | 
| 283 |  |  |  |  |  |  | my $container = $cloudfiles->create_container(name => 'testing'); | 
| 284 |  |  |  |  |  |  |  | 
| 285 |  |  |  |  |  |  | # use an existing container | 
| 286 |  |  |  |  |  |  | my $existing_container = $cloudfiles->container(name => 'testing'); | 
| 287 |  |  |  |  |  |  |  | 
| 288 |  |  |  |  |  |  | my $total_bytes_used = $cloudfiles->total_bytes_used; | 
| 289 |  |  |  |  |  |  | say "used $total_bytes_used"; | 
| 290 |  |  |  |  |  |  |  | 
| 291 |  |  |  |  |  |  | my $object_count = $container->object_count; | 
| 292 |  |  |  |  |  |  | say "$object_count objects"; | 
| 293 |  |  |  |  |  |  |  | 
| 294 |  |  |  |  |  |  | my $bytes_used = $container->bytes_used; | 
| 295 |  |  |  |  |  |  | say "$bytes_used bytes"; | 
| 296 |  |  |  |  |  |  |  | 
| 297 |  |  |  |  |  |  | # returns a Data::Stream::Bulk object | 
| 298 |  |  |  |  |  |  | # as it may have to make multiple HTTP requests | 
| 299 |  |  |  |  |  |  | my @objects = $container->objects->all; | 
| 300 |  |  |  |  |  |  | foreach my $object (@objects) { | 
| 301 |  |  |  |  |  |  | say 'have object ' . $object->name; | 
| 302 |  |  |  |  |  |  | # also size, etag, content_type, last_modified | 
| 303 |  |  |  |  |  |  | } | 
| 304 |  |  |  |  |  |  | my @objects2 = $container->objects(prefix => 'dir/')->all; | 
| 305 |  |  |  |  |  |  |  | 
| 306 |  |  |  |  |  |  | # To create a new object | 
| 307 |  |  |  |  |  |  | my $xxx = $container->object( name => 'XXX' ); | 
| 308 |  |  |  |  |  |  | $xxx->put('this is the value'); | 
| 309 |  |  |  |  |  |  |  | 
| 310 |  |  |  |  |  |  | # To set metadata of an object: | 
| 311 |  |  |  |  |  |  | $xxx->object_metadata({ | 
| 312 |  |  |  |  |  |  | description => 'this is a description', | 
| 313 |  |  |  |  |  |  | useful_number => 17 | 
| 314 |  |  |  |  |  |  | }); | 
| 315 |  |  |  |  |  |  |  | 
| 316 |  |  |  |  |  |  | # To create a new object with the contents of a local file | 
| 317 |  |  |  |  |  |  | my $yyy = $container->object( name => 'YYY', content_type => 'text/plain' ); | 
| 318 |  |  |  |  |  |  | $yyy->put_filename('README'); | 
| 319 |  |  |  |  |  |  |  | 
| 320 |  |  |  |  |  |  | # To fetch an object: | 
| 321 |  |  |  |  |  |  | my $xxx2 = $container->object( name => 'XXX' ); | 
| 322 |  |  |  |  |  |  | my $value = $xxx2->get; | 
| 323 |  |  |  |  |  |  | say 'has name ' . $xxx2->name; | 
| 324 |  |  |  |  |  |  | say 'has md5 ' . $xxx2->etag; | 
| 325 |  |  |  |  |  |  | say 'has size ' . $xxx2->size; | 
| 326 |  |  |  |  |  |  | say 'has content type ' . $xxx2->content_type; | 
| 327 |  |  |  |  |  |  | say 'has last_modified ' . $xxx2->last_modified; | 
| 328 |  |  |  |  |  |  |  | 
| 329 |  |  |  |  |  |  | # To fetch metadata of an object: | 
| 330 |  |  |  |  |  |  | say 'metadata description ' . $xxx2->object_metadata->{'description'}; | 
| 331 |  |  |  |  |  |  | say 'metadata useful_number ' . $xxx2->object_metadata->{'useful_number'}; | 
| 332 |  |  |  |  |  |  |  | 
| 333 |  |  |  |  |  |  | # To download an object to a local file | 
| 334 |  |  |  |  |  |  | $yyy->get_filename('README.downloaded'); | 
| 335 |  |  |  |  |  |  |  | 
| 336 |  |  |  |  |  |  | $object->delete; | 
| 337 |  |  |  |  |  |  |  | 
| 338 |  |  |  |  |  |  | $container->delete; | 
| 339 |  |  |  |  |  |  |  | 
| 340 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 341 |  |  |  |  |  |  |  | 
| 342 |  |  |  |  |  |  | This module was forked from L<Net::Mosso::CloudFiles> which was written by Leon | 
| 343 |  |  |  |  |  |  | Brocard <acme@astray.com>. However, due to Mosso changing its name to Rackspace | 
| 344 |  |  |  |  |  |  | it felt right to fork the module to a new namespace. Upgrading from | 
| 345 |  |  |  |  |  |  | L<Net::Mosso::CloudFiles> 0.44 should only require you to rename all Net::Mosso | 
| 346 |  |  |  |  |  |  | entries to WebService::Rackspace. | 
| 347 |  |  |  |  |  |  |  | 
| 348 |  |  |  |  |  |  | This module provides a simple interface to the Rackspace Cloud Files | 
| 349 |  |  |  |  |  |  | service. "Cloud Files is reliable, scalable and affordable web-based | 
| 350 |  |  |  |  |  |  | storage for backing up and archiving all your static content". | 
| 351 |  |  |  |  |  |  | Find out more at L<http://www.rackspacecloud.com/cloud_hosting_products/files>. | 
| 352 |  |  |  |  |  |  |  | 
| 353 |  |  |  |  |  |  | To use this module you will need to sign up to Rackspace Cloud Files | 
| 354 |  |  |  |  |  |  | and provide a "user" and "key". If you use this module, you will | 
| 355 |  |  |  |  |  |  | incurr costs as specified by Rackspace. Please check the costs. If | 
| 356 |  |  |  |  |  |  | you use this module with your user and key you will be responsible | 
| 357 |  |  |  |  |  |  | for these costs. | 
| 358 |  |  |  |  |  |  |  | 
| 359 |  |  |  |  |  |  | I highly recommend reading all about Cloud Files, but in a nutshell | 
| 360 |  |  |  |  |  |  | data is stored in objects. Objects are referenced by names and objects | 
| 361 |  |  |  |  |  |  | are stored in containers. | 
| 362 |  |  |  |  |  |  |  | 
| 363 |  |  |  |  |  |  | =head1 METHODS | 
| 364 |  |  |  |  |  |  |  | 
| 365 |  |  |  |  |  |  | =head2 new | 
| 366 |  |  |  |  |  |  |  | 
| 367 |  |  |  |  |  |  | The constructor logs you into Cloud Files: | 
| 368 |  |  |  |  |  |  |  | 
| 369 |  |  |  |  |  |  | my $cloudfiles = WebService::Rackspace::CloudFiles->new( | 
| 370 |  |  |  |  |  |  | user => 'myusername', | 
| 371 |  |  |  |  |  |  | key  => 'mysecretkey', | 
| 372 |  |  |  |  |  |  | ); | 
| 373 |  |  |  |  |  |  |  | 
| 374 |  |  |  |  |  |  | A location for the Cloud Files can now be specified. Valid locations are currently I<usa> and I<uk>, the default location is I<usa> | 
| 375 |  |  |  |  |  |  |  | 
| 376 |  |  |  |  |  |  | my $cloudfiles = WebService::Rackspace::CloudFiles->new( | 
| 377 |  |  |  |  |  |  | user => 'myusername', | 
| 378 |  |  |  |  |  |  | key  => 'mysecretkey', | 
| 379 |  |  |  |  |  |  | location  => 'uk', | 
| 380 |  |  |  |  |  |  | ); | 
| 381 |  |  |  |  |  |  |  | 
| 382 |  |  |  |  |  |  | If you wish to use a custom location url instead, I<location_url> can be used | 
| 383 |  |  |  |  |  |  | to override the usual sites: | 
| 384 |  |  |  |  |  |  |  | 
| 385 |  |  |  |  |  |  | my $cloudfiles = WebService::Rackspace::CloudFiles->new( | 
| 386 |  |  |  |  |  |  | user => 'myusername', | 
| 387 |  |  |  |  |  |  | key  => 'mysecretkey', | 
| 388 |  |  |  |  |  |  | location_url  => 'https://my.cloudfile.me/v1.0', | 
| 389 |  |  |  |  |  |  | ); | 
| 390 |  |  |  |  |  |  |  | 
| 391 |  |  |  |  |  |  | =head2 containers | 
| 392 |  |  |  |  |  |  |  | 
| 393 |  |  |  |  |  |  | List all the containers and return them as L<WebService::Rackspace::CloudFiles::Container> objects: | 
| 394 |  |  |  |  |  |  |  | 
| 395 |  |  |  |  |  |  | my @containers = $cloudfiles->containers; | 
| 396 |  |  |  |  |  |  |  | 
| 397 |  |  |  |  |  |  | =head2 create_container | 
| 398 |  |  |  |  |  |  |  | 
| 399 |  |  |  |  |  |  | Create a new container and return it as a L<WebService::Rackspace::CloudFiles::Container> object: | 
| 400 |  |  |  |  |  |  |  | 
| 401 |  |  |  |  |  |  | my $container = $cloudfiles->create_container(name => 'testing'); | 
| 402 |  |  |  |  |  |  |  | 
| 403 |  |  |  |  |  |  | =head2 container | 
| 404 |  |  |  |  |  |  |  | 
| 405 |  |  |  |  |  |  | Use an existing container and return it as a L<WebService::Rackspace::CloudFiles::Container> object: | 
| 406 |  |  |  |  |  |  |  | 
| 407 |  |  |  |  |  |  | my $existing_container = $cloudfiles->container(name => 'testing'); | 
| 408 |  |  |  |  |  |  |  | 
| 409 |  |  |  |  |  |  | =head2 total_bytes_used | 
| 410 |  |  |  |  |  |  |  | 
| 411 |  |  |  |  |  |  | Returns the total amount of bytes used in your Cloud Files account: | 
| 412 |  |  |  |  |  |  |  | 
| 413 |  |  |  |  |  |  | my $total_bytes_used = $cloudfiles->total_bytes_used; | 
| 414 |  |  |  |  |  |  |  | 
| 415 |  |  |  |  |  |  | =head2 connection_cache_class | 
| 416 |  |  |  |  |  |  |  | 
| 417 |  |  |  |  |  |  | =head2 iterator_callback_class | 
| 418 |  |  |  |  |  |  |  | 
| 419 |  |  |  |  |  |  | =head2 key | 
| 420 |  |  |  |  |  |  |  | 
| 421 |  |  |  |  |  |  | =head2 location | 
| 422 |  |  |  |  |  |  |  | 
| 423 |  |  |  |  |  |  | =head2 locations | 
| 424 |  |  |  |  |  |  |  | 
| 425 |  |  |  |  |  |  | =head2 retries | 
| 426 |  |  |  |  |  |  |  | 
| 427 |  |  |  |  |  |  | =head2 timeout | 
| 428 |  |  |  |  |  |  |  | 
| 429 |  |  |  |  |  |  | =head2 user | 
| 430 |  |  |  |  |  |  |  | 
| 431 |  |  |  |  |  |  | =head1 TESTING | 
| 432 |  |  |  |  |  |  |  | 
| 433 |  |  |  |  |  |  | Testing CloudFiles is a tricky thing. Rackspace charges you a bit of | 
| 434 |  |  |  |  |  |  | money each time you use their service. And yes, testing counts as using. | 
| 435 |  |  |  |  |  |  | Because of this, this module's test suite skips testing unless | 
| 436 |  |  |  |  |  |  | you set the following three environment variables, along the lines of: | 
| 437 |  |  |  |  |  |  |  | 
| 438 |  |  |  |  |  |  | CLOUDFILES_EXPENSIVE_TESTS=1 CLOUDFILES_USER=username CLOUDFILES_KEY=15bf43... perl t/simple.t | 
| 439 |  |  |  |  |  |  |  | 
| 440 |  |  |  |  |  |  | =head1 SEE ALSO | 
| 441 |  |  |  |  |  |  |  | 
| 442 |  |  |  |  |  |  | L<WebService::Rackspace::CloudFiles::Container>, L<WebService::Rackspace::CloudFiles::Object>. | 
| 443 |  |  |  |  |  |  |  | 
| 444 |  |  |  |  |  |  | =head1 AUTHORS | 
| 445 |  |  |  |  |  |  |  | 
| 446 |  |  |  |  |  |  | Christiaan Kras <ckras@cpan.org>. | 
| 447 |  |  |  |  |  |  | L<Net::Mosso::CloudFiles> by Leon Brocard <acme@astray.com>. | 
| 448 |  |  |  |  |  |  |  | 
| 449 |  |  |  |  |  |  | =head1 COPYRIGHT | 
| 450 |  |  |  |  |  |  |  | 
| 451 |  |  |  |  |  |  | Copyright (C) 2010-2011, Christiaan Kras | 
| 452 |  |  |  |  |  |  | Copyright (C) 2008-9, Leon Brocard | 
| 453 |  |  |  |  |  |  |  | 
| 454 |  |  |  |  |  |  | =head1 LICENSE | 
| 455 |  |  |  |  |  |  |  | 
| 456 |  |  |  |  |  |  | This module is free software; you can redistribute it or modify it | 
| 457 |  |  |  |  |  |  | under the same terms as Perl itself. |