Refactored to _descend to fix the recursion bug
[dbsrgits/DBM-Deep.git] / lib / DBM / Deep / Engine.pm
index 094f51c..5d9b034 100644 (file)
@@ -5,6 +5,8 @@ use 5.006_000;
 use strict;
 use warnings FATAL => 'all';
 
+our $VERSION = $DBM::Deep::VERSION;
+
 use DBM::Deep::Iterator ();
 
 # File-wide notes:
@@ -13,14 +15,17 @@ use DBM::Deep::Iterator ();
 #   mutex. But, it's the caller's responsability to make sure that this has
 #   been done.
 
+sub SIG_HASH     () { 'H' }
+sub SIG_ARRAY    () { 'A' }
+
 =head1 NAME
 
 DBM::Deep::Engine
 
 =head1 PURPOSE
 
-This is an internal-use-only object for L<DBM::Deep/>. It mediates the low-level
-mapping between the L<DBM::Deep/> objects and the storage medium.
+This is an internal-use-only object for L<DBM::Deep>. It mediates the low-level
+mapping between the L<DBM::Deep> objects and the storage medium.
 
 The purpose of this documentation is to provide low-level documentation for
 developers. It is B<not> intended to be used by the general public. This
@@ -163,7 +168,7 @@ sub delete_key { die "delete_key must be implemented in a child class" }
 =head2 write_value( $obj, $key, $value )
 
 This takes an object that provides _base_offset(), a string for the
-key, and a value. This value can be anything storable within L<DBM::Deep/>.
+key, and a value. This value can be anything storable within L<DBM::Deep>.
 
 This returns 1 upon success.
 
@@ -239,8 +244,8 @@ sub get_next_key {
 
     # XXX Need to add logic about resetting the iterator if any key in the
     # reference has changed
-    unless ( $prev_key ) {
-        $obj->{iterator} = DBM::Deep::Iterator->new({
+    unless ( defined $prev_key ) {
+        $obj->{iterator} = $self->iterator_class->new({
             base_offset => $obj->_base_offset,
             engine      => $self,
         });
@@ -334,6 +339,30 @@ defined sector type.
 
 sub load_sector { $_[0]->sector_type->load( @_ ) }
 
+=head2 cache / clear_cache
+
+This is the cache of loaded Reference sectors.
+
+=cut
+
+sub cache       { $_[0]{cache} ||= {} }
+sub clear_cache { %{$_[0]->cache} = () }
+
+=head2 supports( $option )
+
+This returns a boolean depending on if this instance of DBM::Dep supports
+that feature. C<$option> can be one of:
+
+=over 4
+
+=item * transactions
+
+=back
+
+=cut
+
+sub supports { die "supports must be implemented in a child class" }
+
 =head2 ACCESSORS
 
 The following are readonly attributes.
@@ -342,6 +371,8 @@ The following are readonly attributes.
 
 =item * storage
 
+=item * sector_type
+
 =back
 
 =cut
@@ -350,5 +381,40 @@ sub storage { $_[0]{storage} }
 
 sub sector_type { die "sector_type must be implemented in a child class" }
 
+# This code is to make sure we write all the values in the $value to the
+# disk and to make sure all changes to $value after the assignment are
+# reflected on disk. This may be counter-intuitive at first, but it is
+# correct dwimmery.
+#   NOTE - simply tying $value won't perform a STORE on each value. Hence,
+# the copy to a temp value.
+sub _descend {
+    my $self = shift;
+    my ($value, $value_sector) = @_;
+    my $r = Scalar::Util::reftype( $value ) || '';
+
+    if ( $r eq 'ARRAY' ) {
+        my @temp = @$value;
+        tie @$value, 'DBM::Deep', {
+            base_offset => $value_sector->offset,
+            staleness   => $value_sector->staleness,
+            storage     => $self->storage,
+            engine      => $self,
+        };
+        @$value = @temp;
+        bless $value, 'DBM::Deep::Array' unless Scalar::Util::blessed( $value );
+    }
+    elsif ( $r eq 'HASH' ) {
+        my %temp = %$value;
+        tie %$value, 'DBM::Deep', {
+            base_offset => $value_sector->offset,
+            staleness   => $value_sector->staleness,
+            storage     => $self->storage,
+            engine      => $self,
+        };
+        %$value = %temp;
+        bless $value, 'DBM::Deep::Hash' unless Scalar::Util::blessed( $value );
+    }
+}
+
 1;
 __END__