Moose in Catalyst

The moosified version of our application (AddressBook.pm) from the previous chapter will look like the following code sample. You will need to moosify your application if you want to use any moosified plugin/role in your application.

package AddressBook;
use Moose;
use namespace::autoclean;
use Catalyst( qw/
ConfigLoader
Static::Simple
/);
$app->config( name => 'MyApp' );
$app->setup;
# If you want to make use of modifiers. You can do something like this
before finalize
{
# Do something here before the finalize of every response
};

As discussed earlier, before is the modifier and we can just ask Catalyst to run the last code before executing the finalize method.

The use of namespace::autoclean is a best practice, which cleans up the imported functions from the namespace.

Controller

Moose can be used in the Catalyst controllers to make use of method modifiers like we just did. This is demonstrated in the following code sample with a controller method we had written in Chapter 6:

package AddressBook::Controller::Test;
use Moose;
use namespace::autoclean;
BEGIN { extends 'Catalyst::Controller' };
sub count_users : Local {
my ($self, $c) = @_;
my $count = $c->model('AddressDBI')->count_users();
$c->stash->{message}="There are $count users. ";
}
after count_users => sub {
$c->stash->{message} .= "And they all Like Moose");
};

The last code is a simple example of using Moose in a controller. Just like in the application package, we use Moose and namespace::autoclean. We use extends 'Catalyst::Controller' just as explained in how Moose allows inheritance. The extends is wrapped in a BEGIN block, so the parent is declared as soon as Perl sees the line. This makes sure that the subroutine attributes such as :Local can be resolved at compile time.

The example uses the after method modifier to effectively execute a block of code after the execution of the method count_users.

CatalystX declare

Catalyst declare is an experimental plugin that can make the application, controller, and model description more descriptive and easier. It is not really a plugin, but rather a declarative syntax module based on Devel::Declare. It is a specialized version of MooseX-Declare.

Also, note that CatalystX-Declare will probably change it's behavior in the future a bit, as it really is experimental.

The last example using Catalyst::Declare can be rewritten as follows:

Use CatalystX::Declare
controller AddressBook::Controller::Test;
action count_users as 'count' under '/' {
my $count = $ctx->model('AddressDBI')->count_users();
$ctx->stash->{message}="There are $count users. ";
}
after count_users {
$ctx->stash->{message} .= "And they all Like Moose");
}

Note

The method modifiers are parsed by MooseX-Declare, and therefore, are also a syntax element and not in the procedural around foo => sub { } form. There's also no need to access the arguments directly. $self and $ctx are automatically lexically available, and all other parameters can be defined in the signature as with MooseX-Declare.

At first glance it doesn't look like much. However, once you understand the changes in declaration above, you will be taken through the benefits. As you can notice, using CatalystX::Declare makes a few changes to the way controller and controller methods are defined. The controller itself is defined using the controller keyword and actions are defined using the action.

Please note that actions are defined with action, methods are defined with method as follows.

action count_users as 'count' under '/'

The last line means that this controller method will be executed when the query path is the following controllerpath/count. Notice that it mentions that the name of the controller method is count and the name of the method follows the "/" immediately.

If you are already beginning to wonder if this is similar to the chaining syntax that you have learned earlier, then you are right! CatalystX::Declare simplifies chaining for method declaration. So the following line:

action count_users as 'count' under '/'

is the equivalent of the following line:

sub count_users: Chained('/') PathPart('count') CaptureArgs(0)

As you can see, CatalystX::Declare already provides value in being more descriptive on the chaining syntax. But that's not all. Consider a chaining situation where the base action is the same and there are many chained items. We can achieve that easily by:

Final action customers under base { #Block code goes here }
Final action vendors under base { #Block code goes here }

Or

under count {
final action customers { #Block code goes here }
final action vendors {#Block code goes here }
}

If this had to be written using the regular Perl syntax it would have been:

sub count_users: Chained('count') PathPart('customers') CaptureArgs(0)
{ # Block code goes here }
sub count_users: Chained('count') PathPart('vendors') CaptureArgs(0)
{ #Block code goes here }

CatalystX::Declare also provides a mechanism for method signatures. That is, CatalystX::Declare makes it possible to define the arguments that a method expects in a declarative syntax.

For example, the vendors method can expect a vendor ID and hence can be written as follows:

Final action vendors under base (Int $id ) { #Block code goes here }

As shown with the usage of is a earlier, there are defined types already and it is also simple to define your own data types. Having Int $id will pass any integer after the action's path part in the URL to the controller action.

Hence, the URL Test/count/vendors/12 will execute the above method passing 12 as the parameter. However, Test/count/vendors/abcd will not call this method.

Having method signatures allows for a behavior very similar to method overriding. Suppose, you would like to accept either an ID or a name for the above method and have two different procedures for handling those parameters. You would do something like the following:

Final action vendors_id as vendors under base (Int id ) { #code goes here }
Final action vendors_name as vendors under base (String id ) { #code goes here }

The previous statements will result in the following URLs: Test/count/vendors/12 executing the vendors_id block and Test/count/vendors/abcd executing the vendors_name block.

Of course, this is just an example to demonstrate the concept. There are better uses of this functionality when abstracted carefully. For further reading on CatalystX::Declare refer to the following two links:

http://search.cpan.org/~phaylon/CatalystX-Declare-0.011/lib/CatalystX/Declare.pm

http://www.catalystframework.org/calendar/2009/5

CatalystX

You may have been wondering about the namespace CatalystX. Well CatalystX is the namespace used by Catalyst modules that extend Catalyst but are neither plugins nor typical Catalyst components. Available Catalyst packages include a variety of functionalities that can make your application development easier by reusing code. For a complete list of modules, search for CatalystX on cpan:

http://search.cpan.org/search?m=all&q=catalystx&s=71

You may also want to explore CatalystX::Traits, which are loadable Moose roles that can be used in your application—model, controller, and view. To understand more on this read http://www.catalystframework.org/calendar/2009/10.

For an example of traits, you may want to have a look at the CatalystX SimpleLogin trait at:

http://search.cpan.org/~bobtfish/CatalystX-SimpleLogin-0.09/lib/CatalystX/SimpleLogin.pm

Roles

The with keyword that we have discussed above allows easy use of Moose roles in a Catalyst application.

Following is an example of using roles in a controller:

use Moose;
use namespace::autoclean;
BEGIN { extends 'Catalyst::Controller' };
with 'MyApp::ControllerRole';

To define a role you can use the following syntax:

package MyApp::ControllerRole;
use MooseX::MethodAttributes::Role;
use namespace::autoclean;

Note that this role is similar to any other class except that it uses MooseX::MethodAttributes::Role instead of Moose. This is a Moose extension that allows roles to provide subroutine attributes that will be composed into the consuming class.

Traits are similar to roles except that they are applied to the objects and not the class. What this means is that the meta of any object will access the elements in the class.

For example look at the following code:

package MyApp::Meta::Class::Trait::SomeClass;
use Moose::Role;
sub foo { warn 'foo' }
package Moose::Meta::Class::Custom::Trait::SomeClass;
sub register_implementation { 'MyApp::Meta::Class::Trait::HasTable' }
package MyApp::User; use Moose -traits => 'HasTable';
__PACKAGE__->meta->foo();

You can see that traits are used similar to roles here, except that the meta (class details) of the package is used to access the methods. You can think of this as dynamically applied roles.

Traits makes a lot of things very simple. You may want to explore more about loadable traits and using them with controllers and many such things once you get familiar with Moose. A good read is http://www.catalystframework.org/calendar/2009/10.

Traits really exploit the meta programming capability that Moose brings at your disposal. More on meta programming and Moose can be read at http://search.cpan.org/~flora/Moose-1.07/lib/Moose/Cookbook/Meta/Recipe2.pod

Types

In this section, we will briefly discuss data types as mentioned previously. You are already aware that there are a collection of predefined data types that can be used with isa when declaring a property or with a method parameter when using Catalyst Declare. In this section, we will learn how to define our own data types. Let us start with a well-written example from the MooseX::Types definition (refer to http://search.cpan.org/~rkitover/MooseX-Types-0.21/lib/MooseX/Types.pm):

subtype PositiveInt,
as Int,
where { $_ >= 0 },
message { "Int is not larger than 0" };

Note

Include 0, otherwise the coercion defined below won't work. Because 0 * -1 is still 0 and wouldn't pass.

In the last example, a PositiveInt type is created as a subtype of Int. The constraint for the type is provided by the where block, which in this example checks if the passed argument is greater than 0 for PositiveInt. The message block describes the error message that is returned on failing the type constraint.

Any type or subtype creates the following additional exports:

Is_type:

The is_type accessor checks if the given argument is of a particular type. For example, is_PostiveInt(10);

To_Type:

The to_type accessor does typecasting or coerces one type to another. Note that Moose uses the term coercion for the transformation from one type to another. For example, to_PositiveInt(-10).

However, to make use of to_type, the type must have a coerce declaration as follows:

coerce PositiveInt,
from Int,
via { abs $_ };

The above argument will take any negative number and covert it to positive by multiplying it with -1 which is defined within the via block. The typecast, however, works works only for integers and hence we have restricted coercing to Int using from.

Being able to declare transitions of values from one form to another outside of the classes that use them is a very powerful form of encapsulation. There are also lots of MooseX-Types libraries already on CPAN handling things such as DateTime, URI and Path::Class values among many others.

Model

Moose can potentially offer a lot of value when working with models. An ideal mechanism would be to combine the features of Moose with DBIx::Class. Though work is under way to integrate both, at the time of writing this to leverage the power of DBIC and Moose, the declarations have to be made twice in the model.

The advantage of declaring attributes using Moose along with DBIC in the model is that the attributes can benefit from type checking with "isa". At this stage you may not find many benefits with this approach. However, if you would like to see an example, you may want to follow the link http://code2.0beta.co.uk/reaction/svn/Reaction/0.001/trunk/t/lib/RTest/TestDB/Foo.pm.

Immutable

The last thing that you will have to be aware of is that the Moose definitions of methods and properties and even the class can change at runtime. However, in most cases this flexibility is not required. Informing Moose that your class will not go mutation at runtime can save on some performance. Hence, it is always a good practice to end your Moose classes with the following line:

__PACKAGE__->meta->make_immutable(inline_constructor => 0);

Note

The inline_constructor => 0 is only required if you are inheriting from a non-Moose class that defines its own constructor. You might also want to use MooseX::NonMoose in that case, as that will upgrade the original constructor and make sure that things such as BUILD are called, attributes are initialized, and so on.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.189.199