A scheme for keeping a good structure in the Script Libraries Folder

Hello.

It grows quickly in the new script libraries folder, and soon it is like it used to be with the old script objects we used to load. You’ll either end up with a zillion small script objects, or some big ones, containing far too much for the normal usage of that library.

Well, maybe it is time to think of something new. In java they have a pacackage system where the different levels are separated by dots.

I have been stealing a little from this scheme, and I ended up with top level triplets like app, and text, when it is concerning that specific class of AppleScript Datatype, but I also have added top level triplets for “sys” which are commands for debugging, and IO which relates to input output.

My former library named Output, can now be named io.console, or it can be put into sys.debug.textedit.

A handler for taking text and filenames from two files and showing them in Filemerge can be named app.filemerge.showtwo.

A handler for locking a file with finder can be named app.finder.lockfile

You get the idea. The name of the the classes here are not important, the idea is to have something consistent, that eases the use, as the libraries grow.

I want to mention more specifically former library named Output, can now be named io.console, or it can be put into sys.debug.textedit. Now say I don’t want to have logging to text edit anymore, but want to use Console instead. Then I can look at the former library as a protocol defining the methods for achieving functionality. Then I can make a new library, that implements the same calls, maybe some of them just stubs, that do nothing.

I can then change the reference to my first library (the use ouput : script “sys.debug.textedit”) to the new one (use ouput : script “sys.debug.console”), recompile, and then switched all the log output to the Console.app.

That’s progress! :slight_smile:

Hello.

The other half of this idea, to ease life with libraries, is to: always save a script library as a bundle, whether you need it or not, and keep the document in the description.rtfd file that are inside the bundle.

Make yourself a handler for your favorite Script Editor, that opens the description.rtfd file inside the bundle in TextEdit, and then you are good to read, and document.

(You’ll need a handler for getting at the current bundle’s description.rtfd file as well.)

Not that I want to imply that anyone should do the same as I do but I have one big script library where I have everything grouped in suites. The custom syntax with descriptions is my way of documenting everything as well as the library’s dictionary can be opened with script editor and stored in the library panel. It’s maybe a bit more work to maintain the large XML file but there are some good xml editors out there including Shane’s great AppleScriptObjC explorer for Mavericks with a built in sdef editor.

I’m not afraid that I will end up with a lot of libraries, I’m only afraid that code posted on MS will require some home made library in the future.

Thanks for your feedback DJ. :slight_smile:

That is really a good way, but that can’t change functionality the way I described in the post above by just changing a reference in your script. That is some functionality I don’t see as a common need anyway.

I think your scheme is the best, when everything is well defined, but once it is in that structure with a scripting terminology created for it, then it is much harder to change.

I don’t know if it matters, but the way I described above with many small libraries, makes it easier to share code at MacScripters, since you can post a single compilation unit, that doesn’t do more than what your intention with the post is.

The problem I see with this, is that if you use really many libraries, then you may maybe generate an “internal table overflow error” admittingly, I haven’t come that far with my experiences as of yet.

Your way is good DJ, but I think mine is good too, since it is so easy to extend your library by just putting a script there, and scribble a description that goes into the bundle. :slight_smile: I can also easily change the name of the script library, should I need to restructure the hierarchy, and I can leverage upon the “protocol idiom”, for changing, or “decorating” functionality, either sideways, or further down the “triplets hiearchy”.

Then maybe later one, when everything is pretty much wellformed, and set in stone, then it is time to create suites.

But I think, at least when you have application specific handlers, that those are best enjoyed in a library of their own, and not in a library with many handlers, targeting many applications. But I may be wrong.

You’re right about that one. The one-big-library I mentioned runs all in the context of current application.

Hello.

I just came 'round to say that, all in all, this very much a matter of Personal Taste and Working Style, and that in the end you may go from the naming standard to suites and a script dictionary. (But at least then you won’t have had to rework the writing of sdef files.)

The most important thing here, IMHO is to have some concious scheme, for after a while of dropping things into the Script Libraries folder with random names, you’r still back to having to look for that #$"!*!!$ handler. :slight_smile:

