Hello.
Sometimes, you may think yourself obstructed when trying to write reusable code, because, the code that really makes it all work is in the very “meat” of what could have been reusable.
Then the visitor pattern may come in handy. Below is an illustration of it.
(*
closestRootFolder:
You can have several "roots" below the last one, say in a build-hierachy of c-source, or markdown/html.
closestRootFolder traverses the directory structure upwards to the stopPath, and checks each folder on its way by the visitor you supplied.
parameter: probePath
Finds the closest root folder of a file. The closest root being some folder one or several folders up that satisifes some criteria.
parameter: stopPath
We supply a stopFolder, which is the heighest level, or last tier if you like, for this particular root.
Parameter: Visitor
A script you supply with the handler "validator", that tells us if the current path satisifes the criteria for a root folder.
Returns false, if no root where found, or the path to the closest root for the probepath supplied.
An example visitor script for which I wrote this library for.
The root folder I am looking for contains a folder named "m4", and a makefile.
script visitor
on validator(probePxPath)
tell application id "sevs"
if not (exists disk item (probePxPath & "/makefile")) then return false
if not (exists disk item (probePxPath & "/m4")) then return false
end tell
return true
end validator
end script
*)
on closestRootFolder(probePath, stopPath, visitorScript)
-- We start checking that our arguments actual has some value.
if not hasContents(probePath) then error "isInADomain: probePath can't be empty"
if not hasContents(stopPath) then error "isInADomain: posixPath can't be empty"
if not class of visitorScript is script then error "isInADomain: Visitor script needs to be a script "
set probePxPath to UFS(probePath)
set stopPxPath to UFS(stopPath)
if stopPxPath is not in probePxPath then return false
set containingFolder to pxpathExclFilename(probePxPath)
repeat
set testResult to visitorScript's validator(containingFolder)
if testResult is true then
return containingFolder
else if containingFolder is stopPath then
return false
else
-- we're iterating upwards
set containingFolder to parentPxPathFolder(containingFolder)
end if
end repeat
return true
end closestRootFolder
on hasContents(somePath)
if class of somePath is list and somePath is {} then return false
if class of somePath is text and somePath is "" then return false
if somePath is null or somePath is missing value then return false
return true
end hasContents
on UFS(whatever)
local uxPth
try
if class of whatever is list then set whatever to whatever as text
if class of whatever is not alias and class of whatever is not text then
set uxPth to POSIX path of (whatever as alias)
else if (class of whatever is alias) then
set uxPth to POSIX path of whatever
else
if text 1 of whatever is "'" then ¬
set whatever to text 2 thru -1 of whatever
if text -1 of whatever is "'" then ¬
set whatever to text 1 thru -2 of whatever
if (offset of ":" in whatever) is not 0 then
try
set uxPth to POSIX path of (whatever as alias)
on error e number n
-- maybe it was ok as it was ..
try
(POSIX file whatever as alias)
set uxPth to whatever
on error e number n
error e number n
end try
end try
else
(POSIX file whatever as alias)
set uxPth to whatever
end if
end if
return uxPth
on error e number n
return missing value
end try
end UFS
to parentPxPathFolder(aPxPath)
local tids, parFol
set {tids, my text item delimiters} to {my text item delimiters, "/"}
if (character (length of aPxPath) of aPxPath) = "/" then
set parFol to text items 1 thru -3 of aPxPath as text
else
set parFol to text items 1 thru -2 of aPxPath as text
end if
set my text item delimiters to tids
if parFol = "" then set parFol to "/"
return parFol
end parentPxPathFolder
on pxpathExclFilename(pxPath)
if not (class of pxPath is text and pxPath contains "/") then error "pxpathExclFilename: pxPath isn't a valid posixPath"
if pxPath = "/" then error "pxpathExclFilename: rootfolder (\"/\") given as parameter"
local tids, parFol
set {tids, my text item delimiters} to {my text item delimiters, "/"}
if (character (length of pxPath) of pxPath) = "/" then
set parFol to text 1 thru -2 of pxPath
else
set parFol to text items 1 thru -2 of pxPath as text
end if
set my text item delimiters to tids
if parFol = "" then set parFol to "/"
return parFol
end pxpathExclFilename