Question re writing AppleScript to be truly OO

Hi all,

I know how to call an external AppleScript from another AppleScript - that works fine. However, AppleScript is supposed to be completely object-oriented and I’ve had some trouble finding a decent reference about it.

I want to create a class that has the definition for an object containing information relating to some files that need to be backed up.

I can make all that work but only if everything is in the same script file.

Can I have my class definition in another .scpt file, “call” it then have the other scripts persistently know about it? I’ve found so far that if I call a script from another script that the calling script doesn’t know anything about what’s happening in the script it called.

Sorry if I’ve explained that badly but any help would be much appreciated.

Thanks!

I’d contend that AppleScript is not completely object-oriented. but that would probably be a stray discussion.

As for your question, it would help if you could post a minimal example of your script. I am not sure I have understood your problem, but here the different ways in which I usually split code into different scripts:

[First approach] Use a top-level script:


property x : missing value
on foo()
-- do something
end

[Second approach] Define several scripts in a file:


script ClassA
property y:missing value
on foo()
-- do something
end
end

script ClassB
property z:missing value
on foo()
-- do something
end
end

[Third approach] Use one or more handlers that return script objects:


on newClassAObject()
	script ClassA
		property x : missing value
		on foo()
			-- do something
		end
	end
end
return ClassA
end

The above can be loaded in your main script like this:


property FirstApproach : missing value
property SecondApproach: missing value
property ThirdApproach : missing value
loadLibraries()
-- Use the external script objects
FirstApproach's foo()
SecondApproach's ClassA's foo()
set obj to ThirdApproach's newClassAObject()
obj's foo()

on loadLibraries()
	set scriptPath to (the folder of file (path to me) of application "Finder") as text
	set FirstApproach to load script (scriptPath & "FirstApproach.scpt") as alias
	set SecondApproach to load script (scriptPath & "SecondApproach.scpt") as alias
	set ThirdApproach to load script (scriptPath & "ThirdApproach.scpt") as alias
end loadLibraries

With the first approach you can have persistence of top-level properties, but you have a single instance of your external scripts. The third approach can be used to generate script objects dynamically (this is more “object-oriented”), but the state of such objects is not maintained between different executions. You can mix the two and use store script for persistence, along these lines:


set obj1 to newLibObject()
set obj2 to newLibObject()
-- obj1 and obj2 are independent script objects
set obj1's x to 1
set obj2's x to 2
obj1's foo()
obj2's foo()
-- Make the state of a script object persistent
saveLibObject(obj1)
-- Note that this will override the above (of course): you must save different .scpt files if you want to keep the state of different lib objects
saveLibObject(obj2)

on newLibObject()
	set scriptPath to (the folder of file (path to me) of application "Finder") as text
	load script (scriptPath & "lib.scpt") as alias
end newLibObject

on saveLibObject(object)
	set scriptPath to (the folder of file (path to me) of application "Finder") as text
	store script object in (scriptPath & "lib.scpt") as alias with replacing
end saveLibObject

Maybe others will have better ideas.

I’m not sure what you exactly mean but a common mistake is wrong scoping. Could you make a a small example script to show us your problem?

@druido:

Your example worked just fine. Thanks! I’ve got a few changes to make so that the right variables/settings can be passed to the external scripts but overall it works just fine.

@DJ:

Thanks for the reply! I think my scoping is OK, though. :slight_smile:

Before reading this post I want to tell everyone that this is just one of the hundreds ways of working with script objects.

Well I can see your question now more clearly. Keep in mind that most of those examples around here has some problem with child and parent objects. If you have a function that returns a script object you must set the parent script object of this object to the parent script object of your script object’s parent. Well maybe an example is easier to understand


property parent : parent of parent of me -- parent is script who called me

Another reason to do this is to control the scope of callback/passing functions better. This is what I meant with scoping problems through different objects.

Another thing I don’t see very often is use of constructors. I have a MySQL Object written for my applications and needs a constructor as well. The constructor will set all the initial values, checks if client is installed and will return a script object or null (if script object can’t be constructed).


property name : "objectType"
	property version : 1.0
	
	script prototype
		property name : "objectName"
		property parent : parent of parent of me
		property aValue : missing value
		
		on construct()
			--set your variables to it initial values and do your checks
			set aValue to "hello world!"
			return true
		end construct
	end script
	
	on run argv
		copy prototype to x
		if not x's construct() then
			return null
		end if
		return x
	end run

to load this script object I use:

set aScriptFile to load script file "the file above" --normally i put this at the end of a list
set theObject to run aScriptFile --to construct with no parameters
set theObject to run script aScriptFile with parameters {"anoption"} --construct with parameters
if theObject = null then
--making object failed
end

The reason I used properties name and version is because I load a folder of .scpt files (just every .scpt file in thsi folder) and put it in a list. When I want to have an object I can call with version number and name the right object even if I have more than one of the same object types (different versions) loaded. Sometimes this can be useful when your application needs to be downwards compatible with earlier versions of your script/app. Let’s say I’ve programmed a object to serialize and de-serialize applescript data. When I did some changes in the algorithm I can expand my object with a new algorithm or I can duplicate the object and change it to a new version and keeping the old one. So when de-serialize an old file I can use an older object and serialize it again with the latest object. So with one open and save the file is updated to my latest version.