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

NAME

Moose::Cookbook::Extending::Recipe2 - Acting like Moose.pm and providing sugar Moose-style

SYNOPSIS

  package MyApp::Mooseish;

  use strict;
  use warnings;

  our @EXPORT = qw( has_table );

  use base 'Exporter';
  use Class::MOP;
  use Moose ();

  sub import {
    my $caller = caller();

    return if $caller eq 'main';

    Moose::init_meta( $caller,
                      undef, # object base class
                      'MyApp::Meta::Class',
                    );

    Moose->import( { into => $caller }, @_ );

    __PACKAGE__->export_to_level( 1, @_ );
  }

  sub unimport {
      my $caller = caller();

      no strict 'refs';
      foreach my $name (@EXPORT) {
          if ( defined &{ $caller . '::' . $name } ) {
              my $keyword = \&{ $caller . '::' . $name };

              my ($pkg_name) = Class::MOP::get_code_info($keyword);

              next if $pkg_name ne __PACKAGE__;

              delete ${ $caller . '::' }{$name};
          }
      }

      Moose::unimport( { into_level => 1 } );
  }

  sub has_table {
      my $caller = caller();

      $caller->meta()->table(shift);
  }

DESCRIPTION

The code above shows what it takes to provide an import-based interface just like Moose.pm. This recipe builds on Moose::Cookbook::Extending::Recipe1. Instead of providing our own object base class, we provide our own metaclass class, and we also export a sugar subroutine has_table().

Given the above code, you can now replace all instances of use Moose with use MyApp::Mooseish. Similarly, no Moose is now replaced with no MyApp::Mooseish.

WARNING

This recipe covers a fairly undocumented and ugly part of Moose, and the techniques described here may be deprecated in a future release. If this happens, there will be plenty of warning, as a number of MooseX modules on CPAN already use these techniques.

HOW IT IS USED

The purpose of all this code is to provide a Moose-like interface. Here's what it would look like in actual use:

  package MyApp::User;

  use MyApp::Mooseish;

  has_table 'User';

  has 'username';
  has 'password';

  sub login { ... }

  no MyApp::Mooseish;

All of the normal Moose sugar (has(), with(), etc) is available when you use MyApp::Mooseish.

DISSECTION

The first bit of magic is the call to Moose::init_meta(). What this does is create a metaclass for the specified class. Normally, this is called by Moose.pm in its own import() method. However, we can call it first in order to provide an alternate metaclass class. We could also provide an alternate base object class to replace Moose::Object (see Moose::Cookbook::Extending::Recipe1 for an example).

The Moose::init_meta() call takes three parameters. The first is the class for which we are initializing a metaclass object. The second is the base object, which is Moose::Object by default. The third argument is the metaclass class, which is Moose::Meta::Class by default.

The next bit of magic is this:

  Moose->import( { into => $caller } );

This use of "into" is actually part of the Sub::Exporter API, which Moose.pm uses internally to export things like has() and extends().

Finally, we call __PACKAGE__->export_to_level(). This method actually comes from Exporter.

This is all a bit fragile since it doesn't stack terribly well. You can basically only have one Moose-alike module. This may be fixed in the still-notional MooseX::Exporter module someday.

The unimport() subroutine is basically a copy of the unimport() from Moose.pm. You can copy this verbatim into your code. Again, this doesn't stack well.

Finally, we have our has_table() subroutine. This provides a bit of sugar that looks a lot like has().

AUTHOR

Dave Rolsky <autarch@urth.org>

COPYRIGHT AND LICENSE

Copyright 2006-2008 by Infinity Interactive, Inc.

http://www.iinteractive.com

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