[cgiapp] CGI::Application status update from the maintainer

Mark Stosberg mark at summersault.com
Sat Sep 8 18:10:55 EDT 2012


Brett,

Thanks for the feedback.

>> - A "::Compat" addition that allows people to keep using a maximal
>>     amount of the old API if they need to. (Including a certain amount of
>>     plugins)
>
> Or maybe just do something like how perl5 turns on new version based
> features (e.g., use CGI::Application q/1.2.3/;).

My goal there is to really start fresh, and have a minimal amount of
extra stuff in the core. No one is being forced to upgrade, and changing
a single line of code seems reasonable for those who want to upgrade the
module without updating any of their other project code.

>> - PSGI-native. I'm excited that Perl web frameworks are converging here.
>>     We'll be able to take advantage of PSGI "Middleware" that was designed
>>     with other frameworks in mind. Many things that were CGI::App plugins
>>     before are now better done done as PSGI "Middleware". As a plugin
>>     author, you get the benefit of having more users who may be using
>>     other frameworks. The difference with CGI::App will be that PSGI will
>>     the *only* code path supported.
>
> Nice.
>
> I would like to warn that traditional persistent environments will
> continue to remain relevant, and I think it's a mistake to discount
> this.

I assume you mean mod_perl and FastCGI? PSGI helps code to run in those
environments in a clean way. In those cases, it's definitely preferable
to the old way of having framework code that includes conditionals for
each environment. The guts of CGI.pm are an example of that ugliness.

> Additionally, from what I have seen from the other frameworks, nothing
> solves anything in principle beyond what CAP does. Some make it easier
> to define runmodes or to set up dispatching, but you still need to
> properly create a proper MVC separation and set of supporting modules.
> It's the same amount of work no matter what you use.

I disagree that it's the same amount of work no matter what choices you
make. Some are easier to install, some are easier to learn, and some
have more formality that provide ready-made discipline to help organize
more complex projects.

>> - Uses Any::Moose / Mouse. I endorse the Moose API and Mouse brings
>>     much of that API to lower resource environments, like the CGI
>>     environment where CGI::Application has always performed well.
>>
>>     It's already possible to write a CGI::App subclass based on Moose or
>>     Mouse, but like with PSGI, I think it's in our best interest to
>>     upgrade to first class support.
>
> I am not sure what this buys anyone, to be frank.

I appreciate the frankness. :)

> I know that using these object layering might incite some sort of
> religious war.  Ultimately, this decision is clearly in the hands of
> those who do the work.  I have my reservations about moving away from
> Perlish idioms and towards the oop flavors of the week. Any core might
> be well served by avoiding any sort of meta object sugar over the long
> haul.

Moose was first released over six years ago, so I don't think it's fair
to call it's popularity weekly. With Any::Mouse, performance is good in
CGI environments via Mouse, and also good in persistent environments
with Moose. We've now reached a "Moose tipping point" where a project of
any significant size is likely to be loading Moose or Mouse or Moo
anyway, so whether to load them or not has become last of an issue.

For example, if you want to access Amazon APIs via Perl, you'd likely
choose Net::Amazon::S3, which loads Moose. If you use the modern
Text::XSlate templating system, you'll be loading Mouse. If you want to
conveniently access cookies in Test::WWW::Mechanize than you might use
HTTP::CookieMonster, which loads Moo.

Importantly, there's no requirement to use these in your own modules, or
even switch from CGI::Application to the new project.

> I think my overall point is that CAP and what it provides is timeless.
> The pendulum swings both ways, and it would be nice to see CAP focus
> on improving its strengths and not trying to do what the other kids
> might be doing.

I agree that there are timeless basic principles in there. I appreciate
PSGI and the Moose APIs because there are helping bring the Perl web
development community together by providing good options for re-using
code between frameworks, with PSGI Middleware and Moose roles.

