NAME
MOP4Import::Declare - map import args to $meta->declare_...()
pragma methods.
SYNOPSIS
package YourExporter {
use MOP4Import::Declare -as_base; # "use strict" is turned on too.
use MOP4Import::Opts; # import a type 'Opts' and m4i_args()
use MOP4Import::Util qw/globref/; # encapsulates "no strict 'refs'".
use constant DEBUG => $ENV{DEBUG_MOP4IMPORT};
# This method implements '-foo' pragma,
# and adds method named 'foo()' in $callpack.
sub declare_foo {
my ($myPack, $callpack) = @_;
my $glob = globref("$callpack", 'foo'); # Note: "" to stringify.
*$glob = sub (@) { join("! ", "FOOOOOO", @_) };
}
# This method implements [bar => $x, $y, @z] pragma,
# and adds variables $bar, %bar and @bar in $callpack.
sub declare_bar {
(my $myPack, my Opts $callpack, my ($x, $y, @z)) = m4i_args(@_);
print STDERR "callpack = $callpack->{callpack}\n" if DEBUG;
my $glob = globref($callpack->{callpack}, 'bar');
*$glob = \ $x;
*$glob = +{bar => $y};
*$glob = \@z;
}
};
1
#-------------------
# Then you can use above from command line like:
% perl -MYourExporter=-foo -le 'print foo bar => 3'
FOOOOOO! bar! 3
%
#-------------------
# Or in another file:
package MyApp;
use YourExporter -foo, [bar => "A", "x", 1..3];
# Above means you called:
# use strict;
# use warnings;
# BEGIN {
# YourExporter->declare_foo('MyApp');
# YourExporter->declare_bar('MyApp', "A", "x", 1..3);
# }
print "scalar=$bar\t", "hash=$bar{bar}\t", "array=@bar\n";
# So, you will get:
# scalar=A hash=x array=1 2 3
DESCRIPTION
MOP4Import::Declare is one protocol implementation of MOP4Import family. You can use this module to implement your own exporter in an extensible way.
With MOP4Import::Declare, arguments of import() are mapped into method calls starting with declare_...()
.
"MetaObject Protocol for Import" in this module
import()
method of MOP4Import::Declare briefly does following:
sub import {
my ($myPack, @pragma) = @_;
my Opts $opts = m4i_opts([caller]);
$myPack->dispatch_declare($opts, -strict, @pragma);
}
An object of type MOP4Import::Opts, which is instanciated via m4i_opts(), carries caller information to each pragmas.
dispatch_declare() dispatches declare_PRAGMA()
pragma handlers for each pragma, based on argument types (string, arrayref or coderef).
- -PRAGMA
-
use YourExporter -PRAGMA;
-Identifier
, word starting with-
, is dispatched as:YourExporter->declare_PRAGMA($opts);
Note: You don't need to quote this pragma because perl has special support for this kind of syntax (bareword lead by
-
). - [PRAGMA => ARGS...]
-
use YourExporter [PRAGMA => @ARGS];
ARRAY ref is dispatched as:
YourExporter->declare_PRAGMA($opts, @ARGS);
- NAME, *NAME, $NAME, %NAME, @NAME, &NAME
-
use YourExporter qw/NAME *NAME $NAME %NAME @NAME &NAME/;
These kind of words (optionally starting with sigil) just behaves as ordinally export/import.
- sub {...}
-
use YourExporter sub { ... };
You can pass callback too.
sub { my ($yourExporterPackage, $opts) = @_; # do rest of job }
Simplified case API
If you just want to implement ordinal exporter and feel MOP4Import::Opts is overkill, you can just use scalar caller
as a MOP4Import option. m4i_args(@_) will convert given package name to MOP4Import::Opts
hash object for you.
sub import {
my ($myPack, @pragma) = @_;
my $callpack = caller;
$myPack->dispatch_declare($callpack, -strict, @pragma);
}
DEBUG_MOP4IMPORT environment variable
To inspect what MOP4Import::Declare does, set environment variable DEBUG_MOP4IMPORT
to positive integer. Logs are emitted to STDERR.
DEBUG_MOP4IMPORT=1 perl YourExporter.pm
PRAGMAS
All pragmas below are actually implemented as "declare_PRAGMA" method, so you can override them in your subclass, as you like.
-strict
This pragma turns on use strict; use warnings;
.
-fatal
This pragma turns on use warnings qw(FATAL all NONFATAL misc);
.
[base => CLASS...]
Establish an ISA relationship with base classes at compile time. Like base, this imports %FIELDS
from base classes too.
Note: when target module uses c3 mro, this pragma adds given classes in front of @ISA
.
[parent => CLASS...]
Establish an ISA relationship with base classes at compile time. In addition to "base", this loads requested classes at compile time, like parent.
-as_base, [as_base => FIELD_SPECs...]
This pragma sets YourExporter as base class of target module. Optional arguments are passed to "fields" in fields pragma.
Note: as noted in "base", this pragma cares mro of target module. You can simply inherit classes with "generic" to "specific" order.
[fields => SPEC...]
This pragma adds %FIELDS
definitions to target module, based on given field specs. Each fields specs are either single string or array ref like [FIELD_NAME => SPEC => value, ...]
.
use MOP4Import::Declare
[fields =>
qw/
foo bar baz
/
];
use MOP4Import::Declare
[fields =>
[debug => doc => 'debug level'],
[dbi_dsn => doc => 'DBI connection string'],
qw/dbi_user dbi_password/
];
For more about fields, see whyfields.
field spec hooks.
You can define special hook for field spec. That should named starting with declare___field_with_...
.
sub declare___field_with_foo {
(my $myPack, my $opts, my FieldSpec $fs, my ($k, $v)) = @_;
$fs->{$k} = $v;
# Do other jobs...
}
default
When field bar
in class Foo
has spec default => $VALUE
, method Foo::default_bar
is defined with $VALUE.
sub Foo::default_bar { $VALUE }
If VALUE is CODE ref, it is directly assigned to method symbol.
Note: This spec only cares about defining above default_...
method. To make default value assignment really work, you must have constructor which cooperate well with this. You can use MOP4Import::Base::Configure for that purpose but are not restricted to it. Anyway MOP4Import::Declare itself will be kept constructor agnostic.
[constant => NAME => VALUE, @OPTIONS]
use YourExporter [constant => FOO => 'BAR', or_ignore => 1];
This pragma adds constant sub NAME
to target module.
or_ignore => BOOL
-
If this option is given and given NAME already defined in target module, skip adding.
-inc
This pragma adds target module to %INC
so that make the module require
safe.
[map_methods => [FROM => TO]...]
This pragma looks up actual sub of TO
and set it to target module with name FROM
. For example:
package MyStore {
use MOP4Import::Declare
[parent => qw/Plack::Session::Store::File/]
, [map_methods => [get => 'fetch'], [set => 'store']];
}
use Plack::Builder;
builder {
enable 'Session::Simple', store => MyStore->new(dir => $sess_dir);
$app
};
METHODS
dispatch_declare($opts, PRAGMA...)
This implements MOP4Import::Declare
style type-based pragma dispatching.
YourExporter->dispatch_declare($opts, -foo, [bar => 'baz'], '*FOO');
is same as
YourExporter->declare_foo($opts);
YourExporter->declare_bar($opts, 'baz');
YourExporter->dispatch_import($opts, '*FOO');
dispatch_import($opts, $STRING)
This mimics Exporter like sigil based import. Actually this dispatches import_...
method with respect to leading sigil. (This means you can override each cases in your subclass). If $STRING
has no sigil, "import_NAME" will be called.
use YourExporter qw/*FOO $BAR @BAZ %QUX &QUUX/;
is same as
BEGIN {
YourExporter->import_GLOB($callpack, GLOB => 'FOO');
YourExporter->import_SCALAR($callpack, SCALAR => 'BAR');
YourExporter->import_ARRAY($callpack, ARRAY => 'BAZ');
YourExporter->import_HASH($callpack, HASH => 'QUX');
YourExporter->import_CODE($callpack, CODE => 'QUUX');
}
Note: some complex features like export list @EXPORT
, @EXPORT_OK
and :TAG
based import are not implemented.
If you really want to implement those features, you can inherit this module and simply override dispatch_import
. It will be called for all non reference pragmas.
import_NAME($opts, $name)
This method (hook) is called when simple word (matches /^\w+$/
) is given as import list.
use YourExporter qw/FOO/;
is same as:
BEGIN {
YourExporter->import_NAME(__PACKAGE__, 'Foo');
}
import_SIGIL($opts, $type, $name)
Actual implementation of import_GLOB
, import_SCALAR
, import_ARRAY
, import_CODE
.
TYPES
Opts
This type of object is always given as a second argument of each invocation of declare_PRAGMA
. For more details, see MOP4Import::Opts.
FieldSpec
fields pragma in this module creates this type of object for each field specs. Currently, only name
, doc
and default
are declared. But you can extend FieldSpec in your exporter like following:
use YourBaseObject {
use MOP4Import::Declare -as_base;
use MOP4Import::Types::Extend
FieldSpec => [[fields => qw/readonly required validator/]];
}
package MyApp {
use YourBaseObject -as_base,
[fields => [app_name =>
readonly => 1,
required => 1]]
}
SEE ALSO
AUTHOR
Kobayashi, Hiroaki <hkoba@cpan.org>
LICENSE
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.