The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

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

MOP4Import::Types

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.