Do Mojolicious and Moose play well together?

1.4k views Asked by At

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?

3

There are 3 answers

1
friedo On BEST ANSWER

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!!" );
    }
}
0
NickJHoran On

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.

0
oalders On

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.