Еще раз спасибо, Лиз, за то, что так быстро обратилась к этому.Однако, как я писал на Github, в то время как подклассы работают сейчас, кажется, что невозможно инициализировать атрибуты подкласса.Это прекрасно, я знаю, я могу решить это с помощью метода get-position
.
Это потому, что Proxy
не является "правильным" классом или я что-то пропускаю?
use Test;
class Scalar::History::Proxy is Proxy
{
has @!history;
has $!position = 0; # The assignment gets ignored ...
# ... so do these
submethod TWEAK( *@args )
{
say "Why oh why?";
}
submethod BUILD( *@args )
{
say "Do we never get called";
}
method get-position( \SELF: )
{
$!position // @!history.elems
}
method current-value( \SELF: )
{
@!history[ SELF.get-position ]
}
method get-history( \SELF: Bool :$all = False )
{
my $to-index = $all ?? @!history.elems - 1 !! SELF.get-position;
@!history[ ^$to-index ].Array
}
method reset-history( \SELF: )
{
@!history = ();
$!position = 0;
}
method forward-history( \SELF: $steps )
{
$!position = SELF.get-position + $steps;
$!position = @!history.elems - 1
if $!position >= @!history.elems;
$!position;
}
method rewind-history( \SELF: $steps )
{
$!position = SELF.get-position - $steps;
$!position = 0
if $!position < 0;
$!position;
}
method store-value( \SELF: $new-value, $register-duplicates )
{
# Forget stuff after rewind
if @!history.elems > SELF.get-position + 1
{
@!history.splice( SELF.get-position + 1 );
}
if !($new-value eqv SELF.current-value) || $register-duplicates
{
@!history.push( $new-value );
$!position = @!history.elems - 1;
}
}
}
class Scalar::History
{
method create( $value, ::T $type = Any, Bool :$register-duplicates = False )
{
return-rw Scalar::History::Proxy.new(
FETCH => method ( \SELF: ) {
SELF.current-value() },
STORE => method ( \SELF: T $new-value ) {
SELF.store-value( $new-value, $register-duplicates ); }
) = $value;
}
}
Это проходит все мои тесты
use Scalar::History;
use Test;
subtest 'untyped' =>
{
plan 2;
my $untyped := Scalar::History.create("a");
my $sub = sub foo() { * };
my $rx = rx/x/;
$untyped = $sub;
$untyped = $rx;
$untyped = 42;
ok( $untyped == 42, "Current value is correct" );
is-deeply( $untyped.VAR.get-history, ["a", $sub, $rx], "History is correct" );
}
subtest 'typed' =>
{
plan 3;
my $typed := Scalar::History.create("a", Str);
$typed = "b";
$typed = "42";
ok( $typed == "42", "Current value is correct" );
is-deeply( $typed.VAR.get-history, ["a", "b"], "History is correct" );
dies-ok( { $typed = 2; }, "Cannot assign invalid type" );
}
subtest 'duplicates' =>
{
plan 2;
my $with-duplicates := Scalar::History.create( "a", Str, :register-duplicates(True) );
$with-duplicates = "a";
$with-duplicates = "a";
is-deeply( $with-duplicates.VAR.get-history, ["a", "a"], "duplicates get registered" );
my $no-duplicates := Scalar::History.create( "a", Str );
$no-duplicates = "a";
$no-duplicates = "a";
is-deeply( $no-duplicates.VAR.get-history, [], "duplicates get ignored" );
}
subtest 'position/forward/backward' =>
{
plan 8;
my Int $int := Scalar::History.create(10, Int);
#say $int.VAR.get-position;
$int = 100 ;
$int = 1000 ;
ok( $int.VAR.get-position == 2, "current position is 2 after 3 assignments" );
$int.VAR.rewind-history(2);
ok( $int == 10, "current value is correct after rewind" );
$int.VAR.forward-history(1);
ok( $int == 100, "current value is correct after forward" );
$int.VAR.rewind-history(Inf);
ok( $int == 10, "current value equals start value after rewind to infinity" );
$int.VAR.forward-history(Inf);
ok( $int == 1000, "current value equals last known value after forward to infinity" );
$int.VAR.rewind-history(2);
is-deeply( $int.VAR.get-history, [], "history empty after rewind" );
is-deeply( $int.VAR.get-history(:all), [10, 100], "but still there if needed" );
$int = 101;
$int = 1001;
is-deeply( $int.VAR.get-history, [10, 101], "history gets truncated after rewind and assign" );
}
subtest 'behaviour' =>
{
plan 2;
sub add-one( Int $v ) { return $v + 1 }
my Int $int := Scalar::History.create(1, Int);
$int++;
$int = $int + 1;
$int = add-one( $int );
$int = 42;
is-deeply( $int.VAR.get-history, [1,2,3,4], "historic Int behaves like normal Int" ); # probably testing the language here, but meh
$int.VAR.reset-history();
is-deeply( $int.VAR.get-history(:all), [], "history can be reset" );
}
done-testing;