Writing Cleaner Code

I thoroughly enjoy looking at the code and structure of other scripters’ work. Very clean and efficient code is a thing of beauty. There is an aesthetic quality to well-written code but well-written code is also practical and functional as well. Thinking beyond a particular job and proactively writing code for broader use can make coding much more efficient and profitable.

By taking a ‘modular’ approach to script design, the scripter can save a lot of time and headache on each new job. Sub-routines and libraries allow the scripter to break functions within a script down into smaller pieces or ‘modules’ that are easier to manage, edit and implement into new scripts.

A well-written subroutine is an incredibly powerful and time-saving tool for the scripter. By saving subroutines in the form of a Library, they can be used in any script running on your box, saving even more time. If you already use script Libraries,then you know how important and useful they are. If you are not currently using Libraries, you are probably working too hard.

I admit it, I was slow picking up on both subroutines and libraries. I probably spent hundreds of un-necessary hours re-writing code or scrolling through line after line of code to locate the command I was trying to find. Subroutines, at first, seemed daunting and I just avoided the issue of libraries all together. Once I decided to tackle them though, my abilities as an AppleScript Developer took a quantum leap forward. This leap forward not only opened the door for new ideas, it also allowed me to work more effeciently and become much more profitable.

There are a couple of things to keep in mind when creating subroutines and libraries. The first, is to think beyond the job you are currently working on and think of how you may use this routine in the future. If you think you might use this same routine in the future, go ahead and write it so that it will not be ‘script-specific’ by using variables for each property. Second, don’t try to do too much in a single subroutine. Writing in smaller pieces makes sub-routines easier to manage and more quickly locate within a script. Third, keep your scripts organized. I prefer to organize scripts in folders by the primary application for which they were written or by the function they perform. Save scripts with a name that identifies what they do and include a date. In order to recognize the most recent script at a glance, I append all script snippets with the numerical date without delimiters (ie., 122303 = 12/23/03). This allows me to easily identify the most recent version of a script. By saving script snippets in a central, well-organized location, they can be easily found when needed in the future.

In today’s hectic business world, most people are pressed for time. This is essentially why AppleScript Developers have clients. But, if you’re going to write a subroutine, you might as well do it right the first time. Taking a little extra time to do it right the first time and writing subroutines well enough to be re-used, with little or no modification, may seem like a drain on available time but being pressed for time is all the more reason to do it right the first time.

Suppose you are writing a script for QuarkXPress and in this script you create a picture box on a particular page of a particular document. Let’s say further that this step is only performed one or two times in the script you’re building. If you plan to ever script QuarkXPress again, it is almost a certainty that you will be building a picture box again. This is a great opportunity to write the process as a subroutine, save it as a library, and not have to re-write it.

If the subroutine is written with the afore-mentioned mindset of no specific references and as many possible uses are considered, the routine can be written to receive the required parameters and will work any time you need to automate building a picture box. How many times have you written code that looks like this:

tell application "QuarkXPress Passport"
	activate
	tell document 1
		tell page 1
			make new picture box at beginning with properties {bounds:{"1\"", "1\"", "4\"", "5\""}}
				tell picture box 1
					set runaround to none runaround
					set color to "White"
					set color of frame to "Black"
					set width of frame to "1 pt"
					set image 1 to alias "Perseus:Images For Demos:~Images:chinapress_fpo.tif"
					set bounds of image 1 to proportional fit
					set bounds of image 1 to centered
				end tell
		end tell
	end tell
end tell

This is all well and good but the problem is that every time the script snippet needs to be reused, the code will need to be changed to match the job, or worse, re-written from scratch. This is one of, if not the, most common tasks I perform with QuarkXPress. It took me a long time to realize that since I had written this code once, I shouldn’t have to keep re-writing it every time I wanted to create a picture box.

I finally re-wrote the code to look like this:

makePicBox (pageNum, pBoxNum, pBoxBounds, pBoxRunaround, pBoxColor, pFrameColor, pFrameWidth, picAlias, imgFit, imgBounds)
	tell application "QuarkXPress Passport"
		tell document 1
			try
				tell page pageNum
					make new picture box at beginning with properties {bounds:pBoxBounds}
						tell picture box pBoxNum
							set runaround to pBoxRunaround
							set color to pBoxColor
							set color of frame to pFrameColor
							set width of frame to pFrameWidth
							set image 1 to alias picAlias
							set bounds of image 1 to imgFit
							set bounds of image 1 to imgBounds
					end tell
				end tell
			on error errMsg number errnum
				return ("Error #" & errnum & return & errMsg) as string
			end try
		end tell
	end tell
