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

FreeBSD Manual Pages

  
 
  

home | help
Moose::Cookbook::BasicUserMCostributedoPerlBDocumentTTP_SubtypesAndCoercion(3)

NAME
       Moose::Cookbook::Basics::HTTP_SubtypesAndCoercion - Demonstrates
       subtypes	and coercion use HTTP-related classes (Request,	Protocol,
       etc.)

VERSION
       version 2.2013

SYNOPSIS
	 package Request;
	 use Moose;
	 use Moose::Util::TypeConstraints;

	 use HTTP::Headers  ();
	 use Params::Coerce ();
	 use URI	    ();

	 subtype 'My::Types::HTTP::Headers' => as class_type('HTTP::Headers');

	 coerce	'My::Types::HTTP::Headers'
	     =>	from 'ArrayRef'
		 => via	{ HTTP::Headers->new( @{$_} ) }
	     =>	from 'HashRef'
		 => via	{ HTTP::Headers->new( %{$_} ) };

	 subtype 'My::Types::URI' => as	class_type('URI');

	 coerce	'My::Types::URI'
	     =>	from 'Object'
		 => via	{ $_->isa('URI')
			  ? $_
			  : Params::Coerce::coerce( 'URI', $_ ); }
	     =>	from 'Str'
		 => via	{ URI->new( $_,	'http' ) };

	 subtype 'Protocol'
	     =>	as 'Str'
	     =>	where {	/^HTTP\/[0-9]\.[0-9]$/ };

	 has 'base' => ( is => 'rw', isa => 'My::Types::URI', coerce =>	1 );
	 has 'uri'  => ( is => 'rw', isa => 'My::Types::URI', coerce =>	1 );
	 has 'method'	=> ( is	=> 'rw', isa =>	'Str' );
	 has 'protocol'	=> ( is	=> 'rw', isa =>	'Protocol' );
	 has 'headers'	=> (
	     is	     =>	'rw',
	     isa     =>	'My::Types::HTTP::Headers',
	     coerce  =>	1,
	     default =>	sub { HTTP::Headers->new }
	 );

DESCRIPTION
       This recipe introduces type coercions, which are	defined	with the
       "coerce"	sugar function.	Coercions are attached to existing type
       constraints, and	define a (one-way) transformation from one type	to
       another.

       This is very powerful, but it can also have unexpected consequences, so
       you have	to explicitly ask for an attribute to be coerced. To do	this,
       you must	set the	"coerce" attribute option to a true value.

       First, we create	the subtype to which we	will coerce the	other types:

	 subtype 'My::Types::HTTP::Headers' => as class_type('HTTP::Headers');

       We are creating a subtype rather	than using "HTTP::Headers" as a	type
       directly. The reason we do this is that coercions are global, and a
       coercion	defined	for "HTTP::Headers" in our "Request" class would then
       be defined for all Moose-using classes in the current Perl interpreter.
       It's a best practice to avoid this sort of namespace pollution.

       The "class_type"	sugar function is simply a shortcut for	this:

	 subtype 'HTTP::Headers'
	     =>	as 'Object'
	     =>	where {	$_->isa('HTTP::Headers') };

       Internally, Moose creates a type	constraint for each Moose-using	class,
       but for non-Moose classes, the type must	be declared explicitly.

       We could	go ahead and use this new type directly:

	 has 'headers' => (
	     is	     =>	'rw',
	     isa     =>	'My::Types::HTTP::Headers',
	     default =>	sub { HTTP::Headers->new }
	 );

       This creates a simple attribute which defaults to an empty instance of
       HTTP::Headers.

       The constructor for HTTP::Headers accepts a list	of key-value pairs
       representing the	HTTP header fields. In Perl, such a list could be
       stored in an ARRAY or HASH reference. We	want our "headers" attribute
       to accept those data structures instead of an HTTP::Headers instance,
       and just	do the right thing. This is exactly what coercion is for:

	 coerce	'My::Types::HTTP::Headers'
	     =>	from 'ArrayRef'
		 => via	{ HTTP::Headers->new( @{$_} ) }
	     =>	from 'HashRef'
		 => via	{ HTTP::Headers->new( %{$_} ) };

       The first argument to "coerce" is the type to which we are coercing.
       Then we give it a set of	"from"/"via" clauses. The "from" function
       takes some other	type name and "via" takes a subroutine reference which
       actually	does the coercion.

       However,	defining the coercion doesn't do anything until	we tell	Moose
       we want a particular attribute to be coerced:

	 has 'headers' => (
	     is	     =>	'rw',
	     isa     =>	'My::Types::HTTP::Headers',
	     coerce  =>	1,
	     default =>	sub { HTTP::Headers->new }
	 );

       Now, if we use an "ArrayRef" or "HashRef" to populate "headers",	it
       will be coerced into a new HTTP::Headers	instance. With the coercion in
       place, the following lines of code are all equivalent:

	 $foo->headers(	HTTP::Headers->new( bar	=> 1, baz => 2 ) );
	 $foo->headers(	[ 'bar', 1, 'baz', 2 ] );
	 $foo->headers(	{ bar => 1, baz	=> 2 } );

       As you can see, careful use of coercions	can produce a very open
       interface for your class, while still retaining the "safety" of your
       type constraint checks. (1)

       Our next	coercion shows how we can leverage existing CPAN modules to
       help implement coercions. In this case we use Params::Coerce.

       Once again, we need to declare a	class type for our non-Moose URI
       class:

	 subtype 'My::Types::URI' => as	class_type('URI');

       Then we define the coercion:

	 coerce	'My::Types::URI'
	     =>	from 'Object'
		 => via	{ $_->isa('URI')
			  ? $_
			  : Params::Coerce::coerce( 'URI', $_ ); }
	     =>	from 'Str'
		 => via	{ URI->new( $_,	'http' ) };

       The first coercion takes	any object and makes it	a "URI"	object.	The
       coercion	system isn't that smart, and does not check if the object is
       already a URI, so we check for that ourselves. If it's not a URI
       already,	we let Params::Coerce do its magic, and	we just	use its	return
       value.

       If Params::Coerce didn't	return a URI object (for whatever reason),
       Moose would throw a type	constraint error.

       The other coercion takes	a string and converts it to a URI. In this
       case, we	are using the coercion to apply	a default behavior, where a
       string is assumed to be an "http" URI.

       Finally,	we need	to make	sure our attributes enable coercion.

	 has 'base' => ( is => 'rw', isa => 'My::Types::URI', coerce =>	1 );
	 has 'uri'  => ( is => 'rw', isa => 'My::Types::URI', coerce =>	1 );

       Re-using	the coercion lets us enforce a consistent API across multiple
       attributes.

CONCLUSION
       This recipe showed the use of coercions to create a more	flexible and
       DWIM-y API. Like	any powerful feature, we recommend some	caution.
       Sometimes it's better to	reject a value than just guess at how to DWIM.

       We also showed the use of the "class_type" sugar	function as a shortcut
       for defining a new subtype of "Object".

FOOTNOTES
       (1) This	particular example could be safer. Really we only want to
	   coerce an array with	an even	number of elements. We could create a
	   new "EvenElementArrayRef" type, and then coerce from	that type, as
	   opposed to a	plain "ArrayRef"

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.0		  Moose::Cookbook::Basics::HTTP_SubtypesAndCoercion(3)

NAME | VERSION | SYNOPSIS | DESCRIPTION | CONCLUSION | FOOTNOTES | AUTHORS | COPYRIGHT AND LICENSE

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

home | help