Skip site navigation (1)Skip section navigation (2)

FreeBSD Manual Pages

  
 
  

home | help
Moose::Cookbook::ExtenUser:ContributedoPerloDocuxtending::ExtensionOverview(3)

NAME
       Moose::Cookbook::Extending::ExtensionOverview - Moose extension
       overview

VERSION
       version 2.2013

DESCRIPTION
       Moose provides several ways in which extensions can hook	into Moose and
       change its behavior. Moose also has a lot of behavior that can be
       changed.	This recipe will provide an overview of	each extension method
       and give	you some recommendations on what tools to use.

       If you haven't yet read the recipes on metaclasses, go read those
       first. You can't	write Moose extensions without understanding the
       metaclasses, and	those recipes also demonstrate some basic extension
       mechanisms, such	as metaclass subclasses	and traits.

   Playing Nice	With Others
       One of the goals	of this	overview is to help you	build extensions that
       cooperate well with other extensions. This is especially	important if
       you plan	to release your	extension to CPAN.

       Moose comes with	several	modules	that exist to help your	write
       cooperative extensions. These are Moose::Exporter and
       Moose::Util::MetaRole. By using these two modules, you will ensure that
       your extension works with both the Moose	core features and any other
       CPAN extension using those modules.

PARTS OF Moose YOU CAN EXTEND
       The types of things you might want to do	in Moose extensions fall into
       a few broad categories.

   Metaclass Extensions
       One way of extending Moose is by	extending one or more Moose
       metaclasses. For	example, in
       Moose::Cookbook::Meta::Table_MetaclassTrait we saw a metaclass role
       that added a "table" attribute to the metaclass.	If you were writing an
       ORM, this would be a logical extension.

       Many of the Moose extensions on CPAN work by providing an attribute
       metaclass role. For example, the	MooseX::Aliases	module provides	an
       attribute metaclass trait that lets you specify aliases to install for
       methods and attribute accessors.

       A metaclass extension can be packaged as	a role/trait or	a subclass. If
       you can,	we recommend using traits instead of subclasses, since it's
       much easier to combine disparate	traits than it is to combine a bunch
       of subclasses.

       When your extensions are	implemented as roles, you can apply them with
       the Moose::Util::MetaRole module.

   Providing Sugar Functions
       As part of a metaclass extension, you may also want to provide some
       sugar functions,	just like Moose.pm does. Moose provides	a helper
       module called Moose::Exporter that makes	this much simpler. We will be
       use Moose::Exporter in several of the extension recipes.

   Object Class	Extensions
       Another common Moose extension technique	is to change the default
       object class's behavior.	As with	metaclass extensions, this can be done
       with a role/trait or with a subclass. For example,
       MooseX::StrictConstructor extension applies a trait that	makes the
       constructor reject arguments which don't	match its attributes.

       Object class extensions often include metaclass extensions as well. In
       particular, if you want your object extension to	work when a class is
       made immutable, you may need to modify the behavior of some or all of
       the Moose::Meta::Instance, Moose::Meta::Method::Constructor, and
       Moose::Meta::Method::Destructor objects.

       The Moose::Util::MetaRole module	lets you apply roles to	the base
       object class, as	well as	the meta classes just mentioned.

   Providing a Role
       Some extensions come in the form	of a role for you to consume. The
       MooseX::Object::Pluggable extension is a	great example of this. In
       fact, despite the "MooseX" name,	it does	not actually change anything
       about Moose's behavior. Instead,	it is just a role that an object which
       wants to	be pluggable can consume.

       If you are implementing this sort of extension, you don't need to do
       anything	special. You simply create a role and document that it should
       be used via the normal "with" sugar:

	  package MyApp::User;

	  use Moose;

	  with 'My::Role';

       Don't use "MooseX" in the name for such packages.

   New Types
       Another common Moose extension is a new type for	the Moose type system.
       In this case, you simply	create a type in your module. When people load
       your module, the	type is	created, and they can refer to it by name
       after that. The MooseX::Types::URI and MooseX::Types::DateTime
       distributions are two good examples of how this works. These both build
       on top of the MooseX::Types extension.

ROLES VS TRAITS	VS SUBCLASSES
       It is important to understand that roles	and traits are the same	thing.
       A trait is simply a role	applied	to a instance. The only	thing that may
       distinguish the two is that a trait can be packaged in a	way that lets
       Moose resolve a short name to a class name. In other words, with	a
       trait, the caller can refer to it by a short name like "Big", and Moose
       will resolve it to a class like
       "MooseX::Embiggen::Meta::Attribute::Role::Big".

       See Moose::Cookbook::Meta::Labeled_AttributeTrait and
       Moose::Cookbook::Meta::Table_MetaclassTrait for examples	of traits in
       action. In particular, both of these recipes demonstrate	the trait
       resolution mechanism.

       Implementing an extension as a (set of) metaclass or base object
       role(s) will make your extension	more cooperative. It is	hard for an
       end-user	to effectively combine together	multiple metaclass subclasses,
       but it is very easy to combine roles.

