Site icon FileMakerHacks

Avoiding Brittleness

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.

Exit mobile version