![]() |
|
Published 1999-01-01 Printer-friendly version
"I cant figure out which embed I need to use, because I cant figure out the names", "Embeds in legacy was easy, why is this so hard?" "OOP and ABC is baloney as I have to see what my code is doing" and my favorite, "Why wont someone just sit down and document these embed points?". There are other similar examples. And by the way, the answer to the last question is "You have the documentation. See the Application Handbook."
What we really are staring at is that one does not understand what is being asked of them. Understanding at this point is impossible, as you have gone past something you do not fully understand. So do not blame yourself if you cannot figure this stuff out. The remedy is to go back to where you were doing well and find what it was that you encountered. It is always before the point of confusion. This is a documented fact (easily proven) and is the major reason why education on any subject is failing. This and many teachers are totally unaware of this education debug tool.
However, my task today is not to teach you how to study, but to show you how you can use ABC embeds with a great degree of success and avoid getting into misunderstandings.
My typical reply to questions like the above is another question, "Why do you feel you need to use an embed?" Think about that for a second. This is not a flippant question, but a very good point being made here.
So what is the point? Very good! You are catching on! By the time you finish reading this article, you should understand what I am referring to.
First, lets back up just a bit. Everyone knows that the ABC model is based on objects (OOP, to be technical). So what does this mean? Object Oriented Programming gives us "code re-use", something that the legacy templates did not do. It does more than that, but lets just focus on this one point for now. Some may argue that the legacy templates reused code; actually, they did not. They used a copy of the code. Want to see this in action? Make a window and populate a browse control on it. Generate the code. OK, so what? Now add another browse control (not uncommon in business applications). Now look at the code. See the copy of the original? Just different names for all those routines, but it is the same code, duplicated!
OK, so its duplicated code. At least it works! This is true. It does work. However, I am trying to establish your membership into the Lazy Programmers Society. We do not believe that we should wade through copies of code, just because it is using different data! We want one chunk of code to use at any data we point it at. So, in ABC, we have one and only one browse object. This object has all the code you would expect to find in any browse list. Would it be reasonable to say that any browse in any application should have the same characteristics? Scroll up and down, locate records, edit records, etc.
I think I get it, these are characteristics and actions I should expect a browse to have whether I use it on a Customer file, Transaction file, or any other file, right? Exactly! That was so good, I almost showed you the secret handshake! Now, let us build on this just a bit. Suppose we have a problem with our browse object. What would you expect to have happen?
Uh, lemme see All my browse procedures come from the same code, so all of them would be broken? You are very bright today! They would indeed be broken! Now where would they be fixed? In one place! So once it is fixed, all your browse procedures would be fixed. Actually, in real life, this is rare. What would be a bit more realistic is if you enhanced the browse to work better. All it takes is a simple re-make. I would venture to say that many have already enjoyed enhancements in many ABC objects. If you did not notice, then that is the beauty of this, and you shouldnt unless new features are added.
With the OOP "dialect" of ABC, there exists a natural "no mans land" between your application and what the developers in London are doing. This does not exist in the legacy code. Again, another great benefit of using objects. They can add enhancements without breaking your code. Early on, this was not always the case (especially during the beta phase of ABC) as embed points moved within the code. That can still happen, but it is nowhere near as common as it was in the beta phase. When it does, it is as simple as moving the code up or down one embed point as the usual "fix". So ABC is also a safe code base to use.
Lets move on a bit. But remember code re-use is one of the great benefits of using the OOP model in your application. With that in mind, what can you conclude from the ABC model?
Hmmm. I think I see the point. Does it mean that ABC does all the routine stuff that you will find in a given object? Indeed it does! What routine stuff are we talking about? How about when you read records in a file? Check for an error? Print a report? There are actions that you would expect to find in any application. Just the data one is using is different. So a good rule of thumb is that if you feel you need to code something that could be found in any other procedure of a similar type, chances are that you will not need to embed code!
So this brings up a very good question: Should you even really worry about embeds? In the vast majority of cases where I have seen others code and their embeds, they are not even needed! Let me say that again. Embeds in legacy, which were required to get something done, are most likely not even needed in ABC! There is "Secret Number One"! Of course, members of the Lazy Programmers Society already know this fact and we try to avoid embeds as much as we can! How is this possible? Remember ABC methods (OOP term for a procedure, or executable code) are far more functional than what was provided in the past.
The rules you used in legacy are rarely a good idea to use in ABC, especially when it comes to embeds.
This is interesting stuff, but I still dont see how I can do this. I have to know what the code is doing. Well, let me soothe your nervousness; no, you dont. Now before I get you really confused - yes, you should know what you are going to get out of this. But you do not need to know what the code is doing underneath the covers any more that you need to know how the Clarions OPEN statement works - or any other Clarion statement. In order to use Clarions OPEN statement, you need to know what it is going to give you. However, you did not need to see the code to understand how it worked. The same is true for all ABC methods.
I am not totally convinced. I am used to seeing my code perform, line by line. Actually, what you are encountering is the phenomenon of not understanding what the method is doing. Or more precisely, "whats in it for you?" Well, lets look into the ABC embed names. Stay with me here for a minute. The naming conventions for the methods are indeed interesting. They make more sense if you shift your viewpoint just a tad. For example, in legacy, you need to have a browse do something. The viewpoint used successfully in this case is "How do I get this browse procedure to do what I want?" In ABC, your viewpoint should be, "I am a browse. How do I do this?" If you are assuming this viewpoint, the naming conventions make sense. They are named from the viewpoint of the object in question. This is ABC Secret #2.
I still dont see this. Give me an example actually using a real ABC method. Thought you would never ask! OK, lets use a very simple example - a report. It does not matter what kind of report. What is one thing you could expect a report to do? Print a detail band. OK, but what has to happen before that in order to print a detail band?
I suppose I have to read data from a file or related files first. Bingo! OK, now if you look at the generated code for any report (lets use a Report Wizarded procedure as it will not have any embeds), what is the one line of code that makes a report a report? OK, lets see . Hey! Lookit here! PRINT(RPT:Detail). Excellent! You found it. Now you have to ask, where is this code?
It is in the ThisReport.TakeRecord procedure. Oh I get it now! If I am a report I have to take a recordin order to do something with it! Correct! All the ABC embeds are named with this viewpoint in mind. Now let me get just a bit complex for a second. You can apply filters, sort orders, etc., too (guess what they are named?). If you look closely at the line of code just above the PRINT line, you see a strange line of code:
ReturnValue = PARENT.TakeRecord()
First of all, ReturnValue is declared locally to this method. It is simply a variable that holds a return value from PARENT.TakeRecord. What is the meaning of "Parent"? Websters defines the word like this: an ancestor, precursor, or progenitor. A source, origin or cause. It has its origins in Latin meaning "to bring forth, breed".
Wait hold on! There is fully functioning code that this bit came from? The one that prints the detail band? Yes. In OOP, this is called "Inheritance" or "derivation". This means to come or transmit as from a source.
This is getting interesting. How do I see where this came from as I do not see any Parent objects. No you dont, but before we go there, the "parent" keyword means that we want to call some code that is already defined for us. In other words, there is perfectly working code here, so why not use it? The "parent" refers to an object that has come before. So this means that we have a "child" object in use. Looking at the generated source, what is the "child" objects name?
It looks like "ThisReport". So, if I scroll back up the source, can I see where "ThisReport" came from? Indeed you can, and this is not a bad thing to do. Where did "ThisReport" come from?
Looks like this bit of code. But I cant see what it is doing. Please explain.
ThisReport CLASS(ProcessClass) ! Process Manager TakeRecord PROCEDURE(),BYTE,PROC,VIRTUAL END
OK, "ThisReport" is the name or label of a complex (meaning made up of many parts) structure of a CLASS type. By the way, the CLASS statement is really the only thing that is truly OOP in Clarion. This is called "encapsulation" in OOP circles. This means to enclose, as in a capsule. We can declare the data (properties as it is known in OOP) and procedures (methods). The parameter is the name of the "parent", in this case, "ProcessClass". Which makes sense, as since you are a report, you need to process some records from a file.
Right, makes sense. But we used PARENT.TakeRecord(). However, we did do something very interesting. We overrode the TakeRecord method that exists in the ProcessClass.
Here is where all this comes together. Remember I said that ABCs do all the common stuff? Here is another secret for you. You only need to be concerned with things that are different than the common stuff for a procedure. In other words, a report is going to process records (read through a file) and then print it. So a report is really a "process" object with one line of Clarion code that makes it a report! This is what makes it different! We had to extend the functionality of a process object to add a print command. It (the TakeRecord from the Process Manager) was almost good enough ,and just one more line of code got what we needed. This is why we really do not need a true ReportManager. There is one in ABC, but it really manages the preview and some other things.
OK, this is starting to make some sense. However, you still have not explained which embed point I need. This is true. And furthermore, I am not going to. However, I did promise that I would show you how to find out. And, to make things really interesting, it will be easier than the legacy embeds!
The real "trick" to this is simply to take very logical, but simple steps. Try to break down everything into tiny chucks of action. Since you do not know which embed you need to use (or even if you even need one), you need to start an investigative procedure. Lets use a real life example (this actually happened).
I was working with a client for whom I made a very small Windows app (they were DOS users using a Clarion for DOS app under 95). They loved the simple browse that I gave them and wanted to keep it that way. It simply listed all their customers and their orders in child procedures (not visible on the customer browse). They asked if there was any way possible to show which customers had an order. They did not want to add any controls to the browse, so adding another list box or even a simple string was out of the question. I asked if some sort of color condition would be acceptable. Of course they thought that was a wonderful idea. So that was the task; to change the customer name to another color, only if there was an order attached to the customer. This made sense as they frequently posted completed orders to history and thus they needed to see only the active orders. They could also call customers who did not have active orders and try to get a new order from them. I told them no problem, I would give it to them.
On my drive back to my office, I was thinking, "I have no idea how to do this, or even if it is possible. What have I gotten myself into?" There is an old saying, "If you need something done, give it to a busy man". I decided to make things a bit more interesting. In addition to the customer requirement, I could only use ABC methods and see if I could do this with zero embedded code. Quite a challenge indeed! I certainly started to feel busy!
OK, that was the task, and the rules to get the task done. Where to start? Well, obviously the BrowseCustomer procedure. What had to change? The customer name column in the list box had to have different colors, and only under certain conditions. I had to access the Orders file, so I might as well add the Orders file under the Files button. However, keep in mind that a browse uses a view, and adding a related file under the file list box band would break the browse. It would list the same customer as many times as they have orders. So this had to go under the Other band. You just needed to have access to it. Simple enough.
Now you have to set the colors in the list box. So you go to the color tab and <insert favorite four-letter word here>! Color is not available! You got this cute message on the color tab: Colors are not available <more four-letter words>.
OK, how do I make this list accept colors? There are instructions on how to do this.
Kewl! No problem. Just fire up the list box formatter per the instructions and check the color box. The column changes to red indicating that color is now available. Good.
Now when I go back to the color tab on the Browse box actions, I see the variable that has color turned on. Correct. Remember that the client wanted conditional colors, so lets see the properties for this by clicking on the Properties button.
Well, well! Looky here! A group box for conditional color assignments! Things are indeed looking up! So lets go add one! We press the Insert button and there is an entry field for the condition, as well as four color settings for this condition.
Hmmm, you did say I wanted to use only ABC methods, and what I really need is that code template, "Lookup a non-related record". Cant add code templates here. Things are looking down again. Might as well change the colors as long as you are here. However, lets keep our changes to a minimum and stay focused on our task.
OK, what is the condition I need? Do I really need an ABC embed at this point? Wait a sec! Where are the templates gonna place this code? I did make some changes to the colors. I wonder where these changes are going to be placed if I simply enter a duff condition like "XX". That is excellent thinking and quite valid, too. What does happen if you generate code at this point? Select Project and then Generate. Now right click on the procedure and select module. Do a search for "XX".
Now this is interesting! My dummy formula code is placed in BRW1.SetQueueRecord. SetQueueRecord? That makes sense as a queue controls how this list will look (colors, icons, tree, etc). If we scroll to the top of this source, we see the queue definition and there is a declaration for the color field (four colors to be precise).
I reckon BRW1 is the browse list name. Yup, derived from the BrowseClass. Says so right in the code. Now what do we have so far? You have a way to change the colors, and its in place and ready to go. You may have a possible embed point, SetQueueRecord.
But I still do not know if I need an embed point or not, and I am no closer to using an actual ABC method to do my work for me than before. Embed? I think I am starting to see where this is going! Good! Lets go and check it out.
So I open the embed tree for this procedure, but so many embed points! Now what? Well its a bit busy so we need to collapse the embed tree. Click the "collapse all" button on the toolbar. Six embeds now. Much better than the several hundred! OK, which object? Where is SetQueueRecord?
Local data? Nope, we are not playing with data, so scratch that off the list.
Local objects? Possibly, so it needs to be expanded.
Hey! There is BRW1! Expand this list. Wow! Bunches of embed points again. Good thing theyre alphabetical. I see SetQueueRecord! Good, expand this tree. Data and Code. Makes sense. Now expand the code branch of the tree.
All right, I think I am getting somewhere here. Lets add an embed, so click Insert. Ooooo! Look! A code template, "CallABCMethod"! This is an excellent tool. This template removes the need to write code and possibly introducing typos. So what is the object name?
I am a CustomerBrowse, what do I have to do again? Oh yes, access the Orders file. Hey! Hey! An object called "Access:Orders". I am getting a real good feeling about this! Excellent choice! Cannot be plainer than that, right?
Ok, now what, there must be hundreds if not thousands of ABC methods, which one? Well, lets see what we got here. Even better! Only methods that apply to Access:Orders. Well that certainly narrows down the list! What seems logical here? What are you doing?
I need to see if there is an order on file for this customer. OK, what sounds like it will go and try to get a record from a related file?
Hmmm, Fetch. And down the list there is TryFetch, which sounds logical too. Whats the difference? Bring up the Help file and do a search on your list of methods you think will do the job.
Both try to get a record based on a key value. This sounds like GET(Key,Key) the old way. Well, lets try Fetch as it is first in the list. Now you need a parameter. The prototype here says (Key K) and returns a Byte. All right, lets use the key as it follows with what the docs says. ORD:AccountKey. Return value? Im not sure at this point, so can I throw it away or ignore it? You can always use "I#", an implicit! If you need a "real" variable, you can always come back and add one.
Clicked on OK and there is my code. That doesnt look right. Where are the parentheses? Open it back up and manually add them on the passed parameter entry.
Lets switch to the embeditor and see where this code is going, especially in relation to your conditional color assignment. Click on the source button. You are taken right to the point below the code template generated code. So to use the Fetch method you need to prime the lookup key, you need to add some source. Simply go up the previous white space that is before this code template. This is one of the best features of the embeditor, it allows you to see your code in context with the generated code.
If Fetch gets a record, based on a key value, then the code I need to add is this:
ORD:CustomerID = CUS:CustomerID !Prime key value
Great! It is taking shape. Now remember our task for the colors?
Yes, it is conditional based on whether or not there is a valid order. Correct. So, what is the condition you need to test?
Think a bell just went off as I think it is the Access:Orders.Fetch method. Does it report success or not if a record is found? Indeed it does! Lets cheat and steal the code that we need! As long as we are here in the embeditor, you can copy the code that the code template wrote for you. So, copy Access:Orders.Fetch(ORD:AccountKey) into the clipboard by highlighting it and pressing Ctrl-C. Go back to the embed tree and delete the code template. You dont need it here. You need this method as the condition for the colors.
Exit all the way back to the procedure properties and to access the color tab on the list box, you can use one of 3 ways to get there:
Regardless of how you got to the colors tab, bring up the conditional color dialog. The "XX" is now highlighted. The question you need to ask now is what is Access:Orders.Fetch(ORD:AccountKey) going to do?
It will find a record based on key value and report success or failure, or record found or not found. Correct, so if a record is found (order is there for this customer), what is the "success signal"?
It is always zero, or false. I get it! So I need to test a True/False condition! Right!
Type in either a tilde "~" or NOT (depending which you prefer). Now press Ctrl-V to paste from the clipboard the code "stolen" from the embeditor.
I get it! Since this method will return a non-zero value if a record is not found, we need to really test if a zero value is returned! Correct. Remember the Help told us that Fetch (and TryFetch) try to retrieve a record based on a key value. If it finds a valid record (an order is attached to the customer), it returns zero or LEVEL:Benign. If it did not find a record, it returns something else. All we need to do is test to see if an order actually exists based on a key value. We could care less what the error is. In other words, we are simply testing a True or False condition. So, if a valid record exists, we change the colors, otherwise we do nothing. Compile and test your changes.
Wow! Look at that, very nice! If we remove the tilde, we get the opposite effect. Also, further testing shows us that TryFetch works too. Remember the task we had to handle was simply this; "If a customer has an order on file, change the color, otherwise do nothing." And we set all this in motion by adding one file to the file schematic and checking one box.
Hmmm, that looks promising. It was easy to do as well. How about another example, please? Lets do this. Suppose we have a form procedure that has 3 entry controls for entering details about an invoice. You have a string control that computes the extended total. So, you have a formula that could be expressed like this:
DTL:Extended = (DTL:Qty * DTL:Price) DTL:DiscountAmt
That is easy! I can put this under the Control Event:Accepted embed for each control. No big deal. This is true, you can do this, however, the DTL:Qty entry is a spin box. So you would need to put this code under the Control Event:NewSelection embed too. So now you have four embed points, all performing the same thing. If you ever have to change this formula for some reason, you have to do it in four places, and lets hope you get all of them.
Then I can make a routine and call it four times. You are missing the point. That point is "why?". Why not use just one embed point and be done with it?
ABC has one embed point that will do this? Actually, it has several places where it would work. The choice really boils down to where does it make the most sense? We need to do our investigation again. So we start by asking the most basic question we can, "What object are we looking at?"
Lets see. I have a window open, so it would have to be the window object. Correct. Looking down the embed tree, do we have an object that is named accordingly?
Why, yes! Local Objects, ThisWindow. Very good. So, based on a logical progression, which embed sounds like it would do the most good?
Well, I see two that may look like they could work. I see an Ask and Update method. OK, look these up in the Help file and what are they supposed to do?
Well, the Ask method sounds like it could be what I want, but nothing really sticks out at me. This is a pretty good gauge on whether or not something will work. If it does not sound right, then probably it is not.
However, I do see something that may lead me somewhere, "The Ask method implements the ACCEPT loop for the window and calls the TakeEvent method to handle all events". OK, hold that thought. Lets go check out your other idea, Update.
No, Update does not even sound close. It is for updating stuff to the disk. All I need and want to update a string based on what the user is doing. Now you are catching on! Dont lose sight of what you are after! Now what is TakeEvent all about?
The Help file defines it as "a virtual to process all events". Good. Any event?
Thats what it says. What about just window events (not control events)?
It says "all events". Correct. So, is this embed a good place to put your formula?
Well, it would satisfy the one embed goal. However, it does not sound like the best fit. Looking back at the list of embeds, I see a few other "Take" embeds. Good deduction. Which one sounds like the one to work the best?
Just guessing, but I would have to pick TakeFieldEvent. Why that one?
Because it sounds like it would process any event from any control or field for me. The TakeEvent would process this code even if I dragged the window across my desktop. Sounds logical. Try it and test your application.
That worked! I can spin the box, type in values on any field and it updated the string control! And you used only one embed point! If you were using the legacy templates, this may have required 4 embed points.
As you may have surmised by now, the ABC templates do a lot of work on your behalf. Also, as another note on these "Take" methods, remember that these are named from the viewpoint of the object. In this particular example, we are not using the viewpoint of how one can get this window to do something. You are the window, how do you do the task? If I (as the window object) wanted to process a field event (without regard to any field or event), then I want to "take" a "Field Event". The other embeds similarly named should now make sense.
One other little tip for you. Most of your embeds are event driven. You can use these embeds just like you did in legacy. For example, entry controls have EVENT:Selected and EVENT:Accepted. These mean exactly the same as in legacy.
There you have it. An easy investigative procedure to not only find the correct embeds, but sometimes even how to use an ABC method as well. While I have given only two actual examples, more complex situations are really not that different. My aim here is to illustrate some of the thought processes that you could use in your work. After all, we all want as many ideas as possible to help figure something out. It makes our work easier with more ammunition.
By the way, a similar technique could be used on various properties of these methods. Setting a property with a particular value can change the behavior of a method. There is a code template for this as well. Some properties are accessible via the IDE. For example, in Clarion 5 if you are using filter locators, a simple click of a checkbox can change the behavior from "begins with" to "contains". This will be visible under the "locator behavior" and the locator is set to "filtered". A "begins with" locator will find names like "Sonders" when a search for "Son" is used. A "contains" locator would also find "Sonders" but also "Anderson" and "Johnson". Regardless of the behavior, whatever matches the locator will be the only names in the list.
Armed with this knowledge, how about adding the ability to have your users decide on the behavior? How would you do this? A quick generation of source shows that we have a new line of code in the Init method:
BRW1::Sort0:Locator.FloatRight = 1
How about adding a simple checkbox below the list box? If checked, set FloatRight to True, otherwise set it to False. Now where would you add this code? I am sure you can figure that one out. Hint: What event is generated when a checkbox is clicked?
You should realize by now that ABC objects are designed to handle common situations that can be found in any business application. When you feel you need to use embeds, they should be used in cases where a procedure is going to be unique or different for the circumstance that you are faced with. What I have tried to convey here is how easy it is to figure out any embed you may wish to use or may need. Of equal importance is the code that you no longer need to write, as the functionality is there for you.
Another area to look into is the excellent write-up that all Clarion 4 and 5 users have at their fingertips. This is contained in the Help. In version 4, this can be found under the Contents page, Late Breaking News button. The Contents link will show a popup window with help links. Click on the link, "Making the Transition to Clarions ABC templates". In version 5, this has now moved to the "How Do I?" button (as well as new material). Look under the ABC section and the first link you will find the same topic. In either case, print this out. It is written in plain English. It documents another investigative procedure and backs it up with several real life examples. For Clarion 5 users, a new review of the "How Do I?" section will yield some new topics and updated topics.
There is really no need to be intimidated by ABC. It is there to assist you getting your job done. When an embed is needed, the chances are very good that you will not be required to write as much code as you did in the past, while still writing functional applications. The prime goal you should keep in mind at all times, is to keep it painfully simple.
I sincerely doubt that while learning how these can assist you, it will be an overnight lesson, as much as we all would like that to happen. But it does get easier every day. Part of the resistance to making this transition is that many have no idea how long this learning curve is. I cannot state how long it is, or should be for you. But rest assured it is not as long as you may think. Please keep that in mind as you continue your learning process. Your clients are counting on it.
I sincerely hope this helps. Good luck!
Copyright © 1999-2008 by CoveComm Inc. All Rights Reserved. Reproduction in any form without the express written consent of CoveComm Inc., except as described in the subscription agreement, is prohibited.
Clarion Magazine ISSN 1718-9942
One year: $184
(includes all back issues since '99)
Renewals from $134
Two years: $274
Renewals from $224