You are not logged in.
Pages:: 1
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 12:05:29 pm)
Offline
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 09:31:04 pm)
Offline
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 06:19:38 am)
NG
Offline
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.
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 05:18:38 pm)
Offline
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
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/
latenightsw.com
Offline
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?
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-10 11:24:46 pm)
Offline
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/
latenightsw.com
Offline
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
… 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).
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
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/
latenightsw.com
Offline
Sorry for the delay in responding.
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.
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 (2017-11-17 09:05:56 am)
Offline
As Nigel Garvey pointed out earlier in this discussion, a script object containing ASObjC code may be saved to disk with the store script scripting additions command provided the use framework "Foundation" statement's scope is confined to the script object and not to the top-level script as a whole. This is illustrated in the following script, which is adapted from one of Nigel's examples. As Nigel points out, the script object must be coded below the store script command in order to preserve the readability of OSAX keywords in the script object (in the current example, the display dialog command):
Applescript:
use scripting additions
set fileRef to ((path to desktop as text) & "MyScript.scpt") as «class furl»
store script myScript in fileRef with replacing
(load script fileRef)'s displayUppercase("foobar") --> displays "FOOBAR"
script myScript
use framework "Foundation"
on makeUppercase(aText)
return (current application's NSString's stringWithString:aText)'s uppercaseString() as text
end makeUppercase
on displayUppercase(aText)
display dialog (my makeUppercase(aText))
end displayUppercase
end script
The following is an alternative solution that allows one to code the script object above the store script command, if that is desired for any reason. The two required modifications are (1) to move the use scripting additions command to the script object, and (2) to wrap any scripting additions commands in the top-level script within a using terms from scripting additions block. The using terms from scripting additions block provides a workaround to the restriction that only one use scripting additions command is allowed in the code; it also prevents scripting additions commands from appearing in raw chevron syntax (and sometimes executing successfully on my machine, sometimes not, in true quirky AppleScript fashion):
Applescript:
script myScript
use framework "Foundation"
use scripting additions
on makeUppercase(aText)
return (current application's NSString's stringWithString:aText)'s uppercaseString() as text
end makeUppercase
on displayUppercase(aText)
display dialog (my makeUppercase(aText))
end displayUppercase
end script
using terms from scripting additions
set fileRef to ((path to desktop as text) & "MyScript.scpt") as «class furl»
store script myScript in fileRef with replacing
(load script fileRef)'s displayUppercase("foobar") --> displays "FOOBAR"
end using terms from
Addendum:
Even simpler, just pull the use scripting additions command out of the script object and place it at the top of the top-level script. That makes the using terms from scripting additions statement unnecessary:
Applescript:
use scripting additions
script myScript
use framework "Foundation"
on makeUppercase(aText)
return (current application's NSString's stringWithString:aText)'s uppercaseString() as text
end makeUppercase
on displayUppercase(aText)
display dialog (my makeUppercase(aText))
end displayUppercase
end script
set fileRef to ((path to desktop as text) & "MyScript.scpt") as «class furl»
store script myScript in fileRef with replacing
(load script fileRef)'s displayUppercase("foobar") --> displays "FOOBAR"
This gets back to Nigel's original example, although there doesn't appear to be a requirement for the script object to be coded below the store script command.
Last edited by bmose (2018-06-12 06:20:25 am)
Offline
Pages:: 1