File Coverage

blib/lib/File/Assets/Asset.pm
Criterion Covered Total %
statement 101 129 78.2
branch 49 78 62.8
condition 35 53 66.0
subroutine 21 29 72.4
pod 10 18 55.5
total 216 307 70.3


line stmt bran cond sub pod time code
1             package File::Assets::Asset;
2              
3 23     23   137 use warnings;
  23         46  
  23         972  
4 23     23   126 use strict;
  23         56  
  23         776  
5              
6 23     23   12903 use File::Assets::Util;
  23         75  
  23         789  
7 23     23   155 use File::Assets::Carp;
  23         51  
  23         283  
8 23     23   18200 use File::Assets::Asset::Content;
  23         61  
  23         215  
9              
10 23     23   28044 use XML::Tiny;
  23         46745  
  23         1452  
11 23     23   22728 use IO::Scalar;
  23         118121  
  23         1288  
12 23     23   195 use Object::Tiny qw/type rank attributes hidden rsc outside/;
  23         45  
  23         193  
13 23     23   6854 use Scalar::Util qw/blessed/;
  23         52  
  23         56660  
14              
15             =head1 SYNPOSIS
16              
17             my $asset = File::Asset->new(base => $base, path => "/static/assets.css");
18             $asset = $assets->include("/static/assets.css"); # Or, like this, usually.
19              
20             print "The rank for asset at ", $asset->uri, " is ", $asset->rank, "\n";
21             print "The file for the asset is ", $asset->file, "\n";
22              
23             =head1 DESCRIPTION
24              
25             A File::Asset object represents an asset existing in both URI-space and file-space (on disk). The asset is usually a .js (JavaScript) or .css (CSS) file.
26              
27             =head1 METHODS
28              
29             =head2 File::Asset->new( base => <base>, path => <path>, [ rank => <rank>, type => <type> ])
30              
31             Creates a new File::Asset. You probably don't want to use this, create a L<File::Assets> object and use $assets->include instead.
32              
33             =cut
34              
35             sub _html_parse ($) {
36 2     2   21 XML::Tiny::parsefile(IO::Scalar->new(shift));
37             }
38              
39             sub new {
40 180     180 1 3626 my $self = bless {}, shift;
41 180 50 33     1266 my $asset = @_ == 1 && ref $_[0] eq "HASH" ? shift : { @_ };
42              
43 180         470 my $content = delete $asset->{content};
44 180 100       487 $content = ref $content eq "SCALAR" ? $$content : $content;
45 180 100 100     695 if (defined $content && $content =~ m/^\s*</) { # Looks like tagged content (<script> or <style>)
46 2         15 my $tag = _html_parse \$content;
47 2 50 33     2225 croak "Unable to parse $content" unless $tag && $tag->[0];
48 2         46 $tag = $tag->[0];
49 2         12 my $type = delete $tag->{attrib}->{type};
50 2 100       8 if (! $type) {
51 1 50       13 if ($tag->{name} =~ m/^script$/i) {
    50          
52 0         0 $type = "js"
53             }
54             elsif ($tag->{name} =~ m/^style$/i) {
55 1         2 $type = "css"
56             }
57             }
58 2 50       15 $asset->{type} = $type unless defined $asset->{type};
59 2         6 while (my ($name, $value) = each %{ $tag->{attrib} }) {
  3         16  
60 1 50       6 $asset->{$name} = $value unless exists $asset->{$name};
61             }
62 2         6 $content = $tag->{content}->[0]->{content};
63 2 50       45 $content = "" unless defined $content;
64             }
65              
66 180         690 my ($path, $rsc, $base, $type) = delete @$asset{qw/path rsc base type/};
67 180 100       539 if (defined $type) {
68 74         111 my $_type = $type;
69 74 50       348 $type = File::Assets::Util->parse_type($_type) or croak "Don't understand type ($_type) for this asset";
70             }
71              
72 180 50 100     62875 if ($rsc) {
    100 33        
    100 66        
    50 100        
73 0 0       0 croak "Don't have a type for this asset" unless $type;
74 0         0 $self->{rsc} = $rsc;
75 0         0 $self->{type} = $type;
76 0 0       0 croak "Can't also specify content and ", $self->rsc->file if defined $content;
77             }
78             elsif ($path && $path =~ m/^https?:\/\// || (blessed $path && $path->isa("URI"))) {
79 3         24 my $uri = $self->{uri} = URI->new($path);
80 3   33     344 $self->{type} = $type || do {
81             File::Assets::Util->parse_type($uri->path) or croak "Unable to determine type of $uri";
82             };
83 3         133 $self->{outside} = 1;
84             }
85             elsif ($base && $path) {
86 168 100       538 if ($path =~ m/^\//) {
87 6         33 $self->{rsc} = $base->clone($path);
88             }
89             else {
90 162         772 $self->{rsc} = $base->child($path);
91             }
92 168 50       37964 croak "Can't also specify content and ", $self->rsc->file if defined $content;
93 168 50 66     1053 $self->{type} = $type || File::Assets::Util->parse_type($path) or croak "Don't know type for asset ($path)";
94             }
95             elsif (defined $content) {
96 9 50       23 croak "Don't have a type for this asset" unless $type;
97 9         65 $self->{type} = $type;
98 9         45 $self->{digest} = File::Assets::Util->digest->add($content)->hexdigest;
99 9         313 $self->{content} = \$content;
100             }
101             else {
102 0         0 croak "Don't know what to do";
103             }
104              
105 180   100     980330 my $rank = $self->{rank} = delete $asset->{rank} || 0;
106 180 50 66     634 croak "Don't understand rank ($rank)" if $rank && $rank =~ m/[^\d\+\-\.]/;
107 180         524 $self->{cache} = delete $asset->{cache};
108 180 100       942 $self->{inline} = exists $asset->{inline} ?
    100          
    100          
109             (delete $asset->{inline} ? 1 : 0) :
110             $self->{content} ? 1 : 0;
111 180         631 $self->{attributes} = { %$asset }; # The rest goes here!
112              
113 180         826 return $self;
114             }
115              
116             =head2 $asset->uri
117              
118             Returns a L<URI> object represting the uri for $asset
119              
120             =cut
121              
122             sub uri {
123 136     136 1 1976 my $self = shift;
124 136 100       570 return $self->{uri} unless $self->{rsc};
125 115   66     2551 return ($self->{uri} ||= $self->rsc->uri)->clone;
126             }
127              
128             =head2 $asset->file
129              
130             Returns a L<Path::Class::File> object represting the file for $asset
131              
132             =cut
133              
134             sub file {
135 337     337 1 1453 my $self = shift;
136 337 100       896 return unless $self->{rsc};
137 332   66     4266 return $self->{file} ||= $self->rsc->file;
138             }
139              
140             =head2 $asset->path
141              
142             Returns the path of $asset
143              
144             =cut
145              
146             sub path {
147 235     235 1 1727 my $self = shift;
148 235 100       702 return unless $self->{rsc};
149 217   66     4153 return $self->{path} ||= $self->rsc->path;
150             }
151              
152             =head2 $asset->content
153              
154             Returns a SCALAR reference to the content contained in $asset->file
155              
156             =cut
157              
158             sub content {
159 72     72 1 325 my $self = shift;
160 72   66     381 return $self->{content} || $self->_content->content;
161             }
162              
163             =head2 $asset->write( <content> )
164              
165             Writes <content>, which should be a SCALAR reference, to the file located at $asset->file
166              
167             If the parent directory for $asset->file does not exist yet, this method will create it first
168              
169             =cut
170              
171             sub write {
172 31     31 1 74 my $self = shift;
173 31         56 my $content = shift;
174              
175 31         79 my $file = $self->file;
176 31         256 my $dir = $file->parent;
177 31 100       251 $dir->mkpath unless -d $dir;
178 31         2413 $file->openw->print($$content);
179             }
180              
181             =head2 $asset->digest
182              
183             Returns a hex digest for the content of $asset
184              
185             =cut
186              
187             # NOTE: $asset->digest used to return a unique signature for the asset (based off the filename), but this has changed to
188             # now return the actual hex digest of the content of $asset
189              
190             sub digest {
191 131     131 1 318 my $self = shift;
192 131   66     609 return $self->{digest} || $self->_content->digest;
193             }
194              
195             sub content_digest {
196 0     0 0 0 my $self = shift;
197 0         0 carp "File::Assets::Asset::content_digest is DEPRECATED (use ::digest instead)";
198 0         0 return $self->digest;
199             }
200              
201             sub mtime {
202 0     0 0 0 return 0;
203 0         0 carp "File::Assets::Asset::mtime is DEPRECATED";
204             }
205              
206             sub file_mtime {
207 171     171 0 245 my $self = shift;
208 171 100       381 return 0 unless $self->file;
209 166         23407 return $self->_content->file_mtime;
210             }
211              
212             sub file_size {
213 0     0 0 0 my $self = shift;
214 0 0       0 return 0 unless $self->file;
215 0         0 return $self->_content->file_size;
216             }
217              
218             sub content_mtime {
219 0     0 0 0 my $self = shift;
220 0 0       0 return 0 unless $self->file;
221 0         0 return $self->_content->content_mtime;
222             }
223              
224             sub content_size {
225 0     0 0 0 my $self = shift;
226 0 0       0 return length ${ $self->{content} } unless $self->file;
  0         0  
227 0         0 return $self->_content->content_size;
228             }
229              
230             sub refresh {
231 0     0 0 0 my $self = shift;
232 0 0       0 return 0 unless $self->file;
233 0         0 return $self->_content->refresh;
234             }
235              
236             sub stale {
237 0     0 0 0 my $self = shift;
238 0 0       0 return 0 unless $self->file;
239 0         0 return $self->_content->stale;
240             }
241              
242             sub _content {
243 345     345   844 my $self = shift;
244 345   66     1604 return $self->{_content} ||= do {
245 126 100       348 if (my $cache = $self->{cache}) {
246 70         202 $cache->content($self->file);
247             }
248             else {
249 56         136 File::Assets::Asset::Content->new($self->file);
250             }
251             };
252             }
253              
254             =head2 $asset->key
255              
256             Returns the unique key for the $asset. Usually the path/filename of the $asset, but for content-based assets returns a value based off of $asset->digest
257              
258             =cut
259              
260             sub key {
261 224     224 1 657 my $self = shift;
262 224   66     511 return $self->path || $self->uri || ($self->{key} ||= '%' . $self->digest);
263             }
264              
265             =head2 $asset->hide
266              
267             Hide $asset (mark it as hidden). That is, don't include $asset during export
268              
269             =cut
270              
271             sub hide {
272 0     0 1 0 shift->{hidden} = 1;
273             }
274              
275             =head2 $asset->inline
276              
277             Returns whether $asset is inline (should be embedded into the document) or external.
278              
279             If an argument is given, then it will set whether $asset is inline or not (1 for inline, 0 for external).
280              
281             =cut
282              
283             sub inline {
284 248     248 1 396 my $self = shift;
285 248 50       597 $self->{inline} = shift() ? 1 : 0 if @_;
    100          
286 248         4363 return $self->{inline};
287             }
288              
289             1;
290              
291             __END__
292              
293             # elsif (0 && $base && $content) { # Nonsense scenario?
294             # croak "Don't have a type for this asset" unless $type;
295             # my $path = File::Assets::Util->build_asset_path(undef, type => $type, digest => $self->digest);
296             # $self->{rsc} = $base->child($path);
297             # $self->{type} = $type;
298             # }