NAME
My::Object - a simple object system for Perl 5
SYNOPSIS
package Local::Pony {
use My::Object { name => undef };
sub BUILD_Pony {
my ($self, $name) = @_;
$self->name = $name;
}
sub eatCarrot {
printf "%s eats a carrot.\n", shift->name;
}
}
*pony = Local::Pony->NEW;
my $cuddles = pony("Cuddles");
$cuddles->eatCarrot;
STATUS
Unstable. Stuff might change. As I have no clue what I'm doing, the code might be evil.
DESCRIPTION
A small object system using blessed hashes with autogenerated lvalue accessors. Code reuse is achieved via composition instead of inheritance: @ISA
is not used. Instead, all methods are flattened into the package.
This module takes a worse-is-better approach: There's no encapsulation, no inheritance, no type checking or other bells and whistles.
This is not Moose; it is not even Mo.
Class Declaration
A class is just a package that invokes use
on another class, in particular My::Object
. This composes the callee into the caller, copying all sub declarations into the package.
The use
statement takes a hash of members as optional argument, mapping names to initializers. Accessors for these members will be generated automatically.
Any sub defined within the package may be used as a method, with a reference to the object passed as first argument.
Dynamic Initialization
Initializers are treated as constants by default. If you need to initialize a member dynamically, you may provide a sub reference as initializer. You need to prefix the member name with a -
. This prefix is not part of the name proper.
use Scalar::Util qw(refaddr);
package Local::Node {
use My::Object {
name => undef,
-children => sub { [] },
};
}
my $n1 = Local::Node::NEW->();
my $n2 = Local::Node::NEW->();
printf "0x%X != 0x%X\n", refaddr($n1->children), refaddr($n2->children);
Conflict Resolution
You may use multiple use
statements to compose several classes into the same package. Conflicts arise if the classes have methods with the same name. In contrast, members of the same name will map to the same slot and no conflict arises.
Conflicts can be resolved by adding the line
use subs qw(name_of_conflicting_method);
before using any of the conflicting classes and manually providing an appropriate method implementation:
package Local::Alice {
use My::Object;
sub transmogrify { print "I'm a tiger!\n" }
}
package Local::Bob {
use My::Object;
sub transmogrify { print "I'm a frog!\n" }
}
package Local::AliceAndBob {
use Local::Alice;
use Local::Bob;
}
This will die with Subroutine transmogrify redefined, so we need to resolve the conflict:
package Local::AliceAndBob {
use subs qw(transmogrify);
use Local::Alice;
use Local::Bob;
sub transmogrify {
print "Alice: ";
Local::Alice::transmogrify(@_);
print "Bob: ";
Local::Bob::transmogrify(@_);
}
}
Local::AliceAndBob::NEW->()->transmogrify;
Object Construction
Any class automatically gets a sub NEW
that returns a reference to the default constructor, initializing member variables from named arguments:
package Local::Point {
use My::Object { x => 0, y => 0 };
}
my $p = Local::Point::NEW->(x => 0.1, y => 0.2);
printf "x=%f, y=%f\n", $p->x, $p->y;
Use the symbol table to manually import the constructor into the current package:
*point = Local::Point::NEW;
my $q = point(x => 0.5, y => 0.2);
It's best to do so at BEGIN
time.
Custom Constructors
If you want to provide a custom constructor, prefix the sub's name with BUILD_
and pass the name without prefix to NEW
:
package Local::Point {
use My::Object { x => 0, y => 0 };
sub BUILD_from_coords {
my ($self, $x, $y) = @_;
$self->x = $x;
$self->y = $y;
}
}
BEGIN { *point = Local::Point::NEW('from_coords') }
If you name constructor and package the same (only the part after the last ::
is relevant), ->NEW
will do the right thing:
package Local::Point {
use My::Object { x => 0, y => 0 }
sub BUILD_Point { ... }
}
BEGIN { *point = Local::Point->NEW }
BUGS AND DEVELOPMENT
Development happens at Bitbucket. If you found a bug or have a feature request, use the issue tracker over there.
COPYRIGHT AND LICENSE
Copyright (C) 2014 by Christoph Gärtner <cygx@cpan.org>
Distributed under the Boost Software License, Version 1.0