end makePicBox

Note: In order to keep code samples short, this example does not include all of the properties of picture boxes.

Comparing the two pieces of code, you will notice that the second piece of code does not contain any specific references. They have all been changed to varialbes. When I use this code, in the form of a library, I can simply pass whatever variables are appropriate for the particular job I’m working on. Both routines require about 550 keystrokes. But, by setting up a subroutine like the second example, I can re-use this code in each script thus requiring less than half the number of
keystrokes and therefore takes less than half as long to include in my script.

It is critical to remember is that some variables will refer to classes. (ie., myRunaround = none runaround). These variables cannot be assigned as a string so you will have to have the application in question set any variables that refer to classes that are specific to a particular application.

Example:

tell application "QuarkXPress Passport"
	set myRunaround to none runaround
end tell

The code required to include this routine in any script looks like this:

tell application "QuarkXPress Passport"
	set myRunaround to none runaround
	set myFit to proportional fit
	set myImgBounds to centered
	set theErr to makePicBox(1, 1, {1, 1, 5, 6}, myRunaround, "White", "Black", 1, "Perseus:Images For Demos:~Images:chinapress_fpo.tif", myFit, myImgBounds) of me
end tell

This requires a lot less coding than re-writing the routine each time I need to create a picture box. It is also a lot easier and cleaner than simply copying and pasting code and modifying the properties I want to set.

This is a good first step to cleaning up my code and making the coding process more effecient but it can still be carried further. This subroutine can be saved in a library and used by any script written for QuarkXPress that will be building picture boxes. With a little examination, I found that libraries were not as intimidating as I had feared. They really are quite simple. A library is nothing more than a subroutine or collection of subroutines saved in a separte compiled script. Keep in mind that a library should contain nothing but subroutines.

To create a library, open a new script window in whatever script editing software you use and write or paste the subroutine(s) in the window. Save the script as a compiled script in a location that makes sense such as the scripts folder. On OS Classic this will be in the system folder. Under OSX the scripts folder will be located in the Library folder (Home > Library > Scripts). You may need administrator access to be able to save scripts to this folder.

Now, when the sub-routine is needed it can be called with the following code:

-- First, set the variables
tell application "QuarkXPress Passport"
	set myRunaround to none runaround
	set myFit to proportional fit
	set myImgBounds to centered
end tell
-- Next, load the script file into the active script via the path to the file
set boxLib to "Titan G4:Library:Scripts:My Libraries:QuarkXPress Libs:boxLib.scpt"
-- Then, tell the library to run the subroutine
tell boxLib
	set theErr to makePicBox(1, 1, {1, 1, 5, 6}, myRunaround, "White", "Black", 1, "Perseus:Images For Demos:~Images:chinapress_fpo.tif", myFit, myImgBounds)
end tell
-- Lastly, un-load the library from the active script
set boxLib to ""

Final Tips

If you are concerned about protecting your code, libraries can be saved as run-only to protect the code. The system will still be able to load the library and use its sub-routines.

In order for the active script to know where to find the sub-routine the handler has to be enclosed in a “tell” block. Without the tell block, the script will try to find it in the default location, which is iteself.

When a library is loaded into the active script, it uses memory. It is always a good idea to un-load the library after its task is complete by setting the variable, in this case “boxLib”, to an empty string. A much cleaner way of clearing the call to the library is to set the variable to ‘null’ but I have encountered problems on some machines where ‘null’ is interpreted to be a variable and an error occurs.

If time permits, you could go so far as to write a library for your application which includes routines for the tasks you perform most frequently. This type of planning can save a lot of time on future jobs.

When working with libraries, especially when working with a lot of libraries, remembering the parameters for each subroutine can become a challenge. To quickly return a list of the required parameters, a routine can be included in each library that returns the parameters for each routine in that library. This routine might look something like this:

set boxLib to "Titan G4:Library:Scripts:My Libraries:QuarkXPress Libs:boxLib.scpt"
tell boxLib
	set paramList to returnParams()
end tell
set boxLib to ""

The result of this call would look something like this:

{"Page #", "Box Bounds", "Runaround", "Box Color", "Frame Color", "Frame Width", "Image Path", "Fit", "Image Bounds"}

Lastly, when deploying scripts to another machine or to clients, a decision will have to be made whether to include the subroutines in the script or to deploy the script with the libraries. I usually make this decision based on whether or not I can fit all of the required sub-routines into a single script. If not, I make sure that the libraries are placed in a location where the script can access them. This is usually a folder that I can reliably point the script to. Most often this will be the “Scripts” folder because the location should be the same on every machine.