File Coverage

blib/lib/WebService/Blogger/Blog.pm
Criterion Covered Total %
statement 21 63 33.3
branch 0 16 0.0
condition n/a
subroutine 7 13 53.8
pod 4 5 80.0
total 32 97 32.9


line stmt bran cond sub pod time code
1             package WebService::Blogger::Blog;
2             our $VERSION = '0.23';
3 2     2   14 use warnings;
  2         4  
  2         75  
4 2     2   11 use strict;
  2         4  
  2         42  
5              
6 2     2   11 use Moose;
  2         5  
  2         23  
7 2     2   15427 use XML::Simple ();
  2         6  
  2         45  
8 2     2   22 use URI::Escape ();
  2         5  
  2         29  
9 2     2   9 use Encode ();
  2         3  
  2         44  
10 2     2   1030 use WebService::Blogger::Blog::Entry;
  2         9  
  2         2076  
11              
12             with 'WebService::Blogger::AtomReading';
13              
14             # Blog properties, non-updatable.
15             has id => ( is => 'ro', isa => 'Str', required => 1 );
16             has numeric_id => ( is => 'ro', isa => 'Str', required => 1 );
17             has title => ( is => 'ro', isa => 'Str', required => 1 );
18             has public_url => ( is => 'ro', isa => 'Str', required => 1 );
19             has id_url => ( is => 'ro', isa => 'Str', required => 1 );
20             has post_url => ( is => 'ro', isa => 'Str', required => 1 );
21              
22             # Service attributes.
23             has source_xml_tree => ( is => 'ro', isa => 'HashRef', required => 1 );
24             has blogger => ( is => 'ro', isa => 'WebService::Blogger', required => 1 );
25              
26             # Blog entries.
27             has max_results => ( is => 'rw', isa => 'Num', required => 1, default => 30, );
28             has entries => (
29             is => 'rw',
30             isa => 'ArrayRef[WebService::Blogger::Blog::Entry]',
31             lazy_build => 1,
32             auto_deref => 1,
33             );
34              
35             # Speed Moose up.
36             __PACKAGE__->meta->make_immutable;
37              
38              
39             sub BUILDARGS {
40             ## Parses source XML into initial attribute values.
41 0     0 1   my $class = shift;
42 0           my %params = @_;
43              
44 0           my $tree = $params{source_xml_tree};
45 0           my $id = $tree->{id}[0];
46              
47             # Extract attributes from XML tree and return them to be set in the instance.
48             return {
49             id => $id,
50             numeric_id => $id =~ /(\d+)$/,
51             title => $tree->{title}[0]{content},
52 0           id_url => $class->get_link_href_by_rel($tree, 'self'),
53             public_url => $class->get_link_href_by_rel($tree, 'alternate'),
54             post_url => $class->get_link_href_by_rel($tree, qr/#post$/),
55             %params,
56             };
57             }
58              
59              
60             sub _build_entries {
61             ## Populates the entries attribute, loading all entries for the blog.
62 0     0     my $self = shift;
63              
64             # Search with no parameters.
65 0           return $self->search_entries;
66             }
67              
68              
69             sub search_entries {
70             ## Returns entries matching search criteria.
71 0     0 1   my $self = shift;
72 0           my %params = @_;
73              
74             # Construct request URL, incorporating category criteria into it, if given.
75 0           my $url = 'http://www.blogger.com/feeds/' . $self->numeric_id . '/posts/default';
76 0           $url .= '/-/' . join '/', map URI::Escape::uri_escape($_), @{ $params{categories} }
77 0 0         if $params{categories};
78              
79             # Map our parameter names to Blogger's.
80 0           my %params_to_req_args_map = (
81             max_results => 'max-results',
82             published_min => 'published-min',
83             published_max => 'published-max',
84             updated_min => 'updated-min',
85             updated_max => 'updated-max',
86             order_by => 'orderby',
87             offset => 'start-index',
88             );
89              
90             # Map our sort mode parameter names to Blogger's.
91 0           my %sort_mode_map = (
92             last_modified => 'lastmodified',
93             start_time => 'starttime',
94             updated => 'updated',
95             );
96              
97             # Populate request arguments hash WRT above mappings.
98 0           my %req_args = (
99             alt => 'atom',
100             );
101 0           foreach my $param (keys %params_to_req_args_map) {
102 0 0         my $value = $self->$param if $self->meta->has_attribute($param);
103 0 0         $value = $params{$param} if exists $params{$param};
104 0 0         $req_args{$params_to_req_args_map{$param}} = $value if defined $value;
105             }
106 0 0         if (my $sort_mode = $params{sort_by}) {
107 0           $req_args{orderby} = $sort_mode_map{$sort_mode};
108             }
109              
110             # Execute request and parse the response.
111 0           my $uri_obj = URI->new($url);
112 0           $uri_obj->query_form(%req_args);
113 0           my $response = $self->blogger->http_get($uri_obj);
114 0           my $response_tree = XML::Simple::XMLin($response->content, ForceArray => 1);
115              
116             # Return list of entry objects constructed from list of hashes in parsed data.
117             my @entries
118             = map WebService::Blogger::Blog::Entry->new(
119             source_xml_tree => $_,
120             blog => $self,
121             ),
122 0           @{ $response_tree->{entry} };
  0            
123 0 0         return wantarray ? @entries : \@entries;
124             }
125              
126              
127             sub add_entry {
128             ## Adds new entry with specified properties to the blog and returns it.
129 0     0 1   my $self = shift;
130 0           my %params = @_;
131              
132             # Get the XML for creation of new entry and post it to appropriate URL.
133 0           my $creation_xml = WebService::Blogger::Blog::Entry->xml_for_creation(%params);
134 0           my $response = $self->blogger->http_post(
135             $self->post_url,
136             'Content-Type' => 'application/atom+xml',
137             Content => Encode::encode_utf8($creation_xml),
138             );
139 0 0         die 'Unable to add entry to blog: ' . $response->status_line unless $response->is_success;
140              
141             # Create new entry object from the response.
142 0           my $xml_tree = XML::Simple::XMLin($response->content, ForceArray => 1);
143 0           return WebService::Blogger::Blog::Entry->new(source_xml_tree => $xml_tree, blog => $self);
144             }
145              
146              
147             sub delete_entry {
148             ## Deletes given entry from server as well as list of entries held in blog object.
149 0     0 0   my $self = shift;
150 0           my ($entry) = @_;
151              
152             # Execute deletion request, with a workaround for proxies blocking DELETE method.
153 0           my $response = $self->blogger->http_post(
154             $entry->edit_url,
155             'X-HTTP-Method-Override' => 'DELETE',
156             );
157 0 0         die 'Could not delete entry from server: ' . $response->status_line unless $response->is_success;
158              
159             # Remove the entry from local list of entries.
160 0           $self->entries([ grep $_ ne $entry, $self->entries ]);
161             }
162              
163              
164             sub destroy {
165             ## Removes references to the blog from child entries, so they're
166             ## no longer circular. Blog object as well as entries can then be
167             ## garbage-collected.
168 0     0 1   my $self = shift;
169              
170 0           $_->blog(undef) foreach $self->entries;
171             }
172              
173              
174             1;
175              
176             __END__
177              
178             =head1 NAME
179              
180             WebService::Blogger::Blog - represents blog entity of Google Blogger service.
181              
182             =head1 SYNOPSIS
183              
184             Please see L<WebService::Blogger>.
185              
186             =head1 DESCRIPTION
187              
188             This class represents a blog in WebService::Blogger package, and is
189             not designed to be instantiated directly.
190              
191             =head1 METHODS
192              
193             =head3 C<add_entry(%properties)>
194              
195             =over
196              
197             Adds given entry to the blog:
198              
199             my $new_entry = $blog->add_entry(
200             title => 'New entry',
201             content => 'New content',
202             categories => [ 'news', 'testing', 'perl examples' ],
203             );
204              
205             =back
206              
207             =head3 C<search_entries(%criteria)>
208              
209             =over
210              
211             Returns entries matching specified criteria. The following example
212             contains all possible search conditions:
213              
214             my @entries = $blog->search_entries(
215             published_min => '2010-08-10T23:25:00+04:00'
216             published_max => '2010-07-17T23:25:00+04:00',
217             updated_min => '2010-09-17T12:25:00+04:00',
218             updated_max => '2010-09-17T14:00:00+04:00',
219             order_by => 'start_time', # can also be: 'last_modified' or 'updated'
220             max_results => 20,
221             offset => 10, # skip first 10 entries
222             );
223              
224             =back
225              
226             =head3 C<destroy()>
227              
228             =over
229              
230             Removes references to the blog from child entries, so they're no
231             longer circular. Blog object as well as entries can then be
232             garbage-collected.
233              
234             =back
235              
236             =head1 ATTRIBUTES
237              
238             =head3 C<id>
239              
240             =over
241              
242             Unique ID of the blog, a string in Blogger-specific format as present
243             in the Atom entry.
244              
245             =back
246              
247             =head3 C<numeric_id>
248              
249             =over
250              
251             Numeric ID of the blog.
252              
253             =back
254              
255             =head3 C<title>
256              
257             =over
258              
259             Title of the blog.
260              
261             =back
262              
263             =head3 C<public_url>
264              
265             =over
266              
267             The human-readable, SEO-friendly URL of the blog.
268              
269             =back
270              
271             =head3 C<id_url>
272              
273             =over
274              
275             URL of the blog based on its numeric ID. Never changes.
276              
277             =back
278              
279             =head3 C<post_url>
280              
281             =over
282              
283             URL for publishing new posts.
284              
285             =back
286              
287             =head3 C<entries>
288              
289             =over
290              
291             List of blog entries, lazily populated.
292              
293             =back
294              
295             =head1 AUTHOR
296              
297             Kedar Warriner, C<< <kedar at cpan.org> >>
298              
299             =head1 BUGS
300              
301             Please report any bugs or feature requests to C<bug-webservice-blogger at rt.cpan.org>, or through
302             the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=WebService-Blogger>. I will be notified, and then you'll
303             automatically be notified of progress on your bug as I make changes.
304              
305             =head1 SUPPORT
306              
307             You can find documentation for this module with the perldoc command.
308              
309             perldoc WebService::Blogger
310              
311             You can also look for information at:
312              
313             =over 4
314              
315             =item * RT: CPAN's request tracker
316              
317             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=WebService-Blogger>
318              
319             =item * AnnoCPAN: Annotated CPAN documentation
320              
321             L<http://annocpan.org/dist/WebService-Blogger>
322              
323             =item * CPAN Ratings
324              
325             L<http://cpanratings.perl.org/d/WebService-Blogger>
326              
327             =item * Search CPAN
328              
329             L<http://search.cpan.org/dist/WebService-Blogger/>
330              
331             =back
332              
333             =head1 ACKNOWLEDGEMENTS
334              
335             Many thanks to:
336             - Egor Shipovalov who wrote the original version of this module
337             - Everyone involved with CPAN.
338              
339             =head1 LICENSE AND COPYRIGHT
340              
341             Copyright 2010 Kedar Warriner.
342              
343             This program is free software; you can redistribute it and/or modify it
344             under the terms of either: the GNU General Public License as published
345             by the Free Software Foundation; or the Artistic License.
346              
347             See http://dev.perl.org/licenses/ for more information.
348              
349             =cut