Mine approach, as an intermidary before you come around to writing the sdef file, lets you keep those handlers you want included, neatly organized in the script library.

If you have a login to Apples Developer site

Apple’s Introducing Applescript Libraries video has what they claim the first ever lesson on building a Scripting Definition (sdef) files by Apple. Really helpful and is now what I am using for my suites. Once you get your head around it, it is not as hard as I always thought.

One little trick I use is where I want to have some examples of usage. I write the example in Applescript Editor and then copy and paste it into TextEdit.app as Rich Text.
I then save it as a html file.

Open the file as raw text in and copy html within from to and paste into the sdef documentation. This way I get al the colour formatted text as html.

one caveat, is some times I may have to play with the styles if I have edited the example and re saved the html.
The styles for some reason sometime mismatch.
So am looking for something that will do the same but keep the styles

I can post an example later when I get back home…

Thank you Mark, I am sure this will come in handy. I really like your quick way of making documentation! :slight_smile:

This is something I really wondered how it was done, getting the syntax in pretty colours in the sdef file!

Sounds too much like work :wink:

Here’s a script that can make it a bit easier. Save it as an ASObjC-based library, and call it by passing the text source of the sample script, which you should be able to extract via script. The HTML you want should end up on the clipboard:

use framework "Foundation"
use framework "AppKit"

on putCodeHTMLOnClipboard:theText -- the text of the script
	-- compile script and get styled text
	set theScript to current application's NSAppleScript's alloc()'s initWithSource:theText
	theScript's compileAndReturnError:(missing value)
	set styledSource to theScript's richTextSource()
	-- convert to HTML
	set aDict to current application's NSDictionary's dictionaryWithObject:(current application's NSHTMLTextDocumentType) forKey:(current application's NSDocumentTypeDocumentAttribute)
	set theData to styledSource's dataFromRange:{location:0, |length|:(styledSource's |length|())} documentAttributes:aDict |error|:(missing value)
	-- make XML doc and extract style and body elements
	set xmlDoc to current application's NSXMLDocument's alloc()'s initWithData:theData options:(current application's NSXMLDocumentTidyHTML) |error|:(missing value)
	set theStyle to xmlDoc's nodesForXPath:".//style" |error|:(missing value)
	set theStyleText to (theStyle's objectAtIndex:0)'s XMLString()
	set theBody to xmlDoc's nodesForXPath:".//body" |error|:(missing value)
	set theBodyText to (theBody's objectAtIndex:0)'s XMLString()
	-- put it on the clipboard
	set thePB to current application's NSPasteboard's generalPasteboard()
	thePB's clearContents()
	thePB's writeObjects:{(theStyleText's stringByAppendingString:theBodyText)}
end putCodeHTMLOnClipboard:

Speaking of too much work, that script was embarrassing overkill, and assumed you wanted whole scripts processed. I’d delete it, but the code might be useful to someone anyway.

All you need is something to take the styled text on the clipboard and replace it with trimmed HTML, like this:

use framework "Foundation"
use framework "AppKit"

