[Tutorial] Scripting Libraries

Mavericks (Mac OS X 10.9) ships with an updated version of AppleScript. One of the new features in AppleScript 2.3 is creating your own script libraries. I would like to explain in a small tutorial how this new feature can be very useful.

Pre 2.3 versions
you can use load script commands to return an instance of the script object stored in a file. This is an useful way to store script objects in files so they can be used by multiple AppleScript scripts or applications. The script object will behave much like a library, so this was one way to create your own pseudo-libraries. However, locating and managing such pseudo-libraries was always a mess and you need to write your own “include” function to make sure the correct library and version are loaded, if users would make the effort. Sharing those pseudo-libraries was always difficult and rarely a success because it was hard for another user to understand the library. At the end these pseudo-libraries were mostly used by scripters locally on their machine, particularly those needing to do a lot of writing involving reuse of AppleScript code.

AppleScript Libraries
Script libraries are in basic form are pretty much the same as loading a script object from a file. In simplest form, the primary difference with loading script objects is that you don’t need to know the path to the file, but the library must be stored in a fixed location. That location for libraries is a folder in one of the Libraries folders with the name “Script Libraries” (which doesn’t exist after installing the OS). You can also use AppleScriptObjC code in a library and use it in a normal script file that doesn’t support AppleScriptObjC. At an even higher level you can use your own AppleScript terminology. With the use of AppleScriptObjC, your own terminology and the new loading mechanism, script libraries are a major improvement over just loading scripts.

Creating your first script library
The first thing to do is create a simple script library. It is a library that functions just like loading an script file only you do not need to load the file by its path, instead you call the script library by its name exactly as you address applications by their name. To get started in the easiest way, start with an handler that is missing in the AppleScript language. I think one of the most missed functions is string replacements. Just like a normal stored script object that we load later with the load script command we create a simple script and save it as script in the Library:


on textReplace(sourceText, searchText, replaceText)
	set {TID, AppleScript's text item delimiters} to {AppleScript's text item delimiters, searchText}
	set textItems to every text item of sourceText
	set AppleScript's text item delimiters to replaceText
	set changedText to textItems as string
	set AppleScript's text item delimiters to TID
	return changedText
end textReplace

Save the script above as a script file, not a bundle, and store it in your “Script Libraries” folder of your home folder with the name “Basic Text Utilities.scpt”. Apple suggests that when you are in the development phase you should store the script library in the Library folder of your user’s home folder. By default the folder “Script Libraries” doesn’t exist in the library folder in Mavericks. To use the script library we no longer need to know it’s path. When we use the new object specifier script AppleScript will look in the script libraries for the given script file name. We can use the script library as follows:


use myLib : script "Basic Text Utilities"

myLib's textReplace("Hxllo World!", "x", "a")

or, using a tell block


tell script "Basic Text Uilities"
	textReplace("Hxllo World!", "x", "a")
end

or


script "Basic Text Utilities"'s textReplace("Hxllo World!", "x", "a")

Apart from the new object specifier script there is also the new “use” statement in AppleScript. The use statement for loading a script library loads an instance of the script file. Because there is no terminology definition in this script library we need to bind that instance to a variable (myLib).

Make use of AppleScriptObjC in Script Libraries
In this section I’m not going to explain how AppleScriptObjC works. If you need that explanation, I recommend that you read Shane Stanley’s ebook “AppleScriptObjC Explored” or “Everyday AppleScriptObjC”

As we know, the power of Cocoa compared to AppleScript is huge. But in the past it was difficult for a normal script to make use of AppleScriptObjC. Having script libraries using cocoa and calling them by simple script files makes the use AppleScriptObjC even more attractive than it already was. For instance missing functions as string replacements, finding index of in arrays, sorting data or get key values of dictionaries (record) are all part of the Cocoa API and which does it all at an amazing speed compared to AppleScript. What we’re going to do is create a library as above but this time use AppleScriptObjC instead of AppleScript’s text item delimiters to replace sub-strings in a string.

