How to use handlers in OS X

Dear Friends,
Another very basic, should be simple question. In OS 9, if I put a “handler” script in the folder with my parent script, it seemed to work. Now I’m at a loss!

Parent:
tell application “Finder”
activate
set the_sel to (selection)
set sel_item to (first item of the_sel)
set Target_Folder to (container of sel_item)
set File_Name to (name of sel_item)
set the_ext to (name extension of sel_item)
if not (the_ext is “pdf”) then
if (File_Name ends with the_ext) then
set File_Name to (text 1 thru -5 of File_Name)
end if
end if
set prevAppleScriptdelimiters to AppleScript’s text item delimiters
set AppleScript’s text item delimiters to {“_”} --set the delimiter you like

display dialog "A. Usable string: delete only, press “OK”

B. Unuseable string: delete unneeded, press “Switch” or “Continue.”" default answer File_Name ¬
buttons {“Cancel”, “Continue”, “OK”} default button 3
if button returned of result is “OK” then
set theTruncatedFindingName to text returned of result
set theWorkingName to theTruncatedFindingName
else if button returned of result is “Continue” then
set theTruncatedFindingName to text returned of result
display dialog “Rearrange until you have a name of the form:
given; given_family; or given_middle_family
(where capitalization doesn’t matter):” default answer theTruncatedFindingName ¬
buttons {“Cancel”, “Switch”, “OK”} default button 3
if button returned of result is “OK” then
set theWorkingName to text returned of result
else if button returned of result is “Switch” then
set theWorkingName to word 2 of theHandlersFileName & “_” & word 1 of theHandlersFileName
else if button returned of result is “Cancel” then
exit repeat
end if
end if
set AppleScript’s text item delimiters to prevAppleScriptdelimiters
ConstructtheNewFilingName(theWorkingName)
set theNewFilingName to result as text
my MovetheFiles(theNewFilingName, Target_Folder, theTruncatedFindingName)
display dialog “Done”

end tell

Child (handler):
on ConstructtheNewFilingName(theWorkingName)
tell application “Finder”
set theUpperString to “ABCDEFGHIJKLMNOPQRSTUVWXYZ”
set theLowerString to “abcdefghijklmnopqrstuvwxyz”
set prevAppleScriptdelimiters to AppleScript’s text item delimiters
set AppleScript’s text item delimiters to {“_”} --set the delimiter you like inside the quote marks
if ((count of text items of theWorkingName) is 3) then
set theGivenName to (text item 1 of theWorkingName)
set theMiddleName to (text item 2 of theWorkingName)
set theFamilyName to (text item 3 of theWorkingName)
else if ((count of text items of theWorkingName) is 2) then
set theGivenName to (text item 1 of theWorkingName)
set theMiddleName to {}
set theFamilyName to (text item 2 of theWorkingName)
else if ((count of text items of theWorkingName) is 1) then
set theGivenName to (text item 1 of theWorkingName)
set theMiddleName to {}
set theFamilyName to {}
end if
set AppleScript’s text item delimiters to prevAppleScriptdelimiters
set x to the offset of (character 1 of theGivenName) in theLowerString
if x is greater than 0 then
if the number of characters in theGivenName is greater than 2 then
set theGivenName to (character x of theUpperString) & (characters 2 thru end of theGivenName)
else
set theGivenName to (character x of theUpperString)
end if
end if

	if theMiddleName is not {} then
		set y to the offset of (character 1 of theMiddleName) in theLowerString
		if y is greater than 0 then
			if the number of characters in theMiddleName is greater than 2 then
				set theMiddleName to " " & (character y of theUpperString) & (characters 2 thru end of theMiddleName)
			else
				set theMiddleName to " " & (character y of theUpperString)
			end if
		end if
	end if
	
	if theFamilyName is not {} then
		set z to the offset of (character 1 of theFamilyName) in theLowerString
		if z is greater than 0 then
			if the number of characters in theFamilyName is greater than 2 then
				set theFamilyName to (character z of theUpperString) & (characters 2 thru end of theFamilyName) & ", "
			else
				set theFamilyName to (character z of theUpperString) & ", "
			end if
		end if
	end if
	
	set theNewFilingName to theFamilyName & theGivenName & theMiddleName as string
end tell
return theNewFilingName

end ConstructtheNewFilingName

Results with parent returning the Log events:
ConstructtheNewFilingName(“Bucky_Edgett”)
“Finder got an error: Can’t continue ConstructtheNewFilingName.”

The Event Log gives no indication that the Finder actually tried to do any part of the handler. Is it not finding it? This is all copied more or less verbatim from OS 9 scripts, with some updating in the beginning of the parent for OS X. If I run it with the handler embedded in the Parent (using my ConstructtheNewFilingName) it does work perfectly, so I don’t think there’s a problem with the actual routine in the handler.
I’ve tried creating the simplest Parent/Child “tell app “Finder”/makebeep/end tell” and makebeep.scrpt, but the same thing happens: Finder can’t continue.

I’ve tried putting the handlers in various folders: Libraries, Scripting Additions, etc. No go!

What is the new proper OS X way to do this? Thanks so very much!

While some third-party AS editors may provide a library binding feature, the AppleScript language itself has never provided any such mechanism.

That’s exactly the error I’d expect from that code: you’re sending a ConstructtheNewFilingName command to the Finder, and the Finder doesn’t understand it. Like most error messages in AppleScript it’s supremely unhelpful, but all it means is that the Finder didn’t understand the message, so it was passed on to the AppleScript object and your installed scripting additions, which didn’t understand it either.

