File Coverage

blib/lib/Pixie/Store.pm
Criterion Covered Total %
statement 105 106 99.0
branch 14 16 87.5
condition 2 2 100.0
subroutine 37 38 97.3
pod 9 25 36.0
total 167 187 89.3


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Pixie::Store -- Factory & abstract base class for Pixie stores
4              
5             =head1 SYNOPSIS
6              
7             # you should never have to use this class directly!
8              
9             use Pixie;
10             Pixie->deploy( $dsn, %args );
11             my $px = Pixie->connect( $dsn, %args );
12              
13             =head1 DESCRIPTION
14              
15             Pixie::Store provides Pixie with an abstract interface to the physical storage
16             used to actually store the objects that Pixie manages. It is not a 'public'
17             class; most Pixie users will never have to touch it.
18              
19             However, if you want to add another storage medium to Pixie, start here. (If
20             you want to add specific methods for storing in a particular RDBMS, you should
21             take a look at L before diving into L and its
22             woefully underdocumented friends.
23              
24             =cut
25              
26             package Pixie::Store;
27              
28 24     24   66068 use strict;
  24         46  
  24         982  
29 24     24   129 use warnings;
  24         46  
  24         909  
30              
31 24     24   126 use Carp qw( confess );
  24         47  
  24         1308  
32 24     24   128 use Scalar::Util qw( weaken );
  24         47  
  24         1682  
33              
34 24     24   12844 use Pixie::Name;
  24         60  
  24         816  
35 24     24   13191 use Pixie::ObjectGraph;
  24         68  
  24         794  
36 24     24   3326 use Pixie::FinalMethods;
  24         54  
  24         659  
37              
38 24     24   132 use base qw( Pixie::Object );
  24         47  
  24         33401  
39              
40             our $VERSION = "2.08_02";
41             our %typemap = ( memory => 'Pixie::Store::Memory',
42             bdb => 'Pixie::Store::BerkeleyDB',
43             dbi => 'Pixie::Store::DBI', );
44              
45             #use overload
46             # '""' => 'as_string';
47              
48             #------------------------------------------------------------------------------
49             # Class methods
50              
51             sub deploy {
52 6     6 1 1161 my $class = shift;
53 6 100       220 return $class->subclass_responsibility( @_ ) if ref( $class );
54 5         26 return $class->_deploy( @_ );
55             }
56              
57             sub connect {
58 41     41 1 1510 my $class = shift;
59 41 100       259 return $class->subclass_responsibility( @_ ) if ref( $class );
60 40         262 return $class->_connect( @_ );
61             }
62              
63             sub _deploy {
64 5     5   13 my $class = shift;
65 5         10 my $spec = shift;
66              
67 5         207 my ($type, $path) = $class->get_type_and_path( $spec );
68 5         19 my $type_class = $class->load_store_type( $type );
69              
70 5         24 return $type_class->deploy( $path, @_ );
71             }
72              
73             sub _connect {
74 40     40   76 my $class = shift;
75 40         73 my $spec = shift;
76              
77 40         152 my ($type, $path) = $class->get_type_and_path( $spec );
78 40         150 my $type_class = $class->load_store_type( $type );
79              
80 40         447 my $self = $type_class->connect( $path, @_ );
81 40         113 $self->{spec} = $spec; # TODO: use accessor for this
82              
83 40         236 return $self;
84             }
85              
86             sub get_type_and_path {
87 46     46 0 87 my $class = shift;
88 46         778 my $spec = shift;
89 46         207 return split(':', $spec, 2);
90             }
91              
92             sub load_store_type {
93 48     48 0 2992 my $class = shift;
94 48         84 my $type = shift;
95              
96 48 100       235 confess( "Invalid store type: '$type'" ) unless exists $typemap{$type};
97              
98 47         3908 eval "require " . $typemap{$type};
99 47 100       382 confess( "Error loading $typemap{$type}: $@" ) if $@;
100              
101 46         160 return $typemap{$type};
102             }
103              
104             sub as_string {
105 4     4 0 34 my $class = shift;
106 4         11 my $str = '';
107              
108 4 100       19 if (ref $class) {
109 3         10 $str .= ref($class);
110 3 50       24 $str .= ": $class->{spec}" if $class->{spec};
111             } else {
112 1         3 $str .= $class;
113             }
114              
115 4         68 return $str;
116             }
117              
118              
119             #------------------------------------------------------------------------------
120             # Instance methods
121              
122 1     1 1 891 sub clear { $_[0]->subclass_responsibility(@_) }
123 1     1 1 789 sub store_at { $_[0]->subclass_responsibility(@_) }
124 1     1 1 780 sub get_object_at { $_[0]->subclass_responsibility(@_) }
125 1     1 1 843 sub delete { $_[0]->subclass_responsibility(@_) } # TODO: not used?
126 0     0   0 sub _delete { $_[0]->subclass_responsibility(@_) }
127 1     1 0 1289 sub remove { $_[0]->subclass_responsibility(@_) } # TODO: not used?
128 1     1 0 943 sub rootset { $_[0]->subclass_responsibility(@_) }
129 1     1   814 sub _add_to_rootset { $_[0]->subclass_responsibility(@_) }
130 1     1 0 977 sub remove_from_rootset { $_[0]->subclass_responsibility(@_) }
131 1     1 1 1071 sub lock { $_[0]->subclass_responsibility(@_) }
132 1     1 1 933 sub unlock { $_[0]->subclass_responsibility(@_) }
133 1     1 1 1054 sub rollback { $_[0]->subclass_responsibility(@_) }
134              
135             ## TODO: use a name that's impossible for others to trample over
136             ## best idea is to move this into Pixie::Name->object_graph
137             sub object_graph_for {
138 1     1 0 1543 my $self = shift;
139 1         1 my $pixie = shift;
140              
141 1         14 my $graph = $pixie->get_object_named('PIXIE::Node Graph');
142 1 50       75 unless ($graph) {
143 1         6 $graph = Pixie::ObjectGraph->new;
144 1         9 $pixie->bind_name('PIXIE::Node Graph' => $graph);
145             }
146              
147 1         51 return $graph;
148             }
149              
150             sub remove_from_store {
151 1     1 0 788 my $self = shift;
152 1         2 my $oid = shift;
153              
154 1         12 $self->remove_from_rootset($oid)
155             ->_delete($oid);
156             }
157              
158             # Low level locking
159             # $locker is usually a Pixie
160              
161             sub locked_set {
162 102     102 0 2791 my $self = shift;
163 102   100     809 return $self->{locked_set} ||= {};
164             }
165              
166             sub lock_object_for {
167 6     6 0 199 my $self = shift;
168 6         9 my($oid, $locker) = @_;
169 6         27 $self->locked_set->{ $oid } = $locker->_oid;
170 6         15 return 1;
171             }
172              
173             sub unlock_object_for {
174 30     30 0 132 my $self = shift;
175 30         55 my $oid = shift;
176 30         98 delete $self->locked_set->{$oid};
177             }
178              
179             sub release_all_locks {
180 61     61 0 488 my $self = shift;
181 61         313 my $locked_set = $self->locked_set;
182 3         25 $self->unlock_object_for(@$_)
183 61         247 for map {[$_ => $locked_set->{$_}]}
184             keys %$locked_set;
185 61         1534 return $self;
186             }
187              
188             sub add_to_rootset {
189 2     2 0 41 my $self = shift;
190 2         4 my $thing = shift;
191             # TODO: get the oid out here?
192 2 100       6 $self->_add_to_rootset($thing) unless $self->is_hidden($thing);
193             }
194              
195             ## TODO: use names that are impossible for others to trample over
196             ## best idea is to move this into Pixie::Name
197             sub is_hidden {
198 4     4 0 7 my $self = shift;
199 4         4 my $thing = shift;
200 4         14 $thing->PIXIE::oid =~ /^
201             }
202              
203             sub lock_for_GC {
204 1     1 0 661 my $self = shift;
205 1         3 $self->lock;
206             }
207              
208             sub unlock_after_GC {
209 1     1 0 529 my $self = shift;
210 1         4 $self->unlock;
211             }
212              
213             sub DESTROY {
214 39     39   3250 my $self = shift;
215 39         172 $self->release_all_locks;
216             }
217              
218             1;
219              
220             __END__