>> - Plans to replace CGI.pm with request and response objects. As the
>>     CGI.pm maintainer, I could devote another full post to reasons why I
>>     don't to be using it in another 5 or 10 years. Details here are still
>>     to be determined as well.
>>
>>     Immediately we would see the "query()" method replaced with a "req"
>>     method to represent a "request" object. HTTP has "requests" and
>>     "responses". The idea of a "query object" is a CGI.pm'ism to leave
>>     behind.
>>
>>     About every other Perl framework I've looked at models the response
>>     and request this way, and it's time we implemented this sensible
>>     design as well.
>
> I think I am not really clear on what change in perspective means. Is
> it truly a semantic change or is it just a different name? I am
> somewhat familiar with some of the other new fangled frameworks, but
> not well enough to know what the difference is between this
> request/response versus query objects.

A "Query object" seems to be defined as "whatever functionality CGI.pm"
provides.  I could perhaps understand "query" as a synonym for "HTTP
request", but what does it have to do with the header() method used for
generating HTTP responses?

Consider:

  CGI.pm:
    Set a response header: $q->header('Foo' => 1);
    Get a response header: XXX Can't do it!
    Get a request header : $q->http('Foo');

  Plack::Request/Response:
    Set a response header: $res->header('Foo' => 1 );
    Get a response header: $res->header('Foo');
    Get a request header : $req->header('Foo');

With, CGI.pm, looking at a request header can be confusing and
asymettric. (Perhaps you've been sent a header to tell you whether to
return JSON or HTML). header() for responses, and "http()" for
requests??

A design like that of Plack::Request/Response better models reality,
with a symettric API that handles incoming and outgoing requests in a
uniform way.

Another advantage would be the compability with other frameworks use the
use  "res" and "req" response and requests. That would enable some kinds
of simple plugins to be shared.

>> - The popular and small "::Forward()" and "::Redirect()" plugins will
>>     be merged into the core.
>
> It would be really nice to merge in some bare bones Authentication and
> Authorization support - maybe ever by more fully developing CAP's lifecycle.

More detailed suggestions would be welcome here, but I'm hestitent. Are
there examples of other frames that do something here in a way that's
appealing?

I would generally plan to keep the core small, but would welcome more
full-featured stacks to be shipped that were based on it, as Titanium
did for CGI::Application.

One project that strikes me as interesting here is "Abilities".
It addresses the problem space by using a "role". It is framework
independent, but is intended to be used as part of web applications.

     https://metacpan.org/module/Abilities

This is an example of the kind of code I would like to make more easily
available to CGI::App based projects.

> I am a recent addition to this community, and prefer CAP well over the
> other alternatives.

That's good to hear, and welcome. Could you say more about why you
prefer it and how you use it?

> 1. scalability - it is unnecessarily awkward to have more than 2
> levels of subclassing currently.
>
> Direct subclass of CAP uses cgi_init; child of subclass uses setup;
> anything else must call SUPER::setup.

Another option I use sometimes is to use the callback system:

    # Register an action to happen at the init stage.
    __PACKAGE__->add_callback('init', sub {

    });

Note that the Moose API also helps here as well, as it allows you to say
this:

     # Add some additional functionality after 'setup' runs in the parent
     after 'setup' => sub {
         my $self = shift;
     };

 
https://metacpan.org/module/Moose::Manual::MethodModifiers#BEFORE-and-AFTER-modifiers

The Moose API also provides "BUILD", which like our callback system is
called for every class in the inheritance hierarchy. So another way to
add some "initialization" functionality would be to put this in your
Moose/Mouse based child class:

     # Another way to add extra functionality when setting up classes.
     sub BUILD {
         my $self = shift;
     }

> 2. a more fully developed lifecycle model - similar to the one that
> Apache itself uses.  In particular, it would be really helpful to have
> explicit  phases for state (e.g., Session munging), authentication,
> and authorization.  I imagine those 3 in particular to be extremely
> helpful for building things like role based access control or single
> sign-on into your application.

I agree that session, authentication and authorization are all useful,
but it's not clear to me what significant gains formal phase definitions
here would bring us. Do you have examples where this design has been put
to great use? Currently, our plugins in these areas use the calback
system to hook in at the phases that are useful for them, and that seems
to work well enough.

