File Coverage

blib/lib/Bio/JBrowse/Store/NCList/JSONFileStorage.pm
Criterion Covered Total %
statement 23 25 92.0
branch n/a
condition n/a
subroutine 9 9 100.0
pod n/a
total 32 34 94.1


line stmt bran cond sub pod time code
1              
2             package Bio::JBrowse::Store::NCList::JSONFileStorage;
3             BEGIN {
4 1     1   113 $Bio::JBrowse::Store::NCList::JSONFileStorage::AUTHORITY = 'cpan:RBUELS';
5             }
6             {
7             $Bio::JBrowse::Store::NCList::JSONFileStorage::VERSION = '0.1';
8             }
9              
10 1     1   5 use strict;
  1         2  
  1         35  
11 1     1   6 use warnings;
  1         2  
  1         31  
12 1     1   6 use File::Spec ();
  1         1  
  1         14  
13 1     1   5 use File::Path ();
  1         1  
  1         26  
14 1     1   5 use JSON 2 ();
  1         28  
  1         18  
15 1     1   1479 use IO::File;
  1         1353  
  1         201  
16 1     1   7 use Fcntl ":flock";
  1         2  
  1         157  
17 1     1   1871 use PerlIO::gzip;
  0            
  0            
18              
19             use constant DEFAULT_MAX_JSON_DEPTH => 2048;
20              
21              
22             sub new {
23             my ($class, $outDir, $compress, $opts) = @_;
24              
25             # create JSON object
26             my $json = JSON->new->relaxed->max_depth( DEFAULT_MAX_JSON_DEPTH );
27             # set opts
28             if (defined($opts) and ref($opts) eq 'HASH') {
29             for my $method (keys %$opts) {
30             $json->$method( $opts->{$method} );
31             }
32             }
33              
34             my $self = {
35             outDir => $outDir,
36             ext => $compress ? ".jsonz" : ".json",
37             compress => $compress,
38             json => $json
39             };
40             bless $self, $class;
41              
42             File::Path::mkpath( $outDir ) unless (-d $outDir);
43              
44             return $self;
45             }
46              
47             sub _write_htaccess {
48             my ( $self ) = @_;
49              
50             if( $self->{compress} && ! $self->{htaccess_written} ) {
51             my $hn = File::Spec->catfile( $self->{outDir}, '.htaccess' );
52             return if -e $hn;
53              
54             open my $h, '>', $hn or die "$! writing $hn";
55              
56             my @extensions = qw( .jsonz .txtz .txt.gz );
57             my $re = '('.join('|',@extensions).')$';
58             $re =~ s/\./\\./g;
59              
60             print $h <
61             # This Apache .htaccess file is for
62             # serving precompressed files (@extensions) with the proper
63             # Content-Encoding HTTP headers. In order for Apache to pay attention
64             # to this, its AllowOverride configuration directive for this
65             # filesystem location must allow FileInfo overrides.
66            
67             mod_gzip_item_exclude "$re"
68            
69            
70             SetEnvIf Request_URI "$re" no-gzip dont-vary
71            
72            
73            
74             Header onsuccess set Content-Encoding gzip
75            
76            
77             EOA
78             $self->{htaccess_written} = 1;
79             }
80             }
81              
82              
83             sub fullPath {
84             my ($self, $path) = @_;
85             return File::Spec->join($self->{outDir}, $path);
86             }
87              
88              
89             sub ext {
90             return shift->{ext};
91             }
92              
93              
94             sub encodedSize {
95             my ($self, $obj) = @_;
96             return length($self->{json}->encode($obj));
97             }
98              
99              
100             sub put {
101             my ($self, $path, $toWrite) = @_;
102              
103             $self->_write_htaccess;
104              
105             my $file = $self->fullPath($path);
106             my $fh = IO::File->new( $file, O_WRONLY | O_CREAT )
107             or die "couldn't open $file: $!";
108             flock $fh, LOCK_EX;
109             $fh->seek(0, SEEK_SET);
110             $fh->truncate(0);
111             if ($self->{compress}) {
112             binmode($fh, ":gzip")
113             or die "couldn't set binmode: $!";
114             }
115             $fh->print($self->{json}->encode($toWrite))
116             or die "couldn't write to $file: $!";
117             $fh->close()
118             or die "couldn't close $file: $!";
119             }
120              
121              
122             sub get {
123             my ($self, $path, $default) = @_;
124              
125             my $file = $self->fullPath($path);
126             if (-s $file) {
127             my $OLDSEP = $/;
128             my $fh = IO::File->new( $file, O_RDONLY )
129             or die "couldn't open $file: $!";
130             binmode($fh, ":gzip") if $self->{compress};
131             flock $fh, LOCK_SH;
132             undef $/;
133             eval {
134             $default = $self->{json}->decode(<$fh>)
135             }; if( $@ ) {
136             die "Error parsing JSON file $file: $@\n";
137             }
138             $default or die "couldn't read from $file: $!";
139             $fh->close()
140             or die "couldn't close $file: $!";
141             $/ = $OLDSEP;
142             }
143             return $default;
144             }
145              
146             1;
147              
148             __END__