File Coverage

blib/lib/Atomik/Client.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             # $Id: /mirror/coderepos/lang/perl/Atomik/trunk/lib/Atomik/Client.pm 68152 2008-08-10T22:25:14.315235Z daisuke $
2              
3             package Atomik::Client;
4 1     1   7 use Moose;
  1         2  
  1         11  
5 1     1   12859 use Atomik;
  0            
  0            
6             use Atomik::Client::RequestFactory;
7             use Atomik::Entry;
8             use Atomik::MediaType;
9             use Atomik::Service;
10             use Atomik::WSSE;
11             use LWP::UserAgent;
12              
13             has 'wsse' => (
14             is => 'rw',
15             isa => 'Atomik::WSSE',
16             coerce => 1,
17             );
18              
19             has 'debug' => (
20             is => 'rw',
21             isa => 'Bool',
22             default => 0
23             );
24              
25             has 'strict_content_type' => (
26             is => 'rw',
27             isa => 'Bool',
28             default => 0
29             );
30              
31             has 'request_factory' => (
32             is => 'rw',
33             isa => 'Atomik::Client::RequestFactory',
34             default => sub { Atomik::Client::RequestFactory->new() },
35             handles => {
36             request_create => 'create'
37             }
38             );
39              
40             has 'user_agent' => (
41             is => 'rw',
42             isa => 'LWP::UserAgent',
43             default => sub {
44             LWP::UserAgent->new(
45             agent => "Atomik/$Atomik::VERSION",
46             timeout => 5
47             )
48             }
49             );
50              
51             __PACKAGE__->meta->make_immutable;
52              
53             no Moose;
54              
55             # We auto-generate these methods, cause they are... the same.
56             BEGIN
57             {
58             my $generator = sub {
59             my $type = shift;
60              
61             eval sprintf(<<'EOSUB', $type, $type, uc $type, ucfirst $type);
62             sub %s_get {
63             my ($self, %%args) = @_;
64             my $uri = $args{uri} || confess "no URI given to %s()";
65              
66             my $request = $self->request_create(%%args);
67             my $response = $self->send_request( request => $request );
68              
69             Atomik::DEBUG( $response->as_string );
70              
71             if ( ! $response->is_success ) {
72             confess "Request to $uri failed: " . $response->as_string;
73             }
74              
75             if ($self->strict_content_type) {
76             my $ct = Atomik::MediaType->from_string($response->content_type);
77             $ct->assert_subtype_of( &Atomik::MediaType::%s );
78             }
79              
80             return Atomik::%s->from_xml( $response->content );
81             }
82             EOSUB
83             confess if $@;
84             };
85              
86             foreach my $type qw(entry service feed category) {
87             $generator->($type);
88             }
89             }
90              
91             sub entry_create {
92             my ($self, %args) = @_;
93              
94             my $uri = $args{uri} || confess "no URI given to entry_create()";
95              
96             my $headers = delete $args{headers} || {};
97             $headers->{'Content-Type'} ||= &Atomik::MediaType::ENTRY;
98             if ($args{slug}) {
99             $headers->{Slug} ||= $args{slug};
100             }
101              
102             # If the entry is not an object, then coerce it
103             my $entry = delete $args{entry};
104             if (! blessed $entry ) {
105             $entry = Atomik::Entry->from_any($entry);
106             }
107              
108             my $request = $self->request_create(
109             %args,
110             method => 'POST',
111             content => $entry->as_xml,
112             headers => $headers,
113             );
114             my $response = $self->send_request( request => $request );
115              
116             if (! $response->is_success ) {
117             confess "Request to $uri failed: " . $response->as_string;
118             }
119              
120             Atomik::DEBUG( $response->as_string );
121              
122             if (wantarray) {
123             return ( $response->header('Location'), Atomik::Entry->from_xml( $response->content ) );
124             } else {
125             return $response->header('Location');
126             }
127             }
128              
129             sub entry_update {
130             my ($self, %args) = @_;
131              
132             my $uri = $args{uri} || confess "no URI given to entry_update()";
133             my $entry = $args{entry} || confess "no entry given to entry_update()";
134              
135             # If the entry is not an object, then coerce it
136             if (! blessed $entry ) {
137             $entry = Atomik::Entry->from_any($entry);
138             }
139              
140             my $request = $self->request_create(
141             %args,
142             content => $entry->as_xml,
143             method => 'PUT',
144             );
145              
146             my $content = $entry->as_xml();
147             $request->content_type( (&Atomik::MediaType::ENTRY)->as_string );
148             my $response = $self->send_request( request => $request );
149             if (! $response->is_success) {
150             confess "Request to $uri failed: " . $response->as_string;
151             }
152              
153             if ($self->strict_content_type) {
154             my $ct = Atomik::MediaType->from_string($response->content_type);
155             $ct->assert_subtype_of( &Atomik::MediaType::ENTRY );
156             }
157              
158             if ($self->debug) {
159             print STDERR $response->as_string;
160             }
161              
162             # Some so-called "atom" services don't reply back with a proper
163             # xml here. in such cases, we do the best we can, and return a 0E0
164             my $result = $response->content ?
165             Atomik::Entry->from_xml( $response->content ) : '0E0';
166             return $result;
167             }
168              
169             sub entry_delete {
170             my ($self, %args) = @_;
171              
172             my $uri = $args{uri} || confess "no URI given to entry_update()";
173              
174             my $request = $self->request_create(
175             %args,
176             method => 'DELETE',
177             );
178              
179             $request->content_type( (&Atomik::MediaType::ENTRY)->as_string );
180             my $response = $self->send_request( request => $request );
181             if (! $response->is_success) {
182             confess "Request to $uri failed: " . $response->as_string;
183             }
184              
185             if ($self->strict_content_type) {
186             my $ct = Atomik::MediaType->from_string($response->content_type);
187             $ct->assert_subtype_of( &Atomik::MediaType::ENTRY );
188             }
189              
190             if ($self->debug) {
191             print STDERR $response->as_string;
192             }
193              
194             return 1;
195             }
196              
197             sub send_request {
198             my ($self, %args) = @_;
199             my $request = $args{request};
200             if (my $wsse = $self->wsse) {
201             $wsse->set_headers( $request );
202             }
203              
204             if ($self->debug) {
205             print STDERR $request->as_string;
206             }
207             $self->user_agent->request($request);
208             }
209              
210             1;
211              
212             __END__
213              
214             =head1 NAME
215              
216             Atomik::Client - An Atompub Client
217              
218             =head1 SYNOPSIS
219              
220             use Atomik::Client;
221              
222             my $client = Atomik::Client->new();
223              
224             # You need to know the collection URI of whatever you're dealing with
225             # before hand. One way to obtain it is by getting the service document
226             my $service = $client->service( uri => $service_document_uri );
227              
228             foreach my $workspace ($service->workspaces) {
229             foreach my $collection ($workspace->collections) {
230             $collection->href; # this is a collection URI
231              
232             # What this URI is, is not described in the service document
233             }
234             }
235              
236             # if you know the collection URI, you can operate CRUD operations
237             my $entry_uri = $client->entry_create(
238             uri => $entry_uri,
239             entry => $entry_object,
240             );
241             # you can receive an Atomik::Entry, if you get the result in
242             # list context
243             my ($entry_uri, $entry) = $client->entry_create(...);
244              
245             =head1 METHODS
246              
247             =head2 new(%args)
248              
249             Atomik::Client->new(
250             wsse => {
251             username => $username,
252            
253             [ wsse_username =>
254             [ use_wsse => $bool ]
255              
256             =cut