Name
Class::Abstract - Stop code from making objects of a class that is only a template
Version
Version 0.03
Synopsis
1. Make a Class Abstract (The Usual Way)
package Animal;
use parent 'Class::Abstract'; # Animal is now abstract
package Dog;
use parent -norequire, 'Animal'; # Dog is a normal (concrete) class
package main;
my $dog = Dog->new; # works: you get a Dog object
my $bad = Animal->new; # dies: "Cannot instantiate abstract class Animal directly"
2. Make a Class Abstract With "Use"
package Vehicle;
use Class::Abstract; # same result as: use parent 'Class::Abstract'
3. A Child Class That Stores Its Own Data
package Dog;
use parent -norequire, 'Animal';
sub new {
my ($class, %args) = @_;
my $self = $class->SUPER::new; # the check happens here
$self->{name} = $args{name}; # then add your own data
return $self;
}
package main;
my $rex = Dog->new(name => 'Rex');
4. An Abstract Class That Has Its Own New()
package Shape;
use parent 'Class::Abstract';
sub new {
my ($class, %args) = @_;
Class::Abstract::check_abstract($class); # dies if $class is Shape
return bless { colour => $args{colour} }, $class;
}
package Circle;
use parent -norequire, 'Shape';
package main;
my $c = Circle->new(colour => 'red'); # works
Shape->new(colour => 'red'); # dies
5. Ask Whether a Class Is Abstract
Animal->is_abstract; # 1
Dog->is_abstract; # 0
$rex->is_abstract; # 0 (asks about Dog)
Class::Abstract->is_abstract('Animal'); # 1
Class::Abstract->is_abstract($rex); # 0
6. Make an Object of an Abstract Class Inside a Test
use Test::More;
my $animal = do {
local $Class::Abstract::BYPASS = 1; # turn the check off for this block only
Animal->new;
};
is( $animal->legs, 4, 'default number of legs' );
7. Use With Sub::Abstract
package Animal;
use parent 'Class::Abstract'; # nobody can make an Animal directly
use Sub::Abstract qw(speak); # every child class must write speak()
Description
What This Module Does
An abstract class is a class that is only a template. It holds code that its child classes share, but it is not complete on its own. You should never make an object of it. You make objects of its child classes instead.
Perl has no built-in way to say "this class is abstract". This module adds one. When code tries to make an object of an abstract class, the program dies with a clear error message. Child classes work normally.
Terms Used in This Document
-
class, package
In Perl, a class is a package.
AnimalandDogare package names. -
parent class, child class, @ISA
Each package has a list called
@ISAthat names its parent classes. If@Dog::ISAcontainsAnimal, thenDogis a child ofAnimal, andDogcan useAnimal's methods. -
abstract, concrete
A class is abstract when
Class::Abstractis in its own@ISA. All other classes are concrete (normal). -
invocant
The value on the left of the arrow in a method call. In
Dog->newthe invocant is the string'Dog'. In$rex->speakthe invocant is the object$rex. -
croak, die
Stop the program (or the current
evalblock) with an error message. -
bypass
A setting that turns the check off. See "Turning the check off".
-
test harness
The program that runs test files, for example
proveormake test. It sets the environment variableHARNESS_ACTIVE.
How to Make a Class Abstract
Put Class::Abstract in the class's own @ISA. There are two ways:
package Animal;
use parent 'Class::Abstract'; # way 1 (recommended)
package Vehicle;
use Class::Abstract; # way 2
Both ways have the same result.
How the Check Works
Class::Abstract gives every class below it a method called new().
When new() runs, it looks at the invocant (the class name on the left
of the arrow). If that class has Class::Abstract in its own
@ISA, new() dies. If not, new() makes and returns an empty
object.
This is why child classes work. When Dog calls
$class->SUPER::new, the invocant is still 'Dog', not
'Animal'. Dog does not have Class::Abstract in its own @ISA,
so the check passes.
Only the Class That Asks Is Abstract
Being abstract is not passed down to child classes. If you want an
abstract class below another abstract class, list Class::Abstract in
that class too:
package Animal; use parent 'Class::Abstract'; # abstract
package Mammal; use parent 'Class::Abstract', 'Animal'; # also abstract
package Dog; use parent -norequire, 'Mammal'; # concrete
Turning the Check Off
Sometimes a test needs an object of an abstract class. Two settings turn the check off. If either one is on, the check is off.
-
- The variable
$Class::Abstract::BYPASS
Set it to a true value. Always use
local, so the setting ends at the end of the block:my $animal = do { local $Class::Abstract::BYPASS = 1; Animal->new }; - The variable
-
- The setting
harness_bypasstogether with a test harness
If
$Class::Abstract::config{harness_bypass}is true and the environment variableHARNESS_ACTIVEis true, the check is off.harness_bypassis false by default, so the check is on in your tests unless you change it. - The setting
$BYPASS is checked first. If $BYPASS is true, setting
harness_bypass to 0 does not turn the check back on.
Changed in version 0.03: before 0.03, harness_bypass was 1 by
default. That meant the check was off in every test suite. To get
the old behaviour back, put this near the top of your test files:
$Class::Abstract::config{harness_bypass} = 1;
Methods
Import
use Class::Abstract;
Perl calls import() for you when you write use Class::Abstract;.
You do not normally call it yourself.
It makes the package that contains the use line abstract. It does
this by adding 'Class::Abstract' to that package's @ISA. The
result is the same as use parent 'Class::Abstract'.
Arguments
None that you pass yourself. Perl passes the string
'Class::Abstract' as the first argument. Anything in an import list
(for example use Class::Abstract qw(x y)) is ignored.
Returns
The string 'Class::Abstract'. use throws this value away, so you
only see it if you call Class::Abstract->import yourself.
Side Effects
Adds 'Class::Abstract' to the end of the calling package's @ISA.
It does nothing in these cases:
'Class::Abstract'is already in that@ISA. So writinguse Class::Abstract;twice is safe.- The calling package is
Class::Abstractitself. A class cannot be its own parent. - Perl cannot tell which package is calling (this can happen in
unusual code, such as a
BEGINblock inside a stringeval). Without this rule, the module would change@main::ISAby mistake.
It does not change the global variables $_, $@ or $!, and it
does not touch an alarm() timer. (Before version 0.03 it cleared
$@.)
Example
package Vehicle;
use Class::Abstract; # @Vehicle::ISA is now ('Class::Abstract')
use Class::Abstract; # no change: it is already there
package Car;
our @ISA = ('Vehicle'); # Car is concrete
package main;
my $car = Car->new; # works
Vehicle->new; # dies
To load the module without making the current package abstract, pass
an empty list. Perl then does not call import():
use Class::Abstract (); # main is NOT made abstract
Api Specification
Input
{
class => { type => 'string', optional => 0 }, # always 'Class::Abstract'
}
Output
{
type => 'string',
}
Messages
None. import() never dies and never warns.
New
my $obj = ConcreteClass->new;
my $obj = ConcreteClass->new(name => 'Rex'); # arguments are ignored
my $copy = $obj->new; # new, empty object of the same class
Makes a new, empty object, but only for a concrete class.
- If the class is abstract,
new()dies with an error message. - If the class is concrete,
new()returns an empty hash reference blessed into that class.
Child classes usually call this through $class->SUPER::new from
their own new(). That works because the check looks at the class name
on the left of the arrow (the child), not at the class where new() is
written.
Arguments
-
$invocant(required)The class name or object on the left of the arrow. In
Dog->newit is the string'Dog'. If it is an object, the object's class is used. It must not beundef, the empty string, or a reference that is not an object (such as[]or{}). -
%args(optional)Any other arguments. They are accepted and ignored. This lets a child class pass its arguments to
SUPER::newwithout errors. The child class must store its own data afterSUPER::newreturns.
Returns
A new, empty hash reference, blessed into the class.
Side Effects
None, except that bless creates the package in Perl's symbol table if
it did not exist yet. (The check itself never creates a package.)
It does not change the global variables $_, $@ or $!, and it
does not touch an alarm() timer.
Example
package Animal;
use parent 'Class::Abstract';
package Dog;
our @ISA = ('Animal');
sub new {
my ($class, %args) = @_;
my $self = $class->SUPER::new; # the check happens here
$self->{name} = $args{name};
return $self;
}
package main;
my $rex = Dog->new(name => 'Rex'); # works: Dog is concrete
eval { Animal->new }; # dies: Animal is abstract
print $@; # Cannot instantiate abstract class Animal directly at ...
Api Specification
Input
{
invocant => {
type => 'string|object', # class name, or an object of that class
optional => 0,
min => 1, # a name must not be empty
},
}
Output
{
type => 'object', # a hashref blessed into the class
}
Messages
Message What it means / What to do
------- --------------------------
Cannot instantiate abstract class CLASS CLASS is abstract and no bypass is on.
directly Make an object of a child class instead.
new() invocant must be a class name or new() was called as a plain function
blessed object, got TYPE with a reference that is not an object
(TYPE is ARRAY, HASH, CODE, ...).
Call it as Class->new or $obj->new.
new() requires a defined class name as new() was called as a plain function
invocant with undef or ''. Pass a class name.
Check_Abstract
Class::Abstract::check_abstract($class);
Runs the same check as new(), but does not make an object.
Use it when your abstract class writes its own new() and that
new() does not call $class->SUPER::new. In that case
Class::Abstract::new() never runs, so nothing checks the class. Call
check_abstract() as the first line of your new() to put the check
back.
Arguments
-
$invocant(required)The class to check: a class name (a string) or an object. If it is an object, the object's class is used. In your own
new(), pass the$classvariable. It must not beundef, the empty string, or a reference that is not an object.
Returns
Nothing if the class is concrete or a bypass is on: undef in scalar
context, or an empty list in list context. Otherwise it dies.
Side Effects
None. It obeys $BYPASS and harness_bypass in exactly the same way
as new().
It does not change the global variables $_, $@ or $!, and it
does not touch an alarm() timer.
Example
package Shape;
use parent 'Class::Abstract';
sub new {
my ($class, %args) = @_;
Class::Abstract::check_abstract($class); # dies if $class is Shape
return bless { sides => $args{sides} }, $class;
}
package Square;
our @ISA = ('Shape');
package main;
my $sq = Square->new(sides => 4); # works
Shape->new(sides => 4); # dies: Shape is abstract
Api Specification
Input
{
invocant => {
type => 'string|object',
optional => 0,
min => 1,
},
}
Output
{
type => 'undef',
}
Messages
Message What it means / What to do
------- --------------------------
Cannot instantiate abstract class CLASS The same as for new().
directly
check_abstract() requires a class name The argument was a reference that is
or blessed object, got TYPE not an object. Pass $class or $self.
check_abstract() requires a defined The argument was undef or ''. This
class name usually means "my $class = shift;"
is missing from your new().
Is_Abstract
my $yes_or_no = SomeClass->is_abstract;
my $yes_or_no = $obj->is_abstract;
my $yes_or_no = Class::Abstract->is_abstract('SomeClass');
my $yes_or_no = Class::Abstract->is_abstract($obj);
Tells you whether a class is abstract. It returns 1 (yes) or 0
(no).
A class is abstract only when Class::Abstract is in its own
@ISA, or when the class is Class::Abstract. A child of an
abstract class is not abstract, so is_abstract() returns 0 for
it.
Every class below Class::Abstract can call this method.
Arguments
-
$selfRequired. The class name or object on the left of the arrow. If no target is given (see below), this is the class that is checked.
-
$target(optional)A class name or an object to check instead of the invocant. This is how you ask about any class:
Class::Abstract->is_abstract('Dog'). If$targetisundef, it is treated as "not given".
Returns
The integer 1 if the class is abstract, otherwise the integer 0.
A class name that does not exist (for example, a spelling mistake) gives
0, not an error.
Side Effects
None. Asking about a class that does not exist does not create it.
It does not change the global variables $_, $@ or $!, and it
does not touch an alarm() timer.
Example
Animal->is_abstract; # 1
Dog->is_abstract; # 0
my $rex = Dog->new(name => 'Rex');
$rex->is_abstract; # 0 (asks about Dog)
Class::Abstract->is_abstract('Animal'); # 1
Class::Abstract->is_abstract($rex); # 0 (asks about Dog)
Class::Abstract->is_abstract('Anmial'); # 0 (spelling mistake: no such class)
Api Specification
Input
{
self => { type => 'string|object', optional => 0, min => 1 },
target => { type => 'string|object', optional => 1, min => 1 },
}
Output
{
type => 'integer',
min => 0,
max => 1,
}
Messages
Message What it means / What to do
------- --------------------------
is_abstract() requires a class name or The class to check was a reference
object invocant, got TYPE that is not an object.
is_abstract() requires a class name or The class to check was undef or ''.
object invocant
Public Variables
$Class::Abstract::BYPASS
Default: 0.
When this is true, the check is off for every class. Always change
it with local, so that it goes back to its old value at the end of the
block:
{
local $Class::Abstract::BYPASS = 1;
my $animal = Animal->new; # works, even though Animal is abstract
}
Animal->new; # dies again
Any true value turns the check off. This includes strings that look
false to a person, such as "false", "no" and "0E0".
%Class::Abstract::config
Settings for the module. It has one key.
-
harness_bypass
Default:
0.When this is true and the environment variable
HARNESS_ACTIVEis true, the check is off.HARNESS_ACTIVEis set by test harnesses such asproveandmake test.The default was
1before version 0.03. Set it to1to get the old behaviour back.
Error Messages
This is the full list of messages that the module can produce. The text
is always plain English. It does not change with the locale. Each
message ends with the file and line number of your code, for example
at script.pl line 12.
Message Comes from
------- ----------
Cannot instantiate abstract class CLASS directly new, check_abstract
new() invocant must be a class name or blessed object, new
got TYPE
new() requires a defined class name as invocant new
check_abstract() requires a class name or blessed object, check_abstract
got TYPE
check_abstract() requires a defined class name check_abstract
is_abstract() requires a class name or object invocant, is_abstract
got TYPE
is_abstract() requires a class name or object invocant is_abstract
Class::Abstract internal error: unknown message key KEY (a bug in this
module; please
report it)
CLASS is the class name. TYPE is the kind of reference, such as
ARRAY, HASH or CODE.
Common Pitfalls
-
Your own new() skips the check
If an abstract class writes its own
new()and creates the object withbless,Class::Abstract::new()never runs. Nothing stopsShape->new. Fix: callClass::Abstract::check_abstract($class)on the first line, or build the object with$class->SUPER::new. -
The order of parents matters
Perl uses the first
new()it finds, reading@ISAfrom left to right. Inuse parent 'Animal', 'Class::Abstract', Perl findsAnimal::newfirst, ifAnimalhas one. If thatnew()does not callcheck_abstract(), there is no check. -
Child classes are not abstract automatically
Mammalbelow the abstract classAnimalis concrete unlessMammalalso listsClass::Abstractin its own@ISA. -
"use Class::Abstract;" changes the current package
use Class::Abstract;makes the package it is written in abstract. If you write it in a script only to callis_abstract(), themainpackage becomes abstract. Writeuse Class::Abstract ();instead. With the empty list, Perl does not callimport(). The same is true forrequire Class::Abstract;: it does not make any class abstract. -
undef and empty strings
Class::Abstract::new(undef),new('')and the same calls tocheck_abstract()andis_abstract()die. But anundeftarget in$obj->is_abstract(undef)is treated as "no target", so the invocant is checked. -
Spelling mistakes give 0, not an error
Class::Abstract->is_abstract('Anmial')returns0, because no package with that name hasClass::Abstractin its@ISA. Check the spelling if you get0when you expect1. -
A bypass without "local" never ends
$Class::Abstract::BYPASS = 1;withoutlocalturns the check off for the rest of the program, in every file. -
Strings that look false are true
$BYPASS = 'false'and$BYPASS = 'no'turn the check off, because Perl treats every non-empty string except'0'as true. Use1and0. -
$BYPASS wins over harness_bypass
If
$BYPASSis true, settingharness_bypassto0does not turn the check on. To be sure the check is on, set both:local $Class::Abstract::BYPASS = 0; local $Class::Abstract::config{harness_bypass} = 0; -
Tests that passed before 0.03 may now die
Before version 0.03 the check was off inside a test harness. If your tests make objects of abstract classes, they will now die. See "Turning the check off".
-
isa() is not the same as is_abstract()
Dog->isa('Class::Abstract')is true, becauseClass::Abstractis a grandparent ofDog. UseDog->is_abstractto ask whetherDogitself is abstract. -
new() throws away its arguments
Dog->SUPER::new(name => 'Rex')does not storename. Store it yourself afterSUPER::newreturns. -
Objects made during a bypass stay abstract
An object made while the bypass was on still belongs to the abstract class. Calling
$obj->newon it later, with the check on, dies.
Encoding
The module does not read files, the terminal or the network. The only text it handles is class names, and the error messages that contain them.
-
Class names
Class names may contain any characters that Perl allows in a package name. This includes non-ASCII letters (
Café, Japanese, Chinese) and, for names made withblessor symbolic references, emoji. The module compares names exactly, character by character.Two conditions apply:
- Non-ASCII package names need Perl 5.16 or later. The rest of the module works on Perl 5.8.
- The name must be a character string, not raw UTF-8 bytes.
Names written in source code under
use utf8;are character strings. Names read from a file or the network must be decoded first (for example with "decode" in Encode). A name that is still raw bytes is a different name to Perl, sois_abstract()returns0for it.
-
Error messages
The fixed text of every message is plain ASCII English. A message also contains the class name, so it can contain non-ASCII characters. If you print such a message to a file handle that has no encoding layer, Perl warns
Wide character in print. Add a layer to avoid this:binmode STDERR, ':encoding(UTF-8)'; -
Settings
$BYPASS,harness_bypassandHARNESS_ACTIVEare only tested for true or false. Their text is never used.
Limitations
-
Only the check in new() and check_abstract() protects a class
Code can still make an object of an abstract class with
bless {}, 'Animal', with "dclone" in Storable, withthaw, or with anew()that forgets to callcheck_abstract(). The check is a rule that the module follows when it is asked. It is not built into Perl. -
can('new') returns a method that dies
Animal->can('new')returns\&Class::Abstract::new. This looks like "yes, you can call new()". You can call it, but it dies. -
The bypass is global
$BYPASSturns the check off for every class, not only for the class that a test is about. -
The old package separator is not supported
Perl once allowed
'between parts of a package name (Foo'BarmeaningFoo::Bar). This module does not convert'to::, so such names are treated as concrete. -
Threads
The module shares no data between threads, except
$BYPASSand%config, which are normally only read.import()changes the caller's@ISA, usually when the program is compiled. This is safe if modules are not loaded withrequirefrom several threads at once. -
Not for Moo or Moose
Moo and Moose have their own ways to make abstract classes and required methods. This module is for plain Perl classes.
Dependencies
Carp (part of Perl), Scalar::Util (part of Perl), Readonly, Return::Set.
See Also
-
Makes child classes write certain methods. Use it together with
Class::Abstractfor a complete abstract base class. -
Makes a method callable only from its own class.
-
Makes a method callable only from its own class and its child classes.
Author
Nigel Horne, <njh at nigelhorne.com>
Formal Specification
This section describes the module in the Z notation. You do not need to read it to use the module.
Definitions
Package == seq CHAR
[Object, Ref] -- objects and references; Object ⊆ Ref
┌─ Registry ────────────────────────────────────────────
│ bypass : 𝔹
│ harness_bypass : 𝔹
│ HARNESS_ACTIVE : 𝔹
│ ISA : Package ⇸ seq Package
└───────────────────────────────────────────────────────
┌─ InitRegistry ────────────────────────────────────────
│ Registry'
├───────────────────────────────────────────────────────
│ bypass' = false ∧ harness_bypass' = false
└───────────────────────────────────────────────────────
bypass_active ⇔ bypass ∨ (harness_bypass ∧ HARNESS_ACTIVE)
resolve : (Package ∪ Object) ⇸ Package
resolve(x) = class_of(x) if x ∈ Object
= x if x ∈ Package ∧ #x > 0
is_direct_abstract : Package → 𝔹
is_direct_abstract(c) ⇔ c = 'Class::Abstract'
∨ (c ∈ dom ISA ∧ 'Class::Abstract' ∈ ran ISA(c))
┌─ BadInvocant ─────────────────────────────────────────
│ ΞRegistry
│ inv? : Package ∪ Ref ∪ {⊥}
├───────────────────────────────────────────────────────
│ inv? ∈ Ref ∖ Object ∨ inv? = ⊥ ∨ inv? = ''
│ croak(message for the calling routine)
└───────────────────────────────────────────────────────
┌─ NewAbstract ─────────────────────────────────────────
│ ΞRegistry
│ inv? : Package ∪ Object
├───────────────────────────────────────────────────────
│ is_direct_abstract(resolve(inv?)) ∧ ¬bypass_active
│ croak('Cannot instantiate abstract class ' ⁀ resolve(inv?) ⁀ ' directly')
└───────────────────────────────────────────────────────
Import
┌─ Import ──────────────────────────────────────────────
│ ΔRegistry
│ class? : Package
│ caller? : Package ∪ {⊥}
│ result! : Package
├───────────────────────────────────────────────────────
│ registrable ⇔ caller? ≠ ⊥ ∧ #caller? > 0
│ ∧ caller? ≠ 'Class::Abstract'
│ ∧ ¬is_direct_abstract(caller?)
│ registrable ⇒ ISA'(caller?) = ISA(caller?) ⁀ ⟨'Class::Abstract'⟩
│ ¬registrable ⇒ ISA' = ISA
│ bypass' = bypass ∧ harness_bypass' = harness_bypass
│ result! = class?
└───────────────────────────────────────────────────────
New
┌─ New ─────────────────────────────────────────────────
│ ΞRegistry
│ inv? : Package ∪ Object
│ result! : Object
├───────────────────────────────────────────────────────
│ ¬is_direct_abstract(resolve(inv?)) ∨ bypass_active
│ class_of(result!) = resolve(inv?)
│ result! is a new, empty hash
└───────────────────────────────────────────────────────
NewOp ≙ New ∨ NewAbstract ∨ BadInvocant
Check_Abstract
┌─ CheckAbstract ───────────────────────────────────────
│ ΞRegistry
│ inv? : Package ∪ Object
│ result! : {⊥}
├───────────────────────────────────────────────────────
│ ¬is_direct_abstract(resolve(inv?)) ∨ bypass_active
│ result! = ⊥
└───────────────────────────────────────────────────────
CheckAbstractOp ≙ CheckAbstract ∨ NewAbstract ∨ BadInvocant
Is_Abstract
┌─ IsAbstract ──────────────────────────────────────────
│ ΞRegistry
│ self? : Package ∪ Object
│ target? : Package ∪ Object ∪ {⊥}
│ result! : {0, 1}
├───────────────────────────────────────────────────────
│ c = resolve(if target? = ⊥ then self? else target?)
│ result! = 1 ⇔ is_direct_abstract(c)
└───────────────────────────────────────────────────────
IsAbstractOp ≙ IsAbstract ∨ BadInvocant
-- ΞRegistry in New, CheckAbstract and IsAbstract means that
-- asking about a package never creates it or changes any @ISA.
State Diagram
The module has two independent kinds of state: the state of each class, and the global check mode.
Class States
Every package is in exactly one of these states:
MISSING The package does not exist yet.
PLAIN The package exists. Class::Abstract is not one of its
ancestors, so it has no new() from this module.
ABSTRACT 'Class::Abstract' is in the package's own @ISA.
CONCRETE The package inherits from an abstract class, but
'Class::Abstract' is not in its own @ISA.
Transitions:
use Class::Abstract;
use parent 'Class::Abstract';
push @P::ISA, 'Class::Abstract'
+---------+ -------------------------------> +----------+
| MISSING | | |
+---------+ | |
| | | |
| | Class::Abstract::new('P') | |
| | (bless creates P) | ABSTRACT |
| v | |
| +-------+ same triggers as above | |
| | PLAIN | ------------------------------> | |
| | | <------------------------------ | |
| +-------+ remove 'Class::Abstract' +----------+
| | from @P::ISA (P->new now ^ |
| | fails: "Can't locate object | | remove
| | method new") | | 'Class::Abstract'
| | @P::ISA = ('Animal') | | from @P::ISA
| | (Animal is ABSTRACT or CONCRETE) | | (an abstract
| v add | | parent remains)
| +----------+ ----------------------------->--+ |
+->| CONCRETE | use parent 'Class::Abstract' |
| | <-----------------------------------+
+----------+
@P::ISA = ('Animal') also moves MISSING directly to CONCRETE.
Calls that never change a class's state:
P->is_abstract, Class::Abstract->is_abstract('P') any state -> same state
Class::Abstract::check_abstract('P') any state -> same state
use Class::Abstract; (again, in P) ABSTRACT -> ABSTRACT
Check Mode
+-----------+ local $Class::Abstract::BYPASS = 1 +----------+
| | -------------------------------------------------> | |
| ENFORCING | $config{harness_bypass} = 1 while HARNESS_ACTIVE | BYPASSED |
| | HARNESS_ACTIVE set while harness_bypass is 1 | |
| | <------------------------------------------------- | |
+-----------+ end of the block with "local" +----------+
$BYPASS false AND (harness_bypass false
OR HARNESS_ACTIVE false)
Start state: ENFORCING ($BYPASS = 0, harness_bypass = 0).
What New() and Check_Abstract() Do in Each State
Class state Check mode P->new check_abstract('P')
----------- ---------- --------------------------- -------------------
ABSTRACT ENFORCING dies: "Cannot instantiate dies (same message)
abstract class P directly"
ABSTRACT BYPASSED returns an object of P returns undef
CONCRETE either returns an object of P returns undef
PLAIN either dies: "Can't locate object returns undef
method new" (Perl error)
MISSING either dies: "Can't locate object returns undef
method new" (Perl error)
No call changes the check mode except the assignments shown above.
Licence and Copyright
Copyright 2026 Nigel Horne.
Usage is subject to the GPL2 licence terms. If you use it, please let me know.