File Coverage

blib/lib/Plack/App/DataSection.pm
Criterion Covered Total %
statement 62 84 73.8
branch 8 18 44.4
condition 8 27 29.6
subroutine 16 18 88.8
pod 1 9 11.1
total 95 156 60.9


line stmt bran cond sub pod time code
1             package Plack::App::DataSection;
2 3     3   161526 use strict;
  3         7  
  3         105  
3 3     3   14 use warnings;
  3         5  
  3         123  
4             our $VERSION = '0.05';
5              
6 3     3   1023 use parent qw/Plack::Component/;
  3         381  
  3         18  
7 3     3   50045 use MIME::Base64;
  3         2239  
  3         198  
8 3     3   2549 use Data::Section::Simple;
  3         1660  
  3         126  
9 3     3   2585 use Plack::MIME;
  3         2749  
  3         103  
10 3     3   2633 use HTTP::Date;
  3         16909  
  3         199  
11              
12 3     3   2241 use Plack::Util::Accessor qw(encoding);
  3         721  
  3         23  
13              
14             sub call {
15 3     3 1 39711 my $self = shift;
16 3         5 my $env = shift;
17              
18 3   50     19 my $path = $env->{PATH_INFO} || '';
19 3 50       14 if ($path =~ /\0/) {
20 0         0 return $self->return_400;
21             }
22 3         14 $path =~ s!^/!!;
23              
24 3 100       21 my ($data, $content_type) = $path ? $self->get_content($path) : ();
25              
26 3 100       15 return $self->return_404 unless $data;
27              
28 2         13 return [ 200, [
29             'Content-Type' => $content_type,
30             'Content-Length' => length($data),
31             'Last-Modified' => $self->last_modified,
32             ], [ $data ] ];
33             }
34              
35             sub return_400 {
36 0     0 0 0 my $self = shift;
37 0         0 return [400, ['Content-Type' => 'text/plain', 'Content-Length' => 11], ['Bad Request']];
38             }
39              
40             sub return_404 {
41 1     1 0 3 my $self = shift;
42 1         10 return [404, ['Content-Type' => 'text/plain', 'Content-Length' => 9], ['not found']];
43             }
44              
45             sub data_section {
46 2     2 0 4 my $self = shift;
47              
48 2   33     31 $self->{_reader} ||= Data::Section::Simple->new(ref $self);
49             }
50              
51             sub get_data_section {
52 2     2 0 4 my ($self, $path) = @_;
53 2   33     18 $self->{_data_section_hash} ||= $self->data_section->get_data_section;
54 2 50       310 if ($path) {
55 2         8 $self->{_data_section_hash}{$path};
56             }
57             else {
58 0         0 $self->{_data_section_hash};
59             }
60             }
61              
62 2   50 2   20 sub _cache { shift->{_cache} ||= {} }
63              
64             sub get_content {
65 2     2 0 5 my ($self, $path) = @_;
66              
67 2         20 my $mime_type = Plack::MIME->mime_type($path);
68 2         33 my $is_binary = is_binary($mime_type);
69              
70 2 50       15 unless ($is_binary) {
71 2   50     20 my $encoding = $self->encoding || 'utf-8';
72 2         35 $mime_type .= "; charset=$encoding";
73             }
74              
75 2   33     11 my $content = $self->_cache->{$path} ||= do {
76 2         11 my $content = $self->get_data_section($path);
77 2 50 33     22 $content = decode_base64($content) if $content && $is_binary;
78 2         9 $content;
79             };
80              
81 2         6 ($content, $mime_type);
82             }
83              
84             sub is_binary {
85 2     2 0 13 my $mime_type = shift;
86              
87 2         15 $mime_type !~ /\b(?:text|xml|javascript|json)\b/;
88             }
89              
90             sub last_modified {
91 2     2 0 5 my $self = shift;
92              
93 2   33     10 $self->{last_modified} ||= do {
94 2         5 my $mod = ref $self;
95 2         9 $mod =~ s!::!/!g;
96 2         47 $mod .= '.pm';
97 2         7 my $full_path = $INC{$mod};
98              
99 2         61 my @stat = stat $full_path;
100 2         13 HTTP::Date::time2str( $stat[9] )
101             };
102             }
103              
104             sub dump_dir {
105 0     0 0   my ($self, $dir) = @_;
106 0           require Errno;
107 0           require Path::Class;
108              
109 0           my %data_section = %{ $self->get_data_section };
  0            
110 0           my $base_dir = Path::Class::Dir->new($dir);
111              
112 0 0 0       $base_dir->mkpath or $! != Errno::EEXIST() or die "failed to create dir:$base_dir:$!";
113              
114 0           for my $key (keys %data_section) {
115 0           my ($content) = $self->get_content($key);
116              
117 0           $key =~ s!^/!!g;
118 0           my ($sub_dir, $file) = $key =~ m!^(.*?)([^/]+)$!;
119              
120 0           my $dir_path = $base_dir;
121 0 0         if ($sub_dir) {
122 0           $dir_path = $dir_path->subdir($sub_dir);
123 0 0 0       $dir_path->mkpath or $! != Errno::EEXIST() or die "failed to create dir:$dir_path:$!";
124             }
125              
126 0           $file = $dir_path->file($file);
127 0           my $fh = $file->openw;
128 0           $fh->print($content);
129             }
130             }
131              
132             1;
133             __DATA__