File Coverage

blib/lib/Data/Tie/Watch.pm
Criterion Covered Total %
statement 151 175 86.2
branch 37 54 68.5
condition 11 16 68.7
subroutine 52 66 78.7
pod 4 4 100.0
total 255 315 80.9


line stmt bran cond sub pod time code
1             package Data::Tie::Watch;
2              
3             =head1 NAME
4              
5             Data::Tie::Watch - place watchpoints on Perl variables.
6              
7             =cut
8              
9 3     3   237052 use 5.006;
  3         13  
10 3     3   28 use strict;
  3         12  
  3         103  
11 3     3   31 use warnings;
  3         8  
  3         206  
12 3     3   19 use Carp;
  3         8  
  3         329  
13 3     3   21 use Scalar::Util qw( reftype weaken );
  3         7  
  3         6152  
14              
15             our $VERSION = '1.302.2';
16             our %METHODS;
17              
18             =head1 SYNOPSIS
19              
20             use Data::Tie::Watch;
21              
22             $watch = Data::Tie::Watch->new(
23             -variable => \$frog,
24             -shadow => 0,
25             -fetch => [\&fetch, 'arg1', 'arg2', ..., 'argn'],
26             -store => \&store,
27             -destroy => sub {print "Final value=$frog.\n"},
28             }
29             $val = $watch->Fetch;
30             $watch->Store('Hello');
31             $watch->Unwatch;
32              
33             =cut
34              
35             =head1 DESCRIPTION
36              
37             Note: This is a copy of Tk's Tie::Watch.
38             Copied to avoid the Tk depedency.
39              
40             This class module binds one or more subroutines of your devising to a
41             Perl variable. All variables can have B, B and
42             B callbacks. Additionally, arrays can define B,
43             B, B, B, B, B, B,
44             B, B, B and B callbacks, and hashes
45             can define B, B, B, B and B
46             callbacks. If these term are unfamiliar to you, I I suggest
47             you read L.
48              
49             With Data::Tie::Watch you can:
50              
51             . alter a variable's value
52             . prevent a variable's value from being changed
53             . invoke a Perl/Tk callback when a variable changes
54             . trace references to a variable
55              
56             Callback format is patterned after the Perl/Tk scheme: supply either a
57             code reference, or, supply an array reference and pass the callback
58             code reference in the first element of the array, followed by callback
59             arguments. (See examples in the Synopsis, above.)
60              
61             Tie::Watch provides default callbacks for any that you fail to
62             specify. Other than negatively impacting performance, they perform
63             the standard action that you'd expect, so the variable behaves
64             "normally". Once you override a default callback, perhaps to insert
65             debug code like print statements, your callback normally finishes by
66             calling the underlying (overridden) method. But you don't have to!
67              
68             To map a tied method name to a default callback name simply lowercase
69             the tied method name and uppercase its first character. So FETCH
70             becomes Fetch, NEXTKEY becomes Nextkey, etcetera.
71              
72             =cut
73              
74             =head1 SUBROUTINES/METHODS
75              
76             $watch->Fetch(); $watch->Fetch($key);
77              
78             Returns a variable's current value. $key is required for an array or
79             hash.
80              
81             $watch->Store($new_val); $watch->Store($key, $new_val);
82              
83             Store a variable's new value. $key is required for an array or hash.
84              
85             =cut
86              
87             =head2 new
88              
89             Watch constructor.
90              
91             The *real* constructor is Data::Tie::Watch->base_watch(),
92             invoked by methods in other Watch packages, depending upon the variable's
93             type. Here we supply defaulted parameter values and then verify them,
94             normalize all callbacks and bind the variable to the appropriate package.
95              
96             The watchpoint constructor method that accepts option/value pairs to
97             create and configure the Watch object. The only required option is
98             B<-variable>.
99              
100             B<-variable> is a I to a scalar, array or hash variable.
101              
102             B<-shadow> (default 1) is 0 to disable array and hash shadowing. To
103             prevent infinite recursion Data::Tie::Watch maintains parallel variables for
104             arrays and hashes. When the watchpoint is created the parallel shadow
105             variable is initialized with the watched variable's contents, and when
106             the watchpoint is deleted the shadow variable is copied to the original
107             variable. Thus, changes made during the watch process are not lost.
108             Shadowing is on by default. If you disable shadowing any changes made
109             to an array or hash are lost when the watchpoint is deleted.
110              
111             Specify any of the following relevant callback parameters, in the
112             format described above: B<-fetch>, B<-store>, B<-destroy>.
113             Additionally for arrays: B<-clear>, B<-extend>, B<-fetchsize>,
114             B<-pop>, B<-push>, B<-shift>, B<-splice>, B<-storesize> and
115             B<-unshift>. Additionally for hashes: B<-clear>, B<-delete>,
116             B<-exists>, B<-firstkey> and B<-nextkey>.
117              
118             =cut
119              
120             sub new {
121 200     200 1 252225 my $class = shift;
122 200         1185 my %args = (
123             -shadow => 1,
124             -clone => 1, # Clones are also watched.
125             @_,
126             );
127              
128 200 100       930 croak "-variable is required!" if !$args{-variable};
129              
130 199         899 my $methods = $class->_build_methods( %args );
131 199         984 for ( keys %args ) {
132              
133             # Skip -shadow like options.
134 2782   100     7098 my $type = reftype( $args{$_} ) // '';
135 2782 100       6145 next if $type ne "CODE";
136              
137 1509 100       2893 if ( $methods->{$_} ) {
138              
139             # Assign new valide method from arguments.
140 775         1682 $methods->{$_} = delete $args{$_};
141             }
142             else {
143             # Able to pass in more options for
144             # simplicity. Exclude them here.
145 734         1393 delete $args{$_};
146             }
147             }
148              
149 199         1006 my $watch_obj = $class->_build_obj( %args );
150 199         728 $METHODS{ $watch_obj->{id} } = $methods;
151              
152 199         1361 $watch_obj;
153             }
154              
155             sub _build_methods {
156 199     199   946 my ( $class, %args ) = @_;
157 199         366 my $var = $args{-variable};
158 199   50     646 my $type = reftype( $var ) // '';
159 199         296 my %methods;
160              
161 199 100       1352 if ( $type =~ /(SCALAR|REF)/ ) {
    100          
    50          
162 44         219 %methods = (
163             -destroy => \&Data::Tie::Watch::Scalar::Destroy,
164             -fetch => \&Data::Tie::Watch::Scalar::Fetch,
165             -store => \&Data::Tie::Watch::Scalar::Store,
166             );
167             }
168             elsif ( $type =~ /ARRAY/ ) {
169 62         635 %methods = (
170             -clear => \&Data::Tie::Watch::Array::Clear,
171             -delete => \&Data::Tie::Watch::Array::Delete,
172             -destroy => \&Data::Tie::Watch::Array::Destroy,
173             -exists => \&Data::Tie::Watch::Array::Exists,
174             -extend => \&Data::Tie::Watch::Array::Extend,
175             -fetch => \&Data::Tie::Watch::Array::Fetch,
176             -fetchsize => \&Data::Tie::Watch::Array::Fetchsize,
177             -pop => \&Data::Tie::Watch::Array::Pop,
178             -push => \&Data::Tie::Watch::Array::Push,
179             -shift => \&Data::Tie::Watch::Array::Shift,
180             -splice => \&Data::Tie::Watch::Array::Splice,
181             -store => \&Data::Tie::Watch::Array::Store,
182             -storesize => \&Data::Tie::Watch::Array::Storesize,
183             -unshift => \&Data::Tie::Watch::Array::Unshift,
184             );
185             }
186             elsif ( $type =~ /HASH/ ) {
187 93         655 %methods = (
188             -clear => \&Data::Tie::Watch::Hash::Clear,
189             -delete => \&Data::Tie::Watch::Hash::Delete,
190             -destroy => \&Data::Tie::Watch::Hash::Destroy,
191             -exists => \&Data::Tie::Watch::Hash::Exists,
192             -fetch => \&Data::Tie::Watch::Hash::Fetch,
193             -firstkey => \&Data::Tie::Watch::Hash::Firstkey,
194             -nextkey => \&Data::Tie::Watch::Hash::Nextkey,
195             -store => \&Data::Tie::Watch::Hash::Store,
196             );
197             }
198             else {
199 0         0 croak "Data::Tie::Watch::new() - not a variable reference.";
200             }
201              
202 199         887 \%methods;
203             }
204              
205             sub _build_obj {
206 199     199   826 my ( $class, %args ) = @_;
207 199         427 my $var = $args{-variable};
208 199   50     482 my $type = reftype( $var ) // '';
209 199         343 my $watch_obj;
210              
211 199 100       1470 if ( $type =~ /(SCALAR|REF)/ ) {
    100          
    50          
212 44         375 $watch_obj = tie $$var, 'Data::Tie::Watch::Scalar', %args;
213             }
214             elsif ( $type =~ /ARRAY/ ) {
215 62         460 $watch_obj = tie @$var, 'Data::Tie::Watch::Array', %args;
216             }
217             elsif ( $type =~ /HASH/ ) {
218 93         634 $watch_obj = tie %$var, 'Data::Tie::Watch::Hash', %args;
219             }
220              
221 199         806 $watch_obj->{id} = "$watch_obj";
222 199         428 $watch_obj->{type} = $type;
223              
224             # weaken $watch_obj->{-variable};
225              
226 199         699 $watch_obj;
227             }
228              
229             =head2 DESTROY
230              
231             Clean up global cache.
232              
233             Note: Originally the 'Unwatch()' method call was placed at just before the
234             return of 'callback()' which appeared to be the logical place for it.
235             However it would occasionally provoke a segmentation fault (possibly
236             indirectly).
237              
238             =cut
239              
240             sub DESTROY {
241 199     199   654 $_[0]->callback( '-destroy' );
242 199         683 $_[0]->Unwatch();
243 199         2828 delete $METHODS{"$_[0]"};
244             }
245              
246             =head2 Unwatch
247              
248             Stop watching a variable by releasing the last
249             reference and untieing it.
250              
251             Updates the original variable with its shadow,
252             if appropriate.
253              
254             =cut
255              
256             sub Unwatch {
257 398     398 1 125298 my $var = $_[0]->{-variable};
258 398 100       1030 return if not $var;
259              
260 199   50     716 my $type = reftype( $var ) // '';
261 199 50       455 return if not $type;
262              
263 199         327 my $copy;
264 199 100       1165 $copy = $_[0]->{-ptr} if $type !~ /(SCALAR|REF)/;
265 199         404 my $shadow = $_[0]->{-shadow};
266 199         350 undef $_[0];
267              
268 199 100       1116 if ( $type =~ /(SCALAR|REF)/ ) {
    100          
    50          
269 44         243 untie $$var;
270             }
271             elsif ( $type =~ /ARRAY/ ) {
272 62         295 untie @$var;
273 62 100 66     536 @$var = @$copy if $shadow && $copy;
274             }
275             elsif ( $type =~ /HASH/ ) {
276 93         375 untie %$var;
277 93 50       761 %$var = %$copy if $shadow;
278             }
279             else {
280 0         0 croak "not a variable reference.";
281             }
282             }
283              
284             =head2 base_watch
285              
286             Watch base class constructor invoked by other Watch modules.
287              
288             =cut
289              
290             sub base_watch {
291 199     199 1 849 my ( $class, %args ) = @_;
292 199         1472 +{%args};
293             }
294              
295             =head2 callback
296              
297             Execute a Watch callback, either the default or user specified.
298             Note that the arguments are those supplied by the tied method,
299             not those (if any) specified by the user when the watch object
300             was instantiated. This is for performance reasons.
301              
302             $_[0] = self
303             $_[1] = callback type
304             $_[2] through $#_ = tied arguments
305              
306             =cut
307              
308             sub callback {
309 1254     1254 1 3010 my ( $watch_obj, $mkey, @args ) = @_;
310             my $id =
311             $watch_obj->{-clone}
312             ? $watch_obj->{id}
313 1254 100       3352 : "$watch_obj";
314              
315 1254 50 66     5904 if ( $METHODS{$id} && $METHODS{$id}{$mkey} ) {
316 1172         3068 return $METHODS{$id}{$mkey}->( $watch_obj, @args );
317             }
318              
319 82         704 my $method_name = $mkey =~ s/^-(\w+)/\L\u$1/r;
320 82         249 my $method = sprintf( "Data::Tie::Watch::%s::%s",
321             "\L\u$watch_obj->{type}\E", $method_name );
322              
323             # Should also finish its current action.
324 82         114 my @return;
325             {
326 3     3   28 no strict 'refs';
  3         7  
  3         384  
  82         117  
327 82         337 @return = $method->( $watch_obj, @args );
328             }
329              
330 82 50       163 return @return if wantarray;
331 82         298 return $return[0];
332             }
333              
334             ###############################################################################
335              
336             package # temporarily disabled from PAUSE indexer because of permission problems
337             Data::Tie::Watch::Scalar;
338              
339 3     3   21 use Carp;
  3         5  
  3         940  