What you need to do is load your library script into a property in your main script, and send the ConstructtheNewFilingName to that:

property _MySupportLib : load script (alias "path:to:library:script:here")

...
_MySupportLib's ConstructtheNewFilingName(theWorkingName)
...

The library will be bound to your main script when the main script is compiled. This means your main script is completely portable once compiled; however, any time you save changes to the library script you’ll need to recompile your main script to update it as well.

You might also like to take a look at AppleMods, which provides a more powerful, dynamic third-party library loading system called Loader. Unfortunately it’s a bit short on developer documentation at the moment: as Loader’s original developer I’ve promised the AM maintainer I’d supply these docs, but other work is currently taking priority so I’ve not managed to finish them yet. If it interests you, put in a feature request and perhaps it’ll motivate me to deliver them a bit sooner. :slight_smile:

HTH

has

Dear hhas,
Thanks a heap. That works perfectly. Another new routine accomplished!

I’m not sure about the concept of “binding,” but in the old OS 9 and earlier AppleScript, of coure we had subroutines that were kept as separate scripts. And we had osaxen that worked as “libraries” of routines, too. That’s why I had my “Construct…” as a separate script!

AS just went out to look for them by name. If the subscripts weren’t in the folder with the main script, it looked in the Scripting Additions folder. It would only read the osaxen from there. Apparently now I can put them anywhere. Neato! I’ll have little bits all over the place!

Thank you again. I’m getting back into my work!

It’s no biggie - ‘variable binding’ is just another name for ‘variable assignment’.

To clarify my earlier statement: languages like Java and Python provide an ‘import’ statement that both loads a library and binds it to a variable, e.g.:

import math # import the math library and assign it to variable 'math'
print math.log10(100) # --> 2.0

AppleScript doesn’t (which sucks), so you have to load library scripts and bind them to properties yourself. For simple cases where you’re statically binding one or two libraries to another script, the code I showed will do the job. In more complex situations, it makes sense to use something like Loader to handle most of the complexity automatically (though it’s still not as good as a built-in ‘import’ command would be).

The AppleScript language component itself has never had the ability to do something like that. It’s totally ignorant about where the scripts it runs come from (a basic limitation of the OSA), so has no way of finding the folder your main script is in. So there’s no way AppleScript alone could silently and automatically merge code from other scripts in that folder into your main script.

Some third-party AppleScript editors do provide their own library support systems, however. e.g. I think Script Debugger does a C-like include that merges source code from one script directly into another. Sounds as if what you were experiencing was something like that.

HTH

Dear Friends,
I may have to post this as a separate thread. Not sure who will see it down here!

Followed hhas’s excellent recommendation to use the “property _MySupportLib: load script…” declaration. For my very limited purposes, this will be fine. I even figured out how to merge several handlers into one script, and call them up independently. That is slick!

So, I took the two “handlers” (which I expect to use in several of these name-grabbing, file-gathering main scripts) out of my main script, and created a “library” script to hold them. The first handler (ConstructtheNewFilingName) works fine. It’s just some text munging. But the second handler is now totally broken! It is called MovetheFilesToLeftWindow(theNewFilingName, Target_Folder, theTruncatedFindingName), and uses variables set in the main script. At least, it USED to use the variables when it was embedded in the main script and called with a “my MovetheFiles…etc.”

on MovetheFilesToLeftWindow(theNewFilingName, Target_Folder, theTruncatedFindingName)
tell application “Finder”
activate
set Files_to_Gather to every file of Target_Folder whose name contains theTruncatedFindingName
if not (exists folder theNewFilingName of Target_Folder) then
set theNewFolder to make new folder at Target_Folder ¬
with properties {name:theNewFilingName}
make new Finder window to theNewFolder
tell front window
set current view to column view
set bounds to {6, 63, 477, 764}
end tell
close front window
else
set theNewFolder to every folder of Target_Folder whose name is theNewFilingName
end if
move Files_to_Gather to theNewFolder
end tell
end MovetheFilesToLeftWindow

Now, although it gets called up from the “CollectMove.scpt” library (which is slick as all get-out!) it gives me the error
“Can’t make «class cfol» “pooh” of «class cfol» “bah” of «class cfol» “yak” of «class cfol» “yuk” of «class cdis» “Archive” of application “Finder” into the expected type.”

This is the correct path, i.e. it’s the proper collection of folders. I suspect I’m going to have similar problems with all the “imported” variables. Note that I’ve very carefully checked the variable names and orders in both my main and library scripts. The subroutine obviously got SOME kind of info right: it constructed the correct path, based on the varible Target_Folder. It just won’t recognise it as the designation of a folder.

Note also that I’ve played the maddening “guess the exact arcane syntax I’ll accept in this situation” game, changing the command “set Files_to_Gather to every file of Target_Folder whose name contains theTruncatedFindingName” with parentheses, adding and removing the word “folder,” etc. in lots of ways I can’t now remember.

Any suggestions for getting the subroutine to work the way it did in the main script, now that it’s in a library, would be greatly appreciated. Thanks again hhas for your brilliant library suggestion. I’m sure it’ll soon be working just fine.

Dear Friends,
“Never mind.” The lost is found. After searching here for an hour or so, I found the solution embedded way down in a thread. I had left my handler calls (if that’s what you call them) within my Finder tell. As soon as I moved the _MySupportLib’s ConstructtheNewFilingName(theWorkingName) lines out from within the Finder tell, into the main “body” of the parent script, the whole suite of parent and child scripts worked perfectly. Live and learn!

Thank you all for your help and patience.