Tie-Trace

 view release on metacpan or  search on metacpan

lib/Tie/Trace.pm  view on Meta::CPAN

package Tie::Trace;

use strict;
use warnings;
use PadWalker ();
use Tie::Hash ();
use Tie::Array ();
use Tie::Scalar ();
use Carp ();
use Data::Dumper ();
use base qw/Exporter/;

use constant {
  SCALAR    => 0,
  SCALARREF => 1,
  ARRAYREF  => 2,
  HASHREF   => 4,
  BLESSED   => 8,
  TIED      => 16,
  };

our @EXPORT_OK  = ('watch');
our %EXPORT_TAGS = (all => \@EXPORT_OK);

our %OPTIONS = (debug => 'dumper');
our $QUIET   = 0;

our $AUTOLOAD;

sub AUTOLOAD{
  # proxy to Tie::Std***
  my($self, @args) = @_;
  my($class, $method) = (split /::/, $AUTOLOAD)[2, 3];
  my $sub = \&{'Tie::Std' . $class . '::' . $method};
  defined &$sub ? $sub->($self->{storage}, @args) : return;
}

sub TIEHASH  { Tie::Trace::_tieit({}, @_); }
sub TIEARRAY { Tie::Trace::_tieit([], @_); }
sub TIESCALAR{ my $tmp; Tie::Trace::_tieit(\$tmp, @_); }

sub watch(\[$@%]@){
  my $s  = shift;
  my $s_type = ref $s;
  my $s_ = $s;

  if($s_type eq 'SCALAR'){
    $s_ = $$s;
  }elsif($s_type eq 'ARRAY'){
    $s_ = [ @$s ];
  }elsif($s_type eq 'HASH'){
    $s_ = { %$s };
  }

  Carp::croak("must pass one argument.") unless $s;
  my @options = @_;
  my $var_name;
  eval{
    $var_name = PadWalker::var_name(1, $s);
  };
  my $pkg = defined $var_name ? (caller)[0] : undef;
  my $tied_value = tie $s_type eq 'SCALAR' ? $$s : $s_type eq 'ARRAY' ? @$s : %$s, "Tie::Trace", var => $var_name, pkg => $pkg, @options;
  local $QUIET = 1;

  if($s_type eq 'SCALAR'){
    $$s = $s_;
  }elsif($s_type eq 'ARRAY'){
    @$s = @$s_ if @$s_;
  }elsif($s_type eq 'HASH'){
    %$s = %$s_ if %$s_;
  }
  return $tied_value;
}

sub _dumper{
  my($self, $value) = @_;
  local $Data::Dumper::Terse   = 1;
  local $Data::Dumper::Indent  = 0;
  local $Data::Dumper::Deparse = 1;
  $value = Data::Dumper::Dumper($value);
}

sub storage{
  my($self) = @_;
  return $self->{storage};
}

sub parent{
  my($self) = @_;
  return $self->{parent};
}

sub _match{
  my($self, $test, $value) = @_;
  if(ref $test eq 'Regexp'){
    return $value =~ $_;
  }elsif(ref $test eq 'CODE'){
    return $test->($self, $value);
  }else{
    return $test eq $value;
  }
  return;
}

sub _matching{
  my($self, $test, $tested) = @_;
  return 1 unless $test;
  if($tested){
    return 1 if grep $self->_match($_, $tested), @$test;
  }
  return 0;
}

sub _carpit{
  my($self, %args) = @_;
  return if $QUIET;

  my $class = (split /::/, ref $self)[2];
  my $op = $self->{options} || {};

  # key/value checking

lib/Tie/Trace.pm  view on Meta::CPAN

use warnings;
use strict;

use base qw/Tie::Trace/;

sub STORE{
  my($self, $key, $value) = @_;
  $self->_carpit(key => $key, value => $value)  unless $QUIET;
  local $QUIET = 1;
  Tie::Trace::_data_filter($value, $self, {__key => $key});
  $self->{storage}->{$key} = $value;
};

sub DELETE {
  my($self, $key) = @_;
  my $deleted = delete $self->{storage}->{$key};
  $self->_carpit(key => $key,
                 value => sprintf("DELETED(%s)", $self->_dumper(defined $deleted ? $deleted : 'undef')),
                 filter => sub{$_[0] =~ s/^\'(.+)\'$/$1/; $_[0] =~s /\\'/'/g}
                )  unless $QUIET;
  return $deleted;
}

sub CLEAR{
  my($self) = @_;
  return $self->Tie::Hash::CLEAR;
}

# Array /////////////////////////
package
 Tie::Trace::Array;

use warnings;
use strict;

use base qw/Tie::Trace/;

sub STORE{
  my($self, $p, $value) = @_;
  $self->_carpit(point => $p, value => $value)  unless $QUIET;
  local $QUIET = 1;
  Tie::Trace::_data_filter($value, $self, {__point => $p});
  $self->{storage}->[$p] = $value;
}

sub DELETE{
  my($self, $p) = @_;
  my $deleted = delete ${$self->{storage}}[$p];
  $self->_carpit(point => $p,
                 value => sprintf("DELETED(%s)", $self->_dumper(defined $deleted ? $deleted : "undef")),
                 filter => sub{$_[0] =~ s/^\'(.*)\'$/$1/; $_[0] =~s /\\'/'/g}
                )  unless $QUIET;
  return $deleted;
}

sub SPLICE{
  my $self  = shift;
  my $sz  = @{$self->{storage}};
  my $off = @_ ? shift : 0;
  my $fetchsize = $self->FETCHSIZE;
  my $caller_pkg = (caller)[0];
  my $func = "";
  if($caller_pkg eq "Tie::Trace::Array"){
    $func = (caller 1)[3];
    $func =~s/^Tie::Trace::Array:://;
  }
  $off   += $sz if $off < 0;
  my $len = @_ ? shift : $sz - $off;
  my $to  = $off + $len -1;
  my $p = $off eq $to ? $off : $off < $to ? "$off .. $to" : $off;
  my @point = ($func and $func ne 'STORESIZE') ? () : (point => $p);
  $self->_carpit(@point, value => \@_, filter => sub {$_[0] =~ s/^\[(.*)\]$/$func\($1\)/} )  unless $QUIET;
  local $QUIET = 1;
  if(@_){
    my $cnt = 0;
    foreach(@_){
      Tie::Trace::_data_filter($_, $self, {__point => $off + $cnt++});
    }
  }
  my $ret = splice(@{$self->{storage}}, $off, $len, @_);
  if(@_ != $len){
    my $diff = scalar @_ - $len;
    local $QUIET = 1;
    for(my $i = 0;$i < @{$self->{storage}}; $i++){
      my $value = $self->{storage}->[$i];
      Tie::Trace::_data_filter($value, $self, {__point => $i});
      $self->{storage}->[$i] = $value;
    }
  }
  return $ret;
}

sub FETCHSIZE{
  my($self) = shift;
  return scalar @{$self->{storage} ||= []};
}

sub PUSH{
  my($self, @value) = @_;
  return $self->SPLICE($self->FETCHSIZE, 0, @value);
}

sub UNSHIFT{
  my($self, @value) = @_;
  return $self->SPLICE(0, 0, @value);
}

sub POP{
  my($self) = @_;
  return $self->SPLICE(-1);
}

sub SHIFT{
  my($self) = @_;
  return $self->SPLICE(0, 1);
}

sub STORESIZE {
  my ($self, $p) = @_;
  $self->SPLICE($p, $self->FETCHSIZE - $p);
  return undef;



( run in 2.229 seconds using v1.01-cache-2.11-cpan-5e09290becf )