Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

I've written a few templates over the years. Not as many as template mavens like Mike Hanson and Lee White, but I've touched just about every aspect of the template language at one time or another. And more often than not I find myself peering into a dark place somewhere in the bowels of the template engine, wondering what gyrations I need to go through to get the AppGen to spit out the code I want. That's not because the template system is bad; , it's just very powerful and very complex. 

This complexity is one of the reasons I rarely start with a template. I almost always write code to solve the problem, and then figure out how to get the AppGen to generate the exact same code. Debugging source code can be tricky; debugging template code can be trickier; doing both at the same time is just plain dumb. 

...

Code Block
#At(%DataSection),Priority(3100)
%[2520]M2MClassInstanceName  class
ListCheckbox                   &CML_UI_ListCheckbox
Persister                      &CML_Data_ManyToManyLinksPersisterForABC
Links                          &CML_Data_ManyToManyLinks
Construct              procedure
Destruct       procedure Destruct       procedure
DisplayCheckboxData    procedure
Init          procedure DisplayCheckboxData        procedure
LoadEnrollmentData   procedure Init procedure
SaveEnrollmentData     procedure
SetCheckboxIcon        procedure
          procedure LoadEnrollmentData          end   procedure SaveEnrollmentData             procedure SetCheckboxIcon    
           procedure
                           end                   
#EndAt#EndAt

I've used the %[offset] technique that pads the contents of %M2MClassInstanceName to 25 characters so my class declaration formats nicely. Of course I'm persisting in using four space indents, so it won't look quite the same as generated code, but then the generated is wildly inconsistent anyway. I've even kowtowed to Clarion's annoying and cloying two space indents so my code aligns with the generated code. Nice, eh?

The next #At section embeds code in the window manager's Init method:

...

This is where that checkbox comes into play. Greg Fasolt, who has been alpha testing the classes and the template , (and who set me on this journey last fall) told me he had a number of forms where he wanted to use this template. In those situations there is no left side browse, which actually makes things a bit simpler. The "left" side primary key value is already in memory so there's no need to reload and redisplay the list box while it's being displayed.

...

Code Block
#AT(%BrowserMethodCodeSection,,'TakeNewSelection','()'),PRIORITY(9000)
    #If(%LeftFileIsInBrowse)
        #If(%ControlInstance = %LeftBrowseControlInstance)
%M2MClassInstanceName.LoadEnrollmentData()  
        #EndIf
    #EndIf
    #If(%ControlInstance = %RightBrowseControlInstance)
%M2MClassInstanceName.DisplayCheckboxData()
    #EndIf
#EndAt

This is the part of the template bit that caused me a lot of grief. This template that is attached to one browse control via the REQ attribute, and it needs to generate code into another browse control. First I tried all kinds of crazy ways to get at the context of the other browse, none of which were successful. I had actually been down a similar road years before when I wrote a template chain to generate ASP.NET MVC code from Clarion, but it took a while and a few conversations with Mike Hanson before I understood/realized/remembered the answer. remembered the answer. 

Pay attention, because this is really good stuff.

#AT statements have the ability to progressively filter the possible locations where they can generate code. For instance, the very next #AT statement in the template looks like this:

...

The help points out that the instance values are omittable:

But because Because there's just one #Embed statement for browser method code sections I really can generate the source for one browse from a template attached to another browse, as long as I can identify that other browse from my template. 

...

This code provides me with two vital bits of information for the right and when needed the left browses: the control instance so I can generate the code when neededappropriate, and the name of the left browse's manager class instance. 

Here again is that block of template code that can generate into both browses. Because I've omitted the template instance parameter, the block will generate for all browses. But Unfortunately it's not possible to put a filter outside of the #AT block, but I can add the filtering I need inside the #AT block, so that when the block is generated for a specific browse I include just the code I need. 

This is a very useful technique; if you write a lot of templates you either have come across it or you will come across it. 

...

Both the template and the source can still be improved. The template could pre-fill the primary key values from the file metadata, and the generated class source could use some tightening. And maybe Greg or another reader will find a bug or an obvious feature lack I've missed. But this is a good start. 

Summary

It's been a long road from that first question Greg Fasolt raised about the original class and template. I had the most fun with the classes and the unit tests, and lost the most hair with the template.