=head1 NAME

MOP4Import - Introduction to Thin Meta-Object Protocol to build extensible exporters

=head1 SYNOPSIS

Create inner-classes C<MyApp::Artist> and C<MyApp::CD>
using L<MOP4Import::Types>.

  package MyApp;
  use MOP4Import::Types
    (Artist => [[fields => qw/artistid name/]]
     , CD   => [[fields => qw/cdid artistid title year/]]);

  sub print_artist_cds {
    (my $self, my Artist $artist) = @_;
    my @cds = $self->DB->select(CD => {artistid => $artist->{artistid}});
    foreach my CD $cd (@cds) {
      print tsv($cd->{title}, $cd->{year}), "\n";
    }
  }


Define your exporter C<MyExporter> with help of L<MOP4Import::Declare>.

  package MyExporter {
    use MOP4Import::Declare -as_base;
    use MOP4Import::Util qw/globref/;
    
    sub declare_foo {
      my ($myPack, $opts, $callpack) = @_;
      # then add some method
      *{globref($callpack, 'foo')} = sub (@) { join("! ", "FOOOOOO", @_) };
    }
    
    sub declare_bar {
      my ($myPack, $opts, $callpack, $x, $y, @z) = @_;
      my $glob = globref($callpack, 'bar');
      *$glob = \ $x;
      *$glob = +{bar => $y};
      *$glob = \@z;
    }
  
    sub declare_say {
      my ($myPack, $opts, $callpack) = @_;
      require feature;
      feature->import('say');
    }
  };

Then use C<MyExporter> in your class like following:

  ### Import MyExporter into your class MyApp.
  package MyApp;
  use MyExporter -say, -foo, [bar => "A", "x", 1..3];
  
  # Above means you called:
  #  use strict;
  #  use warnings;
  #  MyExporter->declare_say($opts, 'MyApp');
  #  MyExporter->declare_foo($opts, 'MyApp');
  #  MyExporter->declare_bar($opts, 'MyApp', "A", "x", 1..3);
  
  say foo bar => 3;
  
  say "scalar=$bar\t", "hash=$bar{bar}\t", "array=@bar";


=head1 DESCRIPTION

MOP4Import, "Meta-Object Protocol for Import", I propose here,
is a set of (experimental) protocols
(and their dispatcher implementations) to write B<exporter> modules
in some B<extensible> ways.

For more about design background, see L<whyfields|MOP4Import::whyfields>.

=head2 What kind of problems does this try to solve?

It is well known for Perl programmers that
Perl's L<use|perlfunc/use> behaves like following code:

  use YourExporter LIST;

  # means:

  BEGIN { require YourExporter; YourExporter->import( LIST ); }

And this C<< YourExporter->import >> usually used to B<modify caller module>
just compiling about, like L<Exporter>.
This is a kind of Meta Programming.
If correctly used, this can help reducing some boilerplates and
have more chances to check programs statically.

However, since C<import> is a method and
a class can have atmost one C<import> method,
there is a difficulty about reusability.
For example, imagine some module X can be used like following
and you favored it:

  use X ':foo', ':bar';

You may want to reuse ":foo" pragma feature in your own module Y,
but X::import is monolithically writen with "if .. elsif ...",
so you end up to copy-n-paste specific portion to your Y::import.
(And someday other guy find your :foo is nice... repeated.)

Source of this problem here is that single function C<import> provides
both B<protocol(entry point)> and B<implementation>,
and there is no way to selectively reuse/override specific case
of import specs.

So, I propose some protocols for this area.
These protocols maps import list of "use Module LIST"
into specific method invocations like C<< Module->declare_foo() >>
, C<< Module->dispatch_pragma_bar() >>...

=head1 MODULES

=head2 MOP4Import::Declare

L<MOP4Import::Declare> protocol dispatcher basically maps each
C<import()> arguments to method calls starting with B<declare_...>.
Typicall use of MOP4Import::Declare is like following:

  use MOP4Import::Declare -strict, [fields => qw/foo bar/];

Above means basically:

  BEGIN {
    require MOP4Import::Declare;
    
    MOP4Import::Declare->declare_strict(+{}, __PACKAGE__);
    
    MOP4Import::Declare->declare_fields(+{}, __PACKAGE__, qw/foo bar/);
  }

These methods are called I<MOP4Import declaration pragma>,
or simply I<pragma>. This module also provides some useful pragmas
like C<-strict>, C<< [fields => qw/foo bar/] >> .. shown above
and serves as a base for other MOP4Import dispatchers.

If you are curious about my advocates of using fields,
see L<whyfields|MOP4Import::whyfields>.

This module also mimics type sigil specific import found in L<Exporter>,
so you don't have to write codes to export usual vars/subs/symbols.


=head2 MOP4Import::Types

L<MOP4Import::Types> is another style of protocol dispatcher.
This helps defining many B<inner classes> at once like following:

  package MyApp;
  use MOP4Import::Types
    T1 => [pragma, pragma..],
    T2 => [pragma,...];

Above means basically:

  package MyApp;
  sub T1 () {'MyApp::T1'}
  package MyApp::T1 {
    use MOP4Import::Declare pragma, pragma..;
  }
  sub T2 () {'MyApp::T2'}
  package MyApp::T2 {
    use MOP4Import::Declare pragma, ...;
  }

From protocol perspective, first is briefly same as:

  package MyApp;
  BEGIN {
    require MOP4Import::Types;

    MOP4Import::Types->dispatch_pairs_as(type => +{}, $callpack
       T1 => [pragma, pragma..],
       T2 => [pragma,...]
    );
  }

And that is

  package MyApp;
  BEGIN {
    require MOP4Import::Types;

    MOP4Import::Types->declare___type(+{}, $callpack, T1 => pragma, pragma..);
    MOP4Import::Types->declare___type(+{}, $callpack, T2 => pragma,...);
  }

=head2 MOP4Import::Util

L<MOP4Import::Util> provides several functions to handle GLOBs 
and C<%FIELDS> such as C<globref($class,$name)>, C<fields_hash($class)> ...

=head1 SAMPLES

=head2 MOP4Import::Base::Configure

L<MOP4Import::Base::Configure> is a sample (but enough usable)
base class for Object Orientation. To use this as a base class, simply:

  use MOP4Import::Base::Configure -as_base;

This class is designed to fully utilize B<statically checked> L<fields>.
Also, fields getters are automatically generated for I<public> fields.
To set fields values, call C<< $obj->configure(key => value, key2 => value2...) >>.
You can have set-hook as C<onconfigure_key($value)>.

Also there is its variant L<MOP4Import::Base::CLI>, which is designed for
Command Line applications.

=head2 MOP4Import::PSGIEnv

L<MOP4Import::PSGIEnv> defines an inner-class C<Env>
with predeclared L<PSGI> standard L<$env fields|PSGI/The Environment>.

To use B<static checking> for PSGI env, just C<use> this module
and change argument declaration from C<my ($env)> to C< (my Env $env) >.

Note: You are not limited to standard PSGI items.
you can add fields just by listing your own extensions.

  use MOP4Import::PSGIEnv qw/mypsgi.extension/;
  
  return sub {
    (my Env $env) = @_;
    return [200, [], ["PATH_INFO is ", $env->{PATH_INFO}
  		    , extension => $env->{'mypsgi.extension'}
  		  ]];
  }


=head1 SEE ALSO

L<MOP4Import::Declare> L<MOP4Import::Types>
L<MOP4Import::whyfields>

=head1 AUTHOR

KOBAYASHI, Hiroaki E<lt>hkoba@cpan.orgE<gt>

=head1 LICENSE

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.

=cut
