Tuesday, June 18, 2019

#1 2019-04-12 09:32:20 pm

t.spoon
Member
From:: BFE, Massachusetts
Registered: 2013-01-13
Posts: 395

Find parent folder handler code share, plus a couple of curious ?s

I wanted a handler to return a folder a specified distance up the path hierarchy from a submitted path. But Applescript supports an annoying number of possible ways to specify a path, and I wanted it to handle all of them. It's working fine, I'm posting it to share, but at the end I've got a question I came across while working on it, in case anyone would care to chime in. It's not important, it seems to be working fine in testing, I'm just idly curious.

Also, if anyone has improvements or suggestions to add features, let me know.

The script:

Applescript:


use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions


-- TESTING VARIOUS TYPES

set aliasPath to path to downloads folder
tell application "Finder" to set folderObject to folder aliasPath
set fileObject to item 1 of folderObject
set HFStextPath to aliasPath as text
set POSIXtextPath to POSIX path of aliasPath
set POSIXfile to POSIX file POSIXtextPath

set stepsUp to 2

set aliasTest to step_up_path(aliasPath, stepsUp)
set folderObjectTest to step_up_path(folderObject, stepsUp)
set fileObjectTest to step_up_path(fileObject, stepsUp)
set HFStextTest to step_up_path(HFStextPath, stepsUp)
set POSIXtextTest to step_up_path(POSIXtextPath, stepsUp)
set POSIXfileTest to step_up_path(POSIXfile, stepsUp)


-- Accepts a file or folder reference submitted in any format - alias, Applescript file or folder object, POSIX file, or a POSIX or HFS path as text. Returns reference to higher folder in the same format in which the path was submitted. Second argument is the number of steps to traverse up the heirarchy.
on step_up_path(aPath, numberOfSteps)
   set pathClass to the class of aPath
   if pathClass is «class furl» then -- class of POSIX path
       set aPath to aPath as alias
   else if pathClass is text then
       try
           set aPath to alias aPath
           set textPathType to "HFS"
       on error
           try
               set aPath to (POSIX file aPath) as alias
               set textPathType to "POSIX"
           on error
               display dialog "Error: Text was input to the function \"step_up_path\", but the text could not be converted to a valid HFS or POSIX path."
               return
           end try
       end try
   end if
   set originalPath to aPath
   
   tell application "Finder"
       repeat numberOfSteps times
           try
               set aPath to the parent of aPath
           on error
               display dialog "Error: In the function \"step_up_path,\" the number of requested steps (" & numberOfSteps & ") was greater than the number of path elements in:" & return & originalPath as text
               return
           end try
       end repeat
       
       if pathClass is alias then
           set returnPath to aPath as alias
       else if pathClass is «class furl» then
           set returnPath to POSIX path of (aPath as alias)
       else if (pathClass is folder) or (pathClass is document file) then
           set returnPath to aPath
       else if pathClass is text then
           if textPathType is "HFS" then
               set returnPath to (aPath as alias) as text
           else if textPathType is "POSIX" then
               set returnPath to POSIX path of (aPath as alias)
           end if
       end if
   end tell
   return returnPath
   
   
end step_up_path

OK, two questions that came up writing this.

ONE

Applescript:

set aliasPath to path to downloads folder
set aliasClass to the class of aliasPath
set textPath to aliasPath as text

-- works
set convertClassExplicitly to textPath as alias

-- errors
set convertClassWithVariable to textPath as aliasClass

If you can store a class in a variable, why can't you convert things to that class specifying it as a variable? Is there a way to do this? I guess it can be meta-scripted:

Applescript:

set aliasPath to path to downloads folder
set aliasClass to the class of aliasPath
set textPath to aliasPath as text

-- works
set convertClassExplicitly to textPath as alias

-- works
set convertClassWithVariable to run script "set convertClassWithVariable to \"" & textPath & "\" as " & aliasClass as text