Start with a blank script and save it as a script bundle. Save it in the folder containing “Basic Text Utilities.scpt” with the name “ASOC Text Utilities.scptd”. When you store the script as script bundle, toolbar item to show the bundle contents will appear. Click this button and a drawer will appear. Inside the drawer you’ll see the contents of your bundle. Inside this drawer there is a checkbox to enable AppleScriptObjC, which enables the key value OSAAppleScriptObjCEnabled key in the info.plist file. Make sure that the checkbox is checked because by default it is not in the AppleScript Editor. Now add the following code to your script.


on textReplace(sourceText, searchText, replaceText)
	set cocoaString to current application's NSMutableString's stringWithString:sourceText
	cocoaString's replaceOccurrencesOfString:(searchText as string) withString:(replaceText as string) options:(current application's NSCaseInsensitiveSearch) range:{0, (cocoaString's |length|())}
	return cocoaString as unicode text
end textReplace

Save the script again and its use is identical to our AppleScript example:


use myLib : script "ASOC Text Utilities"

myLib's textReplace("Hxllo World!", "x", "a")


tell script "ASOC Text Uilities"
	textReplace("Hxllo World!", "x", "a")
end


script "ASOC Text Utilities"'s textReplace("Hxllo World!", "x", "a")

Script Library with our own terminology
By adding our own terminology in our script library we can make script libraries behave like scripting additions. The advantage is that we can use features similar to Scripting Additions without using the complex languages of C/C++, using instead AppleScript(ObjC) code. Of course, there is a lot more you can do in a Scripting Addition, things like optional parameters which aren’t supported in a scripting library with it’s own terminology. There are also classes and events you can create in a scripting addition as well. But apart from those shortcomings, these work from your (calling) script pretty much the same as Scripting Additions. First let’s take a look at how to define our own terminology.

Scripting definition files are files that are used by AppleScript to know the terminology (syntax) of applications or scripting addition in certain contexts. Real AppleEvents that are used to communicate through the system for interprocess communications are defined by a four letter code. These codes would make reading the AppleScript source code unreadable. Sometimes we use these when we want an UTF-8 encoded string for example. We use the class with a four letter code: «class utf8». Because AppleScript doesn’t have a definition for this class, we’re forced to use the raw class name. We’re going to use a definition file for the same purpose but instead of defining a class for script libraries we define commands and types. types are enumerators which can be useful for parameters with predefined values.

A valid but empty sdef file looks like the file below. It’s an XML file that contains a dictionary element.

But first we’re going to add a suite. A suite is more or less a separator to divide different commands in different groups. For example, you can split all your commands into different suites like “Text Suite”, “List Suite”, “Record Suite” and “Date Suite” for examples of different categories. For now we’re going to use “Text Suite”. Then we need to add a code attribute to the suite element. Here you choose a four-letter code. Apple says that they use only lower case letters and that lower case codes should only be used by Apple, or at least not in scripting definitions used in script libraries. So you must have at least 1 character uppercase or it will conflict with Apple’s own coding and rules. What I will do is using the odd position capital for commands, suite and parameter codes and using the even positions capital for enumerators. But it’s all up to you how you’re going to use them in the future, as long as at least one character is uppercase.

For all following XML elements we are going to a description attribute. This is because this description will be shown when the dictionary is opened by AppleScript Editor. So the description attributes are good for documentation about the script library for your own use but especially if you ever want to distribute it.

Once we have created a suite we can add commands and types to that suite. For now, our command is an AppleScript handler, so commands needs to be defined in an named parameter style. If we are not using restricted parameter names and restricted handler names, then we’re free to use whatever we want as long as the handler and named parameters match those in the script. For this example, I want a command named “replace in string”. I add an element “command” with an attribute name. The attribute “name” is the name of the handler we’re going to use in the script later. We also need code here for this command but this time it must be 8 characters long with the first 4 characters are the code that’s been used for the suite. So our code ends with “StRe”.

After adding the command we want to add parameters to the command. There is a direct-parameter element which is the parameter that’s directly behind the command. First example, the string after a do shell script command is called a direct-parameter. The direct-parameter contains a type and description. The direct-parameter can only be defined once and unlike named parameters it doesn’t need a code. The type can be a self-defined enumeration type or standard AppleScript types (classes) like integer, text, and record for example.

Our command needs more than one parameter so we extend our command by adding named parameters. According to the sdef manual, named parameters support the attribute “optional”, because script libraries doesn’t support it I’m not going to use those attributes here. The named parameters has a name, code, type and description. The name attribute of can contain also almost any word, but it’s better not to use confusing names. with or without are usually to set the boolean value of one ore more named parameters in one parameter. So a logical name for our second parameter to use something like “search for” and “replace with” as our third named parameter. To show how enumrations works we add another parameter: how we search the string byte search is text search. With enumerations we need to fill in our own type. This type filled here we’re going to define later.

We also need to define our enumerator. We fill in numerations directly in the suite element next to the commands. As I said, for enumeration I use another capital type (even positions are capitalized) to avoid conflicts. And the attribute name of the enumeration element needs to be excactly the same as the value of the type we have used for “using search option”. So first we need to create a container for every enumerator later:

The last part we need to do to finish out sdef file is defining the enumerators. The enumerators have a name and code attribute as well. The names will work as labels or constants.

Now save the file somewhere and give and name it “ASOC Text Utilities.sdef”, and remember were you save it. You can test if the file is correctly saved by openening the file with AppleScript Editor. If the file is not a proper sdef format it will say “there is nothing to show”.

Now we need to add the scripting definition file to the scripting bundle. We’re going to re-use the script library “ASOC Text Utilities.scptd” and open it with AppleScript-editor. Open the drawer in to show it’s bundle contents (blue-white icon in your toolbar) and drop the “ASOC Text Utilities.sdef” in the file table. Fill in the Scripting Definition text field “ASOC Text Utilities”, the name of the sdef file without extensions. This the library itself, when editing, as software that will understand the terminology,

replace the code in the script library with the following code and save it:


on replace in string sourceText search for searchText replace with replaceText using search option searchOption
	if searchOption = text search then
		set searchOption to current application's NSCaseInsensitiveSearch
	else
		set searchOption to current application's NSLiteralSearch
	end if
	set cocoaString to current application's NSMutableString's stringWithString:sourceText
	cocoaString's replaceOccurrencesOfString:(searchText as string) withString:(replaceText as string) options:searchOption range:{0, (cocoaString's |length|())}
	return cocoaString as Unicode text