on turnStyledClipIntoHTML()
	-- get styled text off clipboard as NSAttributedString
	set thePB to current application's NSPasteboard's generalPasteboard()
	set thestyledTextArray to thePB's readObjectsForClasses:({current application's NSAttributedString}) options:(missing value)
	set theStyledText to thestyledTextArray's objectAtIndex:0
	-- convert to HTML
	set aDict to current application's NSMutableDictionary's dictionaryWithObject:(current application's NSHTMLTextDocumentType) forKey:(current application's NSDocumentTypeDocumentAttribute)
	set theData to theStyledText's dataFromRange:{location:0, |length|:(theStyledText's |length|())} documentAttributes:aDict |error|:(missing value)
	-- convert data to string
	set theHTML to current application's NSString's alloc()'s initWithData:theData encoding:(current application's NSUTF8StringEncoding)
	-- extract relevant part; you could use TIDs if you'd prefer...
	set theHTML to (theHTML's componentsSeparatedByString:"</body>")'s objectAtIndex:0
	set theHTML to (theHTML's componentsSeparatedByString:"<style type=\"text/css\">")'s objectAtIndex:1
	set theHTML to current application's NSString's stringWithFormat_("<style type=\"text/css\">%@</body>", theHTML)
	-- put the HTML on the clipboard
	thePB's clearContents()
	thePB's writeObjects:{theHTML}
end turnStyledClipIntoHTML

That’s better…

WOW.

perfect. Thank you Shane. That is truly sweet. :smiley:

Hello Shane. I agree totally with Mark!

Well, here is something that turned out to not be as straight forward as I wanted it to. A script to get hold of the description.rtfd for an app or a script bundle. it gets the description.rtfd from frontmost doc in Script Debugger, or AppleScript Editor, or from the selected app / script bundle in Finder.

It also illustrates my point, of establish some kind of taxomony, by using a naming convention, that you can easily add scripts with single or more handlers into, so that changing/adding functionality doesn’t need to be a lot of work, before something is stable. I hope you get what I mean.

use AppleScript version "2.3"
use scripting additions
property ed_names : {"AppleScript Editor", "Script Debugger"}
property ed_ides : {"ToyS", "asDB"}
on qualifyASE()
	local editorProbe, found
	set {editorProbe, found} to ¬
		{name of ¬
			application (path to frontmost application as text), ¬
			false}
	
	repeat with i from 1 to count ed_names
		if editorProbe begins with item i of ed_names then
			set {editorProbe, found} to {item i of ed_ides, true}
			exit repeat
		end if
	end repeat
	if found then
		return editorProbe as string
	else
		return missing value
	end if
end qualifyASE

use AppleScript version "2.3"
use scripting additions
on this_me(developing)
	local this_instance
	if developing is true then
		set this_instance to a reference to me
	else
		set this_instance to a reference to application (path to frontmost application as text)
	end if
	return this_instance
end this_me

Taxomony, may well be as they say: the lowest form of science, but as many lower forms of things, it is also a vantage point for development! :slight_smile:

property parent : AppleScript
use AppleScript version "2.3"
use scripting additions
use aeq : script "ase_editor_qual"
use art : script "ase_runtime_thisme"
global this_instance
set this_instance to art's this_me(false)

set edname to aeq's qualifyASE()
try
	if edname is not missing value then
		
		using terms from application "AppleScript Editor"
			tell (application id edname)
				set script_path to (path of document 1)
				
			end tell
		end using terms from
		
	else if (name of (application (path to frontmost application as text))) = "Finder" then
		
		tell application "Finder"
			set the_sel to its selection
			if the_sel is not {} and (count the_sel) = 1 then
				
				if (name extension of item 1 of the_sel) is not in {"app", "scptd"} then ¬
					error number 5002
				set script_path to POSIX path of (item 1 of the_sel as alias)
			else
				-- Can't figure out a bundle name from this...
				error number 5001
			end if
			
		end tell
	else
		-- We haven't got an AppleScript Editor as frontmost, or Finder, its a baad condition!
		error number 5000
	end if
	
	set description_path to script_path & "/Contents/Resources/description.rtfd"
	set the description_path to description_path as POSIX file as alias
	set description_path to POSIX path of description_path
	try
		do shell script "/usr/bin/open -e " & quoted form of description_path & " & &>/dev/null"
	on error e number n
		error e
	end try
on error e number n
	if n = 5000 then
		my showErrorAndDie("This script can only be run in the context of Finder or *an* AppleScript Editor.")
	else if n = 5001 then
		my showErrorAndDie("The selection in the Front Finder window is either empty or invalid.")
	else if n = 5002 then
		my showErrorAndDie("This script can only be used on app or scptd bundles.")
	else
		my showErrorAndDie("The front script document has not be saved as a Script bundle or as an Application bundle.")
	end if
end try

on showErrorAndDie(errText)
	global this_instance
	tell this_instance
		display dialog errText with title "Edit description.rtfd" with icon caution buttons {"Ok"} default button 1
		error number -128
	end tell
end showErrorAndDie