But that always feels confusing and hack-y to me.

As you can see in my script, I just worked around it with a long "if...then" statement for all the classes.


Hackintosh built February, 2012 |  Mac OS Sierra
GIGABYTE GA-Z68X-UD3H-B3 | Core i5 2500k | 16 GB DDR3 | GIGABYTE Geforce 1050 TI 4GB
250 GB Samsung 850 EVO | 4 TB RAID
Dell Ultrasharp U3011 | Dell Ultrasharp 2007FPb

Offline

 

#2 2019-04-13 12:06:01 am

Shane Stanley
Member
From:: Australia
Registered: 2002-12-07
Posts: 5699

Re: Find parent folder handler code share, plus a couple of curious ?s

t.spoon wrote:

If you can store a class in a variable, why can't you convert things to that class specifying it as a variable?



The error is a compilation error, not a runtime error, so if you use a variable, the compiler has no way of knowing whether it contains a class. It's just a rule of the compiler. Such rules are generally about flagging potential errors when scripts are written, rather than when they are run, which is arguably safer, if at times seemingly less convenient.


Shane Stanley <sstanley@myriad-com.com.au>
www.macosxautomation.com/applescript/apps/
latenightsw.com

Offline

 

#3 2019-04-13 04:56:11 am

Nigel Garvey
Moderator
From:: Warwickshire, England
Registered: 2002-11-20
Posts: 4880

Re: Find parent folder handler code share, plus a couple of curious ?s

Hi t.spoon.

I'd be inclined to use TIDs on the path rather than getting the Finder to work its way up through the containers. But subjectively, it doesn't seem to make any difference to the speed. You'll notice my comment at the top of the script that it works as far back as Leopard too except for the two 'use' lines. These lines don't compile in Leopard because 'use' was only introduced with Mavericks (10.9). But if the script's compiled in Mojave and opened in Leopard, the only thing you'll see of the two lines is the comment saying they don't work. The 'use' instructions aren't visible in Script Editor and don't cause the script to error when it runs.

Applescript:


use AppleScript version "2.4" -- Yosemite (10.10) or later, but it works in Leopard (10.5) too (except for these two lines!)
use scripting additions


-- TESTING VARIOUS TYPES

set anAlias to (path to downloads folder)
set aFile to anAlias as «class furl»
set HFSPath to anAlias as text
tell application "Finder"
   set FinderFolder to folder HFSPath
   set FinderFile to file 1 of FinderFolder
end tell
tell application "System Events"
   set SystemEventsFolder to folder HFSPath
   set SystemEventsFile to file 1 of SystemEventsFolder -- System Events's 'file 1' may be different from the Finder's!
end tell
set POSIXPath to POSIX path of anAlias
set POSIXfile to POSIX file POSIXPath -- Should be the same as aFile

set stepsUp to 2

set aliasTest to step_up_path(anAlias, stepsUp)
set fileTest to step_up_path(aFile, stepsUp)
set FinderFolderTestTest to step_up_path(FinderFolder, stepsUp)
set FinderFileTest to step_up_path(FinderFile, stepsUp)
set SystemEventsFolderTest to step_up_path(SystemEventsFolder, stepsUp)
set SystemEventsFileTest to step_up_path(SystemEventsFile, stepsUp)
set HFSPathTest to step_up_path(HFSPath, stepsUp)
set POSIXPathTest to step_up_path(POSIXPath, stepsUp)
set POSIXfileTest to step_up_path(POSIXfile, stepsUp)