end replace in string

Now our script that will use this library has something new. Because we’re using a scripting definition it means that we have our own terminology and have extended the syntax of AppleScript. Because the library’s syntax is added to the AppleScript interpreter that it using the library you can also directly use the library without a tell block.

use script "ASOC Text Utilities"

replace in string "Hxllo World!" search for "X" replace with "e" using search option text search

or you can still use a safer tell block:


tell script "ASOC Text Utilities"
	replace in string "Hxllo World!" search for "X" replace with "e" using search option binary search
end

For safety it’s better to use tell script block when you’re in the context of another application. When you’re in the context of the current application, I think, it’s pretty safe to call the command without the tell block around it.

Sharing libraries
Script libraries is a great improvement for AppleScript but on the other hand maybe less for MacScripter. First as you may have noticed my custom AppleScript syntax isn’t supported by the AppleScript markup on MacScripter. Also having your own library can be confusing when you seek for help on MacScripter because not everybody is using the same libraries. So I would already warn everyone for this.

Software I’ve used
In the past I used Sdef Editor.app from Shadow Lab but they stopped supporting this application in 2007. It still works in Mavericks and works correctly (and it’s free). The sdef editor was developed for easily writing sdef files for applications and scripting additions. This editor has full support of the sdef file while script libraries only use a small part of it. So it can be a bit confusing. For the week before this was written I have been using Shane’s ASObjC Explorer for Mavericks. This version of OSObjC Explorer has a built-in sdef editor that makes creating script libraries even easier, especially when using your own terminology. Also the sdef editor in Shane’s application will only show you the options that are needed.

using the return element in sdef
For documentation purpose I use the return element in the sdef file. Here you define the return type of a command.

I hope this tutorial is useful and I welcome any comments about this tutorial.

  • Shane has also a great video tutorial on his website for changing the case of a string object on Mac OS X Automation using AppleScriptObjC.

