1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
|
#! /usr/local/bin/perl -w
# NOTES:
# Invariants and pre- and post-conditions are expected
# to return undef if they fail.
#
# Pre- and post-conditions receive the same argument list
# as the implementation itself. Methods and constructors
# may have as many pre- and post-conditions as they
# require.
#
# Pre- and post-conditions and invariants may be declared
# optional. Optional conditions may be switched on and off
# using the &check method (see examples below).
#
# The subroutine &self always returns a reference to
# the invoking object. However, that reference is still
# also passed as the first argument.
#
# The implementation's return value is available in the
# method's post-condition(s) through the subroutine
# &value, which returns a reference to a scalar or an array
# (depending on the calling context).
#
# &value also provides access to the value of an attribute within
# that attribute's pre- and post-conditions.
#
# The value of the object prior to a method is available in the
# post-conditions via the &old subroutine, which returns a copy
# of the object as it was prior to the method call.
#
# Methods can be declared abstract. They croak if not redefined.
#
# Class methods and attributes can be declared.
#
# The constructor implementation is invoked *after* the object
# is created and blessed into the class. It only needs to
# initialize the object returned by &self. Its return value is ignored.
#
# The implementations of all base class constructors are called
# automatically by the derived class constructor (and passed
# the same argument list)
#
# Attributes are private to the class in which they're declared.
# Attributes cannot be accessed directly, only via their
# accessor methods. This is true even within class methods.
# All generated accessors return a reference to their attribute.
#
# Accessors may only have preconditions.
#
# Accessors and methods inherit (all) the preconditions of
# every ancestral accessor or method of the same name.
#
package QueueBase;
use Class::Contract 'old';
contract {
abstract method 'append';
abstract method 'next';
ctor 'new';
impl { print "QueueBase::new!\n" };
};
package ClientQueue;
use Class::Contract 'old';
contract {
inherits QueueBase;
invar { print "appends: ", self->flags->{append} || 0, "\n"; };
invar { print "nexts: ", self->flags->{next} || 0, "\n"; };
optional invar {
@{self->queue} > 0 || undef;
}; failmsg "Empty queue detected at %s after call";
attr queue => ARRAY;
attr flags => HASH;
class attr 'first';
method 'append';
optional pre { print "first append\n" if ${self->first}; 1; };
pre {
print "<<<0>>>\n";
return 0 unless shift(@_)->isa("Client");
print "<<<0.1>>>\n";
1;
}; failmsg "Expected Client object";
post {
return unless @{self->queue} == @{old->queue} + 1;
return unless self->queue->[-1]{id} == $_[0]{id};
return 1;
};
impl {
print "<<<1>>>\n";
${self->first} = 0;
print "<<<2>>>\n";
self()->flags->{append}++;
print "<<<3>>>\n";
push @{self->queue}, shift;
print "<<<4>>>\n";
};
method 'next';
post {
return unless @{self->queue} == @{old->queue} - 1;
return 1;
}; failmsg "Expected removal of a single Client object";
impl {
self->flags->{next}++;
shift @{self->queue}
};
ctor 'new';
pre {
return unless @_ >= 1 && !grep {!$_->isa('Client')} @_;
return 1;
}; failmsg "constructor must be passed an initial Client obj";
impl {
@{self->queue} = ( shift );
${self->first} = 1;
};
};
package OrderedQueue;
use Class::Contract 'old';
contract
{
inherits 'ClientQueue';
method 'append';
post {
return unless $_[0]{id} > self->queue->[-2]{id};
}; failmsg "Client appended out of order";
ctor 'new';
impl { print "OrderedQueue::new!\n" };
};
package Client;
my $nextid = 1;
sub new {
bless { id => $nextid++ }, ref($_[0]) || $_[0];
}
package Main;
use Class::Contract qw(check);
check my %contract => 0 for (__ALL__); # TURN OFF ALL OPTIONAL CHECKS
check %contract for ('ClientQueue'); # TURN ON OPTIONAL CHECKS
# FOR ClientQueue ONLY
print "[[[1]]]\n";
my $client = Client->new();
print "[[[2]]]\n";
my $order_queue = OrderedQueue->new($client);
$client = Client->new();
print "[[[3]]]\n";
$order_queue->append($client);
print "[[[4]]]\n";
$client = Client->new();
my $client2 = Client->new();
print "[[[5]]]\n";
# Uncomment following to get append out of order error
# $order_queue->append($client2);
$order_queue->append($client);
print "[[[6]]]\n";
$client = "not a client";
# Expected Client object
eval '$order_queue->append($client)';
print $@ if $@;
print $order_queue->next(), "\n";
print $order_queue->next(), "\n";
print $order_queue->next(), "\n";
# Nothing left in queue: Expected removal a single Client object
my $val = $order_queue->next();
print "$val\n";
1;
|