340             our @ISA = qw( Data::Tie::Watch );
341              
342             sub TIESCALAR {
343 44     44   221 my ( $class, %args ) = @_;
344 44         93 my $variable = $args{-variable};
345 44         166 my $watch_obj = Data::Tie::Watch->base_watch( %args );
346              
347 44         133 $watch_obj->{-value} = $$variable;
348              
349 44         208 bless $watch_obj, $class;
350             }
351              
352             # Default scalar callbacks.
353              
354 44     44   88 sub Destroy { undef %{ $_[0] } }
  44         267  
355 71     71   535 sub Fetch { $_[0]->{-value} }
356 37     37   253 sub Store { $_[0]->{-value} = $_[1] }
357              
358             # Scalar access methods.
359              
360 75     75   73812 sub FETCH { $_[0]->callback( '-fetch' ) }
361 37     37   42256 sub STORE { $_[0]->callback( '-store', $_[1] ) }
362              
363             ###############################################################################
364              
365             package # temporarily disabled from PAUSE indexer because of permission problems
366             Data::Tie::Watch::Array;
367              
368 3     3   27 use Carp;
  3         5  
  3         3548  
369             our @ISA = qw( Data::Tie::Watch );
370              
371             sub TIEARRAY {
372 62     62   277 my ( $class, %args ) = @_;
373 62         176 my ( $variable, $shadow ) = @args{ -variable, -shadow };
374 62         104 my @copy;
375 62 50       256 @copy = @$variable if $shadow; # make a private copy of user's array
376 62 50       211 $args{-ptr} = $shadow ? \@copy : [];
377 62         234 my $watch_obj = Data::Tie::Watch->base_watch( %args );
378              
379 62         359 bless $watch_obj, $class;
380             }
381              
382             # Default array callbacks.
383              
384 1     1   15 sub Clear { $_[0]->{-ptr} = () }
385 0     0   0 sub Delete { delete $_[0]->{-ptr}->[ $_[1] ] }
386 62     62   105 sub Destroy { undef %{ $_[0] } }
  62         318  