revision a: 29-10-13 Changed the named parameter with into by because with is normally used for boolean true values (thanks to Shane for pointing it out).
revision b: 01-11-13 Rearranged the order of topics in a more obvious way, thanks to McUsr
revision c: 04-11-13 Rewrote the tutorial and added some more information to it, writing 3 different kinds of libraries, extensively discudding the scripting definition file and put as much suggestion in it as possible.

Some Light editing done by A.C. Bell

2 Likes

Hello.

I had a slight problem with compiling the code, which I pinned down to being having Script Debugger 4.5 set up as the default editor. Not so understandable, as it is pretty pre Applescript 2.6.

Quitting the editors after having set AppleScript Editor to the default AppleScript Editor, and then restarting both of them solved all issues, at least as long as I compile the new code in AppleScript Editor.

Having read this excellent description rather carefully, though I haven’t thoroughly tested it yet, I have decided to include it with our other tutorials in unScripted. Well done.

McUsrII’s comment that this tutorial must be tested in the Script Editor rather than in Script Debugger arises from the the differences in way SE handles dictionaries compared to SD. I have no doubt that a fix for SD will make this right eventually.

Hello.

I want to add that ScriptLibraries included by the new “use” keyword, that are without an sdef file, works flawlessly with my ScriptDebugger 4.5.7 that I bought originally for Snow Leopard. -Now, that is value for money! Thank you Late Night Software!

Hi,
I had watch the Developers video on script libraries and have written a library. Similar to this one actually. Seems everyone wants to replace text. :slight_smile:

In my search for an answer to one problem I have, I came across you well constructed Tutorial. But I do not think you cover what i am trying to solve.

The same as you I have some options. My options are ‘using any match’ or ‘using exact match’

But what I want is to be able to write the command with or with out the ‘using
’ options. Similar to where you can use: ‘replacing’ or not in the save command.

My sdef does contain the optional=yes in the parameter.


But this does not seem to make it optional and I still get the error.

I already employ error controls in the script libraries handler to catch invalid classes being used as arguments. But they are inside the handler. I cannot find a way to catch an error in the parameters though.

Do you or any one have any ideas

Many thanks

You can’t designate parameters as optional – AppleScript has never supported optional parameters in its handlers, and terminology is just repackaging handlers.

Ah I think I see,. Although not sure how would go about it.

A a test I added

to my sdef inside the command.

This allowed the replacing to be placed on the end or not.

But is the code working?

AppleScript has always allowed this sort of thing:

on addUp(x, y, z)
	return x + y + z
end addUp
addUp(5, 6, 7, 8)
--> 18

where it just ignores the extra argument, but it won’t handle this type of thing:

on addUp(x, y, z)
	return x + y + z
end addUp
addUp(5, 6)
--> {5, 6} doesn't match the parameters {x, y, z} for addUp.

When you add terminology, all you are doing is effectively wrapping a new interface around ordinary AppleScript handlers; you’re not changing the underlying rules.

That’s not to say it couldn’t be made to work, though, so feel free to log a request for it.

Hello.

I have been wondering if it is possible to write an sdef that would make handlers take parenthesis.
Is that possible, -to write and sdef file for a handler like addUp(x,y,z)?

Thanks. (A “no” will suffice.)

Which I think is what it is doing.
Thanks

No. (Or commas.)

While writing this tutorial I’ve tested the optional parameter and found out that you can add this attribute to the parameter element but it’s value is ignored. As for applications and scripting additions the binding process is completely different and therefore optional values can be used. The most important reason is script libraries, using it’s own terminology or not, is an instantiated script object and not an C library that’s dynamically loaded directly into the AppleScript Interpreter. That means, like Shane also said, it’s limited to the rules of AppleScript objects and handlers.

Because script libraries are instantiated AppleScript objects the optional parameters should be made in the AppleScript language. I like the idea of optional parameters but how would that work since AppleScript handlers supports both labeled parameters and positional parameters? If you have an handler for uppercasing you would have something like:

on makeUpper(theString, mode:1) --optional without own terminology
end makeUpper

on makeUpper theString case style:sentence case --option with terminology using enumeration
end makeUpper

@Mark Hunte:

While that’s true, there’s an important exception: if you are using the same term as Apple, you should use the same code. Mark’s example uses the code standard additions uses for the replacing parameter (in store script).

