Mail archive

Re: [Acf] RFC: attempt to "simplify" acf

From: Nathan Angelacos <>
Date: Tue, 31 Oct 2006 00:21:22 +0000


Thank you very much for your work. Your comments are very good. I
haven't had a chance to /look/ at your code yet (will tomorrow, time
permitting), but a few comments on your description below.

> Hi,
> I have been looking at Nathan's deom of Lua acf. Done some testing with
> it and finally I think I have managed to understand what thins MVC
> things is all about and what Nathan have been talkinb about all time.
> I have tried to reimplement it, making it more object oriented and
> making the *Framework* alot simpler. It does mean that it requires
> somewhat more from the acf programmer.

nice. This is probably much better.
> I have uploaded some ideas to
> svn://
> The intersting parts are:
> lib/cf.lua
> www/cgi-bin/webconf
> app/test/test.{model,controller,view}.lua
> app/test/test.view.lsp
> I'll try to explain what I have tried to do and how I have been
> thinking. (I guess this i mostly for Nathan, but I send it here anyway)
> I'll refer to the current acf as the "old" and my demo as "new", but
> remeber this is just ideas. It would be nice with some feedback.
> The front webconf.
> The old webconf is kind of a fron controller. The new is more a front
> view which creates a front controller object (cf).
> The old webconf tried to be a frontend for all kinds of views, textview,
> xml view etc etc. The new does not. It tries *only* to be the web view.
> However, since cf is an object, it would be easy to create an xml view
> or text view (for ajax stuff for example)

ok. that makes sense. I like it.

> The cf (declared in lib/cf.lua) will contain a cf.model, cf.controller
> and cf.view. those are loaded from path_info. Look at the code below:

VERY nice!

> #!/usr/bin/haserl --shell=lua
> <?
> require "cf"
> cf = WebCfObject:new()
> cf:init( ENV.PATH_INFO, FORM )
> cf:init_uri( ENV.SCRIPT_NAME )
> cf:run_controller()
> ?>
> <html>
> <body>
> <div id="content">
> <?
> if cf.success then
> cf.view:content( )
> else
> cf:view_error()
> end
> ?>
> </div>
> </body>
> </html>
> The ENV.PATH_INFO and FORM are passed over to cf:init. If we were making
> a cliconf that is executed from commandline, we would not have FORM or
> ENV.PATH_INFO. We would still be able to create a "prefix/name/action"
> string and a indata table (similar to FORM) by parsing the argv.
> cf:init(path_info, indata) will create cf.prefix,, cf.action
> based on path_info (look at the code. its not trying to be smart..) and
> then it also creates cf.controller, cf.model and cf.view from that info.
> Note that the cf.view is just like cf.model and cf.controller, a lua
> script. Its not handeled as a special case. All are equal, and all are
> objects.

This is very nice and clean, I like it a lot.
> If any of this fails, cf.success is set to false and cf.error_msg has a
> error message (I did some attempt to make it possible to localize it
> with local translations. I dont' know if its worth it)
> The new cf, will not try to load any core.controller and core.model, nor
> will it trigger a acf_core.controller or acf_core.model in case any of
> the files are missing. It will just set cf.success and cf.error_msg.
> (the idea is: "don't try to be smart")

Ok.. I think I was using core as a place to store library/common
functions. It sounds like you do that with the objects. Your way is
cleaner, and does a better job of it. Thanks!

> the cf:init_uri() only exist in WebCfObject and not in the parent,
> CfObject. CLI controllers, won't have the uri. Thats also why its called
> separately.
> the cf:run_controller will do the actual execution of the controller. If
> the action does not exist, cf.success will be set to false with a proper
> error message in cf.error_msg.
> Another difference here is that old webconf first checked if file exist
> and if it did, it ran dofile(). The new just runs loadfile() and if nil
> is returned, set cf.success to false. The extra check if file exist is
> not needed since loadfile does exactly the same thing as dofile, except
> it won't trigger the assert upon failure.

DOH! Why didn't I think of that?! ;-) Yes, this is good.
> The rest of the webconf can be considered as the old "generic.view". It
> will create the header and the footer. (yes, if you dont want that, then
> dont use webconf. creat another front controller)

hmm... In the default case, that's great - but what about the case of
log files, where the action is to download a logfile as a mime-encoded
text attachment, or a CRL list from the OpenSSL CA? (or... a
dynamically generated .png file... or you get the idea.)

In defense of "generic.view"... ;-) for most cases, you are correct -
webconf can generate the header, menu, stylesheet, etc. but in the
exceptions, do you really want another front-controller?

If we have one front-controller, then session management,
authentication, user control, etc can all be managed at the front
controller. If multiple front controllers are created, then there are
multiple points of entry into the application (as a whole), and multiple
points of failure on authentication/session management.

Its a two-edged sword - either you have one front controller, that can
fan-out into multiple views, or you have multiple front-controllers that
manage the same authentication mechanisms.

I adopted the single front controller, and used "generic" as the view of
last resort. Unfortunately, I think most people thought "generic" was
the "one-true-view", so it got overused.

Is there some way that the controller could override the view info, so,
that, for instance, the header/menu/tabs/ view:content footer/etc. are
all rendered, but if there is an exception, the controller (or view) can
inform webconf *NOT* to render everything?

That is, in the default case, the view:content is rendered inside a
<div=content> </div> block, but if the view decides it needs something
else, it can prevent that, and render the entire page itself?

That would allow binary downloads, graphics, etc... while covering the
generic case 90% of the time.

> The new webconf will always try to run cf.view:content() unless the
> cf.success is set to false. Its up to the view to handle errors.
> The missing parts so far are authentification and menu rendering. I'm
> thinking of a auth.lua lib that contains an object to handle that. I'm
> also thinking of adding session handling in haserl so you have a global
> SESSION variable, just like ENV and FORM.

I was planning on managing this in the front controller (webconf). what
are you thinking?

> Menu rendering is what i intend to take a closer look at as next step.
> I'm thinking of just let it be a lua table to make things more simple.
> To conclude:
> In old webconf you didnt need to create a view. The framework would take
> care of it and do various different things, depending on what files it
> found. The new framework dont even attempt to be smart. It requires that
> the following files are there:
> prefix/name.model.lua
> prefix/name.view.lua
> prefix/name.controller.lua
> preifx/menu.lua
> If any of those are not there, everyting will fail. It doesnt mean we
> cant use any "generic" view. It could be a symblink.
> We could also have some good use for scaffolding.
> Check it out with:
> apk_add subversion make
> svn co svn://
> cd ncopa && make install-all
> setup-webconf
> Natanael Copa
> PS. a quick thought.. what if we included the menu in name.view.lua?

Its /possible/ to have a single controller/view pair in multiple menus.
 Imagine the generic "logfile" view, for viewing all log files, and the
"OpenVPN logfile tab" under the OpenVPN menu. The controller/view is
the same - the only thing that changes is the menu (originating
controller) it was called from.

That's why menu was in a separate file.

Thanks for your work on this, Natanael! Its looking good.
Received on Tue Oct 31 2006 - 00:21:22 UTC