Creating and using a library

Danny Goodman’s Handbook (p287-288), suggests code to call already written subroutines (compiled script) included in a library stored in our Scripting additions folder. This simplifies very much the writting of scripts.

But still this little loading script has at least 5 lines. I am trying to use a handler that would call and execute this subroutine. I call this handler CALL:

on call(LibraryName, SubroutinesName&Arguments)
get (path to scripting additions as string) & LibraryName
set CalledLibrary to load script file result
tell CalledLibrary
set Result to (SubroutinesName&Arguments)
end tell
end call

The 1 line code to call the subroutine would read:

call(LibraryName, SubroutinesName&Arguments)

Is this possible ?? Thanks in advance.

:wink: :wink: :wink:

Lots of times writing more lines of code is much faster than less. For example:

set a to 1
set b to 2
set c to 3
set d to 4
set e to 5
set f to 6
set g to 7
set h to 8
set i to 9
set j to 10

Is much faster than:

set {a, b, c, d, e, f, g, h, i, j} to {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

Up to 5 times faster!

However, you can use a one-line for your needs (no need for subroutines):

(load script alias "path:to:script.scpt")'s aHandler("parameter")

You could use your “universal subroutine call” handler using a “run script” statement, but it would be problematic:

on call(LibraryName, SubroutinesName, Arguments)
	if class of Arguments is not string then
		set Arguments to xToString(Arguments)
		set Arguments to """ & Arguments & """
	end if
	run script "(path to scripts folder as string) & "" & LibraryName & """ & return & ¬
		"set CalledLibrary to load script file result" & return & ¬
		"tell CalledLibrary to return " & SubroutinesName & "(" & Arguments & ")"
end call
on xToString(x)
		error x * 5
	on error msg
		«class ktxt» of ((text 12 thru -16 of msg) as Unicode text as record)
	end try
end xToString

This would accept as “Arguments” a list of positional arguments. But you are resting lots of speed and it is not very economic (and may need lots of testing).

Hello jj,

I appreciate very much your comments, since you seem to be expert at Apple Script. I like to play around with AS, but am a beginner … and french speaking. I try mostly to keep my code simple (as I think of it as simple…) and as short as possible. For now, speed is not realy a concern. Understanding the possibilities is …

I am a little confused :shock: :shock: :shock: about the code that you generously offered me. Can you gime some little comments??

on Call(LibraryName, SubroutinesName, Arguments) ------How do I write arguments if more than 1
if class of Arguments is not string then -----???
set Arguments to xToString(Arguments) -----???
set Arguments to “”" & Arguments & “”" -----???
end if

run script "(path to scripting additions as string) & "" & LibraryName & """ & return & ¬
	"set CalledLibrary to load script file result" & return & ¬
	"tell CalledLibrary to return " & SubroutinesName & "(" & Arguments & ")"  -----????

end Call

on xToString(x) -----???
error x * 5
on error msg
«class ktxt» of ((text 12 thru -16 of msg) as Unicode text as record)
end try
end xToString

I am sure that I will get it will a little help … Thanks again very much. :shock: :shock: :shock:

First he attempt to create what you call my “universal routine” (it’s really that, you named it right …) is to learn further Apple Script. Second, because the idea came to my mind, I want to see if it is possible to minimize the code needed to use a subroutine. If it is, don’t you think it is easier to call:

Call(library, subroutines, {parameter1,parametre2,parameter3})

than to locate and open the original copy of the subroutine and then copy 20 lines of code … in the script. In any event the use of this universal calling will prove to be useful by using it … It may prove not :frowning: :frowning: :frowning: … Is it possible that is my question ??

Theproblem now, is to insert the parameters in a universal way. Lets take a simple example, and try to have it run … Lets take this little subroutine that substitutes some text by another in a word:

on substitute(theText, toReplace, newText)
set AppleScript’s text item delimiters to the toReplace
set the allTheText to every text item of theText
set AppleScript’s text item delimiters to the newText
set theText to the allTheText as string
set AppleScript’s text item delimiters to “”
return theText
end substitute

What I am trying to write is a subroutine that would respond to:

call(TextLib,subtitute,{“Billarry”,“B”,“H”}) where TextLib is a library that contains the substitute subroutine. Do you think it is possible to achieve ??

:wink: :wink: :wink:

With the help of jj, the call subroutine looks like this at the present (it suposes that your library is located in the Scripting Additions folder as D. Goodman suggested) :

on callsubroutine(library, subroutine, {parameters})
run script “(path to scripting additions as string) & “” & library & “”” & return & ¬
“set librarysresult to load script file result” & return & ¬
"tell librarysresult to return " & subroutine & “(” & parameters & “)”
end callsubroutine

It worked with a 1 parameter subroutine … now to have it work with more parameters :idea: :idea: :idea: Any idea ???

For the “run script” stuff to work you would need pass any parameter as a string (where the handler “xToString” will try it). What I posted was a very simple for-test version, and it would need lots of testing, error trapping, etc.

Believe me, this one:

(load script alias "path:to:script.scpt")'s aHandler("parameter")

And billaryclinton’s explanation (which is basically the same), is a one-line of code, very economic, no error and speed. :wink:

Believe me, this one: Code: (load script alias “path:to:script.scpt”)'s aHandler(“parameter”)

Thanks jj,

It works. You are a genious … :lol: :lol: :lol: :lol: :lol: :lol: :lol:

Can you give some details on the code ?? Why is there a parenthesis before the load command ?? Where can I find documentation to further understand this code ???

Thanks again.

I think I understand …!!! The parenthesis is required because you used the contracted form “'s” of load script alias …

Billary if you give the call(library,subroutine,parameters) a try , and find a way let me know …

Your handler does not work. For instance, it ignores completely the parameters provided. This should throw allways the same results:

callsubroutine("dateLib", "setDefaultFormat", {"hhmm dd/mm/yy"})
callsubroutine("dateLib", "another", "no care")
callsubroutine("dateLib", "this routine does not exist", "these are random parameters")

You can’t access handlers by name, as in other languages, as JavaScript. So, you need an external call via “run script”, “do script”, “osascript”, etc. This means that you can only pass plain text to these runners. This means a very limited ratio: no unicode text, no styled text and other vital information, unless you use the clipboard, which is unsafe and bad code design. Also, coercing a big list or record to string is a very slow process…
Definitivelly, I don’t understand why you need a routine for a task which is already implemented (or at least semi-implemented) in AS.
If you need a robust library loader, take a look to HAS’s “Loader”. But it won’t provide access to subroutines nor support for parameters. It is bad design and a great lack on speed; or at least, I understand so… :rolleyes:

Just for the fun of it … :lol: :lol: :lol: I find that my call command is simpler code to understand for newbie like me … Our search could have lead to somethings … maybe sometimes :idea: :idea: :idea: .

Thanks for your patience and advice … both of you jj and billary. Best wishes … and a lot of peace of mind whatever you do to get it !!!

Well, here is a more robust routine using a prototype function. However, it does support only a single parameter. No labels, you must copy this routine to your script in order to use it, and a log etc. Definitivelly, it is not worth the effort nor “universal”…

(but it was funny, real!)

[This script was not automatically tagged for color coded syntax by Convert Script to Markup Code] :lol:

It is funny to receive your email this afternoon while I was myself trying to have that CALL subroutine to work. I certainly don’t have your talent yet for Apple Script but I was trying to find a way to have a CALL for a subroutine with more than 1 parameter.

I thank you. I will try to understand your work … I am sure that we should find an answer to this existantial problem :lol: :lol: :lol:

Is the problem of parameters different if we try to create a subroutine with this format:

loadLib(libraryName,handlerName(parameter1,parameter2.parameter3)) rather than
loadLib(libraryName,handlerName, {parameter1,parameter2.parameter3})

:!: :!: :!:

Nope. You should pass handler + parameters in string format, which is not interesting:

(load script file "path:to:file.scpt")'s hand(par1, par2)

Is much better than:

run script "(load script file "path:to:file.scpt")'s hand(par1, par2)"

(which is mostly absurd).

The problem with parameters is that they can’t be universal if you don’t know what parameters you must pass to the handler. For example:

on handlerName(param1, param2)
     --> ...

to handlerName with x
     --> ...

to handlerName given a:y
     --> ...
end handlerName

It is not only the number of parameters, but also the kind and format of such parameters.

After a few hours of work … I realized :rolleyes: :rolleyes: :rolleyes: that to call something a handler as to have the handler code in the script, thus using more than 1 line of code … :rolleyes: :rolleyes: :rolleyes: That one was obvious, but I have missed it all the way … Gush !!! :rolleyes: :rolleyes: :rolleyes: !!!

After a few hours of work … I also found that jj’s one line universal subroutine calling is great, mostly because (1) it is a real one line code statement (2) it can call any subroutine located in a single library and (3) it can include multiple whatever parameters. This is jj’s code:

get (load script alias ((path to scripting additions as string) & "DeltaTimeLib2"))'s DeltaTime(2, 6.5)

---For this statement to work, you have to store the handler example below as compiled script in your scripting additions folder and name it "DeltaTimeLib2", or try your own :

on DeltaTime (deltaDays, deltaHours)
	set theDate to current date
	set theDate to theDate + (deltaDays * days)
	set theDate to theDate + (deltaHours * hours)
	return theDate
end DeltaTime

After a few hours of work … I also found a way to get nearer of my universal handler. I achevied the 1 line code objective, but still have to insert a handler in the script :cry: :cry: :cry: . I also worked around the multiple parameters problem … There is a limitation tough: the library can only include 1 subroutine that is a RUN handler. Try this real example, it works :

---An example of the universal code line to call the library
Call("DeltaTimeLib", {2, 6.5})

---The handler that you paste in your script somewhere .....

on Call(libraryName, parameterList)
	copy (load script file ((path to scripting additions as string) & libraryName)) to loadResult
	run script loadResult with parameters parameterList
end Call

---The Run handler that will be called by the 1 line universal statement (compiled script located in your scripting additions folder):
on run {deltaDays, deltaHours}
	set theDate to current date
	set theDate to theDate + (deltaDays * days)
	set theDate to theDate + (deltaHours * hours)
	return theDate
end run

Comments appreciated :lol: :lol: :lol: !!!