I thought so to until I watched video 416 of the wwdc (Introducing AppleScript Libraries). There it say “The use of codes, comprised of all lower case letter, is reserved by Apple.” and as “Sal Soghoian” says that you need to use at least 1 upper case character in your code, not saying anything about re-using codes by Apple. Am I missing something here, please tell me then I can change that in my tutorial above as well.

Thanks for the prompt reply Shane.

With regards to the codes reserved by Apple, the same exception as Shane stated above pertains to those. Just writing this for completion. There is a long list of the codes for the return types in: Cocoa Scripting Guide table 1-1.

I can’t hurt to also read: TN 2106 Scripting Interface Guidelines.

I found the second link now, while looking for the first one, but it looks like a good read up. :slight_smile:

Edit

There is “of course” also a new AppleScript Language Guide that ships with this fine Operating System!

Not as sexy, but it’s possible to have pseudo-optional parameters in AppleScript by passing them in a list or a record. You still have to pass something (ie. the list or the record), but it doesn’t have to be fully populated:

on addUp(aList)
	set |sum| to 0
	repeat with thisValue in aList
		set |sum| to |sum| + thisValue
	end repeat
	
	return |sum|
end addUp

addUp({5, 6, 7})
--> 18

addUp({})
--> 0
on addUp(aRecord)
	set defaults to {x:0, y:0, z:0}
	set aRecord to aRecord & defaults
	
	tell aRecord to return (its x) + (its y) + (its z)
end addUp

addUp({x:5, y:6})
--> 11

addUp({})
--> 0

Thanks.I Know about the code thing and my own codes comply. As Shane say I used apple code here. But only because I was doing a test. Not to be used in my real code.

@Nigel Garvey

Thanks for the idea.

I don’t know if you want to use this in the Tutorial. But I was looking to dynamically choose which CharacterSet to use from NSCharacterSet.

So for example if I ran my script to remove characters and set the CharacterSet with a string:

 set thisSet to "alphanumericCharacterSet"
set changedText to remove characters in "<%p>%s: object_: %@ name:- %@  899userInfo:.,) %@" not in thisSet adding characters " @-_.)" replacing with "-"

I would need to coerce the string into a class method name.

What I came up with was inside the Script Libraries handler I use the NSSelectorFromString function.
Then have NSMutableCharacterSet class perform the selector.

set s to current application's NSSelectorFromString(current application's NSString's stringWithString:thisSet)
	set characterSet to (current application's NSMutableCharacterSet's performSelector:s)
	set charString to current application's NSString's stringWithString:charText
characterSet's addCharactersInString:charString

This returns which ever NSCharacterSet I need. And saves me having to use a load of if statements of a repeat loop to work out what NSCharacterSet is needed.

Note: I am using a NSMutableCharacterSet instead of NSCharacterSet because I will be adding characters to the set later on.

I think Sal was trying to simplify what is actually a fairly tricky business. The commands themselves should be unique both in the term used and the code. But for parameters, and enumerations, the rules are different. For example, if you are going to have an enumeration with yes/no/ask enumerators, using an exact copy of the one used in standard additions is perfectly fine, and makes sense in terms of the pool of available codes – just don’t mess with it. Importantly, it pretty much guarantees that you will not conflict with another definition – and avoiding conflicts is what the guidelines are all about. The same goes for parameters, although it is not compulsory that they be the same.

Apple still have some defined suites, and although they haven’t been updated for a long time, the whole idea behind them was that developers would use consistent terms and codes.

From tech note TN2106:

The quote was aimed at app developers under the pre-use regime. With the use command, the terminology is being pooled, so you can’t just rely on your terms trumping scripting addition terms.

Three things:

The use command, and especially the ability to avoid scripting additions, gives us control over how conflicts are handled, so we possibly don’t have to be so rigid about it all. The fact that conflicts are called out at compile time is a good thing. But it doesn’t hurt to try to avoid them in the first place, to avoid the extra work of including extra tell or using terms from blocks.

The best way to limit conflict with the command part is to try to use multi-word terms. Scripting additions with very large dictionaries can be a serious cause of conflict. (If third-party scripting additions disappeared, this would all be a lot simpler.)

Finally, the all-lowercase rule of thumb is pretty rough. There are quite a few case where Apple has used other characters. (Most of the parameters to the say command, for instance, are all-uppercase.)

