chtlogo001.gif

hndbetterembeddingweek2.gif

Object Name Suggestion Revisited

During last week's "Embedding Best Practice" session a suggestion came up that was probably not considered or handled adequately in the time I'd allotted for the discussion.

One developer, whose name I've forgotten, made the suggestion that sticking with the generic template-generated name "BRW3" for the BrowseClass that I was deriving and using as my embed-code target was not optimal, given that the objective of my article was intended, at least partly, to achieve after-the-fact code-readablity.

I agreed in principle that good names for variables, objects and controls are important for clarity, but defended my decision to not change "BRW3" to something else in light of the fact that there are no other browses on the window and consequently no other BrowseClass, "BRWx" instances in the procedure to add confusion. Had there been others, it would certainly have been advantageous to distinguish them with names that described their purpose.

In deference to the point that good names are important, I've renamed my BrowseClass instance "BRW" for want of something better. I think we're all pretty accustomed to the ABC browse template's convention of using the "BRW" prefix. And while this change does not really add clarity in this case, it concedes the point that under different conditions where ambiguity might be present, it's important and entirely possible to rename certain objects which normally receive generic, template-generated names when they must be referenced in hand-embedded-code.

While on that discussion, it was pointed out that the browse queue also receives a numbered name (eg: Queue:Browse) that can be equally confusing when multiple BrowseClass instances are populated on the same window.

I didn't make any attempt to rename that since there's a better way to refer to this queue in code than using it's generated name. In this case, it may be referred to as "BRW.Q" using the BrowseClass instance name, a dot and the letter "Q". This name is a direct reference to the queue with the funny name (See Image) and it side-steps any potential name-recognition problem.


week2_001.png

Used this way, the name implies "the browse queue belonging to BrowseClass "BRW". Inside the browse class methods where most of my code resides I can simply call this queue "SELF.Q" since it is the queue belonging directly to the "BRW" entity itself.


week2_002.png

Renaming, Re-Prototyping Causes Orphaned Embeds?

Mike Hanson asked if the newly created methods I was inserting into ABC BrowseClass would cause orphaned embed-code if I renamed my methods or changed their prototypes. No it doesn't. At least not in the newest Clarions, C8, C9 and C91. It once did, but that's been handled.

I was sure when I replied to his question, that orphans did not result from method renaming or re-prototyping when using this embedding technique. Then, after last week's session I began second-guessing myself. Had I remembered that correctly?

This past week, while reworking my experimental, demonstration application, HNDFAVORITER.APP, I encountered serveral opportunities to both rename and re-prototype some of my hand-inserted methods to disover that luckily, I had remembered correctly. ;-)

The embeds stay in place and do not become orphaned or lost. Orphans result from removing methods, in the same way that orphans result from removing any control that's hosting embed code from your window. Renaming a window control does not cause embed-code orphaning. So the behaviour of embedded class methods hosting embed code, closely follows that of window controls when it comes to code orphans.

 

Using Local Hand-Coded Procedures To Host Embed Code

Bruce Johnson asked a question to the effect "Couldn't you use local procedures, instead of class methods to do what you're doing?" My quick answer was something along the lines of, "Probably, as long as you handle creating the prototypes yourself."

That question was a good one, worth exploring further, and perhaps the impetus for a another "Clarion Embedding Best Practice" paper. But before Bruce writes that paper, I'll cover some of the implications of that question here.

The technique Bruce suggested is a viable aternative. It works just as well as the one I was illustrating, as long as you a define "Local" procedure as one that is declared inside of the host procedure. That is, a procedure which is prototyped in a MAP/END structure inside the data definition area of the host procedure, between the procedure's name and its CODE statement.

To illustrate what I mean by this, here follows a series of screen-snaps showing two test procedures. In both cases, the code for these procedures is located in the exact same, "Local Procedures" embed point right near the bottom of the embeditor area of the host procedure called MainBrowseReadWinFavorites() in my HNDFAVORITER.APP.

I've called these two procedures, MyLocalProcedure and MyModuleProcedure for good reasons that I'll explain in a minute. Here are screen snaps of the code sections of these two "Local" procedures.


week2_003.png

Why is one procedure more "Local" than the other and able to see all of the variables, structures and controls inside of our host procedure, MainBrowseReadWinFavorites()? The code for MyModuleProcedure() is located in the same place, yet it can't see the variables, structures and controls defined inside the host procedure. In fact, MyModuleProcedure can't really be called a "Local" procedure, can it? It doesn't recognize anything defined inside the host procedure.