USING YOUR EXTENSION
       There are a number of ways in which an extension	can be applied.	In
       some cases you can provide multiple ways	of consuming your extension.

   Extensions as Metaclass Traits
       If your extension is available as a trait, you can ask end users	to
       simply specify it in a list of traits. Currently, this only works for
       (class) metaclass and attribute metaclass traits:

	 use Moose -traits => [	'Big', 'Blue' ];

	 has 'animal' => (
	     traits => [ 'Big',	'Blue' ],
	     ...
	 );

       If your extension applies to any	other metaclass, or the	object base
       class, you cannot use the trait mechanism.

       The benefit of the trait	mechanism is that is very easy to see where a
       trait is	applied	in the code, and consumers have	fine-grained control
       over what the trait applies to. This is especially true for attribute
       traits, where you can apply the trait to	just one attribute in a	class.

   Extensions as Metaclass (and	Base Object) Roles
       Implementing your extensions as metaclass roles makes your extensions
       easy to apply, and cooperative with other role-based extensions for
       metaclasses.

       Just as with a subclass,	you will probably want to package your
       extensions for consumption with a single	module that uses
       Moose::Exporter.	However, in this case, you will	use
       Moose::Util::MetaRole to	apply all of your roles. The advantage of
       using this module is that it preserves any subclassing or roles already
       applied to the user's metaclasses. This means that your extension is
       cooperative by default, and consumers of	your extension can easily use
       it with other role-based	extensions. Most uses of Moose::Util::MetaRole
       can be handled by Moose::Exporter directly; see the Moose::Exporter
       docs.

	 package MooseX::Embiggen;

	 use Moose::Exporter;

	 use MooseX::Embiggen::Role::Meta::Class;
	 use MooseX::Embiggen::Role::Meta::Attribute;
	 use MooseX::Embiggen::Role::Meta::Method::Constructor;
	 use MooseX::Embiggen::Role::Object;

	 Moose::Exporter->setup_import_methods(
	     class_metaroles =>	{
		 class	   => ['MooseX::Embiggen::Role::Meta::Class'],
		 attribute => ['MooseX::Embiggen::Role::Meta::Attribute'],
		 constructor =>
		     ['MooseX::Embiggen::Role::Meta::Method::Constructor'],
	     },
	     base_class_roles => ['MooseX::Embiggen::Role::Object'],
	 );

       As you can see from this	example, you can use Moose::Util::MetaRole to
       apply roles to any metaclass, as	well as	the base object	class. If some
       other extension has already applied its own roles, they will be
       preserved when your extension applies its roles,	and vice versa.

   Providing Sugar
       With Moose::Exporter, you can also export your own sugar	functions:

	 package MooseX::Embiggen;

	 use Moose::Exporter;

	 Moose::Exporter->setup_import_methods(
	     with_meta	     =>	['embiggen'],
	     class_metaroles =>	{
		 class => ['MooseX::Embiggen::Role::Meta::Class'],
	     },
	 );

	 sub embiggen {
	     my	$meta =	shift;
	     $meta->embiggen(@_);
	 }

       And then	the consumer of	your extension can use your "embiggen" sub:

	 package Consumer;

	 use Moose;
	 use MooseX::Embiggen;

	 extends 'Thing';

	 embiggen ...;

       This can	be combined with metaclass and base class roles	quite easily.

   More	advanced extensions
       Providing your extension	simply as a set	of traits that gets applied to
       the appropriate metaobjects is easy, but	sometimes not sufficient. For
       instance, sometimes you need to supply not just a base object role, but
       an actual base object class (due	to needing to interact with existing
       systems that only provide a base	class).	To write extensions like this,
       you will	need to	provide	a custom "init_meta" method in your exporter.
       For instance:

	 package MooseX::Embiggen;

	 use Moose::Exporter;

	 my ($import, $unimport, $init_meta) = Moose::Exporter->build_import_methods(
	     install	     =>	['import', 'unimport'],
	     with_meta	     =>	['embiggen'],
	     class_metaroles =>	{
		 class => ['MooseX::Embiggen::Role::Meta::Class'],
	     },
	 );

	 sub embiggen {
	     my	$meta =	shift;
	     $meta->embiggen(@_);
	 }

	 sub init_meta {
	     my	$package = shift;
	     my	%options = @_;
	     if	(my $meta = Class::MOP::class_of($options{for_class})) {
		 if ($meta->isa('Class::MOP::Class')) {
		     my	@supers	= $meta->superclasses;
		     $meta->superclasses('MooseX::Embiggen::Base::Class')
			 if @supers == 1 && $supers[0] eq 'Moose::Object';
		 }
	     }
	     $package->$init_meta(%options);
	 }

       In the previous examples, "init_meta" was generated for you, but	here
       you must	override it in order to	add additional functionality. Some
       differences to note:

       "build_import_methods" instead of "setup_import_methods"
	   "build_import_methods" simply returns the "import", "unimport", and
	   "init_meta" methods,	rather than installing them under the
	   appropriate names.  This way, you can write your own	methods	which
	   wrap	the functionality provided by Moose::Exporter.	The
	   "build_import_methods" sub also takes an additional "install"
	   parameter, which tells it to	just go	ahead and install these
	   methods (since we don't need	to modify them).

       "sub init_meta"
	   Next, we must write our "init_meta" wrapper.	The important things
	   to remember are that	it is called as	a method, and that %options
	   needs to be passed through to the existing implementation. We call
	   the base implementation by using the	$init_meta subroutine
	   reference that was returned by "build_import_methods" earlier.

       Additional implementation
	   This	extension sets a different default base	object class. To do
	   so, it first	checks to see if it's being applied to a class,	and
	   then	checks to see if Moose::Object is that class's only
	   superclass, and if so, replaces that	with the superclass that this
	   extension requires.

	   Note	that two extensions that do this same thing will not work
	   together properly (the second extension to be loaded	won't see
	   Moose::Object as the	base object, since it has already been
	   overridden).	This is	why using a base object	role is	recommended
	   for the general case.

	   This	"init_meta" also works defensively, by only applying its
	   functionality if a metaclass	already	exists.	This makes sure	it
	   doesn't break with legacy extensions	which override the metaclass
	   directly (and so must be the	first extension	to initialize the
	   metaclass). This is likely not necessary, since almost no
	   extensions work this	way anymore, but just provides an additional
	   level of protection.	The common case	of "use	Moose; use
	   MooseX::Embiggen;" is not affected regardless.

       This is just one	example	of what	can be done with a custom "init_meta"
       method.	It can also be used for	preventing an extension	from being
       applied to a role, doing	other kinds of validation on the class being
       applied to, or pretty much anything that	would otherwise	be done	in an
       "import"	method.

LEGACY EXTENSION MECHANISMS
       Before the existence of Moose::Exporter and Moose::Util::MetaRole,
       there were a number of other ways to extend Moose. In general, these
       methods were less cooperative, and only worked well with	a single
       extension.

       These methods include metaclass.pm, Moose::Policy (which	uses
       metaclass.pm under the hood), and various hacks to do what
       Moose::Exporter does. Please do not use these for your own extensions.

       Note that if you	write a	cooperative extension, it should cooperate
       with older extensions, though older extensions generally	do not
       cooperate with each other.

CONCLUSION
       If you can write	your extension as one or more metaclass	and base
       object roles, please consider doing so. Make sure to read the docs for
       Moose::Exporter and Moose::Util::MetaRole as well.

AUTHORS
       o   Stevan Little <stevan.little@iinteractive.com>

       o   Dave	Rolsky <autarch@urth.org>

       o   Jesse Luehrs	<doy@tozt.net>

       o   Shawn M Moore <code@sartak.org>

       o   xxxx	x<section>xx'xx	(Yuval Kogman) <nothingmuch@woobling.org>

       o   Karen Etheridge <ether@cpan.org>

       o   Florian Ragwitz <rafl@debian.org>

       o   Hans	Dieter Pearcey <hdp@weftsoar.net>

       o   Chris Prather <chris@prather.org>

       o   Matt	S Trout	<mst@shadowcat.co.uk>

COPYRIGHT AND LICENSE
       This software is	copyright (c) 2006 by Infinity Interactive, Inc.

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

perl v5.32.1		      Moose::Cookbook::Extending::ExtensionOverview(3)

NAME | VERSION | DESCRIPTION | PARTS OF Moose YOU CAN EXTEND | ROLES VS TRAITS VS SUBCLASSES | USING YOUR EXTENSION | LEGACY EXTENSION MECHANISMS | CONCLUSION | AUTHORS | COPYRIGHT AND LICENSE

Want to link to this manual page? Use this URL:
<https://www.freebsd.org/cgi/man.cgi?query=Moose::Cookbook::Extending::ExtensionOverview&sektion=3&manpath=FreeBSD+13.0-RELEASE+and+Ports>

home | help