(And in case you are wondering: if you write your dictionary using ASObjC Explorer, the red colouring that comes up when you enter a code that conflicts is based on my building a list from trawling through Apple’s headers, so it should be reasonably accurate. But it just means you should not use it for a command, or unless you also use the same name.)

Save yourself some effort – just pass the string and let the bridge convert it for you:

set s to current application's NSSelectorFromString(thisSet)

Thanks Shane for the thorough answer.

I was expecting the use of parameters as you said and I had it written down in as you replied in my first edition of this tutorial. I was aware what the documentations says about code usage while making scripting additions and applications scriptable. But after watching the video I wasn’t sure, so I chose for a better-safe-than-sorry way. The sdef is extending the terminology like scripting additions but it’s still just an AppleScript object. And besides that I couldn’t really find anything that says explicitly that it needs to follow the guidelines for scriptability in applications or scripting additions.

Done,:rolleyes:

Ta.

Hi a quick question.

If I use use script “Applescript Utilities”

And in my Applescript outside or inside any tell blocks

 set posixFile to POSIX file "/Users/UserName/Library/SomeFooPath"

I get an error “Expected end of line, etc. but found class name.”

from the file part of the command.

I do not have any single word file verbs in my script libraries.
the closes is <command name=“existence of file path” code=“MOSXEOFP” description=“Check the existence of a file or folder path”

Does any one know why I am getting this.

if I use a Applescript Utilities tell block around all the other code where needed instead of the global use clause all is ok. But that defeats the point of having a global use clause.

*SLIGHT UPDATE;

Another example:

I can use:

use finder : application "Finder"
set thisPath to item 1 of (get selection of finder) as alias

with none of my custom script libraries loaded.

Works fine. But if I add the:

 set g to POSIX file "/Users/UserName/Library/SomeFooPath"

any where else in the script.
like so:

use finder : application "Finder"
set thisPath to item 1 of (get selection of finder) as alias
 set g to POSIX file "/Users/UserName/Library/SomeFooPath"

I still get the error.

POSIX File is part of the standard scripting addition. When you use the “use script” statement you should use the line “use scripting additions” as well if you want to make use of scripting additions. When you use the “use script” statement, scripting additions are by default not included into your script. It’s something that I should have mentioned in my tutorial I guess.

Thanks.

Annoyingly that was the first thing I tried. But clearly did not have enough coffee this morning because I now realise I must have used

use script “scripting additions” which obviously did nor work.
Doh


Hello.

I am currently writing some sdef files, and I have a couple of questions:

I have some handlers that takes a list of lists of integers as a parameter (a matrix), how do I specify that?

Am I right in that it is better to not specify the return type, than do so for a command?

I also wonder how you specify an AppleScript Object as a parameter.

Thanks.

A list of any. That’s the best you can do.

If you know the type, by all means specify it.

Usually as any.

In script libraries, types are purely informative – they don’t actually do anything, unlike in apps and scripting additions. So just use common sense, and add explanations where you think they will help.

Hello.

Thanks a whole lot Shane. I was really wondering about this. My qualms with return types, is of course when the return types varies, which isn’t good practice, but which choose from list for instance uses anyway. :slight_smile:

Hello.

I am at a loss,wondering about how I make scripting terminology for a handler that takes no parameters, if that is possible.

It doesn’t just work by leaving out the direct parameter definition, and any other parameter description. I hope I have overlooked something.

Most simplest form of a command is like:

<command name="command name" code="suitcomm" />

where the implementation is like:

on command name
   return true
end command name

which should be used like

use script "name of library"

set x to command name 

p.s. Tested and worked in Mavericks

Hello.

I can’t compile anything like that, maybe I am doing something wrong, (I get the message: Expected «given», «into», «with», «without» or other parametername, but found end of line) I am on Mavericks too.

use AppleScript version "2.3"
use scripting additions
use framework "Foundation"
use framework "AppKit"

