File Coverage

blib/lib/App/MatrixTool/ServerIdStore.pm
Criterion Covered Total %
statement 18 71 25.3
branch 0 24 0.0
condition 0 8 0.0
subroutine 6 14 42.8
pod 3 4 75.0
total 27 121 22.3


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2015 -- leonerd@leonerd.org.uk
5              
6             package App::MatrixTool::ServerIdStore;
7              
8 1     1   946 use strict;
  1         2  
  1         29  
9 1     1   3 use warnings;
  1         1  
  1         39  
10              
11             our $VERSION = '0.08';
12              
13 1     1   4 use Errno qw( ENOENT );
  1         1  
  1         48  
14 1     1   5 use File::Basename qw( dirname );
  1         1  
  1         55  
15 1     1   4 use File::Path qw( make_path );
  1         8  
  1         47  
16 1     1   12 use MIME::Base64 qw( encode_base64 decode_base64 );
  1         1  
  1         702  
17              
18             =head1 NAME
19              
20             C - storage keyed by server name and an ID
21              
22             =head1 DESCRIPTION
23              
24             Provides a simple flat-file database that stores data keyed by a remote server
25             name and ID field. This is persisted in a human-readable file.
26              
27             =cut
28              
29             sub new
30             {
31 0     0 0   my $class = shift;
32 0           my %args = @_;
33              
34             return bless {
35             path => $args{path},
36             data => {},
37 0   0       encode => $args{encode} // "base64",
38             }, $class;
39             }
40              
41             =head1 METHODS
42              
43             =cut
44              
45             sub _open_file
46             {
47 0     0     my $self = shift;
48 0           my ( $mode ) = @_;
49              
50 0           my $path = $self->{path};
51              
52 0 0 0       if( $mode eq ">>" and not -f $path ) {
53 0           make_path( dirname( $path ) );
54             }
55              
56 0 0         if( open my $fh, $mode, $path ) {
57 0           return $fh;
58             }
59              
60 0 0 0       return undef if $! == ENOENT and $mode eq "<";
61 0           die "Cannot open $path - $!\n";
62             }
63              
64             sub _read_file
65             {
66 0     0     my $self = shift;
67 0 0         return if $self->{have_read};
68              
69 0 0         if( my $fh = $self->_open_file( "<" ) ) {
70 0           while( <$fh> ) {
71 0 0         m/^\s*#/ and next; # ignore comment lines
72 0           my ( $server, $id, $key ) = split m/\s+/, $_;
73              
74 0 0         defined $key or warn( "Unable to parse line $_" ), next;
75              
76 0           $self->{data}{$server}{$id} = $self->_decode( $key );
77             }
78             }
79              
80 0           $self->{have_read}++;
81             }
82              
83             sub _encode
84             {
85 0     0     my $self = shift;
86 0 0         return encode_base64( $_[0], "" ) if $self->{encode} eq "base64";
87 0           return $_[0];
88             }
89              
90             sub _decode
91             {
92 0     0     my $self = shift;
93 0 0         return decode_base64( $_[0] ) if $self->{encode} eq "base64";
94 0           return $_[0];
95             }
96              
97             =head2 list
98              
99             %id_data = $store->list( server => $name )
100              
101             Returns a kvlist associating IDs to byte strings of data stored for the given
102             server.
103              
104             =cut
105              
106             sub list
107             {
108 0     0 1   my $self = shift;
109 0           my %args = @_;
110              
111 0           my $server = $args{server};
112 0           $self->_read_file;
113              
114 0           my %ret;
115 0           foreach my $id ( keys %{ $self->{data}{$server} } ) {
  0            
116 0           $ret{$id} = $self->{data}{$server}{$id};
117             }
118              
119 0           return %ret;
120             }
121              
122             =head2 get
123              
124             $key = $store->get( server => $name, id => $id )
125              
126             Returns a byte string associated with the given server and ID, or C if
127             no such is known.
128              
129             =cut
130              
131             sub get
132             {
133 0     0 1   my $self = shift;
134 0           my %args = @_;
135              
136 0           my $server = $args{server};
137 0           my $id = $args{id};
138 0           $self->_read_file;
139              
140 0 0         return unless $self->{data}{$server};
141 0           return $self->{data}{$server}{$id};
142             }
143              
144             =head2 put
145              
146             $store->put( server => $name, id => $id, data => $bytes )
147              
148             Stores a byte string associated with the server and ID.
149              
150             =cut
151              
152             sub put
153             {
154 0     0 1   my $self = shift;
155 0           my %args = @_;
156              
157 0           my $server = $args{server};
158 0           my $id = $args{id};
159              
160 0 0         if( exists $self->{data}{$server}{$id} ) {
161 0 0         return if $self->{data}{$server}{$id} eq $args{data};
162 0           warn "ServerIdStore is overwriting a key with a different value!\n";
163             }
164              
165 0           my $fh = $self->_open_file( ">>" );
166 0           $fh->print( "$server $id " . $self->_encode( $args{data} ) . "\n" );
167              
168 0           $self->{data}{$server}{$id} = $args{data};
169             }
170              
171             =head1 AUTHOR
172              
173             Paul Evans
174              
175             =cut
176              
177             0x55AA;