Disclaimer: This response was composed over at least 90 minutes of
coming back to it several times in between playing with Dylan, helping
with dinner, and getting quite buzzed off a nice bottle of Sancerre
listening to The Beach Boys, Woodie Guthrie, Creedence Clearwater
Revival and Ramones.
Carl Karsten wrote:
>>> So I am taking a look at what the app wiz generated. I am bummed that
>>> there is
>>> generic code in the 'instance layer'. like FrmEvents.py, pasted below.
>> Please remember that one of the main purposes of the AppWizard is to
>> *generate code* to show people how they might write their own Dabo code.
>> We've generated code that people can tweak, add to, or remove as they
>> see fit.
>
> But thats the beauty of OOP - the tweak, add to, or remove is done in the
> subclass. I know you guys know how OOP works, but it seems you are mixing in
> some cut/paste style oop too. put that code in the super class and give the
> app
> developer a blank slate to work with. If you have designed things well, most
> of
> what the app developer will need to do is set properties, and add some code
> into
> hook methods (I think that's what they are called)
I completely understand where you are coming from, Carl. I really do.
>>> 1. A header would be nice, like the file name. where do you want me to
>>> post
>>> that request?
>> Let me get this straight: you want a header with the file name, when you
>> ostensibly already know the file name because you are, after all,
>> editing it!
>
> yes. I want something there when I cut/paste it into an email, or print it,
> or
> post it to a web page.
What do you picture this header looking like?
>> Docstrings and/or comment headers could be added if you really think
>> they'd add value, such as describing what it is this file does.
>
> I don't think useful Docstrings can be generated. but I am thinking: what
> info
> is available at the time of generation that might come in handy later?
> database
> name? list of fields and data types? some of this stuff might be worth
> jamming
> in as comments. Maybe the create table command in the SQL lite syntax.
It's a kind of irony, because the rest of your message is ranting at all
the places where we generate code, but here you are arguing for
generating more stuff!
As you (rightly) notice, we are generating more code than necessary. You
actually haven't found the place where it is really egregious: take a
look at the PagSelect*.py files. Now that is *icky* and really should be
calling a common method in the superclass for adding the controls, but
my point remains the same: this generated code is to show people one way
of coding their app that they can run with, while hopefully providing
them with a directory and file structure that makes a lot of sense.
>>> 2. Why all the imports? FrmBase seems to be the only one needed, and I am
>>> not
>> Ok, the 'import os' isn't needed. Must be a holdover from something
>> subsequently removed. But the other imports are needed. See
>> initProperties().
>
> ack! missed that before. now I have more to rant about: put that stuff in
> the
> super class!
This part of the code shows them how to set which classes to use for the
grid, the edit page, and the select page for this particular form. How
can that get moved to the superclass without taking away that part of
the show?
>>> sure I see why you are doing 'from' and not just 'import.' wouldn't it be
>>> better to do:
>>>
>>> import FrmBase
>>> class FrmEvents(FrmBase.FrmBase):
>>>
>>> So that there isn't an extra.. um.. something ?
>> It is a style issue, completely subjective. When importing from modules
>> to do subclasses, I like to import just the class I want to instantiate
>> or subclass. Hence, from x import Y.
>>
>
> So the style is: have FrmBase 3 times with some keywords instead of just 2
> and a
> dot - odd sense of style :)
3 and a dot, and no argument that I'm odd. But in this case, I think
you'll find lots of Pythonistas using this style.
>>> 3. self.super() - I thought the x_pre()/x()/x_post() pattern kept you from
>>> having to run the code in the super class because there was none?
>> What if you put some stuff in the FrmBase method? You'd want that to
>> fire, no?
>
> ack - I forgot the controlling method: x() calls x_pre()/x_execute()/x_post()
>
> _pre() and _post() are empty, _execute() is what does X what you would expect
> X() to do. X is nothing more than:
>
> def x_pre(y): return true
> def x_execuite(y): do useful stuff, return useful value.
> def x_post(y): return true
>
> def x(y):
> if x_pre(y):
> r=x_execute(y)
> x_post(y)
> return r
>
> Now in the sub class you can put code in _pre and _post and don't have to
> worry
> about calling .super.
That isn't the point. The point is that our Dabo framework code can put
stuff in _pre and _after without worrying that the user subclass code
will forget to call super(), and thus keeping our framework code from
running.
> I heard Ed talk about this pattern somewhere. VFE has it everywhere, and it
> is
> kinda nice. however, I have always wondered how useful it really is. The
> same
> thing can be accomplished by having just x(). if the app developer wants more
> code, it goes in the subclass doing a self.super() at the appropriate place.
>
> I think the usefulness is readability, but I am not sure the lack of an 'if'
> and
> '.super()' make it any more readable.
>
> but useful or not, you should be consistent - if you are going to use the
> pattern, don't abuse the pattern ;)
Can you explain, in a sentence, and realizing that we aren't inside your
brain and frame of reference, world view, etc., how you think we are
abusing it? Basically, self.super() is exactly like vfp's dodefault().
> rolling Ed's comments into here:
>
>> Exactly. There is no code in the *framework* for those methods. But
>> you have two levels of sub-framework classes, and it sure doesn't
>> hurt to add that call. Technically, though, the super() call in
>> FrmBase.initProperties() is not needed, since initProperties() is
>> empty at the framework level. Again, though, it doesn't hurt, and is
>> more of a "belt-and-suspenders" design.
>
> It does hurt:
> 1. it makes the reader think the super class does something.
>
> 2. It makes tracing in a debugger harder if you are stepping though the code,
> and you have to step into it to see what it does. The warp to some other
> place
> and back is distracting.
We can take the self.super() out of FrmBase, but taking it out of the
subclasses of FrmBase wouldn't be good, because then the user would have
to put it in if they ever put any code in the FrmBase methods.
>>> 4. self.NameBase = "frmEvents", self.Caption = "Events"... shouldn't these
>>> things be in an xml file?
>> If you are into that sort of thing, this is basically what the
>> fieldspecs option does. Check the "use fieldspecs" checkbox in the
>> wizard to see the difference in the generated code. Actually, that page
>> was removed. Edit line 753 in AppWizard.py to set useFieldSpecs to True.
>> Notice that your application has far fewer files, and you can edit the
>> field/column definitions inside ui/fieldSpecs.fsxml which will propagate
>> to pretty much the entire UI. There are pros/cons to each way, and I
>> think each is valid. Use what you like the most, but the non-fieldspecs
>> way will give you a much better feel for how to write actual Dabo code,
>> and will give you more control to make the customizations you want, and
>> to scale up as you go.
>
> I have to disagree, as strongly as someone with 30 min of experience can. :)
I think you should try both ways, and spend more than 30 minutes with
it, and do more than one app, and then come back with specific changes
in the form of an 'svn diff' and then we can discuss it.
> I think you spent too much time editing .spr files and thought that was a
> good
> thing :)
Why in the world would I ever edit an spr file? A vcx or scx, sometimes
out of necessity because of VFP's inability to sort out inheritance in
some edge cases, prg files sure, but I don't recall ever hand-editing a
generated spr file.
What you are implying is that the AppWizard generated output is meant to
be regeneratable, which is false. Generate the code, tweak, maintain,
and never regenerate.
> I will have to get back to you after I have spent another 30 min or so.
Please do!
> Ed says:
>> The plan in the future is to give an option to generate code, as is
>> currently done, or to generate files compatible with the Class
>> Designer. This way you can take the generated files and either edit
>> them in a text editor, if that's your preference, or in the Class
>> Designer/IDE, if you prefer that way of working.
>
> Ok, that explains why the old way was turned off. still skeptical that the
> generated code is a good thing.
Healthy skepticism is a good thing. Absolutism isn't.
>>> 5. afterInit(self) is just one big constant wrapped around "Events", right?
>>> so
>>> why isn't the code in an abstract class that reads "Events" from an
>>> attribute ?
>> afterInit() is a method that gets called from the dPemMixin superclass
>> after most of the initialization phase has been completed. I don't
>> understand what you mean about it being "one big constant".
>
> The constant is the 'afterInit templete' that has the name of the table
> stuffed
> into the middle of this line:
>
> primaryBizobj = app.biz.Events(app.dbConnection)
>
> So most of that line and the other 4 lines (and comments) are the same in
> every
> frmFoo.py that was generated.
We make no bones about this being generated code.
> Why isn't the last line: self.creation() in the super class, right after the
> call to afterInit()?
>
> The rest of the code should be reduce a line in
> def initProperties(self):
> self.BizObjName ="Events"
>
> and then in the super class,
>
> # Instantiate the bizobj:
> app = self.Application
> primaryBizobj = getattr( app.biz, self.BizObjName)(app.dbConnection)
>
> # Register it with dForm:
> self.addBizobj(primaryBizobj)
You are right, of course. That would be better generated code. It would
be even better to remove the comments and reduce it to:
app = self.Application
self.addBizobj(getattr(app.biz, self.BizObjName)(app.dbConnection)
And then we could leave off the afterInit() in the subclass entirely,
and just tell people when they want to add a child bizobj to do
something like:
def afterInit(self):
self.super()
app = self.Application
primarybiz = self.getBizobj()
primarybiz.addChild(getattr(app.biz,
"venues")(app.dbConnection))
Which would indeed be a bit better. Patches accepted. You have to
understand the evolution of the AppWizard. It was only relatively
recently that we even had a FrmBase defined. So the next step of better
abstraction, in light of also maintaining compatibility with the
fieldspecs way, never occurred.
I don't think it is such a big deal, but everyone has their own idea of
what the AppWizard is supposed to be. I happen to think it does a great
job doing what it is intended to do as it is. And the comments and
repeated code only serve to show people the calls they need to make.
>> The code is
>> here in the FrmEvents subclass so that the developer can see what steps
>> need to happen in a non-abstract way, and to easily override or modify
>> the behavior. For example, if you want a child bizobj you just write the
>> python code to do that underneath the instantiation of the primaryBizobj.
>>
>> Again, this generated code isn't meant to be static, it is meant to be
>> customized. When making a new app, I'll start with the AppWizard. But
>> then I'll modify, tweak, add, remove and end up with something much more
>> than what the AppWizard did for me.
>
> Use OOP the way I say! or ponder my ranting in the hopes that it isn't a
> complete waste of time.
Not a waste at all. Thanks for the debate and discussion. If this stuff
bothers you enough (after you've used it a while), then please
contribute patches to make it even better.
> my current thought: if what you are saying is true, why subclass? just
> generate
> a ton of code. Maybe there is a happy medium between OOP and generated
> code,
> but I don't see it.
There is a happy medium. Ideally, we'd be generating just the structure,
with very minimal custom stuff in each subclass. Generate the structure;
inherit the base functionality.
>> Think of the AppWizard output as one honking demo showing you how to
>> interact with the DataNav framework. DataNav is a set of classes in
>> dabo/lib/datanav that build on top of dBizobj, dForm, and others.
>
> For 'real use' I think there should be something that does something like
> what
> the AppWizard currently does. I have a feeling with some tweaking to the
> framework (which *may* render all current project useless) and lots of
> tweaks
> to the AppWiz (which won't harm anything) there will be a lot less code. not
> sure what the advantage is. maybe easier to debug. for me anyway. :)
I know you are right: there are lots of things that could be done to
improve the framework. There's plenty of framework code that isn't used
anywhere anymore, I'm sure, which would be low-hanging fruit. Please
delve in and start hunting down places to improve, and when you are
confident that either 1) it won't break backwards compatibility or 2) it
is such a great and important change that we can deal with breaking
backward compatibility then submit patches.
But always remember, practicality beats purity every single time.
--
pkm ~ http://paulmcnett.com
_______________________________________________
Post Messages to: [email protected]
Subscription Maintenance: http://leafe.com/mailman/listinfo/dabo-dev