Update 28 April 2014: Make sure to read the illuminating comments following the article, with various suggestions to make your code even less brittle.
The other day I was working with an OnRecordCommit script trigger — let’s call it “Trigger Script” — and, not surprisingly, I wanted this script to run whenever a record in a certain table was committed. Except… well… not exactly always… you see, there was this one other script— let’s call it “Other Script” — which had a Commit Record step right smack in the middle, and in that particular circumstance I definitely did not want Trigger Script to execute.
Luckily, we can include a parameter when a script is triggered, so I added the highlighted script parameter to the OnRecordCommit trigger as follows:
…which evaluates as 0 (zero) when the current script is “Other Script”; otherwise it evaluates as 1. Next I added some new steps at the top of Trigger Script to instruct it to bail out if necessary.
…and problem solved. But… not optimally, because what happens if I realize “Other Script” isn’t the ideal name for that script, and decide to rename it?
[Yes, I realize that I could have used a $$variable to pass the “don’t execute” information between the scripts — but if I’d done that, I wouldn’t have an excuse to talk about hard-coded object references.]
Many years ago I heard Albert Harum Alvarez use the term brittleness to refer to code that breaks if you modify a referenced object, for example: let’s say your script instructs FileMaker to go to a certain (hard-coded) layout number, but then later you change the order of your layouts without remembering to update the script… two words: “kah-blooey!” (Don’t ever hard-code layout numbers in your code; they’re way too volatile. Seriously.)
More often though, brittleness can occur when we refer to objects by name, via functions such as, but not limited to: Get(LayoutName), Get(ActiveFieldName) or, as per my script parameter above, Get(ScriptName). Well it turns out that a Belgian FileMaker developer named Fabrice Nordmann has written a custom function, FM_Name_ID, that can translate almost any FileMaker object name into its corresponding internal id… and vice versa.
That’s right, this one CF translates in both directions: input an ID, you get back a name; feed it a name, out pops an ID. I’ve been working with internal layout ids in various ways for many years, but it never occured to me that a single custom function could be used for both encoding and decoding. It’s a great idea, and I wish I would have thought of it.
You can find the syntax for the custom function here:
www.fmfunctions.com/fname/FM_Name_ID
…and a detailed writeup by Fabrice here:
www.1-more-thing.com/FileMaker-Avoid-Hard-coding.html
So, to cut to the chase, I was able to remove the hard-coded script name reference from my script parameter and replace it with…
…which, admittedly, is not as readable, but is certainly more robust. As Fabrice suggests, it’s a good idea to add a comment like “// Other Script”… though truth be told, it’s not much work to throw FM_Name_ID ( 341 ; "S" ; "" ; "" ) into the Data Viewer if you can’t remember what script you were referring to.
One of the things I love about custom functions is the open-source nature of them, and if I’m going to use a CF, I want it to have as few arguments as possible, and I also want to be able to use it without having to give it much thought. So, I decided to spawn a few “baby” (single-purpose) versions from Fabrice’s mothership CF for my own use, which incidentally, you can find, along with Fabrice’s original CF, in today’s demo file: CF-Sandbox.
The baby versions are FMNID_Layout, FMNID_Table, FMNID_Script, etc, and here’s the final version of the parameter:
Incidentally, I did include one original CF in today’s demo file; it’s called FNO, which is short for “field name only” — basically, it’s the GetFieldName function minus the “table::” portion.
I wrote it to faciliate working with the Get(ActiveFieldName) function…
Before: If ( Get(ActiveFieldName) = “organization” ; …
After: If ( Get(ActiveFieldName) = FNO ( customers::organization ) ; …
…and now I can rename “organization” to “business”, “department”, “division”, or what have you, with a 100% clean conscience.
Post script: 19 April 2011
In the comment section below, Geoff Gerhard offers an alternative method to solve this problem. I have updated this article’s demo file to include his approach, which I actually prefer to mine.