File Coverage

blib/lib/SPOPS/Tie.pm
Criterion Covered Total %
statement 57 92 61.9
branch 0 26 0.0
condition 0 11 0.0
subroutine 19 23 82.6
pod n/a
total 76 152 50.0


line stmt bran cond sub pod time code
1             package SPOPS::Tie;
2              
3             # $Id: Tie.pm,v 3.8 2004/06/02 00:48:22 lachoy Exp $
4              
5 19     19   74500 use strict;
  19         49  
  19         1532  
6 19     19   112 use base qw( Exporter );
  19         108  
  19         3777  
7 19     19   119 use vars qw( $PREFIX_TEMP $PREFIX_INTERNAL );
  19         58  
  19         1290  
8 19     19   3094 use Data::Dumper qw( Dumper );
  19         13871  
  19         1327  
9 19     19   1767 use Log::Log4perl qw( get_logger );
  19         71313  
  19         193  
10 19     19   3162 use SPOPS::Exception qw( spops_error );
  19         43  
  19         204  
11              
12             @SPOPS::Tie::EXPORT_OK = qw( IDX_DATA IDX_CHANGE IDX_SAVE IDX_INTERNAL IDX_TEMP
13             IDX_CHECK_FIELDS IDX_LAZY_LOADED
14             $PREFIX_TEMP $PREFIX_INTERNAL );
15             $SPOPS::Tie::VERSION = sprintf("%d.%02d", q$Revision: 3.8 $ =~ /(\d+)\.(\d+)/);
16              
17 19     19   3933 use constant IDX_DATA => '_dat';
  19         56  
  19         1553  
18 19     19   105 use constant IDX_CHANGE => '_chg';
  19         39  
  19         926  
19 19     19   105 use constant IDX_SAVE => '_svd';
  19         45  
  19         855  
20 19     19   106 use constant IDX_INTERNAL => '_int';
  19         779  
  19         945  
21 19     19   102 use constant IDX_TEMP => '_tmp';
  19         66  
  19         1040  
22 19     19   108 use constant IDX_IS_LAZY_LOAD => '_ill';
  19         39  
  19         894  
23 19     19   98 use constant IDX_LAZY_LOADED => '_ll';
  19         39  
  19         1107  
24 19     19   100 use constant IDX_LAZY_LOAD_SUB => '_lls';
  19         82  
  19         933  
25 19     19   103 use constant IDX_CHECK_FIELDS => '_chk';
  19         50  
  19         876  
26 19     19   101 use constant IDX_IS_MULTIVALUE => '_imv';
  19         38  
  19         819  
27 19     19   271 use constant IDX_MULTIVALUE => '_mv';
  19         34  
  19         851  
28 19     19   92 use constant IDX_IS_FIELD_MAP => '_ifm';
  19         36  
  19         854  
29 19     19   95 use constant IDX_FIELD_MAP => '_fm';
  19         38  
  19         75795  
