If [ some condition exists // comments about the conditional can go here]
If [some condition exists]
If [some condition exists]
If [some condition exists]
If [some condition exists]
Script Step
Script Step
If [some other condition exists]
Script Step
Script Step
If [yet another condition]
Script Step
Script Step
Script Step
Script Step
Script Step
Script Step
Script Step
If [yet another condition]
Script Step
Script Step
Script Step
Script Step
Script Step
Script Step
If [still another condition]
Script Step
Script Step
Script Step
Script Step
Else
Script Step
Script Step
Loop
Perform Script [ "Subscript" ]
End Loop
Script Step
Script Step
Script Step
Script Step
Script Step
Script Step
End If
Script Step
Perform Script [ "Subscript with More Conditionals" ]
Script Step
Script Step
Script Step
Script Step
Else
Show Custom Dialog ["Error: something went wrong"]
End If
Script Step
Script Step
Script Step
Script Step
Script Step
Script Step
Script Step
Script Step
Else If [ some condition here ]
Script Step
Else
Show Custom Dialog ["Error: something went wrong"]
End If
End If
Else
Show Custom Dialog ["Error: something went wrong"]
End If
Else
Show Custom Dialog ["Error: something went wrong"]
End If
Else
Show Custom Dialog ["Error: something went wrong"]
End If
Else
Show Custom Dialog ["Error: something went wrong"]
End If
I sure have, and admittedly, I’ve written a few of these myself. The blocks of “If statements” are known to FileMaker developers and programmers in general, as nested conditionals, and perhaps that’s because the script often ends up resembling a bird’s nest.
This kind of script is usually written when a developer needs to handle complex business logic. I once wrote one of these when a client’s solution needed to convert one type of service to another. There were many things that needed to be validated in order for the service to be converted. If any one of those conditions weren’t met, the script had to back out of the conversion process and report an error back to the user telling them why the script couldn’t be converted.
When I was done, and it worked, I was proud of myself. The client was happy, and I moved on to the next task.
A few years passed. My client’s business had changed and they called me with a small change request to this particular script. Ever eager to help, I said, “of course I can do that!”
With my eyebrows furrowed, I opened my script of nested conditionals and went to work. After a few seconds of review, my eyes got a bit wider, then glassy, and at some point they may have crossed. So many ifs. Which one went with which error? It was so hard to read. Where to start making changes? Why hadn’t I commented this better? Or would comments have made it even longer and more confusing?
This script was fragile, and I was scared to break it. But there was no other way, because I had promised my client it could be done. I knew it could. I made the change. Things broke. I fixed them. Other things broke. I fixed those. After rinsing and repeating several more times, it finally it worked.
Phew! Mission accomplished. Yet I felt a bit embarrassed that I had to put my client through so many rounds of bug fixes before it worked correctly. Even with the Script Debugger, it had been difficult to find and fix bugs. Aside from that, I never wanted to write a script so fragile again! If reading and editing my own code was this difficult, this was not the way I wanted to do it.
I tried to imagine what it would have been like if another developer had written the script. Well, I found out when I was working on a huge FileMaker solution for a large corporation that had been built by 6 or 7 developers over 15-20 years. It’s next to impossible to decipher and modify another developer’s nested conditionals. Not only is the script fragile, unless it was commented carefully and updated every time it was modified (how often does that happen?), you don’t even have the benefit of knowing what the person was thinking when it was written.
I tilted my head to the right, contemplating how to avoid this going forward. And then I realized:
Lesson learned: avoid nested conditionals for cleaner, more readable code.
When FileMaker development gets challenging and a good reference for software best practices is needed, I often ask myself, “What would WebObjects do?” (WebObjects is an object-oriented web application framework I learned years ago. It was created by Next/Apple, so I know it’s smarter than me.) WebObjects, like most smart object-oriented web application frameworks, had a way of handling errors that makes it easier to debug. One key idea from that model that could be helpful to FileMaker scripting, which is not structured as an object-oriented language, is that each script logs and returns its own errors, and identifies where the error came from.
So here’s how we do it now, as part of our larger FileMaker development framework:
When the above approach is employed, the script will transform from the house of cards above to a friendly neighborhood of stable, brick ranch houses which, in the FileMaker Script Workspace, looks something like this:
# Use a variable called "errorLog" (or just "error") to hold the errors. Before beginning the script, assign the errorLog variable to an error if any of the validation requirements for the task are not met. For example: Set Variable [ $errorLog ; Case ( condition 1 ; "Error: can't do it because condition 1 was not met." ; condition 2 ; "Error: can't do it because condition 2 was not met." ; condition 3 ; "Error: can't do it because condition 3 was not met." ; condition 4 ; "Error: can't do it because condition 4 was not met." ; condition 5 ; "Error: can't do it because condition 5 was not met." ) ] # Then, check to make sure the errorLog variable is empty before each "stacked" conditional, and append any subsequent errors to the errorLog variable. If [ IsEmpty ( $errorLog ) and nextCondition ] Script Step Script Step Script Step Script Step Else Set Variable ( $errorLog ; $errorLog & "Insert your error message here." ) EndIf If [ IsEmpty ( $errorLog ) and nextCondition ] Script Step Script Step Script Step Script Step Script Step Script Step Else Set Variable ( $errorLog ; $errorLog & "Insert your error message here." ) EndIf If [ IsEmpty ( $errorLog ) and nextCondition ] Script Step Script Step Script Step Script Step Else Script Step Script Step Loop # Perform A Subscript with Stacked Conditionals that Returns a $resultError variable if something went wrong Perform Script ( "Smart Subscript with Error Result" ) Set Variable ( $errorLog ; #Assign ( $resultError ) ) Exit Loop If [ something is true ] End Loop EndIf If [ IsEmpty ( $errorLog ) and nextCondition ] Script Step Script Step Script Step Script Step Script Step Script Step End If # If appropriate, report the error(s) captured to the user If [ Length ( $errorLog ) ] Show Custom Dialog ( "An Error Occurred" ; $errorLog ) EndIf
Exit Script [ #( "resultError" ; $errorLog ) & #( "resultSource" ; Get ( ScriptName ) ) ]
Note: The Exit Script [] script step at the end of the code snippet above uses a custom function named #, originally created by Jereme Bante, which allows multiple parameters to easily be passed into a script via FileMaker’s script parameter and out of a script using FileMaker’s script result. If you’re intrigued, you can read more about that here: http://filemakerstandards.org/pages/viewpage.action?pageId=557462. It should also be noted that a second custom function that converts the name-value pairs in the string of # functions parameter into actual FileMaker variables. That custom function is called #Assign in the example above, and it’s used to convert the script result sent by the “Exit Script” step in the subscript. If nothing in this italicized paragraph makes sense, don’t worry – the point is that subscripts should send any error(s) captured in the subscript back to the calling script so the calling script can handle them appropriately. You can make that happen any way you want.
Changing the way these complex scripts are written to a “stacked” conditional format rather than a nested conditional format has provided the following benefits:
That’s all for this post, folks! I hope this helps somebody. If it helped you, or if you have questions, comments, or confusion, let a gal know by sending me a note through our contact us page.