That's why I called it MyModuleProcedure(). Its MAP/END structure and procedure prototype are located in the module area above the host procedure's code area. In fact, like an uninvited guest, it's standing outside the room and can't see the people at the party.

Take a look now at the MAP/END structures and prototypes for these "Local-Style" and "Module-Style" procedures and the reasons why these two procedures are so different in behaviour.


week2_004.png
In the image above, the red arrow points to the host procedure, MainBrowseReadWinFavorites(), inside of which we are doing our hand embedding work. The mossy green arrow is our "Local" procedure. It can "see inside the room" and address all of the variables, structures and controls located inside the room (i.e. inside the host procedure). It can easily host hand embeds of the sort I am doing in my "Best Practices" paper of last week.


week2_005.png

In this second image (above), the red arrow points to the host procedure MainBrowseReadWinFavorites() as before. And the mossy-green arrow points to the "Local" procedure, MyLocalProcedure() as before. The blue arrow points to the "Module" procedure, MyModuleProcedure(). Notice that the only difference between the "local" and "module" procedures is where they are "MAPPED" or "DEFINED" into the overall application. The code for both procedures is in the same place, but the procedure prototypes are located inside and outside the code area of the host procedure, respectively.

That explains further the cryptic answer I gave last week when Bruce asked his question. In short, it'll work just fine if you know what you're doing concerning the structure and placement of your procedure prototypes. Frankly, a "local" procedure approach to embedding your app may have some advantage as it requires less movement from inside the embeditor back to the template interface, back to the embeditor.

On the other hand, my deriving-classes-approach, has the advantage of allowing you to leverage the ABC "Classes" tab template interface to assist with the structuring and placement of your procedure (class method) prototypes. And, while embedding inside those derived classes, you could encounter a structure naming advantage too, as I did by using SELF.Q instead of say, Queue:Browse.

 

Using Local Hand-Coded Classes To Host Embed Code

There's a middle ground between these the two approaches, namely, "Local Classes".

The trick here, is to define a class inside the host procedure in a manner similar to the "local" procedure definition. In this case, a MAP/END structure is not required but a CLASS()/END structure is required. You've seen this kind of CLASS() structure numerous times if you use the embeditor to enter your ABC procedures.


week2_006.png

The difference is that we're going to define the class structure by hand and we're not going to derive it from an existing class, the way the ABC templates do, though we could if we wanted to.

In the screen snap above, indicated with the red arrow, is an example class definition I dropped into my demo app inside the same host procedure MainBrowseReadWinFavorites().

The code section for this local class is placed again in the "Local Procedures" area of your host procedure via the Clarion Emebeditor. This area is right at the bottom of the embeditor window. You can reach it with Ctrl-End.

This is barely any different than the code section you saw for the MyLocalProcedure() example (see image below). The only appreciable difference is that the naming convention for classes includes the class instance name followed by a dot followed by the procedure (method) name: MyLocalClass.MyLocalMethod PROCEDURE().
week2_007.png

The merits of this approach are exactly the same as a local procedure and have to do mainly with the fact that you're not having to move back and forth between the embeditor and the template interfaces.

You can also derive this "local" class from an existing class if you want to give it extra capabilities that are already available in ABC or CHT classes.

 

Conclusion

Entertaining that last thought, (i.e. derviving from an existing class), leads one to consider that doing this by hand isn't necessary, since most templates of any consequence, at least in ABC or CHT, can already derive classes for you.

We even have a generic template in our toolkit called "EmbedObject" which writes the code to drop and derive any libsrc class into your procedure on your behalf. Since this template has a "Classes" tab, as do all templates ABC, CHT or Third Party, (those that provide ABC compliant templates and classes), you can add embeddable methods into any class being dropped into your procedure, without having to wire in the procedure prototypes manually.

And so, with that final insight, we return full circle to the "Clarion Embedding Best Practice" discussion of last week and pick a class already on our procedure via template, that in our view is the most appropriate host for our method-based embedding technique.

The spirit of this discussion concerns writing readable, maintainable code, not enforcing rules to be slavishly followed to the detriment of productivity. So take from these articles what you resonate with and feel is beneficial and discard the rest.

Though before you discount too much a suggested "best practice" like this one, keep in mind that spaghetti is food not a coding style.

Cheers...

Gus Creces
The Clarion Handy Tools Page
www.cwhandy.com
gcreces@gmail.com

 


chtcopyrightgray.gif