| |||||||||||||||||||||||||||||
|
| |||||||||||||||||||||||||||||
Print This Article REALbasic University: Column 072
DoRbScript: Part IILast time I explained what RbScript was and showed how to create a simple dynamic plugin system for your program using RbScript. But some of you may be wondering how that differs from using AppleScripts. Before we get to our project, DoRbScript, let's take a quick look at the differences between AppleScripts and RbScripts.
AppleScript versus RbScriptAppleScript is a language invented by Apple that is designed to communicate with programs via Apple Events. (Apple Events are an inter-application messaging system Apple created: a standardized way of different programs talking to each other.) AppleScripts have their own syntax, and that syntax can be expanded as each program can add its own verbs and nouns to the language. (The vocabulary of each program is called its Dictionary. More on that in a bit.) This expandable nature of AppleScript makes it flexible and powerful, but it also makes it frustrating, since different programs can react differently to the same commands. For instance, one program may like the phrase "third word of the sixth paragraph of the first document" while another will generate an error. Once you're familiar with the way a particular program wants its AppleScript it can be great, but getting there can be a challenge. REALbasic has decent support for AppleScript in that you can drag any AppleScript into your project window to add that script as a new REALbasic command. You can even pass information back and forth, though not as interactively as with an RbScript. The biggest disadvantage of an AppleScript is that the only way to add to REALbasic is to drag one into your project file: there's no way to add a new script while your program is running. (Theoretically there are some workarounds to this, as there are OS calls to tell the System to compile an AppleScript, so you could use Declare statements to pass a fresh script to the OS, but I don't know if anyone's actually done that, and at any rate, it sounds fragile and complicated.) You can, however, execute compiled AppleScripts -- these are often called "applets" and are small applications. You can run them with the folderItem.launch command. So if you make your program scriptable, it would be possible to have a Scripts menu that dynamically adds AppleScripts from a particular folder, and the user could add or remove such scripts at will. That would end up being very similar to what we did last time. For most Mac users, the AppleScript approach makes more sense, since AppleScript is a standard and many people are used to it. However, since RbScripts are written in the REALbasic language, you may be more comfortable using it instead of having to learn AppleScript. Also, making your application scriptable is no simple task. Depending on what your program does, making it scriptable could mean supporting dozens or even several hundred AppleScript commands. Some AppleScript support may not be possible due to limitations in how REALbasic handles Apple Events. If you go the RbScript route, you don't have to worry about those problems (but of course your users may not want to learn RbScript just to expand your app). The bottom line is either approach can give you ways for other people to externally script your program: which approach you take is up to you.
The DoRBScript ProjectThe project we're going to create this time is unusual in that it will make use of both AppleScript and RbScript. In short, we're going to create a faceless scriptable program that will execute RbScripts and return the result. To do this will involve several steps:
Creating the "aete" ResourceOur first step involves creating a "dictionary" of AppleScript terms our program understands. Normally this is a complex step. However, DoRBScript is a relatively simple program: it just does one thing (process an RbScript). So our dictionary only needs one command. A program's AppleScript dictionary is stored in a special resource of type "aete" (note that it's lower case). Normally you edit resources in a program like Apple's ResEdit; however, it does not support editing aete resources out of the box (there are enhancements to ResEdit to allow it to edit aete resources, but they're unsupported and not ideal). You could shell out $256 for Resorcerer, a sort of super-ResEdit which does everything (including aete editing). But if you'd prefer to go the cheap route, download the free EightyRez. EightyRez doesn't edit anything but aete resources, so it's not a general resource editor, but for aete resources it's pretty good. It's also only available in a Classic (Mac OS) version. But since you generally aren't editing aete resources every day, it's a good substitute until you can afford Resorcerer. For our example we'll use EightyRez. EightyRez definitely has a "minimal" interface. Launch it and select "New" from the file menu. This will create a window like this: ![]() I have no idea what any of these things mean. Just kidding! Seriously, editing an aete resources can get really complicated and it's far too much to detail here. There are several good references on how to do make your app scriptable, including Matt Neuburg's book and REALbasic Developer magazine has a regular AppleScript column which covers stuff like this. What we're going to do today is add a verb to our dictionary. To do that, we select the "Events" line and choose "New Event" from the edit menu. That brings up a blank window which we'll fill in like this: ![]() What this is saying is that the command we are adding has an id (a four-character string) of "dorb" and belongs to the "misc" class of commands. The name of the command is "DoRBScript" -- that's the command we'll use in our AppleScript. The "Description" and "Reply desc" fields are just comments -- they don't effect how the command works. But the "Reply type" and "Direct param Type" fields must be set to TEXT. That's telling AppleScript that the "DoRBScript" command requires a string as a parameter and that it returns a string. That's critical, because otherwise AppleScript doesn't know how the command works. When you're done with this, save it. I named mine "dorbscript (aete 0)" but it doesn't really matter what you name it. Remember, whatever's in your aete resource is what AppleScript calls your program's Dictionary. Try looking at the aete resources of programs you have installed on your computer (do not edit them, just look). For instance, the Tex-Edit word processor is known for having excellent scripting capability. When we look at it within EightyRez, we see this: ![]() A specific command in EightyRez looks like this: ![]() When we launch Script Editor and use the Open Dictionary command to look at Tex-Edit, we see a help file that looks strangely familiar: ![]() See the relationship between aete and the Dictionary? It's much easier to read in the Dictionary window, but the basic info is the same as when we were editing the aete resource. I've already created the full aete file used in the DoRBScript project, which you can download here.
Next WeekWe create the scriptable DoRBScript application.
NewsWere you interested in REALbasic Developer magazine but hesitant to purchase a full year's subscription? Guess what: you can now order copies of individual issues via our new Back Issues form! It's a great way to try out an issue or two and see if you like it, or pick up an issue you missed because you subscribed late. Just follow this link to order a back issue: http://www.rbdeveloper.com/backissues.
LettersThis week we've got some questions from Tansu Onturk. He writes:
Wow, that's a lot of questions! But as a fellow graphic designer, I know what a challenge it is learning a new task like programming. Let me tackle a few of these questions and see what we can figure out. If I understand your first problem correctly, you've got two key issues:
Both of these are fairly easy, though the first is a little more difficult to explain without seeing your exact code. What I'll do, though, is create my own example and hopefully you can learn enough from that to figure out how to modify your own code to work the same way. So, we've got a ListBox and we want a "file save" button to change state (enabled or disabled) according to what happens in the ListBox. My example doesn't actually save anything, but it does toggle fileSaveButton: ![]() This works by putting in this code in the CellAction event of listBox1:
In my example, the second column (column 1) is set to be inline editable. When the user clicks inside the cell to edit it, we assume they've made a change and enable the file save button. Note that word "assume" -- we aren't bothering to check to see if the user actually changed any data, though we could: we'd have to compare the current value to the previous value, which we'd have had to have saved elsewhere. If the values are different, we know the user changed something, and then could set the fileSaveButton state. Also, in a real program, you'd probably want to call a method here and not directly modify the button. That's because you probably will want to do several things, like set a flag for the "Save" menu item, and set the window's close box to "dirty" (under Mac OS X).
You can download this example program here. The next part of your question, about saving over an existing file, is very simple. You just need to separate your saving system into two methods, not one. The first method is a "save as" method: it sets a folderItem from the Save As dialog. The second method just uses that folderItem and saves to that file. Note that in these examples, we have this window property: theFile as folderItem. The SaveAs method:
Note that if the user hits cancel in the SaveAs routine we never set theFile: that's important if you use this routine as an actual "Save As" command, since you wouldn't want the folderItem of the current document set to nil if the user changed their mind and canceled. So we set their response to the temporary variable f and only set the theFile property if we're sure they've chosen a new file. The Save method (which takes f as folderItem as a parameter):
Now if you're still wondering about how to overwrite the old file, you shouldn't need to worry about that. Presumably you've got your data in memory (it's loaded into the ListBox or wherever you've got it stored) so all you have to do is recreate the file from scratch on disk the way you normally would (using the .CreateTextFile or .CreateBinaryFile methods) and it will write over the old file and save into it the current data. As to your game engine question, I went to Google and did a search for realbasic "game engine" (note the important quotes) and it came with with hundreds of hits. There are several game engines created in REALbasic, and other game engines that let you create games with REALbasic, so I'd advise you to search around and see if there's a project out there that does something similar. And feel free to write me, but just don't expect a quick response as I'm very busy. I'll sometimes respond to questions without publishing the letter, but in general I don't like to do that. Publishing your letter and my response helps thousands of people, while a personal response only benefits you. About the Column REALbasic University is a weekly instructional column on programming with REALbasic and is brought to you by REALbasic Developer, the magazine for REALbasic programmers. Each week we answer select reader questions, and we're always open to ideas for future columns. Send your questions to . (Keep your questions simple and specific. General queries like "How do I write my own web browser?" will be neglected.) Your question won't be answered immediately, but will be answered in a future column. (If you don't want your correspondence published, just be sure to indicate that when you write. Otherwise it's fair game.) About the Author See the REALbasic University Archives
REALbasic University contents ©2001-2004 by Marc Zeedar and REALbasic Developer. All Rights Reserved.
| |||||||||||||||||||||||||||||