http://qs321.pair.com?node_id=11118211


in reply to first steps with Mojolicious::Lite

I intended to comment sooner and forgot to do so.... so late to the party but here are some things that I think might be helpful:

One of the overarching goals of a MVC framework is to keep the controller as thin as possible. You want to push environmental decisions to configuration that can be decided at build time, business logic into the model, and leave in your application just startup decisions, and routes with thin controllers that just wire end points to the model, while passing the results through to the appropriate views.

In your application there's an opportunity to simplify your controller subs (the hybrid route / controller subs) by abstracting the business logic into its own module that can be accessed through a helper. I prefer to have helpers for physical resources such as storage, database, etc, that get instantiated at startup, and other helpers for business logic that, when instantiated, have the physical resource objects injected through their constructors. Dependency injection. That allows testing to happen in isolation of various components. But the biggest win is that by keeping the controllers thin, you can to a large degree look upon the framework as just one way of presenting your model. If next week you decided that you wanted to go with CGI or Catalyst or Dancer, the model shouldn't have to change at all.

A small reference app that demonstrates this might be something like:

#!/usr/bin/env perl use Mojolicious::Lite; use Time::HiRes 'time'; use FindBin qw($Bin); use lib "$Bin/lib"; my $startup = time; plugin Config => {file => "$Bin/my-hello-app.beta.conf"}; helper storage => sub { # instantiate a storage class; filesystem, database, or in-memory +based on config... # As long as the present the same interface, it doesn't matter. }; helper useragent => sub {return state $ua = Mojo::UserAgent->new;}; helper myhello => sub { my $c = shift; return state $hello_obj = do { require Model::Hello; Model::Hello->new( storage => $c->storage, ua => $c->useragent, ); }; }; get '/hello' => sub { my $c = shift; $c->render(json => $c->myhello->hello); }; get '/_live' => sub { shift->render( json => { alive_since => (time - +$startup) } ) }; app->start;

This does not demonstrate an actual Model class.

Now look how small your controller subs have become. Of course this is just a hello, but the controller can pass to the model calls whatever stash / param values it needs to. The application doesn't deal directly with storage, either, making the model and the application easier to test.

The other things I wanted to mention: Mojolicious comes with a user agent. No need to bring in LWP::UserAgent. And it includes its own testing framework:

#!/usr/bin/env perl use Test::More; use Test::Mojo; use FindBin qw($Bin); use lib "$Bin/../lib"; my $t = Test::Mojo->new(Mojo::File->new("$Bin/../hello")); $t->get_ok('/_live') ->status_is(200) ->json_has('/alive_since'); $t->get_ok('/hello') ->status_is(200) ->json_is({success => 1, code => 200, message => 'OK'}); done_testing();

Dave