[Pangloss] Fwd: [openframe] [RFC] OpenFrame AppKit ideas

Steve Purkis spurkis at quiup.com
Tue Apr 8 10:49:19 BST 2003


FYI:

Begin forwarded message:

> From: "James A. Duncan" <jduncan at fotango.com>
> Date: Mon Apr 7, 2003  6:34:32  pm Europe/London
> To: openframe at astray.com
> Subject: Re: [openframe] [RFC] OpenFrame AppKit ideas
> Reply-To: openframe at astray.com
>
>
> On Monday, April 7, 2003, at 03:29 PM, Steve Purkis wrote:
>
>> Hi,
>>
>> This is a [long] cross-posted message.  Please watch replies.
>
> /me will refrain from cross posting - Steve, if you want to forward to 
> pangloss then go ahead.  I know nothing about the pangloss mailing 
> list topic so its probably safer if I stay entirely on topic in a 
> mailing list I am familiar with :-)
>
>> Application Kit
>> ---------------
>> OpenFrame::AppKit is a good example for OpenFrame, but it doesn't
>> quite meet my needs.  For example, it limits:
>
> AppKit is largely just that - an example.  I don't think that we 
> actually use anything from it internally at Fotango anymore, and if we 
> do its certainly well on its way to being refactored out of the > system.
>
>>     +	Support for different template engines (Petal)
>
> I'm not entirely sure how it limits it.  Its easy to write another 
> segment and plug it in to OpenFrame instead of the AppKit supplied TT2 
> segment.  This should be straightforward to do.  If it does actually 
> stop you from doing this I'd really like to hear what it is that stops 
> you from doing it.
>
>>     +	Support for multiple languages / localisation
>
> We've managed to do this with OpenFrame - see 
> http://www.cig.canon-europe.com.  Again, I think its something that 
> isn't built in to AppKit, but not that AppKit places arbitrary limits 
> that stops it from happening.
>
>>     +	Cookie-less Sessions
>
> Certainly possible, we do this on the CIG site in places (for offline 
> client integration), again we don't use AppKit heavily, if at all.
>
>> With some refactoring, I could achieve the above:
>>     +	write a Petal segment
>>     +	move hard-coded HTML out of Segments that use it,
>> 	sub-class & wheel in Locale::Maketext or Locale::gettext
>>     +	make SessionLoader segment abstract, then subclass for
>> 	Cookies, URL's, and Form params.
>
> Or not worry too much about having a pure abstract class, and merely 
> subclass for URL and form parameters.  Although without knowing 
> exactly what you're trying to do I'm guessing URL and form parameters 
> will look largely the same.
>
>> But I still wouldn't be happy, because while the AppKit does a lot
>> of things I like, it also does things I don't agree with.  Like:
>
> The way Pipeline, therefore OpenFrame and therefore OpenFrame::AppKit 
> is written doesn't suppose you would necessarily agree  with it.  
> _We're_ not entirely happy with it and we spent a lot of time writing 
> it! :-)
>
>> I'm forced to invent a way of handling errors.  I suppose I could
>> just stick them in the Session and let the template writer deal
>> with it.  But then I gotta remove them on each request, and what
>> if I want to handle them before they get to the template?
>
> Thats fine, invent a way to handle errors.  Its pretty hard to come up 
> with a clever way of abstracting errors and localizing them, and have 
> it cross cut your presentation and business logic.  This is a 
> difficult problem, and not one I've managed yet to solve or find a 
> system that elegantly solves it for me.  Rather than putting a two-bit 
> hack into OpenFrame for this, we've left it out 'cause we've not yet 
> encountered a sensible way of doing this without varying degree of 
> hoopage.   I suppose the best way from a pure programming perspective 
> is to throw an exception that knows how to render itself in multiple 
> languages.  However, I hate message files that are based on key value 
> pairs and thats the easiest way to be able to hand someone a file to 
> translate.  Come to think of it, I just hate presentation layers.... 
> :-)
>
>> Also, session storage should be more flexible - disk, memory, db,
>> etc.  Could make a cache interface and pass in a Disk/Memory cache
>> to the SessionLoader as desired.
>
> I would recommend re-implementing a AppKit::SessionLoader to suit your 
> needs.  All the SessionLoader seems to do is get some sort of id from 
> somewhere, and pass it to OpenFrame::AppKit::Session to handle loading 
> and saving it.  Currently OpenFrame::AppKit::Session uses 
> Cache::FileCache, but I'm reasonably convinced (although I've not 
> looked in a long time) that you can simply subclass and rewrite the 
> store() and fetch() methods to use your own storage mechanism.
>
>> But most importantly, I'm not convinced entry points are a good
>> idea.  They don't give enough control over the path of execution:
>
> Something we've done at fotango to great effect is to give the 
> pipeline control over the flow of execution.  Rather than trying to 
> put the entirety of an application in one segment we split it up into 
> its constituent parts.  For example, we'd have an image loader, a 
> title changer, a rotator, and a renderer.  We'd stick them in a 
> pipeline-of-pipelines that if drawn in xml might look something like 
> this:
>
> 	<pipeline>
> 	  <segment> album_loader </segment>
> 	  <segment> image_loader </segment>
> 	  <pipeline>
> 		<segment> decline_without_image </segment >
> 		<segment> rotate_an_image </segment>
> 		<segment> change_an_image_title </segment>
> 		<segment> render_an_image </segment>
> 	  </pipeline>
> 	  <pipeline>
> 		<segment> decline_without_album </segment>
> 		<segment> change_an_albums_title </segment>
> 		<segment> render_an_album </segment>
> 	  </pipeline>
> 	</pipeline>
>
> The loader's will load the appropriate object when they can, and then 
> the decliners sat at the top of the embedded pipelines return a 
> Pipeline::Production if there isn't something the rest of the chain of 
> execution down that particular pipeline can handle.  By using this 
> method rather than AppKit::App's fairly obscure entry-point 
> mini-language, we've been able to get expressive classes that cause 
> the model do one thing and one thing only.  By joining them together 
> in various ways we can quickly create new applications or by writing 
> the merest sliver of code bring as-yet unused parts of the model into 
> play.
>
>>     +	What if the user leaves some required form fields blank?
>> 	The entry point wouldn't fire, so you'd have to write
>> 	lots of entry points in order to tell them what they need
>> 	to fill in order to continue.
>
> When we have used AppKit::App we've tended to only put one or two 
> elements of forms in the entry point - just enough to differentiate 
> from the next entry point.  We also post the form to itself, and 
> redirect on success rather than error.  That way the template can get 
> at the arguments pointed to the system.  If there are 0 length 
> elements then we can put the error messages in the appropriate places. 
>  I'm not sure this is the best way of doing it, but it is _a_ way of 
> doing it.
>
>>     +	What do you do if your parameter names are generated on
>> 	the fly?
>
> I think I'd ensure that one particular parameter was set in stone, and 
> then write a subclass of Pipeline::Segment to deal with it rather than 
> relying on anything that AppKit provided.  AppKit::App is always going 
> to cater for the general case.  I can think of loads of edge cases 
> that will break AppKit::App, which is why the inheritance hierarchy is 
>  reasonably long - you can step in before it gets to AppKit::App 
> (after either Pipeline::Segment, or AppKit::DispatchOnURI) and deal 
> with the situation in a more appropriate manner.
>
>> I'd prefer something that just dispatches a request to an object
>> based on the URI's path (ie: DispatchOnURI).
>
> You could certainly subclass DispatchOnURI now without much fear.
>
>> Alternative: Actions
>> --------------------
>>
>> So with that in mind, I thought maybe the OpenFrame AppKit could do
>> with some tools similar to Jakarta's Struts?  For those of you not
>> familiar with Struts, I'll sum up what I think are some of its major
>> advantages:
>>
>>   Actions
>> 	A thing that gets done, like logging in, making a payment,
>> 	or displaying a summary.  These are usually short 'n sweet
>> 	ie, a thin layer passing messages to your app objects.
>
> Thats essentially what a Pipeline::Segment _should_ do.  The hangman 
> example is actually a pretty good example of this. All the business 
> logic is held inside Games::GuessWord (or Games::WordGuess, I can 
> never remember which), and it acts as the controller for the 
> application.  Although I'll admit that the Hangman example could use 
> some refactoring and that it does kinda tie the controller and bits of 
> the view together, its model is removed from the other two elements.
>
>>   Validating 'Action Forms'
>> 	Each Action can have a Form with instance vars that are
>> 	filled out from the HTTP request params of the same name.
>> 	Or you can fill it out yourself, which is handy when your
>> 	params are generated on the fly from a db or something.
>> 	This form is then validated (ie: to make sure required
>> 	fields are present and not garbage).
>>
>>   URL -> Action mapping
>> 	Each Action has a url, set in a config file.
>
> The other problem with this is a localization issue - if you take a 
> piece of a URL, for example 'red.html', your french translater is 
> probably going to want to translate this to 'rouge.html'.   Depending 
> on how tightly your URL's are bound to your actions you're going to 
> either have to duplicate the section that calls into your application 
> _or_ have some sort of pattern matching.  I'm not particularly in fond 
> of either of those two outcomes.
>
> In general I detest config files.  Config files are mini-languages, 
> and can be far better expressed in a programming language.  I get the 
> feeling I hold a different opinion on this from most people > however....
>
>>   Action Forwards
>> 	Each Action can have one or more named 'forwarding' url's
>> 	which can be used to choose which template page to load.
>> 	    success => "/success.html"
>> 	    failure => "/failure.html"
>> 	these are also set in a config file.
>
> See above :-)  Again with this I'd rather see the programmer decide 
> what is going to happen, rather than having a configuration file that 
> gets placed in obscure parts of the system.
>
>>   Errors
>> 	Named flags that are looked up in a messages file.
>> 	Which means no more hard-coded HTML, and translatable
>> 	message files.
>
> I've bashed out my dislike of all exception systems above :-).
>
>> So if we start off with the above, and mix it with the things I
>> like about the AppKit, and Perl in general, bake for 3 hours and
>> let cool, we end up with something like this...
>>
>>   OpenFrame::AppKit::Action	<abstract>
>>     +	dispatch_on_uri()
>> 	validates & executes this action.
>>     +	validate()
>> 	validates Request in store, producing an ActionForm.
>>     +	execute()
>> 	executes the action...
>> 	On success produces a Template (similar to idea of forwarding
>> 	could get template from a config file, for example)
>> 	Any Messages or Errors are stored in the pipe.
>>
>>   OpenFrame::AppKit::Action::Form
>>     +	simple hash.  will probably look like the Request, but
>> 	may differ if validate() processes things, like
>> 	stripping .X/.Y off of image inputs.
>
>>   OpenFrame::AppKit::Session
>>     +	reserve the names:
>> 		errors
>> 		error.*
>> 	for errors
>>     +	add localisation hooks here?
>>
>>   OpenFrame::AppKit::Errors
>>     +	request-scope error flags
>>     +	this class would look just like Session except:
>> 		has_errors()
>>     +	add localisation hooks here?
>>
>>   OpenFrame::AppKit::Template
>>     +	Template name, and other params potentially.  Would be
>> 	used by TT2, which currently gets template name from the
>> 	Request.
>>
>>   OpenFrame::AppKit::Segment::TemplateLoader	<abstract>
>>     +	Can be sub-classed for your favourite templating system
>> 	(TT2, Petal, etc.).
>>     +	Looks for OpenFrame::AppKit::Template object in Store.
>>     +	Creates template processing vars with scoping like this:
>> 	  Session	no prefix
>> 	  Errors 	'error.' prefix
>> 	So it's easy to add other scoped variables.  all you need
>> 	to do is reserve a prefix (or sub-class)...
>>
>>   OpenFrame::Response::NotFound
>>     +	HTTP 404 error, replacing hard-coded version in
>> 	OpenFrame::Segment::ContentLoader
>>     ?	Not ideal, but easier to localise than current version.
>>
>>   OpenFrame::Response::InternalError
>>     +	HTTP 500 error, replacing hard-coded version in
>> 	OpenFrame::Segment::HTTP::Response
>>     ?	Not ideal, but easier to localise than current version.
>
> One of the things I like _least_ about OpenFrame is the proliferation 
> of dumb data objects that it seems to encourage. This is partly my 
> fault because I also dislike the idea of introducing named keying into 
> a Pipeline::Store,subclass but of course people have gone and done 
> this anyway :-)
>
>  Lots of these things seem like just that without much in the way of 
> behaviour which causes me to feel a little disturbed about them.  The 
> current OpenFrame gets away with just a couple of data objects: 
> OF::Request, OF::Response, and OF::Cookies (OF::Cookie is just a 
> wrapper around CGI::Cookie, but at least that gives it *some* 
> behaviour :-) and I'd be worried about setting a trend by having more 
> data objects than behavioral objects in the system.
>
>> Localisation
>> ------------
>> I'm still a bit fuzzy on this.  AFAICS, localisation needs to
>> be done in three places:
>>
>> 	1. Reporting Application Errors (404, 500, etc.)
>> 	2. Choosing which template to process
>> 	3. Displaying messages to the user from a template
>>
>> Which means we need hooks in these places.  But perhaps the
>> problem could be solved in general by a Localisation segment
>> that puts something in the Store that other segments can use?
>
> We do that at Fotango for CIG as well as other systems that are being 
> worked on at the moment.  We have a Site-map class that maps URLs to 
> abstract paths in the system, and back to templates.  The site-map 
> also carries all the text and error messages that are used by the 
> templates.  The problem with it is that although the information is 
> kept in one place and one place only it also seems to be a pain to 
> remember just where that place is.  Again, I hate presentation layers 
> so I'm probably not best to comment on this mechanism as opposed to 
> any other - I hate them all :-)
>
> For now I remain unconvinced.  Not that what you're proposing isn't 
> valuable, but I don't think that AppKit is the right place for it 
> straight away.   This is the main reason that some of the things that 
> we've done at Fotango similar to what you're suggesting  haven't been 
> rolled into AppKit (well, that and general time limitations).
>
> However, I think that OpenFrame as-a-whole would benefit from 
> something along these lines, and would allow you to do it easily (in 
> as much as openframe on its own gives you a good starting point, some 
> useful scaffolding, and won't get in the way too much).
>
> Hmm, dictionary.com -> thesaurus -> strut -> Lintel? Crossbar? Joist? 
> Girder?
>
> Regards,
> James.
>
>
> _______________________________________________
> http://www.astray.com/mailman/listinfo/openframe



More information about the Pangloss mailing list