File Coverage

blib/lib/XML/OPDS/Acquisition.pm
Criterion Covered Total %
statement 78 79 98.7
branch 23 26 88.4
condition 1 3 33.3
subroutine 12 12 100.0
pod 4 4 100.0
total 118 124 95.1


line stmt bran cond sub pod time code
1             package XML::OPDS::Acquisition;
2              
3 5     5   33 use strict;
  5         9  
  5         147  
4 5     5   24 use warnings FATAL => 'all';
  5         39  
  5         196  
5 5     5   27 use Types::Standard qw/Str ArrayRef InstanceOf Object/;
  5         10  
  5         34  
6 5     5   4181 use Moo;
  5         9  
  5         28  
7 5     5   1645 use DateTime;
  5         12  
  5         140  
8 5     5   25 use XML::Atom;
  5         7  
  5         292  
9 5     5   36 use XML::Atom::Person;
  5         10  
  5         116  
10 5     5   41 use XML::Atom::Entry;
  5         18  
  5         4679  
11              
12             =head1 NAME
13              
14             XML::OPDS::Acquisition - Acquisition elements for OPDS feeds
15              
16             =head1 SETTERS/ACCESSORS
17              
18             The following accessors are read-only and are meant to be passed as an
19             hash to the C<new> constructor.
20              
21             =head2 id
22              
23             Optional. The example specification uses uuid like
24             C<urn:uuid:2853dacf-ed79-42f5-8e8a-a7bb3d1ae6a2>. If not provided, the
25             C<href> (with the prefix if provided) will be used instead.
26              
27             =head2 prefix
28              
29             If provided, every uri will have this string prepended, so you can
30             just pass URIs like '/path/to/file' and have them consistently turned
31             to 'http://myserver.org/path/to/file' if you set this to
32             'http://myserver.org'. See also L<XML::OPDS> C<prefix> method.
33              
34             =cut
35              
36             has id => (is => 'ro', isa => Str);
37              
38             has prefix => (is => 'ro', isa => Str, default => sub { '' });
39              
40             =head2 href
41              
42             The URI of the resource. Required.
43              
44             =cut
45              
46             has href => (is => 'ro', isa => Str, required => 1);
47              
48             =head2 title
49              
50             The title. Required.
51              
52             =cut
53              
54             has title => (is => 'ro', isa => Str, required => 1);
55              
56             =head2 files
57              
58             An arrayref with the download files. The prefix is added if set.
59              
60             =head1 OPTIONAL ATTRIBUTES
61              
62             The following attributes are optional and describe the publication.
63              
64             =head2 authors
65              
66             An arrayref of either scalars with names, or hashrefs with C<name> and
67             C<uri> as keys. C<uri> is optional.
68              
69             =head2 summary
70              
71             Plain text.
72              
73             =head2 description
74              
75             HTML allowed.
76              
77             =head2 language
78              
79             =head2 issued
80              
81             The publication date.
82              
83             =head2 publisher
84              
85             =head2 updated
86              
87             A DateTime object with the time of the last update of the resource.
88              
89             =cut
90              
91             has authors => (is => 'ro', isa => ArrayRef);
92              
93             has summary => (is => 'ro', isa => Str);
94              
95             has description => (is => 'ro', isa => Str);
96              
97             has language => (is => 'ro', isa => Str);
98              
99             has issued => (is => 'ro', isa => Str);
100              
101             has publisher => (is => 'ro', isa => Str);
102              
103             has updated => (is => 'rw', isa => InstanceOf['DateTime'],
104             default => sub { return DateTime->now });
105              
106             has files => (is => 'ro', isa => ArrayRef[Str], default => sub { [] });
107              
108             has _dt_formatter => (is => 'ro', isa => Object, default => sub { DateTime::Format::RFC3339->new });
109              
110             =head2 thumbnail
111              
112             The uri of the thumbnail
113              
114             =head2 image
115              
116             The uri of the image
117              
118             =head1 METHODS
119              
120             Usually they are for internal usage.
121              
122             =head2 identifier
123              
124             =head2 authors_as_links
125              
126             Return a list of L<XML::Atom::Person> objects from the C<authors>
127             value.
128              
129             =head2 files_as_links
130              
131             Return a list of L<XML::Atom::Link> objects constructed from C<files>,
132             C<image>, C<thumbnail>, with the appropriate C<rel> and C<type>.
133              
134             =head2 as_entry
135              
136             Return the acquisition L<XML::Atom::Entry> object.
137              
138             =cut
139              
140             has thumbnail => (is => 'ro', isa => Str);
141              
142             has image => (is => 'ro', isa => Str);
143              
144             has _dc => (is => 'lazy',
145             isa => Object,
146             default => sub {
147             XML::Atom::Namespace->new(dc => 'http://purl.org/dc/elements/1.1/');
148             });
149              
150             sub identifier {
151 18     18 1 30 my $self = shift;
152 18   33     160 return $self->id || $self->prefix . $self->href;
153             }
154              
155             sub authors_as_links {
156 18     18 1 29 my $self = shift;
157 18         32 my @out;
158 18 100       50 if (my $authors = $self->authors) {
159 1         13 foreach my $author (@$authors) {
160 2 100       9 my $hash = ref($author) ? $author : { name => $author };
161 2 50       6 if (my $name = $hash->{name}) {
162 2         7 my $author_obj = XML::Atom::Person->new(Version => 1.0);
163 2         113 $author_obj->name($hash->{name});
164 2 100       205 if (my $uri = $hash->{uri}) {
165 1         6 $author_obj->uri($self->prefix . $uri);
166             }
167 2         79 push @out, $author_obj;
168             }
169             }
170             }
171 18         45 return @out;
172             }
173              
174             sub files_as_links {
175 18     18 1 31 my $self = shift;
176 18         27 my @out;
177 18         141 my %mime = (
178             tex => 'application/x-tex',
179             pdf => 'application/pdf',
180             html => 'text/html',
181             epub => 'application/epub+zip',
182             muse => 'text/plain',
183             txt => 'text/plain',
184             zip => 'application/zip',
185             png => 'image/png',
186             jpg => 'image/jpeg',
187             jpeg => 'image/jpeg',
188             gif => 'image/gif',
189             mobi => 'application/x-mobipocket-ebook',
190             );
191             # maybe support open-access, borrow, buy, sample, subscribe ? 8.4.1
192 18         28 my @all = map { +{ rel => 'acquisition', href => $_ } } @{$self->files};
  20         77  
  18         56  
193 18 50       66 die "Missing acquisition links" unless @all;
194              
195 18 100       58 if (my $thumb = $self->thumbnail) {
196 5         12 push @all, { rel => 'image/thumbnail', href => $thumb };
197             }
198 18 100       51 if (my $image = $self->image) {
199 5         13 push @all, { rel => 'image', href => $image };
200             }
201 18         37 foreach my $file (@all) {
202 30         38 my $mime_type;
203 30 100       151 if ($file->{href} =~ m/\.(\w+)\z/) {
204 28         67 my $ext = $1;
205 28         47 $mime_type = $mime{$ext};
206             }
207 30 100       55 next unless $mime_type;
208 28         78 my $link = XML::Atom::Link->new(Version => 1.0);
209 28         1754 $link->rel("http://opds-spec.org/$file->{rel}");
210 28         615 $link->href($self->prefix . $file->{href});
211 28         463 $link->type($mime_type);
212 28         480 push @out, $link;
213             }
214 18 50       44 if (@out) {
215 18         98 return @out;
216             }
217             else {
218 0         0 die "Links are required"
219             };
220             }
221              
222             sub as_entry {
223 18     18 1 42 my $self = shift;
224 18         65 my $entry = XML::Atom::Entry->new(Version => 1.0);
225 18         1620 $entry->id($self->identifier);
226 18         1801 $entry->title($self->title);
227 18         1882 $entry->updated($self->_dt_formatter->format_datetime($self->updated));
228 18 100       4181 if (my $lang = $self->language) {
229 1         21 $entry->set($self->_dc, language => $lang);
230             }
231 18         173 foreach my $author ($self->authors_as_links) {
232 2         35 $entry->add_author($author);
233             }
234 18 100       82 if (my $summary = $self->summary) {
235 1         20 $entry->summary($summary);
236             }
237 18 100       144 if (my $desc = $self->description) {
238 5         17 $entry->content($desc);
239             }
240 18         3845 foreach my $link ($self->files_as_links) {
241 28         301 $entry->add_link($link);
242             }
243 18         570 return $entry;
244             }
245              
246             1;