> 3. a more fully developed plugin/event system; I think this goes along
> with #2 (i.e., a few more hooks), but in addition to this I have
> always thought it would be useful to have some sort of mechanism
> through which plugins might be able to query one another.  A good
> example (and actually the main motivation for #2 and #3) are the CAP
> Authorization and Authentication plugins.  The short list of troubles
> I have had with using these two are:
>
>   * when used together, Authorization is called before Authentication,
>   making it awkward to handle authorization errors of unauthenticated
>   guests (or maybe Authz assumes an authenticated session);
>
>   * default behavior of Authz is to query directly the Authorization
>   plugin instance for a username; this works fine in that situation,
>   but there is no clear way for plugin A to make information available
>   to plugin B;
>
> That's just an example. I think that these improvements would go a
> long way to encouraging the timeless benefits that you've outlined
> about CAP above.

Regarding "more hooks", we already have an API to add custom hooks who
folks who want more than the standard ones. Some plugins use this
functionality to provide hooks that come part of their own published
API.

Regarding "plugins querying each other", the Moose "Meta Object Protocol"
provides functionality in this area that we could take advantage of.
We can check to see if an object "does" a role, or check to
see if the "original_package_name" matches the plugin we can expect.

   https://metacpan.org/module/Moose::Manual::Roles
   https://metacpan.org/module/Class::MOP::Method

> 4. more flexibility with the query object...err response object; I've
> run into some hoops to jump through when I wanted to use CGI::Simple
> and be able to upload capabilities on or off in a sub class.

You'll have be more specific here. The framework already allows you to
use alternate query objects, and requires very few methods of it.

Splitting the query object into response and request objects would
immediately provide flexibility, as you could replace the objects
independently. :)

Regarding turning uploads on and off independently, Plack::Request
includes flexibility here, as upload handling is split off into it's own
module, as Plack::Request::Upload. You could easily make an alternate
class for uploads that behaved differently.

Perhaps want you want to do in your case is to reject "POST" requests as
soon as possible. In that case, using a Plack "Middleware" may be best
solution, as it can take action even before the content is prepared for
processing by your application.

> 5. persistence - I would like to say that I'd like to learn more about
> CAP and the ecosystem in persistent environments. It's my impression
> that there are some corner cases or funny issues with it. I do not
> claim this is true, but I think that we can all agree that while
> persistent environments such as mod_perl are considered old fashioned,
> I think that they will prove remain relevant amist the tide of
> alternatives and "middleware" laden configurations. For me, the
> ultimate goal would be to use CAP to create a responsive and
> consistent daemon type application as served by Apache, defined
> strictly through things like runmodes, plugins, etc. Is PSGI the path
> to this? Maybe, maybe not.

I use CGI::Application almost exclusively under mod_perl 2 now, and am
not aware of significant "gotchas" there. When I switch my large
application over to PSGI (from vanilla mod_perl 2), it will still be
running under mod_perl 2, only with a PSGI adapter.

I don't see Apache/mod_perl2 and "Middleware" as being at odds with each
other. Sometimes they provide different solutions. Sometimes an Apache
solution may perform better, sometimes a Middleware solution may be
preferred for being hackable pure Perl.

I'm personal curious to evaluate replacing Apache with Starman as an app
server, with Nginx in front of it serving static content and acting as a
reverse-proxy. (I already use Nginx this way, but only with Apache on
the backend).

> 6*. the last mile - in application frameworks, I am unsure of any that
> take the finite state machine model to its logical max. This thought
> may be way out there, but defining things like runmodes only takes you
> so far. Going a step further, perhaps done through more feature dispatching
> or routing, it'd be really nice to be able to define the application
> runmodes in terms of a transition function (e.g., current runmode,
> input, resulting runmode). In otherwords, support defining an
> application to the fullest extent possible though some sort of runmode
> dispatch table annotations.

I'll have to wait for the more specific proposal on this point. :)

     Mark


More information about the cgiapp mailing list