Applescript Annoyance
January 24, 2020 10:04 PM   Subscribe

I keep making the same error programming Applescripts, and am looking for a way not to!

I build a lot of Applescripts including (among other things) multiple search/replace actions. I find it easiest to create the search/replace patterns by recording examples "live" via the BBedit app into Applescript Editor, so I don't need to worry about entering escape characters, committing typos, etc..

But that means the scripts refer to the name of whatever doc I was working on. And I nearly always forget to change the string to refer to a generic document. And then I can't figure out why the script won't run.

So....I wind up with something like this:

replace "dfd" using "fdf" searching in text 1 of text document "untitled text 19" options {search mode:grep, starting at top:true}

...having forgotten to change it to something like this:

replace "dfd" using "fdf" searching in text 1 of text document 1 options {search mode:grep, starting at top:true}

Any suggestions? I'm committed to Applescript (i.e. not Automator).
posted by Quisp Lover to Computers & Internet (13 answers total)
 
Best answer: There are many different ways to improve this, all will involve changing how you work in one way or another. You could: reduce the frequency of defects in the scripts in the first place, add some kind of "quality assurance" step that can detect defects in your scripts before you run those scripts, or make it easier for you to debug and pinpoint the defect that is causing the issue after you've run the script and noticed it didn't work.

To reduce the frequency of defects in the first place, you could stop generating less flexible code using a recording tool and get more fluent in learning how to directly write the scripts by hand (maybe slower, but far less likely to be wrong in this specific way). Depending on what your task is exactly, it may also be possible in the longer run to switch from applescript to a general purpose programming language (e.g. python) and write a single higher-quality python script that can flexibly perform the search/replace actions you need to do without needing the code to be rewritten every time.

To identify defects before the scripts are run, basic options are testing and code review. For testing, one simple automated testing approach could be to run a special tool over your applescript code that complains with an error message if it detects common kinds of errors. It might not be very hard to write a custom script that can detect the exact kind of error you describe -- search through the applescript code and complain if it finds the words "of text document" when the next word is not a number. For code review, you or another person reads through the code, thinks about what the code is doing and how that compares to what you're trying to achieve, and decides if it looks good or not. You can learn to self-review code pretty effectively, but often having a second pair of eyes on some code is far more effective at spotting mistakes that you don't notice in something you've created yourself.

To make it easier to debug and pinpoint errors, maybe a really simple place to start would be to start writing and updating and referring to a document that lists common issues you encounter with your scripts, and how to fix them (when you figure out a fix). Then get into the habit of reading the document when something goes wrong and updating it when you encounter a new issue you haven't seen before.
posted by are-coral-made at 2:34 AM on January 25, 2020


Best answer: Something else that might help is always using the same bbedit document to prototype your edits in, rather than opening new untitled ones each time. Naming it something like "fixme-bbedit-prototype" should let you do a fairly no-brainer search and replace to fix dangling instances in your scripts.

To take this idea to the next level, you could make a script that wraps your script editor. On entry, the wrapper script could open fixme-bbedit-prototype for you as well as the script you're editing, so you'd have it available when you needed it. On exit from the script editor, the wrapper could make an automated extra edit that replaces (with per-instance verification, if you like) all instances of "fixme-bbedit-prototype" with 1 in the script you've just been editing.
posted by flabdablet at 4:17 AM on January 25, 2020


Response by poster: a-c-m, every few months some AppleScript or Grep issue pops up for which the answer is finally learning a real language like Python. I poke around, ask coder friends, and it always returns the same answer: too much learning curve to overcome errant trivial issues. You don’t learn to fly a helicopter so you can stretch out your fresh spaghetti.
posted by Quisp Lover at 7:42 AM on January 25, 2020


Quite so. Ideally you'd just pick and eat it the same day, and faffing about with unfamiliar tools is only ever going to slow that down.

Personally AppleScript gives me hives, but then again I enjoy doing all kinds of stuff in bash that probably shouldn't be done in bash. When what you mainly have is nails, almost anything that fits your hand will do for a hammer.
posted by flabdablet at 7:57 AM on January 25, 2020 [1 favorite]


Response by poster: Agreed. Maslow failed to register the bizarre versatility of a really trusty hammer. In fact, a person who's perpetually switching tools for perfect optimality would be a crazy inefficient bedbug (I think lots of home cooks get like that with their zillion Williams Sonoma gadgets).

You and a-c-m both suggested scripting a post-proofing run. The problem is that unless someone's created a wrapping/proofing app for Applescript Editor (possible, come to think of it; I need to check), I'd need to proof elsewhere.

But if I could copy the script into BBEdit, a single search/replace grep maneuver would suffice; I wouldn't even need to scriptify it. OTOH it's a bit of a hassle to always copy finished scripts to BBEdit for the proofing, but, hmmm...I guess that's where scripting would come in.

Copy text from Applescript Editor
Paste into BBEdit doc
Execute grep search/replace
Delete unsaved BBEdit doc
Paste back into Applescript Editor

It's a bit Williams Sonoma, but would work.
posted by Quisp Lover at 8:14 AM on January 25, 2020


unless someone's created a wrapping/proofing app for Applescript Editor (possible, come to think of it; I need to check), I'd need to proof elsewhere

Can Applescript Editor really not eat its own dog food? In other words, is there truly no way to write an Applescript that you can launch instead of Applescript Editor, that then opens Applescript Editor for you, waits for you to finish using it, then feeds it a bunch of scripted instructions telling it to search for all instances of a string within the last script it edited and replace it with another?