30              
31             my $log = get_logger();
32              
33             $PREFIX_TEMP = 'tmp_';
34             $PREFIX_INTERNAL = '_internal';
35              
36             # Tie interface stuff below here; see 'perldoc perltie' for what
37             # each method does. (Or better yet, read Damian Conway's discussion
38             # of tie in 'Object Oriented Perl'.)
39              
40              
41             # First activate the callback for the field check, then return the
42             # object. The object always keeps track of the actual properties, the
43             # class, whether the object's properties have been changed and keeps
44             # any temporary data that lives only for the object's lifetime.
45              
46             sub TIEHASH {
47 0     0     my ( $class, $base_class, $p ) = @_;
48 0   0       $p ||= {};
49              
50             # See if we're supposed to do any field checking
51              
52 0           my $HAS_FIELD = $class->_field_check( $base_class, $p );
53              
54             # Be able to deal with either an arrayref or a hashref of multivalue fields
55              
56 0 0         if ( ref $p->{multivalue} eq 'HASH' ) {
57 0           $p->{multivalue} = { map { lc $_ => lc $p->{multivalue}{ $_ } } keys %{ $p->{multivalue} } };
  0            
  0            
58             }
59              
60 0 0         if ( ref $p->{multivalue} eq 'ARRAY' ) {
61 0           $p->{multivalue} = { map { lc $_ => 1 } @{ $p->{multivalue} } };
  0            
  0            
62             }
63              
64             # Be sure all field map fields are lower-cased
65 0 0         if ( ref $p->{field_map} eq 'HASH' ) {
66 0           $p->{field_map} = { map { lc $_ => lc $p->{field_map}{ $_ } } keys %{ $p->{field_map} } };
  0            
  0            
67             }
68              
69 0           return bless ({ class => $base_class,
70             IDX_TEMP() => {},
71             IDX_INTERNAL() => {},
72             IDX_CHANGE() => 0,
73             IDX_SAVE() => 0,
74             IDX_DATA() => {},
75             IDX_IS_LAZY_LOAD() => $p->{is_lazy_load},
76             IDX_LAZY_LOADED() => {},
77             IDX_LAZY_LOAD_SUB()=> $p->{lazy_load_sub},
78             IDX_IS_MULTIVALUE()=> ( ref $p->{multivalue} eq 'HASH' ),
79             IDX_MULTIVALUE() => $p->{multivalue},
80             IDX_IS_FIELD_MAP() => ( ref $p->{field_map} eq 'HASH' ),
81             IDX_FIELD_MAP() => $p->{field_map},
82             IDX_CHECK_FIELDS() => $HAS_FIELD }, $class );
83             }
84              
85 0     0     sub _field_check { return undef; }
86              
87             # Just go through each of the possible things that could be
88             # set and do the appropriate action.
89              
90             sub FETCH {
91 0     0     my ( $self, $key ) = @_;
92 0 0         return unless ( $key );
93 0           my $cmp_key = lc $key;
94 0 0         $log->is_debug &&
95             $log->debug( " tie: Trying to retrieve value for ($key)" );
96 0 0         return $self->{ IDX_CHANGE() } if ( $key eq IDX_CHANGE );
97 0 0         return $self->{ IDX_SAVE() } if ( $key eq IDX_SAVE );
98 0 0         return $self->{ IDX_TEMP() }{ $cmp_key } if ( $key =~ /^$PREFIX_TEMP/ );
99 0 0         return $self->{ IDX_INTERNAL() }{ $cmp_key } if ( $key =~ /^$PREFIX_INTERNAL/ );
100 0 0         return undef unless ( $self->_can_fetch( $key ) );
101 0 0 0       if ( $self->{ IDX_IS_FIELD_MAP() } and
102             $self->{ IDX_FIELD_MAP() }{ $cmp_key } ) {
103             #warn "(FETCH) using field map: old value ($cmp_key) new ($self->{ IDX_FIELD_MAP() }{ $cmp_key })";
104 0           $cmp_key = $self->{ IDX_FIELD_MAP() }{ $cmp_key };
105             }
106 0 0 0       if ( $self->{ IDX_IS_LAZY_LOAD() } and
107             ! $self->{ IDX_LAZY_LOADED() }{ $cmp_key } ) {
108 0           $self->_lazy_load( $key );
109             }
110 0 0 0       if ( $self->{ IDX_IS_MULTIVALUE() } and $self->{ IDX_MULTIVALUE() }{ $cmp_key } ) {
111             #warn "(FETCH) using multivalue for key $cmp_key";
112 0           return [ keys %{ $self->{ IDX_DATA() }{ $cmp_key } } ];
  0            
113             }
114 0           return $self->{ IDX_DATA() }{ $cmp_key };
115             }
116              
117              
118 0     0     sub _can_fetch { return 1 }
119              
120              
121             sub _lazy_load {
122             my ( $self, $key ) = @_;
123             my $cmp_key = lc $key;
124             unless ( ref $self->{ IDX_LAZY_LOAD_SUB() } eq 'CODE' ) {
125             spops_error "Lazy loading activated but no load function specified!";
126             }
127             $log->is_info &&
128             $log->info( "Lazy loading [$key]; is-loaded marker empty" );
129             $self->{ IDX_DATA() }{ $cmp_key } =
130             $self->{ IDX_LAZY_LOAD_SUB() }->( $self->{class},
131             $self->{ IDX_DATA() },
132             $key );
133             $self->{ IDX_LAZY_LOADED() }{ $cmp_key }++;
134             }
135              
136              
137             # Similar to FETCH
138              
139             sub STORE {
140             my ( $self, $key, $value ) = @_;
141             my $cmp_key = lc $key;
142             $log->is_debug &&
143             $log->debug( " tie: Storing [$key] => [", ( defined $value ) ? $value : 'undef', "]" );
144             return $self->{ IDX_CHANGE() } = $value if ( $key eq IDX_CHANGE );
145             return $self->{ IDX_SAVE() } = $value if ( $key eq IDX_SAVE );
146             return $self->{ IDX_TEMP() }{ $cmp_key } = $value if ( $key =~ /^$PREFIX_TEMP/ );
147             return $self->{ IDX_INTERNAL() }{ $cmp_key } = $value if ( $key =~ /^$PREFIX_INTERNAL/ );
148             return undef unless ( $self->_can_store( $key, $value ) );
149             $self->{ IDX_CHANGE() }++;
150              
151             if ( $self->{ IDX_IS_FIELD_MAP() } and
152             $self->{ IDX_FIELD_MAP() }{ $cmp_key } ) {
153             #warn "(STORE) using field map: old value ($cmp_key) new ($self->{ IDX_FIELD_MAP() }{ $cmp_key })";
154             $cmp_key = $self->{ IDX_FIELD_MAP() }{ $cmp_key };
155             }
156              
157             # Non-multivalue properties just return the newly stored value
158              
159             unless ( $self->{ IDX_IS_MULTIVALUE() } and $self->{ IDX_MULTIVALUE() }{ $cmp_key } ) {
160             $self->{ IDX_IS_LAZY_LOAD() } && $self->{ IDX_LAZY_LOADED() }{ $cmp_key }++;
161             return $self->{ IDX_DATA() }{ $cmp_key } = $value;
162             }
163              
164             #warn "(STORE) using multivalue for key $cmp_key";
165              
166             # If we're using multiple values we need to see what type of
167             # $value we've got
168              
169             # If $value is undef, we clear out all values in the object
170              
171             unless ( defined $value ) {
172             $self->{ IDX_DATA() }{ $cmp_key } = {};
173             return undef;
174             }
175              
176             my $typeof = ref $value;
177              
178             # If a scalar, just set it
179              
180             unless ( $typeof ) {
181             $self->{ IDX_DATA() }{ $cmp_key }{ $value } = 1;
182             return $value;
183             }
184              
185             # If array, set it (if the array is empty, then we're
186             # resetting the values)
187              
188             if ( $typeof eq 'ARRAY' ) {
189             #warn "(STORE) Current value of ($cmp_key)", Dumper( $self->{ IDX_DATA() }{ $cmp_key } ), "";
190             $self->{ IDX_DATA() }{ $cmp_key } = { map { $_ => 1 } @{ $value } };
191             #warn "(STORE) Value after set of ($cmp_key)", Dumper( $self->{ IDX_DATA() }{ $cmp_key } ), "";
192             return undef;
193             }
194              
195             # If hash, go through each of the potential options and
196             # perform the action; everything else is ignored
197              
198             if ( $typeof eq 'HASH' ) {
199             my $remove_fields = ( ref $value->{remove} eq 'ARRAY' ) ? $value->{remove} : [ $value->{remove} ];
200             foreach my $rmv ( @{ $remove_fields } ) {
201             next unless ( $rmv );
202             delete $self->{ IDX_DATA() }{ $cmp_key }{ $rmv };
203             }
204              
205             my $modify_fields = $value->{modify} || {};
206             foreach my $mdfy ( keys %{ $modify_fields } ) {
207             delete $self->{ IDX_DATA() }{ $cmp_key }{ $mdfy };
208             $self->{ IDX_DATA() }{ $cmp_key }{ $modify_fields->{ $mdfy } } = 1
209             }
210             return undef;
211             }
212              
213             # We don't know how to handle anything else
214              
215             spops_error "Cannot handle a value type of [$typeof] with multivalues";
216             }
217              
218             sub _can_store { return 1 }
219              
220              
221             # For EXISTS and DELETE, We can only do these actions on the actual
222             # data; use the object methods for the other information.
223              
224             sub EXISTS {
225             my ( $self, $key ) = @_;
226             $log->is_debug &&
227             $log->debug( " tie: Checking for existence of ($key)" );
228             return exists $self->{ IDX_DATA() }{ lc $key };
229             $log->error( "Field '$key' is not valid, cannot check existence" );
230             }
231              
232              
233             sub DELETE {
234             my ( $self, $key ) = @_;
235             $log->is_debug &&
236             $log->debug( " tie: Clearing value for ($key)" );
237             delete $self->{ IDX_DATA() }{ lc $key };
238             $self->{ IDX_CHANGE() }++;
239             }
240              
241              
242             # We've disabled the ability to do: $object = {} or %{ $object } = ();
243             # nothing bad happens, it's just a no-op
244              
245             sub CLEAR {
246             my ( $self ) = @_;
247             $log->error( "Trying to clear object through hash means failed; use object interface" );
248             }
249              
250              
251             # Note that you only see the data when you cycle through the keys
252             # or even do a Data::Dumper::Dumper( $object ); you do not see
253             # the meta-data being tracked. This is a feature.
254              
255             sub FIRSTKEY {
256             my ( $self ) = @_;
257             $log->is_debug &&
258             $log->debug( " tie: Finding first key in data object" );
259             keys %{ $self->{ IDX_DATA() } };
260             my $first_key = each %{ $self->{ IDX_DATA() } };
261             return undef unless defined $first_key;
262             return $first_key;
263             }
264              
265              
266             sub NEXTKEY {
267             my ( $self ) = @_;
268             $log->is_debug &&
269             $log->debug( " tie: Finding next key in data object" );
270             my $next_key = each %{ $self->{ IDX_DATA() } };
271             return undef unless defined $next_key;
272             return $next_key;
273             }
274              
275             1;
276              
277             __END__