-- Accepts a file or folder reference submitted as an AppleScript alias or file, an HFS or POSIX path, or a Finder or System Events object. Returns reference to higher folder in the same format in which the path was submitted. Second argument is the number of steps to traverse up the heirarchy.
on step_up_path(pathOrSpecifier, numberOfSteps)
   set inputClass to the class of pathOrSpecifier
   try
       set pathString to pathOrSpecifier as text
       set isSystemEventsObject to false
   on error
       tell application "System Events" to set pathString to path of pathOrSpecifier
       set isSystemEventsObject to true
   end try
   if ((pathString ends with ":") or (pathString ends with "/")) then set pathString to text 1 thru -2 of pathString
   
   set astid to AppleScript's text item delimiters
   if (pathString begins with "/") then
       set AppleScript's text item delimiters to "/"
   else
       set AppleScript's text item delimiters to ":"
   end if
   try
       set newPath to (text 1 thru text item -(1 + numberOfSteps) of pathString) & AppleScript's text item delimiters
       if (newPath is "//") then set newPath to "/"
   on error
       set AppleScript's text item delimiters to astid
       display dialog "Error: In the function \"step_up_path,\" the number of requested steps (" & numberOfSteps & ") was greater than the number of container elements in:" & return & pathString
       return
   end try
   set AppleScript's text item delimiters to astid
   
   if (inputClass is text) then
       set output to newPath
   else if (inputClass is alias) then
       set output to newPath as alias
   else if (inputClass is «class furl») then
       set output to newPath as «class furl»
   else if (isSystemEventsObject) then
       tell application "System Events" to set output to disk item newPath
   else
       tell application "Finder" to set output to item newPath
   end if
   
   return output
   
end step_up_path


NG

Offline

 

#4 2019-04-13 09:27:58 am

t.spoon
Member
From:: BFE, Massachusetts
Registered: 2013-01-13
Posts: 395

Re: Find parent folder handler code share, plus a couple of curious ?s

Thanks for the explanation, Shane.

Nigel - thanks for your version, especially pointing out that System Events file/folder objects need to be handled separately from Finder file/folder objects.

- Tom.


Hackintosh built February, 2012 |  Mac OS Sierra
GIGABYTE GA-Z68X-UD3H-B3 | Core i5 2500k | 16 GB DDR3 | GIGABYTE Geforce 1050 TI 4GB
250 GB Samsung 850 EVO | 4 TB RAID
Dell Ultrasharp U3011 | Dell Ultrasharp 2007FPb

Offline

 

#5 2019-04-16 07:48:35 pm

CK
Member
From:: UK
Registered: 2018-11-04
Posts: 80

Re: Find parent folder handler code share, plus a couple of curious ?s

I skimmed the two main offerings on the thread, and I think the salient points have been covered.  Instead, I just wrote the script below, but haven't tested it fully yet.  I am hoping that it is capable of handling filepaths of any nature (posix including tilde abbreviated filepaths, HFS, alias objects, and Finder filepaths).  It returns the result as a posix path.

Applescript:

property sys : application "System Events"


to ascend thru filepath as text by levels as integer
       local filepath, levels
       
       if levels < 1 then return the filepath's POSIX path
       sys's alias named filepath as alias as text
       [result, "::"] as text as alias
       
       ascend thru the result by (levels - 1)
end ascend

ascend thru "~/Downloads" by 2 --> "/Users/"

I haven't evaluated efficiency in practice, and I'm mindful there are a lot of coercions happening in these few lines of code, which may impact on performance.  Using a recursive handler puts a theoretical stack limit on the number of levels it could ascend before crapping out; but I don't imagine this being an issue in practice.

I'll do some further testing/debugging over the next week.  This is a good thread, so thank you for starting it.  I'd not considered this operational task before, but I can see it would a useful one to add to my filesystem library, once I've confirmed it is reliable.  So do feel free to comment, improve it, or break it.

Last edited by CK (2019-04-16 07:49:42 pm)

Offline

 

#6 2019-04-17 04:07:26 am

Nigel Garvey
Moderator
From:: Warwickshire, England
Registered: 2002-11-20
Posts: 4880

Re: Find parent folder handler code share, plus a couple of curious ?s

CK wrote:

I am hoping that it is capable of handling filepaths of any nature (posix including tilde abbreviated filepaths, HFS, alias objects, and Finder filepaths).


