File Coverage

blib/lib/App/Stash.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


line stmt bran cond sub pod time code
1             package App::Stash;
2              
3             =head1 NAME
4              
5             App::Stash - persistent application data storage
6              
7             =head1 SYNOPSIS
8              
9             use App::Stash;
10             $stash = App::Stash->new({application => "test"});
11             $stash->data->{'test'} = 1;
12             $stash->d->{'test'} = 1;
13              
14             after new run:
15              
16             use App::Stash;
17             $s=App::Stash->new({application => "test"});
18             print $s->data->{'test'}, "\n";
19             print $s->dao->test, "\n";
20              
21             =head1 WARNING
22              
23             experimental, use on your own risk :-)
24              
25             =head1 DESCRIPTION
26              
27             The purpose of the module is to transparently save stash data (structure)
28             across application (script) execution. The save is done in L
29             method. This has certain limitations. Basically make sure you never store
30             object in the L as this one may get destroyed before L
31             object does.
32              
33             The module+style is inspired by L. Unlike L it uses
34             L for storage and not L. The stash is saved to
35             F<$HOME/.app-name/stash.json>. It is in the "pretty" format so it should be
36             easy to read and edit. I wanted to go with L but using it in
37             DESTROY method causes C on my Perl.
38              
39             Warn: no file locking in place, use L or similar to have just one
40             instance of program running or send a wish list bug report and wait for
41             implementation of stash file locking. :)
42              
43             =cut
44              
45 1     1   190686 use warnings;
  1         2  
  1         42  
46 1     1   6 use strict;
  1         1  
  1         64  
47              
48             our $VERSION = '0.02';
49              
50 1     1   963 use File::HomeDir;
  1         8131  
  1         91  
51 1     1   11 use File::Path qw( mkpath );
  1         2  
  1         51  
52 1     1   6 use Path::Class;
  1         2  
  1         66  
53 1     1   757 use JSON::Util;
  0            
  0            
54              
55              
56             use base qw( Class::Accessor::Chained::Fast );
57             __PACKAGE__->mk_accessors(qw( application directory stash_filename ));
58              
59             =head1 PROPERTIES
60              
61             application
62             directory
63             stash_filename
64              
65             See L for a description of C and C.
66             C is the full path to the file where stash data will be
67             stored. All three are optional.
68              
69             =head1 METHODS
70              
71             =head2 new()
72              
73             Object constructor.
74              
75             =cut
76              
77             sub new {
78             my $class = shift;
79             my $self = $class->SUPER::new(@_);
80              
81             unless ( $self->application ) {
82             my $caller = (caller)[0];
83             $self->application($caller);
84             }
85              
86             unless ( $self->directory ) {
87             my $dir = dir( home(), "." . $self->_clean( $self->application ));
88             $self->directory($dir);
89             }
90             my $dir = $self->directory;
91             unless ( -d "$dir" ) {
92             mkpath("$dir")
93             || die "Error mkdiring " . $self->directory . ": $!";
94             }
95              
96             unless ( $self->stash_filename ) {
97             my $stash_filename = file($self->directory , "stash.json" )->stringify;
98             $self->stash_filename($stash_filename);
99             }
100              
101             return $self;
102             }
103              
104             =head2 d
105              
106             Shortcut for L.
107              
108             =head2 data
109              
110             Returns reference to the stash data.
111              
112             =cut
113              
114             *d = *data;
115             sub data {
116             my $self = shift;
117              
118             $self->load
119             if (not $self->{'data'});
120              
121             return $self->{'data'};
122             }
123              
124             =head2 dao()
125              
126             Returns L passed to L. So basically the
127             data structure becomes an object. See L for details.
128              
129             Note: L is not compile time dependency. It will be used
130             if installed. If not the exception will be thrown only when calling L.
131             So if you plan to use it, make it a dependency of your module/program.
132              
133             =cut
134              
135             sub dao {
136             my $self = shift;
137             if (not $INC{'Data/AsObject.pm'}) {
138             eval 'use Data::AsObject;';
139             die $@ if $@;
140             }
141             if (not $INC{'Storable.pm'}) {
142             eval 'use Storable;';
143             die $@ if $@;
144             }
145             return Data::AsObject::dao(Storable::dclone($self->data));
146             }
147              
148             =head2 clear
149              
150             Will delete stash data and remove the file with the stash data from the
151             disk.
152              
153             =cut
154              
155             sub clear {
156             my $self = shift;
157             delete $self->{'data'};
158             unlink($self->stash_filename) or die 'failed to unlink '.$self->stash_filename.' - '.$!;
159             return;
160            
161             }
162              
163             =head2 load
164              
165             Load stash data from disk. Called automatically by first call to L.
166             Can be used to revert current stash data to the state before current execution.
167              
168             =cut
169              
170             sub load {
171             my $self = shift;
172             $self->{'data'} = eval { JSON::Util->decode([ $self->stash_filename ]) } || {};
173             return;
174             }
175              
176             =head2 save
177              
178             Save stash data to disk - F<$HOME/.app-name/stash.json>. Called automatically
179             via DESTROY method when L object is going to be destroyed.
180              
181             Will throw an exception if the file save fails.
182              
183             =cut
184              
185             sub save {
186             my $self = shift;
187              
188             eval { JSON::Util->new->encode($self->data, [ $self->stash_filename ]); };
189             die 'failed to save application stash - '.$@
190             if $@;
191            
192             return;
193             }
194              
195             =head2 DESTROY
196              
197             Calls L and prints warning if it fails.
198              
199             =cut
200              
201             sub DESTROY {
202             my $self = shift;
203              
204             eval { $self->save(); };
205             warn $@ if $@;
206             }
207              
208             sub _clean {
209             my ( $self, $text ) = @_;
210             $text = lc $text;
211             $text =~ s/[^a-z0-9]+/_/g;
212             return $text;
213             }
214              
215             1;
216              
217              
218             __END__