File Coverage

blib/lib/Media/Type/Simple.pm
Criterion Covered Total %
statement 109 113 96.4
branch 29 36 80.5
condition 4 5 80.0
subroutine 24 24 100.0
pod 13 13 100.0
total 179 191 93.7


line stmt bran cond sub pod time code
1             package Media::Type::Simple;
2              
3 4     4   80184 use v5.10.0;
  4         16  
  4         191  
4              
5 4     4   21 use strict;
  4         156  
  4         128  
6 4     4   20 use warnings;
  4         17  
  4         126  
7              
8 4     4   19 use Carp;
  4         7  
  4         337  
9 4     4   3467 use Exporter::Lite;
  4         3133  
  4         25  
10 4     4   3371 use File::Share qw/ dist_file /;
  4         38696  
  4         302  
11 4     4   4970 use Storable qw( freeze thaw );
  4         16757  
  4         358  
12              
13 4     4   3553 use version 0.77; our $VERSION = version->declare('v0.30.6');
  4         9375  
  4         35  
14              
15             our @EXPORT = qw( is_type alt_types ext_from_type ext3_from_type is_ext type_from_ext );
16             our @EXPORT_OK = (@EXPORT, qw/ add_type /);
17              
18             # TODO - option to disable reading of MIME types with no associated extensions
19              
20             =head1 NAME
21              
22             Media::Type::Simple - MIME Types and their file extensions
23              
24             =begin readme
25              
26             =head1 REQUIREMENTS
27              
28             The following non-core modules are required:
29              
30             Exporter::Lite
31             File::Share
32             File::ShareDir
33              
34             =end readme
35              
36             =head1 SYNOPSIS
37              
38             use Media::Type::Simple;
39              
40             $type = type_from_ext("jpg"); # returns "image/jpeg"
41              
42             $ext = ext_from_type("text/plain"); # returns "txt"
43              
44             =head1 DESCRIPTION
45              
46             This package gives a simple functions for obtaining common file
47             extensions from media types, and from obtaining media types from
48             file extensions.
49              
50             It is also relaxed with respect to having multiple media types
51             associated with a file extension, or multiple extensions associated
52             with a media type, and it includes media types for encodings such
53             as C. It is defined this way in the default data, but
54             this does not meet your needs, then you can have it use a system file
55             (e.g. F) or custom data.
56              
57             By default, there is a functional interface, although you can also use
58             an object-oriented interface. (Different objects will not share the
59             same data.)
60              
61             =for readme stop
62              
63             =head2 Methods
64              
65             =cut
66              
67             my $Default; # Pristine copy of data_
68             my $Work; # Working copy of data
69              
70             =over
71              
72             =item new
73              
74             $o = Media::Type::Simple->new;
75              
76             Creates a new object. You may optionally give it a filehandle of a file
77             with system Media information, e.g.
78              
79             open $f, "/etc/mime.types";
80             $o = Media::Type::Simple->new( $f );
81              
82             =begin internal
83              
84             When L is called for the first time without a file handle, it
85             checks to see if the C<$Default> instance is initialised: if it is
86             not, then it initialises it and returns a L of C<$Default>.
87              
88             We operate on clones rather than the original, so that any changes
89             made, e.g. L, will not affect all other instances.
90              
91             =end internal
92              
93             =cut
94              
95             sub new {
96 3     3 1 608 my $class = shift;
97 3         39 my $self = { types => { }, extens => { }, };
98              
99 3         13 bless $self, $class;
100              
101 3 50       18 if (@_) {
102 0         0 my $fh = shift;
103 0         0 return $self->add_types_from_file( $fh );
104             }
105             else {
106 3 100       14 unless (defined $Default) {
107 2         37 my $file = dist_file('Media-Type-Simple', 'mime.types');
108 2 50       1380 open my $fh, '<', $file
109             or croak "Unable to open ${file}: $!";
110 2         16 $Default = $self->add_types_from_file( $fh );
111 2         98 close $fh;
112             }
113 3         25 return clone $Default;
114             }
115             }
116              
117             =begin internal
118              
119             =item _args
120              
121             An internal function used to process arguments, based on C<_args> from
122             the L package. It also allows one to use it in non-object
123             oriented mode.
124              
125             When L is called for the first time without a reference to the
126             class instance, it checks to see if C<$Work> is defined, and it is
127             initialised with L if it is not defined. This means that
128             C<$Work> is only initialised when the module is used.
129              
130             =item self
131              
132             An internal function used in place of the C<$self> variable.
133              
134             =item args
135              
136             An internal function used in place of shifting arguments from stack.
137              
138             =end internal
139              
140             =cut
141              
142             # _args, self and args based on 'self' v0.15
143              
144             sub _args {
145 7433     7433   8138 my $level = 2;
146 7433         9257 my @c = ();
147 7433   66     20949 while ( !defined($c[3]) || $c[3] eq '(eval)') {
148 7433         7643 @c = do {
149             package DB; # Module::Build hates this!
150 7433         9184 @DB::args = ();
151 7433         57685 caller($level);
152             };
153 7433         40665 $level++;
154             }
155              
156 7433         19883 my @args = @DB::args;
157              
158 7433 100       15898 if (ref($args[0]) ne __PACKAGE__) {
159 56 100       134 unless (defined $Work) {
160 2         45 $Work = __PACKAGE__->new();
161             }
162 56         4465 unshift @args, $Work;
163             }
164              
165 7433         47440 return @args;
166             }
167              
168             sub self {
169 6559     6559 1 10537 (_args)[0];
170             }
171              
172             sub args {
173 874     874 1 1759 my @a = _args;
174 874         3264 return @a[1..$#a];
175             }
176              
177              
178             =item add_types_from_file
179              
180             $o->add_types_from_file( $filehandle );
181              
182             Imports types from a file. Called by L when a filehandle is
183             specified.
184              
185             =cut
186              
187             sub add_types_from_file {
188 2     2 1 25 my ($fh) = args;
189              
190 2         81 while (my $line = <$fh>) {
191 792         1619 $line =~ s/^\s+//;
192 792         1077 $line =~ s/\#.*$//;
193 792         3187 $line =~ s/\s+$//;
194              
195 792 50       1926 if ($line) {
196 792         1442 self->add_type(split /\s+/, $line);
197             }
198             }
199 2         10 return self;
200             }
201              
202             =item is_type
203              
204             if (is_type("text/plain")) { ... }
205              
206             if ($o->is_type("text/plain")) { ... }
207              
208             Returns a true value if the type is defined in the system.
209              
210             Note that a true value does not necessarily indicate that the type
211             has file extensions associated with it.
212              
213             =begin internal
214              
215             Currently it returns a reference to a list of extensions associated
216             with that type. This is for convenience, and may change in future
217             releases.
218              
219             =end internal
220              
221             =cut
222              
223             sub is_type {
224 50     50 1 5222 my ($type) = args;
225 50         103 my ($cat, $spec) = split_type($type);
226 50         95 return self->{types}->{$cat}->{$spec};
227             }
228              
229             =item alt_types
230              
231             @alts = alt_types("image/jpeg");
232              
233             @alts = $o->alt_types("image/jpeg");
234              
235             Returns alternative or related Media types that are defined in the system
236             For instance,
237              
238             alt_types("model/dwg")
239              
240             returns the list
241              
242             image/vnd.dwg
243              
244             =begin internal
245              
246             =item _normalise
247              
248             =item _add_aliases
249              
250             =end internal
251              
252             =cut
253              
254             {
255              
256             # Some known special cases (keys are normalised). Not exhaustive.
257              
258             my %SPEC_CASES = (
259             "audio/flac" => [qw( application/flac )],
260             "application/cdf" => [qw( application/netcdf )],
261             "application/dms" => [qw( application/octet-stream )],
262             "application/x-java-source" => [qw( text/plain )],
263             "application/java-vm" => [qw( application/octet-stream )],
264             "application/lha" => [qw( application/octet-stream )],
265             "application/lzh" => [qw( application/octet-stream )],
266             "application/mac-binhex40" => [qw( application/binhex40 )],
267             "application/msdos-program" => [qw( application/octet-stream )],
268             "application/ms-pki.seccat" => [qw( application/vnd.ms-pkiseccat )],
269             "application/ms-pki.stl" => [qw( application/vnd.ms-pki.stl )],
270             "application/ndtcdf" => [qw( application/cdf )],
271             "application/netfpx" => [qw( image/vnd.fpx image/vnd.net-fpx )],
272             "audio/ogg" => [qw( application/ogg )],
273             "image/fpx" => [qw( application/vnd.netfpx image/vnd.net-fpx )],
274             "image/netfpx" => [qw( application/vnd.netfpx image/vnd.fpx )],
275             "text/c++hdr" => [qw( text/plain )],
276             "text/c++src" => [qw( text/plain )],
277             "text/chdr" => [qw( text/plain )],
278             "text/fortran" => [qw( text/plain )],
279             );
280              
281              
282             sub _normalise {
283 193     193   221 my $type = shift;
284 193         256 my ($cat, $spec) = split_type($type);
285              
286             # We "normalise" the type
287              
288 193         253 $cat =~ s/^x-//;
289 193         371 $spec =~ s/^(x-|vnd\.)//;
290              
291 193         407 return ($cat, $spec);
292             }
293              
294             sub _add_aliases {
295 56     56   126 my @aliases = @_;
296 56         77 foreach my $type (@aliases) {
297 188         282 my ($cat, $spec) = _normalise($type);
298 188         815 $SPEC_CASES{"$cat/$spec"} = \@aliases;
299             }
300             }
301              
302             _add_aliases(qw( application/mp4 video/mp4 ));
303             _add_aliases(qw( application/json text/json ));
304             _add_aliases(qw( application/cals-1840 image/cals-1840 image/cals image/x-cals application/cals ));
305             _add_aliases(qw( application/mac-binhex40 application/binhex40 ));
306             _add_aliases(qw( application/atom+xml application/atom ));
307             _add_aliases(qw( application/fractals image/fif ));
308             _add_aliases(qw( model/vnd.dwg image/vnd.dwg image/x-dwg application/acad ));
309             _add_aliases(qw( image/vnd.dxf image/x-dxf application/x-dxf application/vnd.dxf ));
310             _add_aliases(qw( text/x-c text/csrc ));
311             _add_aliases(qw( application/x-helpfile application/x-winhlp ));
312             _add_aliases(qw( application/x-tex text/x-tex ));
313             _add_aliases(qw( application/rtf text/rtf ));
314             _add_aliases(qw( image/jpeg image/pipeg image/pjpeg ));
315             _add_aliases(qw( text/javascript text/javascript1.0 text/javascript1.1 text/javascript1.2 text/javascript1.3 text/javascript1.4 text/javascript1.5 text/jscript text/livescript text/x-javascript text/x-ecmascript aplication/ecmascript application/javascript ));
316              
317              
318             sub alt_types {
319 5     5 1 2455 my ($type) = args;
320 5         15 my ($cat, $spec) = _normalise($type);
321              
322 5         10 my %alts = ( );
323 5         71 my @cases = ( "$cat/$spec", "$cat/x-$spec", "x-$cat/x-$spec",
324             "$cat/vnd.$spec" );
325              
326 5 100       26 push @cases, @{ $SPEC_CASES{"$cat/$spec"} },
  4         14  
327             if ($SPEC_CASES{"$cat/$spec"});
328              
329 5         12 foreach ( @cases ) {
330 32 100       54 $alts{$_} = 1, if (self->is_type($_));
331             }
332              
333 5         44 return (sort keys %alts);
334             }
335             }
336              
337             =item ext_from_type
338              
339             $ext = ext_from_type( $type );
340              
341             @exts = ext_from_type( $type );
342              
343             $ext = $o->ext_from_type( $type );
344              
345             @exts = $o->ext_from_type( $type );
346              
347             Returns the file extension(s) associated with the given Media type.
348             When called in a scalar context, returns the first extension from the
349             list.
350              
351             The order of extensions is based on the order that they occur in the
352             source data (either the default here, or the order added using
353             L or calls to L).
354              
355             =cut
356              
357             sub ext_from_type {
358 11 100   11 1 3230 if (my $exts = self->is_type(args)) {
359 10 100       66 return (wantarray ? @$exts : $exts->[0]);
360             }
361             else {
362 1         5 return;
363             }
364             }
365              
366             =item ext3_from_type
367              
368             Like L, but only returns file extensions under three
369             characters long.
370              
371             =cut
372              
373             sub ext3_from_type {
374 4     4 1 2902 my @exts = grep( (length($_) <= 3), (ext_from_type(@_)));
375 4 100       27 return (wantarray ? @exts : $exts[0]);
376             }
377              
378             =item is_ext
379              
380             if (is_ext("image/jpeg")) { ... }
381              
382             if ($o->is_type("image/jpeg")) { ... }
383              
384             Returns a true value if the extension is defined in the system.
385              
386             =begin internal
387              
388             Currently it returns a reference to a list of types associated
389             with that extension. This is for convenience, and may change in future
390             releases.
391              
392             =end internal
393              
394             =cut
395              
396             sub is_ext {
397 7     7 1 1113 my ($ext) = args;
398 7 50       15 if (exists self->{extens}->{$ext}) {
399 7         16 return self->{extens}->{$ext};
400             }
401             else {
402 0         0 return;
403             }
404             }
405              
406             =item type_from_ext
407              
408             $type = type_from_ext( $extension );
409              
410             @types = type_from_ext( $extension );
411              
412             $type = $o->type_from_ext( $extension );
413              
414             @types = $o->type_from_ext( $extension );
415              
416             Returns the Media type(s) associated with the extension. When called
417             in a scalar context, returns the first type from the list.
418              
419             The order of types is based on the order that they occur in the
420             source data (either the default here, or the order added using
421             L or calls to L).
422              
423             =cut
424              
425             sub type_from_ext {
426 5     5 1 5360 my ($ext) = args;
427              
428 5 50       20 if (my $ts = self->is_ext($ext)) {
429 5         11 my @types = map { $_ } @$ts;
  15         35  
430 5 100       55 return (wantarray ? @types : $types[0]);
431             }
432             else {
433 0         0 croak "Unknown extension: $ext";
434             }
435             }
436              
437             =begin internal
438              
439             =item split_type
440              
441             ($content_type, $subtype) = split_type( $type );
442              
443             This is a utility function for splitting content types.
444              
445             =end internal
446              
447             =cut
448              
449             sub split_type {
450 1037     1037 1 1161 my $type = shift;
451 1037         2265 my ($cat, $spec) = split /\//, $type;
452 1037         2453 return ($cat, $spec);
453             }
454              
455             =item add_type
456              
457             $o->add_type( $type, @extensions );
458              
459             Add a type to the system, with an optional list of extensions.
460              
461             =cut
462              
463             sub add_type {
464 794     794 1 2694 my ($type, @exts) = args;
465              
466 794 50 100     2601 if (@exts || 1) { # TODO - option to ignore types with no extensions
467              
468 794         1315 my ($cat, $spec) = split_type($type);
469              
470 794 100       1427 if (!self->{types}->{$cat}->{$spec}) {
471 791         1600 self->{types}->{$cat}->{$spec} = [ ];
472             }
473 794         1411 push @{ self->{types}->{$cat}->{$spec} }, @exts;
  794         1238  
474              
475              
476 794         1591 foreach (@exts) {
477 1120 100       2192 self->{extens}->{$_} = [] unless (exists self->{extens}->{$_});
478 1120         2159 push @{self->{extens}->{$_}}, $type
  1120         1859  
479             }
480             }
481             }
482              
483             =item clone
484              
485             $c = $o->clone;
486              
487             Returns a clone of a Media::Type::Simple object. This allows you to add
488             new types via L or L without affecting
489             the original.
490              
491             This can I be used in the object-oriented interface.
492              
493             =cut
494              
495             sub clone {
496 3     3 1 7 my $self = shift;
497 3 50       16 croak "Expected instance" if (ref($self) ne __PACKAGE__);
498 3         43 return thaw( freeze $self );
499             }
500              
501              
502             =back
503              
504             =for readme continue
505              
506             =head1 REVISION HISTORY
507              
508             For a detailed history see the F file included in this distribution.
509              
510             =head1 SEE ALSO
511              
512             The L module has a similar functionality, but with a more
513             complex interface.
514              
515             L will guess the media type from a file extension,
516             attempting to use the F<~/.media.types> file.
517              
518             An "official" list of Media Types can be found at
519             L.
520              
521             =head1 AUTHOR
522              
523             Robert Rothenberg
524              
525             =head2 Contributors
526              
527             =over
528              
529             =item Martin McGrath
530              
531             =back
532              
533             =head2 Acknowledgements
534              
535             Some of the code comes from L module (by Kang-min Liu). The data
536             for the media types is based on the Debian C package,
537             L,
538             although with I changes from the original.
539              
540             =head2 Suggestions and Bug Reporting
541              
542             Feedback is always welcome. Please use the CPAN Request Tracker at
543             L to submit bug reports.
544              
545             The git repository for this module is at
546             L.
547              
548             =head1 COPYRIGHT & LICENSE
549              
550             Copyright 2009-2014 Robert Rothenberg, all rights reserved.
551              
552             This program is free software; you can redistribute it and/or modify it
553             under the same terms as Perl itself.
554              
555             =cut
556              
557             1;
558              
559