It seems to work with these. But it can't handle paths and AS file specifiers to items which don't exist and, although it uses System Events, it can't handle System Events references!

It returns the result as a posix path.


The idea was to return the result in the same form as the input. But ignoring that, the same capabilities as your handler can be obtained without resorting to recursion or undesirable code like:

[result, "::"] as text as alias


[ and ] aren't official AppleScript symbols, some schools of thought reckon the result variable shouldn't be used in code in case other lines are subsequently added between it and the line that's supposed to set it, and coercing a list to text without explicitly setting the TIDs first is generally considered sloppy.  wink

But the following still uses the almost forgotten black art of using multiple colons at the ends of HFS paths to get aliases to container folders:

Applescript:


to ascend thru filepath by levels
   tell application "System Events" to set filepath to (alias (filepath as text))'s path
   
   repeat levels times
       set filepath to ((filepath as text) & "::") as alias
   end repeat
   
   return filepath's POSIX path
end ascend

ascend thru (path to downloads folder) by 2 --> "/Users/"


NG

Offline

 

#7 2019-04-18 01:03:15 am

CK
Member
From:: UK
Registered: 2018-11-04
Posts: 80

Re: Find parent folder handler code share, plus a couple of curious ?s

Nigel Garvey wrote:

It seems to work with these. But it can't handle paths and AS file specifiers to items which don't exist and, although it uses System Events, it can't handle System Events references!

That's unsurprising about non-existent paths; but surprising to hear about SE references, so thank you for finding that out.

Nigel Garvey wrote:

The idea was to return the result in the same form as the input.

Noted.  I think that is a reasonable expectation, and I'll fix that.

Nigel Garvey wrote:

But ignoring that, the same capabilities as your handler can be obtained without resorting to recursion or undesirable code like:

[result, "::"] as text as alias


[ and ] aren't official AppleScript symbols, some schools of thought reckon the result variable shouldn't be used in code in case other lines are subsequently added between it and the line that's supposed to set it, and coercing a list to text without explicitly setting the TIDs first is generally considered sloppy.  wink

That's fine, I'm aware of these points, and I believe you may have mentioned it in the past to me on the Keyboard Maestro forum.  I don't disagree with any particular point, but I feel familiar enough with these facets to use them reliably and  effectively.  But it is good that you point these things out, and I should at least explain a bit about my choices (which I am perfectly open to being debunked):

▸ I use that "undesirable" syntax because I find it makes my scripts a lot easier for me to read and debug.  Expressions like: set filepath to ((filepath as text) & "::") as alias; are, of course, more widely accepted as good, standard AppleScript form, but I've always found it extremely difficult to read.  It might be a dyslexia issue, it might be a neurosis specific to me, or it might be something I simply perceive to be less "beautiful" to look at than my form (which you find less beautiful; eye, beholder, etc.)

