I'm working on a Mojo app and I'd like to be able to consume some Moose roles to make my life easier.
On CPAN I see MojoX::Moose::Controller, which has very simple internals. I don't see much else on using Moose with Mojo. Any potential issues I should be aware of or is it just smooth sailing?
In my experience, they work fine together. I've successfully built Mojolicious apps with Moose (and Moo should work fine as well.)
Moose can extend the base Mojolicious controller class and then you can do whatever usual Moose things you want. Here's an example:
package MyApp {
use Moose;
extends 'Mojolicious';
with 'MyApp::Role::Whatever';
sub startup {
my $self = shift;
my $r = $self->routes;
$r->get('/')->to('foo#default');
}
}
package MyApp::Foo {
use Moose;
extends 'Mojolicious::Controller';
sub default {
my $self = shift;
$self->render( text => "Helloooooo!!" );
}
}
Since the time that I initially asked this question, we've moved some Mojo + Moose code into production. There are some caveats which I will share here. This answer was actually written by Florian Ragwitz. I'm posting on his behalf.
Mojolicious and Moose can play well together, but some care has to be taken for things to work well, and there are some caveats.
It's important that Moose's constructor is used for creating new objects. Using
MooseX::NonMoose
is an easy way to ensure that. Without calling into Moose
during object construction, many Moose features such as BUILDARGS
, BUILD
,
attribute type constraint checking, and eager builders won't work.
MooseX::NonMoose
will also delegate to the original Mojolicious::Controller
constructor, which has the behaviour of just blessing the constructor arguments
provided into a hash reference. This might result in some odd results.
For example:
package MyController;
use Moose;
use MooseX::NonMoose;
extends 'Mojolicious::Controller';
has foo => (init_arg => 'bar');
Later on...
MyController->new(bar => 42) # bless { foo => 42, bar => 42 }, 'MyController'
Note how the resulting blessed hash reference contains the keys foo
and
bar
, rather than just bar
as you'd expect from a regular Moose class. This
usually isn't a problem as long as you don't intend to use object slots with
the same name as another attribute's init_arg
.
There are also limitations on what Moose extensions can be used. Mojo requires hash-based instances, so you won't be able to use any of the non-hash-based meta instances Moose offers, like MooseX::GlobRef
and MooseX::ArrayRef
. MooseX::StrictConstructor
also won't work in
this environment, because Moose can't tell which constructor arguments were
intended to be consumed by the Mojo constructor.
Overall, combining Mojolicious and Moose should work pretty well in practice as long as you're aware of the small caveats and are OK with not being able to use certain Moose extensions.
They definitely play nicely. I've built an API that runs on a cluster of 20 servers. It's a mojo app that also uses moose classes which consume multiple roles.
The approach I've taken is to layer the application properly, right down to the storage layer. In that respect mojo is only really needed at the upper levels in the stack. Early on I create a moose-based request object that is then pushed down the stack. Lower down, a moose-based response object is created which passes the response back to the upper levels of the stack. Finally mojo takes over and handles the final json response.
We're pushing a lot of production traffic through the stack and it performs brilliantly. One thing I did was to make sure that I use XS versions of modules where possible as this boosted performance of the stack.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With