Looks like no one’s replied in a while. To start the conversation again, simply ask a new question.

Externalise functions into a library

Hi,


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?


Structure

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?


Validation

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.


thanks,

Rob

MacBook Air, OS X Mountain Lion (10.8.3), 2GHz i7, 8GB RAM, 256GB SSD

Posted on Jun 19, 2013 6:43 PM

Reply
Question marked as Best reply

Posted on Jun 22, 2013 2:28 AM

Hello


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.



--lib.scpt
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)
            try
                return h's class = handler
            on error number -1700
                return false
            end try
        end exists_handler
        
        on exists_reference(s)
            try
                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)
            else
                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
            else
                x as «class isot» as string
            end if
        end f2
    end script
end new_lib
--end of lib.scpt



--template.applescript
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

init()
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

https://discussions.apple.com/thread/2477010?start=0&tstart=0


Re: re: my parent

https://discussions.apple.com/thread/2477010?answerId=11852193022#11852193022



Hope this may help,

H

3 replies
Question marked as Best reply

Jun 22, 2013 2:28 AM in response to Rob de Jonge

Hello


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.



--lib.scpt
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)
            try
                return h's class = handler
            on error number -1700
                return false
            end try
        end exists_handler
        
        on exists_reference(s)
            try
                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)
            else
                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
            else
                x as «class isot» as string
            end if
        end f2
    end script
end new_lib
--end of lib.scpt



--template.applescript
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

init()
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

https://discussions.apple.com/thread/2477010?start=0&tstart=0


Re: re: my parent

https://discussions.apple.com/thread/2477010?answerId=11852193022#11852193022



Hope this may help,

H

Jul 19, 2013 10:16 PM in response to Hiroto

Hi,


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!


cheers,

Rob

Jul 19, 2013 10:33 PM in response to Hiroto

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?


cheers,

Rob

Externalise functions into a library

Welcome to Apple Support Community
A forum where Apple customers help each other with their products. Get started with your Apple ID.