Any way to use AppleScript libraries?

I have some AppleScripts that pull in other AppleScript files as libraries. Is there any way to do something like this with an Execute an AppleScript action? (i.e., “install” the library AppleScript some way that Execute an AppleScript actions can invoke handlers from the library script.)

I think I can deal with one of my specific situations another way, since all the immediate AppleScripts do is pass arguments to one of the library handlers. I can put the handler in a macro and have the immediate macros set KM variables then call the macro with the handler script.

But sometimes library use gets more complicated, so I was wondering whether anyone had any ideas about this. (Not that script libraries are necessarily that common in KM-style scripting.)

And in one of the specific cases that just pass arguments, the library script handlers are larger than I would want to put in an Execute an AppleScript action (35 lines in one case). Moreover, some of libraries have quite a few handlers — I collect them over time in application-specific and general “utility” scripts (for example, “FinderUtil”, “MailUtil”, and a general “Util”). It would be ugly and tedious to break out the handlers I want for specific KM macros, and really difficult to maintain.

Could these be turned into a plugin somehow?

Hey Mitchell,

I don't see any good reason to not use OSX's standard AppleScript library system.

Script Library:

~/Library/Script Libraries/z_Test.scptd

Test handler for the script library:

on TEST_LIBRARY_HANDLER()
  set returnText to "This Library is for Testing Only!"
  return returnText
end TEST_LIBRARY_HANDLER

Usage in Keyboard Maestro:

Execute AppleScript { Text Script }.kmmacros (1.7 KB)

Of course you can use a compiled script instead of a text-script, and it will be a little faster.

-Chris

I was thinking more of packaging and installing, to get the whole thing into KM somehow. I use your approach in many of my macros. As I started thinking about posting them, I realized that my scheme of having a bunch of different script files in some random file location wasn’t going to work.

When did the use construct replace property? I haven’t updated my knowledge of AppleScript to align with the newest version. (Are there web pages or documents you would recommend for learning the new changes in AppleScript?) Is the ability to grab scripts by name from ~/Library/Script Libraries new? Or was I just making things difficult for myself all these years by putting them in a random location (~/Scripts)… The random location requires the construction of a full path, e.g.

property finderutil : load script alias (((path to home folder) as string) & "Scripts:Libraries:FinderUtil.scpt")
```
That basically anticipates the **~/Library/Script Libraries** mechanism, but the latter is better because the path can be omitted.

In sum, to distribute macros that use one of my libraries, I can export the macros to my disk, package the .kmacros file with the .scpt library file, zip them, post the zip file, and give users instructions about where to put the library file. Yes?

Hey Mitchell,

Since Mavericks.

AppleScript release notes

Read through those to see the changes.

If you don't follow the Applescript Users List or MacScripter.net it's easy to get way behind.

Apple's official documentation leaves a lot to be desired, but there is some.

Apple's Mac Automation Scripting Guide: Using Script Libraries

This is incomplete and hardly worth reading.

This still works but is outdated:

property finderutil : load script alias (((path to home folder) as string) & "Scripts:Libraries:FinderUtil.scpt")

Unless the library is quite large it's generally better to NOT use a property, because the library script will ONLY be loaded on compile. E.g. if you change the library the changes will not be reflected in the calling script until you recompile.

set testLib to load script alias ((path to library folder from user domain as text) & "Script Libraries:z_Test.scptd:")
set testLibHandlerOutput to testLib's TEST_LIBRARY_HANDLER()

This is also a pitfall in the current library system.

However – if you use a text script in Keyboard Maestro to call the library there is no persistence, and the library is loaded fresh on each run.

Yeah, it can get complicated.  :wink:

-Chris

The AppleScript list is overwhelming and hard to pull useful information from for one’s particular expertise and experience (or lack thereof). Didn’t realize MacScripter was still alive — I’ll take a look. Thanks for the references and information.

So even use buries the library code in the script, and the script won’t change if the library is recompiled? This still leaves the problem of how to handle large amounts of AppleScript code — doesn’t seem to be appropriate to have hundreds or thousands of lines of AppleScript in a step, though of course you would edit it with a script editor and just do a selectall copy paste to put it into a KM acton.

Are you saying that this way of coding it does see changes in the compiled library? I wasn't clear.

Come to think of it, a compiled script would only need a library invoked as a property when it gets compiled. It could get distributed without the library.

Does use have different semantics than property? (I tried to searching, but "property" and "use" are not terms that do much to narrow down a search including "AppleScript". I will check the release notes, though.

Yes; that'll work.

Hey Mitchell,

Yes. When run from a Execute an AppleScript action using the text-script option – as I demonstrated in post #2.

This loads the library afresh on EACH run. As a result it's a trifle slower, but you don't have to worry about propagating changes to the script library.

Yes, and that can be a real advantage in some circumstances – but if a user ever opens the script and compiles it they end up with a broken script.

-Chris

Hey Mitchell,

“use” is similar to an “include” statement in other programming languages.

It is required for any use of AppleScriptObjC:

use framework "Foundation"
use framework "AppKit"
use framework "Quartz"
use framework "WebKit"
use scripting additions

Not all of the frameworks will be necessary for any given script, but these are the major ones.

-Chris

[quote="ccstone, post:9, topic:3788"]
“use” is similar to an “include” statement in other programming languages.
[/quote]
2016-05-19 16:49 CT (Thu)
(fix the memory lapse in the scripts pointed out by Chris)

Chris,

Thanks for laying that out.

Please correct me if I'm wrong, but my understanding is:

(1) All of the use framework statements, and the use scripting additions statement, do NOT require any further qualification when you call handlers/functions in any of those.

  • IOW, you can use the terms directly
  • IF you had the satimage.osax in your Scripting Additions folder, then you could simply state:
set myVar to change "x" into "y" in "some string with x in it"

where "change" is a handler/function in satimage.osax

(2) Whereas, when using script libraries, you must provide a qualifier, the reference you have given to the library, OR, use the script library name directly in a "tell" statement: (added per Chris)

use myLib : script "My Personal Script Library"   ### Must be in Script Libraries folder (fixed)
--- To call any of the handlers/functions, you MUST qualify it with the reference "myLib"

set myVar to changeStr("x", "y", "some string with x in it") of myLib
--- OR you can use this form:
set myVar to myLib's changeStr("x", "y", "some string with x in it")

--- OR use the script lib script name directly in a "tell" statement, as pointed out by Chris:
tell script "My Personal Script Library"   ### Must be in Script Libraries folder
   set myVar to changeStr("x", "y", "some string with x in it") 
end tell

The key difference here being the use of "myLib"

To All Interested in Using Script Libraries:

Once you have used Script Libraries a few times, it will become easy and straight-forward. Unfortunately, all the the Apple AppleScript documentation either hides or obscures this information.

Hey JM,

Correct.

Again correct.

This is almost correct, but you have to declare the script

use myLib : script "My Personal Script Library"

Right. All calls to a library must include a reference TO the library.

You can also reference the library without employing a “use” statement:

tell script "z_Test"
  set libCallResult to TEST_LIBRARY_HANDLER()
end tell

# OR

tell script "z_Test" to set libCallResult to its TEST_LIBRARY_HANDLER()

-Chris

Chris,

Thanks for catching my memory lapse.
I have updated my script examples in my above post.

I also added this example to my post, so everyone could see all examples in one place.
Thanks.

No, I meant the AppleScript you showed with a more dynamic way of loading the library.