I'll stop now because I can feel a bout of hives coming on. This kind of nailing-together would be trivially easy with bash and sed, but perhaps the advanced user-friendliness of the native Apple tools makes it more difficult?
posted by flabdablet at 10:12 AM on January 25, 2020


Response by poster: Good idea but I need to do main composition in Apples script editing program for various reasons. And this error is really a 100% post-problem that can be scraped away last.
posted by Quisp Lover at 10:42 AM on January 25, 2020


I need to do main composition in Apples script editing program for various reasons

Sure, but is there any reason you have to launch that program via its normal launch shortcut rather than via another one that tops and tails what is otherwise a completely normal Applescript Editor session with your own customized pre- and post-processing steps?
posted by flabdablet at 10:47 AM on January 25, 2020


Response by poster: It's trivial to launch a multi-pronged process including opening the script editor whenever I open the script editor.

But if I spend >60 mins developing the script at hand, it wouldn't work to have the final proofing step waiting on pause. For one thing, it would mean not testing the script under development. Super klunky at best. Since it needs to back-load (coming at the end), better to just get used to always running the proofing script at the end.

One kooky idea would be to auto-launch the proofing script whenever I QUIT the script editor! But that would drive me absolutely nuts and create a grotesque havoc of unintended consequences.
posted by Quisp Lover at 12:59 PM on January 25, 2020


Best answer: Real Programmers have this sort of problem in Python too, and what you're looking for is a static analysis tool. Now, in those languages, static analysis can be a complicated business, because you can make it do great and wonderful things, but that doesn't need to be the case here.

People have suggested this upstream - basically what you need to do is identify the common error you're making - done! - write a little script that spots the error, and run the script on your code religiously and ideally just before you test it.

For you, the script might look like this:

#!/bin/sh

if grep -i 'untitled' ${1} ; then
echo "you have an untitled problem in your code"
exit 1
fi
exit 0

Write to a file called, say, static-test, then go to a terminal window and run 'chmod +x static-test' which makes it a program.

Whenever you run ./static-test your-script in the terminal it will hard with a message if you have a problem. I believe from the gui you can drop the script file you want to test on the static-test file and it will open a terminal, but you will have to experiment. (Even tests need testing!) You might even be able to write your test actually *in* AppleScript; and I think Automator can run a program on a file in a directory every time it's saved (even if it's running an AppleScript program)?

As an aside, don't apologise for AppleScript - choosing the right programming language for both you and the problem is an art, and it seems like you are happy with your choice. However, if you want to learn a new language, the shellscript I've used up there is complementary to a lot of programming activities like testing and deployment, and you don't have to go far down the rabbit hole to get useful results.
posted by How much is that froggie in the window at 2:01 PM on January 25, 2020


Response by poster: As an aside, don't apologise for AppleScript

Says the person referring to "Real Programmers"! ;)
posted by Quisp Lover at 2:10 PM on January 25, 2020


I've done a bit of regular expression scripting in BBEdit, but not for ages. This is a problem you don't need to have. I hear what you're saying about not wanting to undertake a big learning experience, but you can probably improve your scripting life a lot by just increasing your knowledge a little bit.

I would need to see a bit more of the script to give you better advice, but maybe start with replacing this line:

     searching in text 1 of text document "untitled text 19"

With this:

     searching in text 1 of text window 1

That way you're sidestepping the document problem. It requires that there is a window 1, but from your description it looks like you're always going to have this anyway and it's simpler than referencing documents in this case.

Something to consider is that all decent scriptable apps (and BBEdit has a great AS implementation) allow you to do things more directly than you would when doing it manually through the GUI. For example you can tell Finder to select an object then do something to it:

tell application "Finder"
     activate
     select "myFile" of folder "Desktop Folder" of folder "UserName" of folder "Users" of startup disk
     set mySelectedFile to the result
     set myResult to properties of mySelectedFile
end tell

But you only need this:

set myFile to alias "Macintosh HD:Users:UserName:Documents:theFileToRefererence" -- this file must exist or it won't compile
tell application "Finder"
     set myResult to properties of myFile
end tell

If you must use a document on disk then you can just open it in the script before you do your stuff:

set myFile to alias "Macintosh HD:Users:yourUserNameHere:Documents:theFileToRefererence" -- this file must exist and doesn't need to be inside a "tell" statement
tell application "BBEdit"
     open myFile -- it should now be window 1
     replace "dfd" using "fdf" searching in text 1 of window 1 {search mode:grep, starting at top:true}
end tell

Also:

• I thoroughly recommend learning how to add the searches straight into the script. Recording GUI actions is cool, but doesn't produce great code. If you've learned regular expressions, I imagine you can learn how to add them to your scripts without a whole lot of stress. In the scripts I have, the only escaping required was quotes. BBEdit's manual is excellent and contains comprehensive sections on regular expressions and AppleScript.
• Get yourself a copy of Script Debugger. It's worth it. Really helpful for understanding AS Dictionaries and great for debugging believe it or not.
• I published a bunch of stuff here but haven't updated it for a very long time. Most of it seems to still work - I use it myself.
posted by mewsic at 9:48 PM on January 25, 2020


To add to my Real Programmers reference: I once made a healthy living writing PowerPoint macros, so in my experience people who call themselves Real Programmers are frequently judgmental assholes. You can write good code in any language of your choosing, and bad code is similarly flexible.
posted by How much is that froggie in the window at 10:19 PM on April 1, 2020


« Older Getting rescinded from college?   |   Curly Girl, Plopping, Sleeping and Fuzzy Back-Head Newer »
This thread is closed to new comments.