387 0     0   0 sub Exists { exists $_[0]->{-ptr}->[ $_[1] ] }
388       0     sub Extend { }
389 138     138   658 sub Fetch { $_[0]->{-ptr}->[ $_[1] ] }
390 177   100 177   270 sub Fetchsize { scalar @{ $_[0]->{-ptr} // [] } }
  177         805  
391 1     1   3 sub Pop { pop @{ $_[0]->{-ptr} } }
  1         14  
392 0     0   0 sub Push { push @{ $_[0]->{-ptr} }, @_[ 1 .. $#_ ] }
  0         0  
393 0     0   0 sub Shift { shift @{ $_[0]->{-ptr} } }
  0         0  
394              
395             sub Splice {
396 0     0   0 my $n = scalar @_; # splice() is wierd!
397 0 0       0 return splice @{ $_[0]->{-ptr} }, $_[1] if $n == 2;
  0         0  
398 0 0       0 return splice @{ $_[0]->{-ptr} }, $_[1], $_[2] if $n == 3;
  0         0  
399 0 0       0 return splice @{ $_[0]->{-ptr} }, $_[1], $_[2], @_[ 3 .. $#_ ] if $n >= 4;
  0         0  
400             }
401 27     27   226 sub Store { $_[0]->{-ptr}->[ $_[1] ] = $_[2] }
402 1     1   3 sub Storesize { $#{ $_[0]->{-ptr} } = $_[1] - 1 }
  1         11  
403 0     0   0 sub Unshift { unshift @{ $_[0]->{-ptr} }, @_[ 1 .. $#_ ] }
  0         0  
404              
405             # Array access methods.
406              
407 1     1   4 sub CLEAR { $_[0]->callback( '-clear' ) }
408 0     0   0 sub DELETE { $_[0]->callback( '-delete', $_[1] ) }
409 0     0   0 sub EXISTS { $_[0]->callback( '-exists', $_[1] ) }
410 0     0   0 sub EXTEND { $_[0]->callback( '-extend', $_[1] ) }
411 147     147   17383 sub FETCH { $_[0]->callback( '-fetch', $_[1] ) }
412 177     177   53340 sub FETCHSIZE { $_[0]->callback( '-fetchsize' ) }
413 1     1   1561 sub POP { $_[0]->callback( '-pop' ) }
414 0     0   0 sub PUSH { $_[0]->callback( '-push', @_[ 1 .. $#_ ] ) }
415 0     0   0 sub SHIFT { $_[0]->callback( '-shift' ) }
416 0     0   0 sub SPLICE { $_[0]->callback( '-splice', @_[ 1 .. $#_ ] ) }
417 27     27   23267 sub STORE { $_[0]->callback( '-store', $_[1], $_[2] ) }
418 1     1   1539 sub STORESIZE { $_[0]->callback( '-storesize', $_[1] ) }
419 0     0   0 sub UNSHIFT { $_[0]->callback( '-unshift', @_[ 1 .. $#_ ] ) }
420              
421             ###############################################################################
422              
423             package # temporarily disabled from PAUSE indexer because of permission problems
424             Data::Tie::Watch::Hash;
425              
426 3     3   27 use Carp;
  3         10  
  3         2247  
427             our @ISA = qw( Data::Tie::Watch );
428              
429             sub TIEHASH {
430 93     93   386 my ( $class, %args ) = @_;
431 93         274 my ( $variable, $shadow ) = @args{ -variable, -shadow };
432 93         142 my %copy;
433 93 50       1112 %copy = %$variable if $shadow; # make a private copy of user's hash
434 93 50       333 $args{-ptr} = $shadow ? \%copy : {};
435 93         390 my $watch_obj = Data::Tie::Watch->base_watch( %args );
436              
437 93         558 bless $watch_obj, $class;
438             }
439              
440             # Default hash callbacks.
441              
442 1     1   16 sub Clear { $_[0]->{-ptr} = () }
443 1     1   16 sub Delete { delete $_[0]->{-ptr}->{ $_[1] } }
444 93     93   151 sub Destroy { undef %{ $_[0] } }
  93         3806  
445 136     136   568 sub Exists { exists $_[0]->{-ptr}->{ $_[1] } }
446 170     170   985 sub Fetch { $_[0]->{-ptr}->{ $_[1] } }
447 89     89   156 sub Firstkey { my $c = keys %{ $_[0]->{-ptr} }; each %{ $_[0]->{-ptr} } }
  89         299  
  89         184  
  89         449  
448 139     139   218 sub Nextkey { each %{ $_[0]->{-ptr} } }
  139         625  
449 47     47   403 sub Store { $_[0]->{-ptr}->{ $_[1] } = $_[2] }
450              
451             # Hash access methods.
452              
453 1     1   1451 sub CLEAR { $_[0]->callback( '-clear' ) }
454 1     1   1482 sub DELETE { $_[0]->callback( '-delete', $_[1] ) }
455 136     136   2054 sub EXISTS { $_[0]->callback( '-exists', $_[1] ) }
456 176     176   17036 sub FETCH { $_[0]->callback( '-fetch', $_[1] ) }
457 89     89   58735 sub FIRSTKEY { $_[0]->callback( '-firstkey' ) }
458 139     139   317 sub NEXTKEY { $_[0]->callback( '-nextkey' ) }
459 47     47   23689 sub STORE { $_[0]->callback( '-store', $_[1], $_[2] ) }
460              
461             =head1 EFFICIENCY CONSIDERATIONS
462              
463             If you can live with using the class methods provided, please do so.
464             You can meddle with the object hash directly and improve watch
465             performance, at the risk of your code breaking in the future.
466              
467             =cut
468              
469             =head1 AUTHOR
470              
471             Originally: Stephen O. Lidie
472              
473             Currently: Tim Potapov, C<< >>
474              
475             =head1 COPYRIGHT
476              
477             Copyright (C) 1996 - 2005 Stephen O. Lidie. All rights reserved.
478              
479             This program is free software; you can redistribute it and/or modify it under
480             the same terms as Perl itself.
481              
482             This is free software, licensed under:
483              
484             The Artistic License 2.0 (GPL Compatible)
485              
486             =cut
487              
488             1;