▸ I think "official" is becoming less and less meaningful as time goes on to think in those terms with AppleScript, as many officially deprecated features still have viable features and functionality that have persisted for years since being deprecated (wasn't info for deprecated about ten years ago, but still returns really performant results?).  I imagine you already know the history of the square brackets (à la linked lists), so we don't necessarily need to go through it unless anyone is interested to know.  But, they do remain viable notation for list objects and, for me, much easier to read, especially when one intermixes their use with curly braces.  One does need to be aware of when the different notations will have a functional impact on script operation, which I don't doubt you're familiar with, and can be useful to have artefacts of a class type persist that let AppleScript do things it shouldn't really be able to, but will probably continue to do until its death.

▸ Specifically, I employ the square brackets because they remain unaffected by TIDs.  Thus, I don't have to care in this instance what the TIDs are set to, because it won't affect the coercion.  Otherwise, yes, you are obviously quite right about the pitfalls of potentially forgetting to set TIDs.

▸ You're not the first to highlight the school of thought regarding the use of the result property, and it is important to point out.  It's not a school of thought I particularly feel I need to worry about for myself, as I suppose I'm already conditioned with some good habits around examining things above and below any line of code I edit (for example), that it's something I do without pause.

Thank you for highlighting these points, though, as it does for me to stop and think as I'm writing my rationale about whether I need to re-evaluate a particular habit; and other readers will be glad to know that my "special" style of scripting is not necessarily one to emulate without knowing about how it can change a script beyond just its appearance.  (These, and a few other quirks specific to my AppleScripting style t does mean I can spot instantly when a script on the internet has been spawned from one of mine, which is nice to come by.)

Nigel Garvey wrote:

But the following still uses the almost forgotten black art of using multiple colons at the ends of HFS paths to get aliases to container folders:

Thankfully, I didn't forget about this art, as it's precisely the technique my script utilises.  Part of me wondered why it didn't appear in the OP's script or your edited version, but as you've pointed out, my handler depends on a fielpath existing.

Nigel Garvey wrote:

Applescript:


to ascend thru filepath by levels
   tell application "System Events" to set filepath to (alias (filepath as text))'s path
   
   repeat levels times
       set filepath to ((filepath as text) & "::") as alias
   end repeat
   
   return filepath's POSIX path
end ascend

ascend thru (path to downloads folder) by 2 --> "/Users/"

I noted your remark above about my resorting to recursion, when, as you demonstrate, the same can be achieved through iteration.  I didn't see it as a resort, but your reputation precedes you for having a keen sense about all things recursive, iterative, and generally algorithmic, as well as code optimisation.  So I would value your take on assessing  one method vs the other in this case in terms of function, operation, efficiciency, and potential pitfalls.

Offline

 

#8 2019-04-18 03:21:30 am

Nigel Garvey
Moderator
From:: Warwickshire, England
Registered: 2002-11-20
Posts: 4880

Re: Find parent folder handler code share, plus a couple of curious ?s

CK wrote:

It might be a dyslexia issue, it might be a neurosis specific to me, or it might be something I simply perceive to be less "beautiful" to look at than my form (which you find less beautiful; eye, beholder, etc.)


I can sympathise with that.  smile  When I was learning ASObjC a couple of years ago, I had to develop my own style just to stay interested in the subject. Otherwise the succession of this's thats and that's theOthers just came across like a game of Mornington Crescent. However, I was careful to keep my style within what I could determine to be good practice and its purpose was — as you yourself say — for clarity in scripts rather than for mere eccentricity or smartarsedness.

I feel familiar enough with these facets to use them reliably and  effectively.


I'm always aware — especially since I got landed with MacScripter's "Moderator" tag — that scripting fora are frequented both by people who know what they're doing and people who don't. It's up to the former group to be as helpful as possible to the latter, providing clear code solutions where needed, explaining how and why they work (not just "Try this:"!), and avoiding any known dodgy practices which may be taken at face value and be perpetuated later here, elsewhere on the Web, or even in people's workplaces. It can be a right pain continually having to correct the same old errors which have been copied from the Web in good faith and a difficult line to steer, when you don't know who the posters are, between criticising their code and putting them off scripting altogether.

While none of us gets it right all the time, it's best to pass on the best code we can in a public arena. What we do on our own machines, though, is another matter.  wink


NG

Offline

 

#9 2019-04-18 03:51:41 am

CK
Member
From:: UK
Registered: 2018-11-04
Posts: 80

Re: Find parent folder handler code share, plus a couple of curious ?s

Nigel Garvey wrote:

While none of us gets it right all the time, it's best to pass on the best code we can in a public arena. What we do on our own machines, though, is another matter.  wink

I agree with the entirety of what you wrote in your last reply.

That gives me something to ponder on.  Is there a an official "good coding practices" for AppleScript published  online that I can reference ?

Offline

 

Board footer

Powered by FluxBB

RSS (new topics) RSS (active topics)