Do Mojolicious and Moose play well together?
Asked Answered
R

3

11

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?

Refuel answered 12/6, 2015 at 13:54 Comment(2)
Check on the Mojolicious Google group or #mojo on Perl's IRC servers.Blackness
Thanks. I'm actually on IRC so that would be my next move, but I thought it would be helpful to document the answer here for those who aren't on IRC.Refuel
S
7

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!!" );
    }
}
Seurat answered 12/6, 2015 at 17:36 Comment(1)
If you are using Moose to extend a non-Moose class, you really ought to use MooseX::NonMoose to avoid clashes in the constructor (among other issue).Ginsberg
R
8

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.

Refuel answered 21/12, 2016 at 22:45 Comment(0)
S
7

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!!" );
    }
}
Seurat answered 12/6, 2015 at 17:36 Comment(1)
If you are using Moose to extend a non-Moose class, you really ought to use MooseX::NonMoose to avoid clashes in the constructor (among other issue).Ginsberg
P
3

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.

Pacifistic answered 15/6, 2015 at 21:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.