|
Packit |
69e8b9 |
NAME
|
|
Packit |
69e8b9 |
Spiffy - Spiffy Perl Interface Framework For You
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
SYNOPSIS
|
|
Packit |
69e8b9 |
package Keen;
|
|
Packit |
69e8b9 |
use Spiffy -Base;
|
|
Packit |
69e8b9 |
field 'mirth';
|
|
Packit |
69e8b9 |
const mood => ':-)';
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
sub happy {
|
|
Packit |
69e8b9 |
if ($self->mood eq ':-(') {
|
|
Packit |
69e8b9 |
$self->mirth(-1);
|
|
Packit |
69e8b9 |
print "Cheer up!";
|
|
Packit |
69e8b9 |
}
|
|
Packit |
69e8b9 |
super;
|
|
Packit |
69e8b9 |
}
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
DESCRIPTION
|
|
Packit |
69e8b9 |
"Spiffy" is a framework and methodology for doing object oriented (OO)
|
|
Packit |
69e8b9 |
programming in Perl. Spiffy combines the best parts of Exporter.pm,
|
|
Packit |
69e8b9 |
base.pm, mixin.pm and SUPER.pm into one magic foundation class. It
|
|
Packit |
69e8b9 |
attempts to fix all the nits and warts of traditional Perl OO, in a
|
|
Packit |
69e8b9 |
clean, straightforward and (perhaps someday) standard way.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
Spiffy borrows ideas from other OO languages like Python, Ruby, Java and
|
|
Packit |
69e8b9 |
Perl 6. It also adds a few tricks of its own.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
If you take a look on CPAN, there are a ton of OO related modules. When
|
|
Packit |
69e8b9 |
starting a new project, you need to pick the set of modules that makes
|
|
Packit |
69e8b9 |
most sense, and then you need to use those modules in each of your
|
|
Packit |
69e8b9 |
classes. Spiffy, on the other hand, has everything you'll probably need
|
|
Packit |
69e8b9 |
in one module, and you only need to use it once in one of your classes.
|
|
Packit |
69e8b9 |
If you make Spiffy.pm the base class of the basest class in your
|
|
Packit |
69e8b9 |
project, Spiffy will automatically pass all of its magic to all of your
|
|
Packit |
69e8b9 |
subclasses. You may eventually forget that you're even using it!
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
The most striking difference between Spiffy and other Perl object
|
|
Packit |
69e8b9 |
oriented base classes, is that it has the ability to export things. If
|
|
Packit |
69e8b9 |
you create a subclass of Spiffy, all the things that Spiffy exports will
|
|
Packit |
69e8b9 |
automatically be exported by your subclass, in addition to any more
|
|
Packit |
69e8b9 |
things that you want to export. And if someone creates a subclass of
|
|
Packit |
69e8b9 |
your subclass, all of those things will be exported automatically, and
|
|
Packit |
69e8b9 |
so on. Think of it as "Inherited Exportation", and it uses the familiar
|
|
Packit |
69e8b9 |
Exporter.pm specification syntax.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
To use Spiffy or any subclass of Spiffy as a base class of your class,
|
|
Packit |
69e8b9 |
you specify the "-base" argument to the "use" command.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
use MySpiffyBaseModule -base;
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
You can also use the traditional "use base 'MySpiffyBaseModule';" syntax
|
|
Packit |
69e8b9 |
and everything will work exactly the same. The only caveat is that
|
|
Packit |
69e8b9 |
Spiffy.pm must already be loaded. That's because Spiffy rewires base.pm
|
|
Packit |
69e8b9 |
on the fly to do all the Spiffy magics.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
Spiffy has support for Ruby-like mixins with Perl6-like roles. Just like
|
|
Packit |
69e8b9 |
"base" you can use either of the following invocations:
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
use mixin 'MySpiffyBaseModule';
|
|
Packit |
69e8b9 |
use MySpiffyBaseModule -mixin;
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
The second version will only work if the class being mixed in is a
|
|
Packit |
69e8b9 |
subclass of Spiffy. The first version will work in all cases, as long as
|
|
Packit |
69e8b9 |
Spiffy has already been loaded.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
To limit the methods that get mixed in, use roles. (Hint: they work just
|
|
Packit |
69e8b9 |
like an Exporter list):
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
use MySpiffyBaseModule -mixin => qw(:basics x y !foo);
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
In object oriented Perl almost every subroutine is a method. Each method
|
|
Packit |
69e8b9 |
gets the object passed to it as its first argument. That means
|
|
Packit |
69e8b9 |
practically every subroutine starts with the line:
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
my $self = shift;
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
Spiffy provides a simple, optional filter mechanism to insert that line
|
|
Packit |
69e8b9 |
for you, resulting in cleaner code. If you figure an average method has
|
|
Packit |
69e8b9 |
10 lines of code, that's 10% of your code! To turn this option on, you
|
|
Packit |
69e8b9 |
just use the "- Base" option instead of the "-base" option, or add the
|
|
Packit |
69e8b9 |
"-selfless" option. If source filtering makes you queazy, don't use the
|
|
Packit |
69e8b9 |
feature. I personally find it addictive in my quest for writing squeaky
|
|
Packit |
69e8b9 |
clean, maintainable code.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
A useful feature of Spiffy is that it exports two functions: "field" and
|
|
Packit |
69e8b9 |
"const" that can be used to declare the attributes of your class, and
|
|
Packit |
69e8b9 |
automatically generate accessor methods for them. The only difference
|
|
Packit |
69e8b9 |
between the two functions is that "const" attributes can not be
|
|
Packit |
69e8b9 |
modified; thus the accessor is much faster.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
One interesting aspect of OO programming is when a method calls the same
|
|
Packit |
69e8b9 |
method from a parent class. This is generally known as calling a super
|
|
Packit |
69e8b9 |
method. Perl's facility for doing this is butt ugly:
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
sub cleanup {
|
|
Packit |
69e8b9 |
my $self = shift;
|
|
Packit |
69e8b9 |
$self->scrub;
|
|
Packit |
69e8b9 |
$self->SUPER::cleanup(@_);
|
|
Packit |
69e8b9 |
}
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
Spiffy makes it, er, super easy to call super methods. You just use the
|
|
Packit |
69e8b9 |
"super" function. You don't need to pass it any arguments because it
|
|
Packit |
69e8b9 |
automatically passes them on for you. Here's the same function with
|
|
Packit |
69e8b9 |
Spiffy:
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
sub cleanup {
|
|
Packit |
69e8b9 |
$self->scrub;
|
|
Packit |
69e8b9 |
super;
|
|
Packit |
69e8b9 |
}
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
Spiffy has a special method for parsing arguments called
|
|
Packit |
69e8b9 |
"parse_arguments", that it also uses for parsing its own arguments. You
|
|
Packit |
69e8b9 |
declare which arguments are boolean (singletons) and which ones are
|
|
Packit |
69e8b9 |
paired, with two special methods called "boolean_arguments" and
|
|
Packit |
69e8b9 |
"paired_arguments". Parse arguments pulls out the booleans and pairs and
|
|
Packit |
69e8b9 |
returns them in an anonymous hash, followed by a list of the unmatched
|
|
Packit |
69e8b9 |
arguments.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
Finally, Spiffy can export a few debugging functions "WWW", "XXX", "YYY"
|
|
Packit |
69e8b9 |
and "ZZZ". Each of them produces a YAML dump of its arguments. WWW warns
|
|
Packit |
69e8b9 |
the output, XXX dies with the output, YYY prints the output, and ZZZ
|
|
Packit |
69e8b9 |
confesses the output. If YAML doesn't suit your needs, you can switch
|
|
Packit |
69e8b9 |
all the dumps to Data::Dumper format with the "-dumper" option.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
That's Spiffy!
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
EXPORTING
|
|
Packit |
69e8b9 |
Spiffy implements a completely new idea in Perl. Modules that act both
|
|
Packit |
69e8b9 |
as object oriented classes and that also export functions. But it takes
|
|
Packit |
69e8b9 |
the concept of Exporter.pm one step further; it walks the entire @ISA
|
|
Packit |
69e8b9 |
path of a class and honors the export specifications of each module.
|
|
Packit |
69e8b9 |
Since Spiffy calls on the Exporter module to do this, you can use all
|
|
Packit |
69e8b9 |
the fancy interface features that Exporter has, including tags and
|
|
Packit |
69e8b9 |
negation.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
Spiffy considers all the arguments that don't begin with a dash to
|
|
Packit |
69e8b9 |
comprise the export specification.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
package Vehicle;
|
|
Packit |
69e8b9 |
use Spiffy -base;
|
|
Packit |
69e8b9 |
our $SERIAL_NUMBER = 0;
|
|
Packit |
69e8b9 |
our @EXPORT = qw($SERIAL_NUMBER);
|
|
Packit |
69e8b9 |
our @EXPORT_BASE = qw(tire horn);
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
package Bicycle;
|
|
Packit |
69e8b9 |
use Vehicle -base, '!field';
|
|
Packit |
69e8b9 |
$self->inflate(tire);
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
In this case, "Bicycle->isa('Vehicle')" and also all the things that
|
|
Packit |
69e8b9 |
"Vehicle" and "Spiffy" export, will go into "Bicycle", except "field".
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
Exporting can be very helpful when you've designed a system with
|
|
Packit |
69e8b9 |
hundreds of classes, and you want them all to have access to some
|
|
Packit |
69e8b9 |
functions or constants
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
or variables. Just export them in your main base class and every subclass
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
will get the functions they need.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
You can do almost everything that Exporter does because Spiffy delegates
|
|
Packit |
69e8b9 |
the job to Exporter (after adding some Spiffy magic). Spiffy offers a
|
|
Packit |
69e8b9 |
@EXPORT_BASE variable which is like @EXPORT, but only for usages that
|
|
Packit |
69e8b9 |
use "-base".
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
MIXINS & ROLES
|
|
Packit |
69e8b9 |
If you've done much OO programming in Perl you've probably used Multiple
|
|
Packit |
69e8b9 |
Inheritance (MI), and if you've done much MI you've probably run into
|
|
Packit |
69e8b9 |
weird problems and headaches. Some languages like Ruby, attempt to
|
|
Packit |
69e8b9 |
resolve MI issues using a technique called mixins. Basically, all Ruby
|
|
Packit |
69e8b9 |
classes use only Single Inheritance (SI), and then *mixin* functionality
|
|
Packit |
69e8b9 |
from other modules if they need to.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
Mixins can be thought of at a simplistic level as *importing* the
|
|
Packit |
69e8b9 |
methods of another class into your subclass. But from an implementation
|
|
Packit |
69e8b9 |
standpoint that's not the best way to do it. Spiffy does what Ruby does.
|
|
Packit |
69e8b9 |
It creates an empty anonymous class, imports everything into that class,
|
|
Packit |
69e8b9 |
and then chains the new class into your SI ISA path. In other words, if
|
|
Packit |
69e8b9 |
you say:
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
package AAA;
|
|
Packit |
69e8b9 |
use BBB -base;
|
|
Packit |
69e8b9 |
use CCC -mixin;
|
|
Packit |
69e8b9 |
use DDD -mixin;
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
You end up with a single inheritance chain of classes like this:
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
AAA << AAA-DDD << AAA-CCC << BBB;
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
"AAA-DDD" and "AAA-CCC" are the actual package names of the generated
|
|
Packit |
69e8b9 |
classes. The nice thing about this style is that mixing in CCC doesn't
|
|
Packit |
69e8b9 |
clobber any methods in AAA, and DDD doesn't conflict with AAA or CCC
|
|
Packit |
69e8b9 |
either. If you mixed in a method in CCC that was also in AAA, you can
|
|
Packit |
69e8b9 |
still get to it by using "super".
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
When Spiffy mixes in CCC, it pulls in all the methods in CCC that do not
|
|
Packit |
69e8b9 |
begin with an underscore. Actually it goes farther than that. If CCC is
|
|
Packit |
69e8b9 |
a subclass it will pull in every method that CCC "can" do through
|
|
Packit |
69e8b9 |
inheritance. This is very powerful, maybe too powerful.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
To limit what you mixin, Spiffy borrows the concept of Roles from Perl6.
|
|
Packit |
69e8b9 |
The term role is used more loosely in Spiffy though. It's much like an
|
|
Packit |
69e8b9 |
import list that the Exporter module uses, and you can use groups (tags)
|
|
Packit |
69e8b9 |
and negation. If the first element of your list uses negation, Spiffy
|
|
Packit |
69e8b9 |
will start with all the methods that your mixin class can do.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
use EEE -mixin => qw(:tools walk !run !:sharp_tools);
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
In this example, "walk" and "run" are methods that EEE can do, and
|
|
Packit |
69e8b9 |
"tools" and "sharp_tools" are roles of class EEE. How does class EEE
|
|
Packit |
69e8b9 |
define these roles? It very simply defines methods called "_role_tools"
|
|
Packit |
69e8b9 |
and "_role_sharp_tools" which return lists of more methods. (And
|
|
Packit |
69e8b9 |
possibly other roles!) The neat thing here is that since roles are just
|
|
Packit |
69e8b9 |
methods, they too can be inherited. Take that Perl6!
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
FILTERING
|
|
Packit |
69e8b9 |
By using the "-Base" flag instead of "-base" you never need to write the
|
|
Packit |
69e8b9 |
line:
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
my $self = shift;
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
This statement is added to every subroutine in your class by using a
|
|
Packit |
69e8b9 |
source filter. The magic is simple and fast, so there is litte
|
|
Packit |
69e8b9 |
performance penalty for creating clean code on par with Ruby and Python.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
package Example;
|
|
Packit |
69e8b9 |
use Spiffy -Base;
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
sub crazy {
|
|
Packit |
69e8b9 |
$self->nuts;
|
|
Packit |
69e8b9 |
}
|
|
Packit |
69e8b9 |
sub wacky { }
|
|
Packit |
69e8b9 |
sub new() {
|
|
Packit |
69e8b9 |
bless [], shift;
|
|
Packit |
69e8b9 |
}
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
is exactly the same as:
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
package Example;
|
|
Packit |
69e8b9 |
use Spiffy -base;
|
|
Packit |
69e8b9 |
use strict;use warnings;
|
|
Packit |
69e8b9 |
sub crazy {my $self = shift;
|
|
Packit |
69e8b9 |
$self->nuts;
|
|
Packit |
69e8b9 |
}
|
|
Packit |
69e8b9 |
sub wacky {my $self = shift; }
|
|
Packit |
69e8b9 |
sub new {
|
|
Packit |
69e8b9 |
bless [], shift;
|
|
Packit |
69e8b9 |
}
|
|
Packit |
69e8b9 |
;1;
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
Note that the empty parens after the subroutine "new" keep it from
|
|
Packit |
69e8b9 |
having a $self added. Also note that the extra code is added to existing
|
|
Packit |
69e8b9 |
lines to ensure that line numbers are not altered.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
"-Base" also turns on the strict and warnings pragmas, and adds that
|
|
Packit |
69e8b9 |
annoying '1;' line to your module.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
PRIVATE METHODS
|
|
Packit |
69e8b9 |
Spiffy now has support for private methods when you use the '-Base'
|
|
Packit |
69e8b9 |
filter mechanism. You just declare the subs with the "my" keyword, and
|
|
Packit |
69e8b9 |
call them with a '$' in front. Like this:
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
package Keen;
|
|
Packit |
69e8b9 |
use SomethingSpiffy -Base;
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
# normal public method
|
|
Packit |
69e8b9 |
sub swell {
|
|
Packit |
69e8b9 |
$self->$stinky;
|
|
Packit |
69e8b9 |
}
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
# private lexical method. uncallable from outside this file.
|
|
Packit |
69e8b9 |
my sub stinky {
|
|
Packit |
69e8b9 |
...
|
|
Packit |
69e8b9 |
}
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
SPIFFY DEBUGGING
|
|
Packit |
69e8b9 |
The XXX function is very handy for debugging because you can insert it
|
|
Packit |
69e8b9 |
almost anywhere, and it will dump your data in nice clean YAML. Take the
|
|
Packit |
69e8b9 |
following statement:
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
my @stuff = grep { /keen/ } $self->find($a, $b);
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
If you have a problem with this statement, you can debug it in any of
|
|
Packit |
69e8b9 |
the following ways:
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
XXX my @stuff = grep { /keen/ } $self->find($a, $b);
|
|
Packit |
69e8b9 |
my @stuff = XXX grep { /keen/ } $self->find($a, $b);
|
|
Packit |
69e8b9 |
my @stuff = grep { /keen/ } XXX $self->find($a, $b);
|
|
Packit |
69e8b9 |
my @stuff = grep { /keen/ } $self->find(XXX $a, $b);
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
XXX is easy to insert and remove. It is also a tradition to mark
|
|
Packit |
69e8b9 |
uncertain areas of code with XXX. This will make the debugging dumpers
|
|
Packit |
69e8b9 |
easy to spot if you forget to take them out.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
WWW and YYY are nice because they dump their arguments and then return
|
|
Packit |
69e8b9 |
the arguments. This way you can insert them into many places and still
|
|
Packit |
69e8b9 |
have the code run as before. Use ZZZ when you need to die with both a
|
|
Packit |
69e8b9 |
YAML dump and a full stack trace.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
The debugging functions are exported by default if you use the "-base"
|
|
Packit |
69e8b9 |
option, but only if you have previously used the "-XXX" option. To
|
|
Packit |
69e8b9 |
export all 4 functions use the export tag:
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
use SomeSpiffyModule ':XXX';
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
To force the debugging functions to use Data::Dumper instead of YAML:
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
use SomeSpiffyModule -dumper;
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
SPIFFY FUNCTIONS
|
|
Packit |
69e8b9 |
This section describes the functions the Spiffy exports. The "field",
|
|
Packit |
69e8b9 |
"const", "stub" and "super" functions are only exported when you use the
|
|
Packit |
69e8b9 |
"-base" or "-Base" options.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
field
|
|
Packit |
69e8b9 |
Defines accessor methods for a field of your class:
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
package Example;
|
|
Packit |
69e8b9 |
use Spiffy -Base;
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
field 'foo';
|
|
Packit |
69e8b9 |
field bar => [];
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
sub lalala {
|
|
Packit |
69e8b9 |
$self->foo(42);
|
|
Packit |
69e8b9 |
push @{$self->{bar}}, $self->foo;
|
|
Packit |
69e8b9 |
}
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
The first parameter passed to "field" is the name of the attribute
|
|
Packit |
69e8b9 |
being defined. Accessors can be given an optional default value.
|
|
Packit |
69e8b9 |
This value will be returned if no value for the field has been set
|
|
Packit |
69e8b9 |
in the object.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
const
|
|
Packit |
69e8b9 |
const bar => 42;
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
The "const" function is similar to <field> except that it is
|
|
Packit |
69e8b9 |
immutable. It also does not store data in the object. You probably
|
|
Packit |
69e8b9 |
always want to give a "const" a default value, otherwise the
|
|
Packit |
69e8b9 |
generated method will be somewhat useless.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
stub
|
|
Packit |
69e8b9 |
stub 'cigar';
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
The "stub" function generates a method that will die with an
|
|
Packit |
69e8b9 |
appropriate message. The idea is that subclasses must implement
|
|
Packit |
69e8b9 |
these methods so that the stub methods don't get called.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
super
|
|
Packit |
69e8b9 |
If this function is called without any arguments, it will call the
|
|
Packit |
69e8b9 |
same method that it is in, higher up in the ISA tree, passing it all
|
|
Packit |
69e8b9 |
the same arguments. If it is called with arguments, it will use
|
|
Packit |
69e8b9 |
those arguments with $self in the front. In other words, it just
|
|
Packit |
69e8b9 |
works like you'd expect.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
sub foo {
|
|
Packit |
69e8b9 |
super; # Same as $self->SUPER::foo(@_);
|
|
Packit |
69e8b9 |
super('hello'); # Same as $self->SUPER::foo('hello');
|
|
Packit |
69e8b9 |
$self->bar(42);
|
|
Packit |
69e8b9 |
}
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
sub new() {
|
|
Packit |
69e8b9 |
my $self = super;
|
|
Packit |
69e8b9 |
$self->init;
|
|
Packit |
69e8b9 |
return $self;
|
|
Packit |
69e8b9 |
}
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
"super" will simply do nothing if there is no super method. Finally,
|
|
Packit |
69e8b9 |
"super" does the right thing in AUTOLOAD subroutines.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
METHODS
|
|
Packit |
69e8b9 |
This section lists all of the methods that any subclass of Spiffy
|
|
Packit |
69e8b9 |
automatically inherits.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
mixin
|
|
Packit |
69e8b9 |
A method to mixin a class at runtime. Takes the same arguments as
|
|
Packit |
69e8b9 |
"use mixin ...". Makes the target class a mixin of the caller.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
$self->mixin('SomeClass');
|
|
Packit |
69e8b9 |
$object->mixin('SomeOtherClass' => 'some_method');
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
parse_arguments
|
|
Packit |
69e8b9 |
This method takes a list of arguments and groups them into pairs. It
|
|
Packit |
69e8b9 |
allows for boolean arguments which may or may not have a value
|
|
Packit |
69e8b9 |
(defaulting to 1). The method returns a hash reference of all the
|
|
Packit |
69e8b9 |
pairs as keys and values in the hash. Any arguments that cannot be
|
|
Packit |
69e8b9 |
paired, are returned as a list. Here is an example:
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
sub boolean_arguments { qw(-has_spots -is_yummy) }
|
|
Packit |
69e8b9 |
sub paired_arguments { qw(-name -size) }
|
|
Packit |
69e8b9 |
my ($pairs, @others) = $self->parse_arguments(
|
|
Packit |
69e8b9 |
'red', 'white',
|
|
Packit |
69e8b9 |
-name => 'Ingy',
|
|
Packit |
69e8b9 |
-has_spots =>
|
|
Packit |
69e8b9 |
-size => 'large',
|
|
Packit |
69e8b9 |
'black',
|
|
Packit |
69e8b9 |
-is_yummy => 0,
|
|
Packit |
69e8b9 |
);
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
After this call, $pairs will contain:
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
{
|
|
Packit |
69e8b9 |
-name => 'Ingy',
|
|
Packit |
69e8b9 |
-has_spots => 1,
|
|
Packit |
69e8b9 |
-size => 'large',
|
|
Packit |
69e8b9 |
-is_yummy => 0,
|
|
Packit |
69e8b9 |
}
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
and @others will contain 'red', 'white', and 'black'.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
boolean_arguments
|
|
Packit |
69e8b9 |
Returns the list of arguments that are recognized as being boolean.
|
|
Packit |
69e8b9 |
Override this method to define your own list.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
paired_arguments
|
|
Packit |
69e8b9 |
Returns the list of arguments that are recognized as being paired.
|
|
Packit |
69e8b9 |
Override this method to define your own list.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
ARGUMENTS
|
|
Packit |
69e8b9 |
When you "use" the Spiffy module or a subclass of it, you can pass it a
|
|
Packit |
69e8b9 |
list of arguments. These arguments are parsed using the
|
|
Packit |
69e8b9 |
"parse_arguments" method described above. The special argument "-base",
|
|
Packit |
69e8b9 |
is used to make the current package a subclass of the Spiffy module
|
|
Packit |
69e8b9 |
being used.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
Any non-paired parameters act like a normal import list; just like those
|
|
Packit |
69e8b9 |
used with the Exporter module.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
USING SPIFFY WITH BASE.PM
|
|
Packit |
69e8b9 |
The proper way to use a Spiffy module as a base class is with the
|
|
Packit |
69e8b9 |
"-base" parameter to the "use" statement. This differs from typical
|
|
Packit |
69e8b9 |
modules where you would want to "use base".
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
package Something;
|
|
Packit |
69e8b9 |
use Spiffy::Module -base;
|
|
Packit |
69e8b9 |
use base 'NonSpiffy::Module';
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
Now it may be hard to keep track of what's Spiffy and what is not.
|
|
Packit |
69e8b9 |
Therefore Spiffy has actually been made to work with base.pm. You can
|
|
Packit |
69e8b9 |
say:
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
package Something;
|
|
Packit |
69e8b9 |
use base 'Spiffy::Module';
|
|
Packit |
69e8b9 |
use base 'NonSpiffy::Module';
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
"use base" is also very useful when your class is not an actual module
|
|
Packit |
69e8b9 |
(a separate file) but just a package in some file that has already been
|
|
Packit |
69e8b9 |
loaded. "base" will work whether the class is a module or not, while the
|
|
Packit |
69e8b9 |
"-base" syntax cannot work that way, since "use" always tries to load a
|
|
Packit |
69e8b9 |
module.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
base.pm Caveats
|
|
Packit |
69e8b9 |
To make Spiffy work with base.pm, a dirty trick was played. Spiffy swaps
|
|
Packit |
69e8b9 |
"base::import" with its own version. If the base modules are not Spiffy,
|
|
Packit |
69e8b9 |
Spiffy calls the original base::import. If the base modules are Spiffy,
|
|
Packit |
69e8b9 |
then Spiffy does its own thing.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
There are two caveats.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
Spiffy must be loaded first.
|
|
Packit |
69e8b9 |
If Spiffy is not loaded and "use base" is invoked on a Spiffy
|
|
Packit |
69e8b9 |
module, Spiffy will die with a useful message telling the author to
|
|
Packit |
69e8b9 |
read this documentation. That's because Spiffy needed to do the
|
|
Packit |
69e8b9 |
import swap beforehand.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
If you get this error, simply put a statement like this up front in
|
|
Packit |
69e8b9 |
your code:
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
use Spiffy ();
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
No Mixing
|
|
Packit |
69e8b9 |
"base.pm" can take multiple arguments. And this works with Spiffy as
|
|
Packit |
69e8b9 |
long as all the base classes are Spiffy, or they are all non-Spiffy.
|
|
Packit |
69e8b9 |
If they are mixed, Spiffy will die. In this case just use separate
|
|
Packit |
69e8b9 |
"use base" statements.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
SPIFFY TODO LIST
|
|
Packit |
69e8b9 |
Spiffy is a wonderful way to do OO programming in Perl, but it is still
|
|
Packit |
69e8b9 |
a work in progress. New things will be added, and things that don't work
|
|
Packit |
69e8b9 |
well, might be removed.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
AUTHOR
|
|
Packit |
69e8b9 |
Ingy döt Net <ingy@cpan.org>
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
COPYRIGHT AND LICENSE
|
|
Packit |
69e8b9 |
Copyright 2004-2014. Ingy döt Net.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
This program is free software; you can redistribute it and/or modify it
|
|
Packit |
69e8b9 |
under the same terms as Perl itself.
|
|
Packit |
69e8b9 |
|
|
Packit |
69e8b9 |
See <http://www.perl.com/perl/misc/Artistic.html>
|
|
Packit |
69e8b9 |
|