Sunday, November 19, 2017

#1 2017-11-08 12:51:13 pm

bmose
Member
From:: Massachusetts
Registered: 2006-01-03
Posts: 219

How to save an Applescript script object created within code to a file

I cannot figure out how to save an Applescript script object created within code to a file.  Two Scripting Additions commands that work in Applescript don't seem to work in ApplescriptObjC, namely store script and open for access...write... (in the latter case, by inserting the script object into a list and saving the list to a file.) In a previous post (http://macscripter.net/viewtopic.php?id=36714), it was suggested to use OSAKit.framework's writeToURL:ofType:usingStorageOptions:error: method. I have been able to get that approach to work provided that the script already exists in a file. For example, the following succeeds:

Applescript:


use framework "Foundation"
use framework "OSAKit"

property || : current application

set inputUrl to (||'s |NSURL|)'s fileURLWithPath:"/path/to/old/script.scpt"
set outputUrl to (||'s |NSURL|)'s fileURLWithPath:"/path/to/new/script.scpt"
set {theScript, theError} to (||'s OSAScript)'s alloc()'s initWithContentsOfURL:inputUrl |error|:(reference)
if theError = missing value then
   theScript's writeToURL:outputUrl ofType:(||'s OSAStorageScriptType) usingStorageOptions:0 |error|:(reference) --> succeeds
end if

How can I use that approach when the script object is created within code? For example, the following fails:

Applescript:


use framework "Foundation"
use framework "OSAKit"

property ||:current application

script theScript
   property foo:"bar"
end script

set outputUrl to (||'s |NSURL|)'s fileURLWithPath:"/path/to/new/script.scpt"
theScript's writeToURL:outputUrl ofType:(||'s OSAStorageScriptType) usingStorageOptions:0 |error|:(reference) --> fails

Last edited by bmose (2017-11-08 01:05:29 pm)

Offline

 

#2 2017-11-09 10:29:59 pm

bmose
Member
From:: Massachusetts
Registered: 2006-01-03
Posts: 219

Re: How to save an Applescript script object created within code to a file

I found a solution. While the store script command does seem to be broken, the open for access...write... approach works. My problem was that I was specifying the file reference as an hfs path. Changing the file reference to a posix path or posix file solved the problem.

To save the script object to a file, save it as a list item.

Applescript:

use framework "Foundation"
use scripting additions

property || : current application

-- The inline script object to be saved to a file
script theScript
   property foo : null
end script

-- Assign the script property a value
set theScript's foo to ||'s NSString's stringWithString:"bar"

-- Save the script object to a file as a list item
set fileRef to "/path/to/target/file.scpt" -- or "/path/to/target/file.scpt" as POSIX file
try
   close access fileRef
end try
set fid to open for access fileRef with write permission
set eof fid to 0
write {theScript} as list to fid
close access fid

To retrieve the saved script object, read the file as a list, and get the list item that is the script object. Be sure to use any needed frameworks.

Applescript:

use framework "Foundation"
use scripting additions

-- Retrieve the script object from the file
set fileRef to "/path/to/target/file.scpt" -- or "/path/to/target/file.scpt" as POSIX file
set theScript to (read fileRef as list from 1)'s item 1

-- Confirm that the script was properly retrieved
set theScript's foo to theScript's foo's uppercaseString()
return theScript's foo as text --> "BAR"

Also, see here for ways to get ASObjC code to run in instantiated script objects: http://www.macscripter.net/viewtopic.php?id=45913

Last edited by bmose (2017-11-09 10:31:04 pm)

Offline

 

#3 2017-11-10 07:17:52 am

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

Re: How to save an Applescript script object created within code to a file

bmose wrote:

While the store script command does seem to be broken, the open for access...write... approach works.


store script does appear to be broken when it's in the same script (object) as use framework "Foundation". It's possible to use it if the ASObjC stuff is moved to another script object in a handler below where store script is used:

Applescript:

-- The inline script object to be saved to a file
script theScript
   property foo : missing value
end script

set theScript's foo to getValue()
store script theScript in file ((path to desktop as text) & "Script.scpt")


on getValue()
   script ASObjC
       use framework "Foundation"
       
       on value()
           return "bar" --current application's NSString's stringWithString:"bar"
       end value
   end script
   
   return ASObjC's value()
end getValue

But then you hit a more fundamental problem in that you want to save a script containing an ASObjC pointer — and that officially can't be done. It would be a waste of time anyway, because it's unlikely the object pointed to would be would still be there when the pointer was eventually retrieved from the saved script.

This in turn casts doubt on your File Read/Write solution. (And giving a file containing a representation of a list the extension ".scpt" isn't recommended.) When I try it, an error occurs on the attempt to read the file as list.

Last edited by Nigel Garvey (2017-11-10 07:19:38 am)


NG

Offline

 

#4 2017-11-10 06:08:58 pm

bmose
Member
From:: Massachusetts
Registered: 2006-01-03
Posts: 219

Re: How to save an Applescript script object created within code to a file

Nigel Garvey wrote:

And giving a file containing a representation of a list the extension ".scpt" isn't recommended.

My bad.  I didn't mean to include a ".scpt" extension.

Nigel Garvey wrote:

But then you hit a more fundamental problem in that you want to save a script containing an ASObjC pointer — and that officially can't be done.

I understand your point and modified my effort to the more modest goal of saving a plain vanilla Applescript script object to a file from a script containing ASObjC code.  This is where things got weird. If I saved the plain vanilla script object to a file as a list item, then retrieved the script object by reading the list back from the file within the same script, I was able to retrieve the script object successfully:

Applescript:

use framework "Foundation"
use scripting additions

property || : current application

-- The inline plain vanilla Applescript script object to be saved to a file
script theScript
   property foo : null
end script

-- Assign the script property a value
set theScript's foo to (||'s NSString's stringWithString:"bar")'s uppercaseString() as text

-- Save the script object to a file as a list item
set fileRef to "/path/to/target/file"
try
   close access fileRef
end try
set fid to open for access fileRef with write permission
set eof fid to 0
write {theScript} as list to fid
close access fid

-- Retrieve the script object from the file within the same script
set fileRef to "/path/to/target/file"
set theScript to (read fileRef as list from 1)'s item 1

-- Confirm that the script was properly retrieved
return theScript's foo --> "BAR" --> Success!!

However, when I tried to retrieve the script object by reading the list back from the file from a different script, the retrieval failed with error -1761, which is Open Scripting Architecture error "There is a component mismatch—parameters are from two different components."

Applescript:

use framework "Foundation"
use scripting additions

property || : current application

-- The inline plain vanilla Applescript script object to be saved to a file
script theScript
   property foo : null
end script

-- Assign the script property a value
set theScript's foo to (||'s NSString's stringWithString:"bar")'s uppercaseString() as text

-- Save the script object to a file as a list item
set fileRef to "/path/to/target/file"
try
   close access fileRef
end try
set fid to open for access fileRef with write permission
set eof fid to 0
write {theScript} as list to fid
close access fid

Attempt to retrieve the script object from a different script:

Applescript:

use framework "Foundation"
use scripting additions

property || : current application

-- Retrieve the script object from the file from a different script
set fileRef to "/path/to/target/file"
set theScript to (read fileRef as list from 1)'s item 1 --> Failed with error -1761

-- Confirm that the script was properly retrieved
return theScript's foo

Any thoughts as to why the retrieval succeeds when performed within the original script but fails in a different script?

Last edited by bmose (2017-11-10 06:18:38 pm)

Offline

 

#5 2017-11-10 07:12:18 pm

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

Re: How to save an Applescript script object created within code to a file

Part of the normal process of saving a script involves flattening, or serializing, the contents, which basically takes a nested structure and its context and converts it into a string of bytes. The process is reversed when reading. (See AEFlattenDesc()).

I'm guessing this flattening isn't happening with your example. It doesn't matter when you read it back into the same script, but it obviously does if you read it elsewhere. And the ASObjC connection is the likely cause.

I think this is what's known technically as a dead horse. It's probably time to stop flogging it wink

But I'm curious about why you were trying it in the first place.


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

Online

 

#6 2017-11-11 12:23:17 am

bmose
Member
From:: Massachusetts
Registered: 2006-01-03
Posts: 219

Re: How to save an Applescript script object created within code to a file

Shane Stanley wrote:

And the ASObjC connection is the likely cause.

It is my understanding that store script has been broken for years in ASObjC. Apple hasn't deprecated store script in plain vanilla Applescript, so they must consider it to be of some value. So I'm wondering why they haven't fixed this. I assume bug reports have been filed, so does that mean that (A) they don't consider it a high priority, (B) it is unfixable because of the way ASObjC is implemented, or (C) some other reason?

Shane Stanley wrote:

But I'm curious about why you were trying it in the first place.

I'm working on a project which, if it were available, would be nicely solved with a script object capable of retaining state between invocations and supplied with handlers incorporating the power of ASObjC code. Unfortunately, because of the aforementioned problem with script objects in ASObjC, I have instead taken the approach of using two scripts. One is a plain vanilla Applescript script object that is instantiated for the current task at hand and that retains state between invocations. Its plain vanilla handlers serve only as front ends for the ASObjcC handlers in the second script. The second script is a script file acting essentially as a singleton and consisting only of the ASObjC handlers that the plain vanilla script object calls. I realize that there are other ways to retain state, but a script object that could both retain state and be coded with ASObjC handlers would be a very nice tool in the toolbox.

Last edited by bmose (2017-11-11 12:24:46 am)

Offline

 

#7 2017-11-11 01:16:06 am

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

Re: How to save an Applescript script object created within code to a file

bmose wrote:

It is my understanding that store script has been broken for years in ASObjC.



I don't think it's ever worked there.

I assume bug reports have been filed



I'm not sure you can assume this -- scripters are notoriously poor at logging bugs.

so does that mean that (A) they don't consider it a high priority, (B) it is unfixable because of the way ASObjC is implemented, or (C) some other reason?



I wouldn't like to guess -- assuming bugs have been filed -- but I certainly wouldn't be surprised if it's A or B.


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

Online

 

#8 2017-11-11 06:35:49 am

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

Re: How to save an Applescript script object created within code to a file

bmose wrote:

Unfortunately, because of the aforementioned problem with script objects in ASObjC …


The problems, as far as I can see, are just that store script doesn't work within the "scope" of a use framework … command and that you can't save scripts containing stored ASObjC values. store script is perfectly capable of storing scripts containing ASObjC code.

I'm not sure exactly what you want, but these three demos save viable script applications to your desktop which use an ASObjC method and store the AS equivalent result in a property:

Applescript:

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

-- Script object instantiated at run time (no label after 'script').
-- Inherits ASObjC code from the compiled script object below it.
script
   property parent : script2
   property lastResult : missing value
   
   if (lastResult is missing value) then
       display dialog "First run"
   else
       display dialog "The previous result was " & lastResult
   end if
   
   set lastResult to |uppercase|("Hello!")
   display dialog "This result is " & lastResult
end script
set script1 to result

store script script1 in (((path to desktop as text) & "Script 1.app") as «class furl») replacing yes

-- Script object compiled into main script, below the 'store script' instruction to preserve the readability of the OSAX keywords.
-- The scope of 'use framework "Foundation"' is confined to this script object.
script script2
   use framework "Foundation"
   
   on |uppercase|(aText)
       return (current application's NSString's stringWithString:aText)'s uppercaseString() as text
   end |uppercase|
end script

Applescript:

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

store script myScript in (((path to desktop as text) & "My Script.app") as «class furl») replacing yes

-- Script object compiled into main script, below the 'store script' instruction to preserve the readability of the OSAX keywords.
-- The scope of 'use framework "Foundation"' is confined to this script object.
script myScript
   use framework "Foundation"
   property lastResult : missing value
   
   on |uppercase|(aText)
       return (current application's NSString's stringWithString:aText)'s uppercaseString() as text
   end |uppercase|
   
   if (lastResult is missing value) then
       display dialog "First run"
   else
       display dialog "The previous result was " & lastResult
   end if
   
   set lastResult to |uppercase|("Hello!")
   display dialog "This result is " & lastResult
end script

Applescript:

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

store script makeScript() in (((path to desktop as text) & "My Script.app") as «class furl») replacing yes

-- Script object instantiated at run time, below the 'store script' instruction to preserve the readability of the OSAX keywords.
-- The scope of 'use framework "Foundation"' is confined to this script object.
on makeScript()
   script
       use framework "Foundation"
       property lastResult : missing value
       
       on |uppercase|(aText)
           return (current application's NSString's stringWithString:aText)'s uppercaseString() as text
       end |uppercase|
       
       if (lastResult is missing value) then
           display dialog "First run"
       else
           display dialog "The previous result was " & lastResult
       end if
       
       set lastResult to |uppercase|("Hello!")
       display dialog "This result is " & lastResult
   end script
   
   return result
end makeScript


NG

Offline

 

#9 2017-11-12 07:09:49 am

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

Re: How to save an Applescript script object created within code to a file

Shane Stanley wrote:

… scripters are notoriously poor at logging bugs.


Scripters generally find it more immediately useful to work round bugs than to complain about them and then sit around waiting for them to be fixed (and for clients to adopt the fixed systems).  wink

The only bug I've ever reported was the annoying Script Editor toolbar button problem. That was on 21st October 2014, back in the days of Yosemite and Script Editor 2.7. I had to go through the palava of signing up as a developer, reading through the spiel on how to file a bug report, and then describing the bug in several different ways in the report form. The response from Apple was b****r all for nearly a year, despite my updating the report twice during that time to note the bug's persistence through a Yosemite update and into El Capitan. Eventually Shane filed a report on it too and almost immediately (7th October 2015) I received an e-mail from Apple Developer Relations saying that Engineering had determined my bug report was a duplicate of another issue and would be closed (presumably meaning the report would be closed, not Engineering). It's now 12th November 2017, Script Editor's up to version 2.10 in High Sierra, and the bug still hasn't been fixed. If other scripters receive similar responses, it's not surprising they don't bother.


NG

Offline

 

#10 2017-11-12 05:49:39 pm

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

Re: How to save an Applescript script object created within code to a file

Nigel Garvey wrote:

The only bug I've ever reported



My choice of the word "poor" was itself poor -- I meant in the sense of something they don't generally do.

Not all bugs get fixed, obviously. But they do get logged, and importantly, the numbers are seen by managers who make the ultimate decisions. I remember Jon Pugh (of Jon's Additions fame) remarking that the single most valuable thing a scripter could do in terms of promoting it at Apple was to post bug reports.

Or as another engineer pointed out: some logged bugs don't get fixed, but no unlogged ones do.


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

Online

 

#11 Yesterday 08:59:36 am

bmose
Member
From:: Massachusetts
Registered: 2006-01-03
Posts: 219

Re: How to save an Applescript script object created within code to a file

Sorry for the delay in responding.

Nigel Garvey wrote:

these three demos save viable script applications to your desktop which use an ASObjC method and store the AS equivalent result in a property

Fantastic, Nigel, they work! Thanks for the solutions. The key is to confine the use framework "Foundation" statement to a script object embedded within the script object to be saved.

Shane Stanley wrote:

the single most valuable thing a scripter could do in terms of promoting it at Apple was to post bug reports

I submitted the store script/use framework "Foundation" bug today. I also had previously submitted an Applescript memory management bug in ASObjC that cropped up in the early days of ARC in OS X 10.8 Mountain Lion (http://www.macscripter.net/viewtopic.php?pid=166877). When OS X 10.9 Mavericks came out less than a year later, the problem was resolved. I don't know if the bug report had anything to do with it, but it was great to see that the problem had been fixed.

Last edited by bmose (Yesterday 10:05:56 am)

Offline

 

Board footer

Powered by FluxBB

RSS (new topics) RSS (active topics)