File Coverage

lib/Bot/BasicBot/Pluggable/Store.pm
Criterion Covered Total %
statement 79 101 78.2
branch 20 28 71.4
condition 9 13 69.2
subroutine 21 25 84.0
pod 12 13 92.3
total 141 180 78.3


line stmt bran cond sub pod time code
1             package Bot::BasicBot::Pluggable::Store;
2             $Bot::BasicBot::Pluggable::Store::VERSION = '1.11';
3 15     15   48508 use strict;
  15         19  
  15         338  
4 15     15   47 use warnings;
  15         16  
  15         333  
5 15     15   51 use Carp qw( croak );
  15         17  
  15         618  
6 15     15   2042 use Data::Dumper;
  15         19911  
  15         691  
7 15     15   7184 use Storable qw( nfreeze thaw );
  15         31417  
  15         845  
8 15     15   4504 use Try::Tiny;
  15         16603  
  15         781  
9 15     15   6520 use Module::Load qw();
  15         11506  
  15         240  
10 15     15   3154 use Log::Log4perl;
  15         167676  
  15         120  
11              
12 15     15   592 use base qw( );
  15         20  
  15         11963  
13              
14             sub new {
15 23     23 1 174 my $class = shift;
16 23         25 my $self;
17 23         94 my $logger = Log::Log4perl->get_logger($class);
18 23 100 66     4767 if ( @_ % 2 == 0 ) {
    100 33        
    50          
    0          
19 16         42 $self = bless {@_} => $class;
20             }
21             elsif ( @_ == 1 and ref $_[0] eq 'HASH' ) {
22 1         4 $self = $class->new_from_hashref( $_[0] );
23             }
24             elsif ( @_ == 1 and !ref $_[0] ) {
25 6         31 $self = $class->new_from_hashref( { type => $_[0] } );
26             }
27             elsif ( !@_ ) {
28 0         0 $self = bless {} => $class;
29             }
30             else {
31 0         0 $logger->warn(
32             "Argument to new() is neither an argument list, a hashref, a string nor empty"
33             );
34             }
35 23         97 $self->init();
36 23         18990 $self->load();
37 23         83 return $self;
38             }
39              
40             sub new_from_hashref {
41 10     10 1 20 my ( $class, $args ) = @_;
42 10         52 my $logger = Log::Log4perl->get_logger($class);
43              
44 10 50       935 if ( ref($args) ne 'HASH' ) {
45 0         0 $logger->warn('Argument to store_from_hashref must be a hashref');
46             }
47              
48 10   50     40 my $store_class = delete $args->{type} || 'Memory';
49              
50 10 50       53 $store_class = "Bot::BasicBot::Pluggable::Store::$store_class"
51             unless $store_class =~ /::/;
52              
53             # load the store class
54 10     10   425 try { Module::Load::load $store_class; }
55 10     0   92 catch { $logger->warn("Couldn't load $store_class - $_"); };
  0         0  
56              
57 10         266 my $store = $store_class->new( %{$args} );
  10         63  
58              
59 10 50       53 croak "Couldn't init a $store_class store\n" unless $store;
60              
61 10         26 return $store;
62             }
63              
64 20     20 1 22 sub init { undef }
65              
66 22     22 1 46 sub load { undef }
67              
68       111 1   sub save { }
69              
70             sub keys {
71 17     17 1 92 my ( $self, $namespace, %opts ) = @_;
72 17   100     57 my $mod = $self->{store}{$namespace} || {};
73 17         59 return $self->_keys_aux( $mod, $namespace, %opts );
74             }
75              
76             sub count_keys {
77 0     0 0 0 my ( $self, $namespace, %opts ) = @_;
78 0         0 $opts{_count_only} = 1;
79 0         0 $self->keys( $namespace, %opts );
80             }
81              
82             sub _keys_aux {
83 18     18   24 my ( $self, $mod, $namespace, %opts ) = @_;
84              
85 18 100       43 my @res = ( exists $opts{res} ) ? @{ $opts{res} } : ();
  10         20  
86              
87 18 100       69 return CORE::keys %$mod unless @res;
88              
89 10         11 my @return;
90 10         8 my $count = 0;
91 10         30 OUTER: while ( my ($key) = each %$mod ) {
92 83         852 for my $re (@res) {
93              
94             # limit matches
95 83 100       148 $re = "^" . lc($namespace) . "_.*${re}.*"
96             if $re =~ m!^[^\^].*[^\$]$!;
97 83 100       322 next OUTER unless $key =~ m!$re!;
98             }
99 8 50       24 push @return, $key if ( !$opts{_count_only} );
100 8 100 100     39 last if $opts{limit} && ++$count >= $opts{limit};
101              
102             }
103              
104 10 50       735 return ( $opts{_count_only} ) ? $count : @return;
105             }
106              
107             sub get {
108 1192     1192 1 1110 my ( $self, $namespace, $key ) = @_;
109 1192         3887 return $self->{store}{$namespace}{$key};
110             }
111              
112             sub set {
113 107     107 1 145 my ( $self, $namespace, $key, $value ) = @_;
114 107         154 $self->{store}{$namespace}{$key} = $value;
115 107         185 $self->save($namespace);
116 107         208 return $self;
117             }
118              
119             sub unset {
120 7     7 1 16 my ( $self, $namespace, $key ) = @_;
121 7         15 delete $self->{store}{$namespace}{$key};
122 7         14 $self->save($namespace);
123 7         16 return $self;
124             }
125              
126             sub namespaces {
127 2     2 1 4 my $self = shift;
128 2         2 return CORE::keys( %{ $self->{store} } );
  2         12  
129             }
130              
131             sub dump {
132 0     0 1   my $self = shift;
133 0           my $data = {};
134 0           for my $n ( $self->namespaces ) {
135 0           warn "Dumping namespace '$n'.\n";
136 0           for my $k ( $self->keys($n) ) {
137 0           $data->{$n}{$k} = $self->get( $n, $k );
138             }
139             }
140 0           return nfreeze($data);
141             }
142              
143             sub restore {
144 0     0 1   my ( $self, $dump ) = @_;
145 0           my $data = thaw($dump);
146 0           for my $n ( CORE::keys(%$data) ) {
147 0           warn "Restoring namespace '$n'.\n";
148 0           for my $k ( CORE::keys( %{ $data->{$n} } ) ) {
  0            
149 0           $self->set( $n, $k, $data->{$n}{$k} );
150             }
151             }
152 0           warn "Complete.\n";
153             }
154              
155             1;
156             __END__
157              
158             =head1 NAME
159              
160             Bot::BasicBot::Pluggable::Store - base class for the back-end pluggable store
161              
162             =head1 VERSION
163              
164             version 1.11
165              
166             =head1 SYNOPSIS
167              
168             my $store = Bot::BasicBot::Pluggable::Store->new( option => "value" );
169              
170             my $namespace = "MyModule";
171              
172             for ( $store->keys($namespace) ) {
173             my $value = $store->get($namespace, $_);
174             $store->set( $namespace, $_, "$value and your momma." );
175             }
176              
177             Store classes should subclass this and provide some persistent way of storing things.
178              
179             =head1 METHODS
180              
181             =over 4
182              
183             =item new()
184              
185             Standard C<new> method, blesses a hash into the right class and
186             puts any key/value pairs passed to it into the blessed hash. If
187             called with an hash argument as its first argument, new_from_hashref
188             will be run with the hash as its only argument. See L</new_from_hashref>
189             for the possible keys and values. You can also pass a string and
190             it will try to call new_from_hashref with a hash reference { type
191             => $string }. Calls C<load()> to load any internal variables, then
192             C<init>, which you can also override in your module.
193              
194             =item new_from_hashref( $hashref )
195              
196             Intended to be called as class method to dynamically create a store
197             object. It expects a hash reference as its only argument. The only
198             required hash element is a string specified by I<type>. This should
199             be either a fully qualified classname or a colonless string that
200             is appended to I<Bot::BasicBot::Pluggable::Store>. All other arguments
201             are passed down to the real object constructor.
202              
203             =item init()
204              
205             Called as part of new class construction, before C<load()>.
206              
207             =item load()
208              
209             Called as part of new class construction, after C<init()>.
210              
211             =item save()
212              
213             Subclass me. But, only if you want to. See ...Store::Storable.pm as an example.
214              
215             =item keys($namespace,[$regex])
216              
217             Returns a list of all store keys for the passed C<$namespace>.
218              
219             If you pass C<$regex> then it will only pass the keys matching C<$regex>
220              
221             =item get($namespace, $variable)
222              
223             Returns the stored value of the C<$variable> from C<$namespace>.
224              
225             =item set($namespace, $variable, $value)
226              
227             Sets stored value for C<$variable> to C<$value> in C<$namespace>. Returns store object.
228              
229             =item unset($namespace, $variable)
230              
231             Removes the C<$variable> from the store. Returns store object.
232              
233             =item namespaces()
234              
235             Returns a list of all namespaces in the store.
236              
237             =item dump()
238              
239             Dumps the complete store to a huge Storable scalar. This is mostly so
240             you can convert from one store to another easily, i.e.:
241              
242             my $from = Bot::BasicBot::Pluggable::Store::Storable->new();
243             my $to = Bot::BasicBot::Pluggable::Store::DBI->new( ... );
244             $to->restore( $from->dump );
245              
246             C<dump> is written generally so you don't have to re-implement it in subclasses.
247              
248             =item restore($data)
249              
250             Restores the store from a L<dump()>.
251              
252             =back
253              
254             =head1 AUTHOR
255              
256             Mario Domgoergen <mdom@cpan.org>
257              
258             This program is free software; you can redistribute it
259             and/or modify it under the same terms as Perl itself.
260              
261             =head1 SEE ALSO
262              
263             L<Bot::BasicBot::Pluggable>
264              
265             L<Bot::BasicBot::Pluggable::Module>