(* ========== debug suite ========== *)
on milli secs
 # from start
	tell current application to set this_time to (its NSDate's timeIntervalSinceReferenceDate()) * 1000
	return this_time
(*
	if start = 0 then
		return this_time
	else
		return (this_time - start)
	end if
*)
	# NSDate's alloc()'s init() to return ((its timeIntervalSince1970()) * 1000)
end milli secs

from the Cocoa Scripting documentation

Cocoa scripting will generate default keys for property and element attributes and for commands, if you do not specify them. For a property, it capitalizes each word of the property’s name except the first word, then removes any spaces. For an element, it specifies the plural of the element type. For a command, the default is NSScriptCommand

apart from that, the handler parentheses are missing

on milliSecs()

Hello Stefan.

Thanks for the input, I had a question earlier in this thread regarding parenthesis, and by the answer I got from Shane, I figured I couldn’t use parenthesis with a command that is supported with terminology.

Edit

I asked the question in post #9, and Shane answered it in post #11, (page 1 :smiley: ).

The motivation, is of course to have the terminology load with the use script clause, so I won’t have to use a script object in order to utilize the commands, but can use them like I can with a scripting additions command.

For the record, the script bundle is named corelibrary, the sdef file is named corelibrary.sdef, the name in the bundle tab is corelibrary, and the id is net.mcusr.corelibrary. ASOBJC is enabled, and the sdef document a valid sdef/xml utf-8 document.

your sdef should have an empty command tag if it is really empty, I guess it uses libxml to parse the file because in libxml there is a difference between and . So you sdef should look like:

Not when you use your own scripting definition, parentheses are not needed then.

sorry, I was wrong, forget the other post

Hello.

Thanks for answering DJ Bazzie Wazzie, I have tried that as well. :slight_smile:

At least it works on my machine only when you write an empty tag as otherwise it won’t work.

What I did was

  1. create new applescript in script editor
  2. save script as script bundle
  3. a bundle inspector (drawer) appears, open it
  4. create sdef file with plain text editor (sublime text)
  5. drag & drop the sdef file inside the bundle
  6. fill in the name (without extension) in the sdef field name in the drawer
  7. save the file again
  8. write the handler as below

on milli secs
return 0
end milli secs

  1. compile (the handler should compile properly)

Hello.

I have done what you have done, and I have also tried with/without use AppleScript version “2.3”
But it just doesn’t work. (I also added a title tag to the dictionary.)

Hello.

Restarting ScriptEditor and closing bundles with various earlier attempts , and “test scripts”, made sure of that there wasn’t any conflicting terminology around, and it worked!

Thanks :slight_smile:

I did add "use AppleScript version “2.3” " in addition to following DJ Bazzie Wazzie’s receipe to the point.

I’m a bit surprised by this. The reason is because I went back and looked at the sdef for ASObjC Runner, which does this very thing for several commands, and they all work fine.

Hello.

It’s surprising that you have to use an “empty tag” though, when there is nothing to be put inside the command tag.

Hindsightly I guess I had saved some hours if I had started out with AppleScriptObjC Explorer in the first place. I managed to wreak havoc with AppleScript Editor over the course, and I actually had to restart it, not only close the script bundle, which I had figured up front I never would have, as long as I didn’t add the script bundle to the dictionary library of AppleScript Editor. Now things are working great, and thanks for all your help and patience! :slight_smile:

Edit

The main take away from me besides using “empty tags”, when there wasn’t any content to put within has been the cycle:

  1. Edit and save the sdef file.
  2. Save the bundle.
  3. Add your applescript handler to the bundle.
  4. Save and test the bundle.

UPDATE: It seems that once it compiled it doesn’t matter?

Looking further into this


Hello.

I think I’ll just use what you call an “empty” tag, when there are nothing like direct-parameter and other parameters inside the command block. It’s a good rule of thumb, particularily, when Shane’s AsObjC Explorer creates such sdef definitions for such commands.

I think I may have really littered Script Editor in the process, but now the process is controllable again. :slight_smile:

When there is no dictionary added to the library of AppleScript Editor, no script in the editor , that uses the library bundle I am editing in the editor; then edititing the sdef, saving the bundle, adding the handler, then compiling the bundle, and re-saving it works well!

This even works if I have to change words in the command or parameters, due to having used some reserved word, like “using” for instance, and re compile, works like a charm now!

I think I’m losing my mind here :expressionless:

Yesterday, behind the same machine I couldn’t compile your sdef file and when changed it into <command > I could compile it. When I set it back to tag it wouldn’t compile again just to make sure that it was the problem. And again when set to style it would compile. Because I was working with libxml2, and it does have a difference between these two (the check for empty tag will return true for <command > and will return false ), I assumed there is a difference for script libraries as well. But today I wanted to get the same “error” again and everything compiles just fine, even without extra saving. So I guess it has to do with some caching or something in the background.

Einstein once said: “Insanity is doing the same thing over and over again and expecting different results.”. I’m in doubt if I’m going insane or Mavericks :D.

FWIW, I just use:

NSData *sdefData = [self.sdefXMLDocument XMLDataWithOptions:NSXMLDocumentTidyXML];

The only messing with XML I do is when I read in an existing sdef, in which case I turn all type attributes into elements, because it makes the interface simpler. And of course the table handles things like escaping reserved characters.

You young 'uns have it easy :wink: In the old pre-Mavericks days, the slightest mistake in an sdef would usually mean at least quitting and relaunching your editor. After much begging, sdef file modification dates are now compared and caches cleared if necessary.

Well it was you or me, so I’m happy :stuck_out_tongue:

Hello.

I believe what ruined it totally for me yesterday, was having several scriptbundles, with almost the same terminology, and some driver scripts as well. I guess I managed somehow to trash the cache, but I am not looking back into it.

Next time I get serious issues, I’ll restart the Script Editor and take it from there.

Thanks for the tidy xml command Shane, I didn’t know it convert tags! :slight_smile:

Found it!

I was having cache issue. It seems that the running instance of AppleScript loves to cache script libraries and it’s definition files. Even when I remove the a script library and then create a new one with the same specs the errors will be thrown on my screen knowing it shouldn’t give an error in the first place. I need to logout and login again in order to make the script compile again. I will look into this if Yosemite have these cache issues as well.

In short: The first steps I posted previously is very important you do them correctly the first time! If not, there is no way you can make the scripting definition to load properly and you’re forces to start over or re-login to the system. I will take these steps in side note in the tutorial as well.

FWIW, one of the biggest caching gotchas is that the modification date check is done with the app or library, not the sdef file. Changing a file inside a bundle doesn’t always change the bundle’s modification date. Even saving over an existing bundle doesn’t always change the bundle’s modification date, either. So you need to think about doing something like touching the library.

Hello.

I tried to run a pair of empty tags thru an xml validator, and it validated just fine. And so does a single empty tag.

We all know that we should use single empty tags, but I was curious and that is why I post the result.

The conclusion is that sdef is based on tidied xml, and just not xml.

And now, over to the chess world championship. :slight_smile:

I was trying to learn how to make my own library based on this tutorial.
I didn’t succeed, and I would like to have your help.
My goal is to make a library which I can call directly, just using

use script "Luciano_Library"

,
AND NOT using the:

use myLib : script  "Luciano_Library"

For example, in the tutorial, I could not find the " Inside the drawer you’ll see the contents of your bundle. Inside this drawer there is a checkbox to enable AppleScriptObjC, "

What I am asking if someone can make a library for me using the script below, just 3 subroutine of different types (alias, integer, text) as a bundle, which would include also:

  • a simplified version of the ScriptingDictionary.sdef
  • Info.plist
    So from this I can learn and make what I need.
    Or as alternative to direct me to a place where I can learn how to do it.

Thanks a lot

Subroutines for the library:

on BeepAlert(numberOfTimes) -- integer
	beep numberOfTimes
end BeepAlert

on WriteTotextFile(fAlias, textToWrite) -- alias, text
	set fileDescriptor to (open for access fAlias with write permission)
	try
		set eof fileDescriptor to 0
		write textToWrite to fileDescriptor -- as «class utf8»
		close access fileDescriptor
	on error errMsg number errNbr
		close access fileDescriptor
		error errMsg number errNbr
	end try
end WriteTotextFile

on listToString(theList, theDelimiter) -- List, text
	set theBackup to AppleScript's text item delimiters
	set AppleScript's text item delimiters to theDelimiter
	-- Perform the conversion
	set theString to theList as string
	set AppleScript's text item delimiters to theBackup
	return theString
end listToString