3 Replies Latest reply: Jul 19, 2013 10:33 PM by Rob de Jonge
Rob de Jonge Level 3 Level 3 (510 points)



I've been working on a new Applescript which is intended as a customizable template. When it's done, I will be publishing it on GitHub. But right now, I'm still a long way from it being done. In order to allow updates of the framework provided with this template separately from updates in the custom code bits, I've decided I want to split as much as possible off into a library file. There are a whole number of questions I have about this, as it's the first time I'm trying to do this.


Please keep in mind this is just a fun thing on the side. I'm not a developer, never have been.


I'll reference the following files below ...


  • library.scpt is the script with the main, common functions that don't change from one to the next use of this framework
  • template.scpt is the template document which provides the minimal structure for using this framework
  • main.scpt is an actual script that I am writing, it started out as a copy of template.scpt and then I started changing it


Loading an external library

There are comments all over the web about how to get one applescript to load another, but for some reason none of it seems to work. Although it seems broken again as I write this, I did at one point get the following line to work


set _library to load script alias ((path to desktop as text) & "library.scpt")


Q1: Where do I find a comprehensive reference and step-by-step guide to making the loading of a library script work?


Configuration settings and variables

The idea is that template.scpt contains a number of properties that library.scpt will need to determine behavior. The configuration settings of the framework, if you will. So I make a copy of template.scpt to main.scpt and I start editing these variables. In addition to this, there are certain variables which will be used by functions in library.scpt and in template/main.


Q2: How do I set a property or variable in main.scpt, which can then be used by functions in library.scpt? And how do I set a property or variable inside a specific function in library.scpt, that a function in main.scpt can read and alter if needed?



There is effectively one function in template/main that does the bulk of the work. This function gets called by the framework, which lives in library.scpt. Let's call that function getLiveData(). My intention is to structure this framework as follows.


library.scpt contains


  • a function init() that does the prep-work like validation of files existing, configuration settings existing, setting starting values of variables, etc.
  • a function main() that is the main routine that runs the script
  • - and a bunch of other functions that are called by init, main or getLiveData.


template/main will look like this


  • property setting A-Z that are essentially configuration settings
  • function getLiveData() : does the getting of live data, which is what the script is centered around
  • init() : invoke the init function
  • main() : invoke the main function


Q3: Does that seem like a sensible setup for a split of common and custom stuff?



One of the things I'm struggling with is how do I validate settings having been made and functions existing in main.scpt. This is effectively what the init() function is for, but I have no idea how I test for a property existing or not.


Q4: In main.scpt I say 'property aSetting : TRUE', how can library.scpt then check if this property exists or not?

Q5: In main.scpt I call init() after the getLiveData() has been declared, but how do I check from init() in library.scpt that a function getLiveData() exists in main.scpt?


Your help and comments will be much appreciated.

And as soon as the script is ready, I'll share it here.




MacBook Air, OS X Mountain Lion (10.8.3), 2GHz i7, 8GB RAM, 256GB SSD
  • Hiroto Level 5 Level 5 (5,865 points)



    In brief, what you're trying to do would be accomplished by setting parent of library script object to the caller script which loads the library at run-time.


    Here's silly scripts just to demonstrate the concepts. The lib.scpt is the complied script of library saved on desktop. The template.applescript is the template of caller. Note that lib.scpt consists of constructor which is to set the parent property of lib script object at run-time and caller (template) calls the constructor with the parent argument set to self. This construct will realise the features requested in Q2, Q4 and Q5. As for Q4 & Q5, you'd need to resort to something like exists_handler() and exists_reference() in lib.scpt because AppleScript language itself does not provide introspection facilities.



    on new_lib(pobj)
        script lib
            property parent : pobj
            property p1 : 1 * minutes -- unit 1
            property p2 : 1 * hours -- unit 2
            property p3 : missing value -- base date
            on exists_handler(h)
                    return h's class = handler
                on error number -1700
                    return false
                end try
            end exists_handler
            on exists_reference(s)
                    s's contents
                    return true
                on error number -1700
                    return false
                end try
            end exists_reference
            on init()
                -- validation of parent's handlers and properties
                set chk1 to exists_handler(a reference to parent's getLiveData)
                set chk2 to ¬
                    exists_reference(a reference to parent's q1) and ¬
                    exists_reference(a reference to parent's q2)
                if not chk1 then error "some handler missing" number 8000
                if not chk2 then error "some property missing" number 8001
                -- get live data from parent
                set p3 to my getLiveData()
            end init
            on main()
            end main
            on f1(x)
                    number x : shift amount
                    return string : string representation of a date (= my q1 ? p3 + x * p1 : p3 + x * p2)
                if my q1 then
                    return f2(p3 + x * p1)
                    return f2(p3 + x * p2)
                end if
            end f1
            on f2(x)
                    date x : source date
                    return string : string representation of x
                        my q2 ? localised string : iso string
                if my q2 then
                    x as string
                    x as «class isot» as string
                end if
            end f2
        end script
    end new_lib
    --end of lib.scpt



    property lib : missing value
    property q1 : true -- unit = minute or hour
    property q2 : false -- output format = localised date or iso date
    on getLiveData() -- to be invoked by lib
        return current date
    end getLiveData
    on init()
        -- create and initialize a instance of lib with its parent set to self
        set lib to new_lib(me) of (load script POSIX file ((path to desktop)'s POSIX path & "lib.scpt"))
        lib's init()
    end init
    on main()
        tell lib
            -- modify properties in lib
            set {its p1, its p2} to {(its p1) * 2, (its p2) * 2}
        end tell
        -- retrieve property and invoke handler in lib
        return {lib's p3, lib's f1(5)}
    end main
    --end of template.applescript



    As for Q3, it depends. In my opinion, the simpler, the better. AppleScript is not designed for large-scale libraries. It's rather involved if not impossible to implement multiple inclusions and manage dependencies. By the way, I don't understand the use of main() in lib.scpt because it is supposed to be a collection of handlers. So I did not implement it in the sample codes listed above.



    As for Q1, AppleScript Language Guide is the primary document you must peruse. Especially the chapters "Variables and Properties" and "Script Objects".


    https://developer.apple.com/library/mac/documentation/AppleScript/Conceptual/App leScriptLangGuide/

    https://developer.apple.com/library/mac/documentation/AppleScript/Conceptual/App leScriptLangGuide/AppleScriptLanguageGuide.pdf


    Also you might find the following old thread of some use. There I've written an afterword for reference.


    tell/scoping problem



    Re: re: my parent




    Hope this may help,


  • Rob de Jonge Level 3 Level 3 (510 points)



    Have been staring at your code for a while now, but I think the lack of caffeine is prohibiting my brain from understand what is going on. I'm sure I'll get there soon enough, I just wanted to say 'thank you' for all the effort into putting that answer together. I really appreciate your help, and not only has it solved my situation but I also think it will help countless others to have this reference available. Thanks so much!




  • Rob de Jonge Level 3 Level 3 (510 points)

    Hi again, Hiroto.


    A little more background on what I'm looking to do. I use GeekTool to display a bunch of stuff on my desktop, and wrote a single template that I can re-use each time.


    The template I wrote basically provides a mechanism where data is pulled from either a live data source or a cache, and then written to the screen. If the source is available (eg. the application is running), data is obtained and output is written to disk and screen. If the source is not available, "cached" output is read from disk and written to screen.


    Have a look at this GitHub repository where the template is published in it's current form. Basically, what I am trying to do is move everything into the library except for the settings, and the bits marked for custom functions and custom live data. This will allow me to update the library without having to edit each script that uses it.


    You seem to know your stuff.

    Any further comments?