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
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.
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.
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
for good reasons that I'll
explain in a minute. Here are screen snaps of the code sections of these two "Local" procedures.
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.
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.
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.
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()
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.
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
, 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
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.
you discount too much a suggested "best practice" like this one, keep in mind that spaghetti is food
not a coding style.
The Clarion Handy Tools Page