Evolution of the Script Parameter

For any FileMaker developer or power user, writing scripts to automate processes is a weekly, if not daily, task. During the course of our script writing, we are able to use script parameters to pass information to the script from a button, script trigger, or another script. This allows us to use scripts, or sub-scripts as I like to call them, for multiple tasks because the required values are not hard-coded into the script. They are set dynamically using script parameters.

As I finish typing that first paragraph, I realize that I have made both a technical and grammatical error. Beginning with FileMaker 7, developers are able to pass a script parameter, singular, to a script. If you review the button setup dialog box or the Perform Script script step, you will see the option labeled “Script Parameter”. Correspondingly, the function to retrieve the information passed to the script is Get ( ScriptParameter ). Yet, all FileMaker developers talk about using script parameters, plural. Why is that?

The simple reason is that we have figured out various methods to include multiple values in a single text string when the script is called, and then parse out those values as the script is executed. For the balance of this post, I’d like to share the evolution of the various methods I have used over the last 15+ years in my script writing. As with everything, there are advantages and disadvantages to each method.

At the beginning, knowing that we needed to use a separator in our text string that did not normally occur, I decided on the vertical pipe or “|” symbol. Then, with help from the PatternCount ( ), Left ( ), Middle ( ), and Right ( ) functions, I parsed the data and set the required local variables in the script.

If the script is expecting to receive three values, or “parameters” the way we typically think about them, the parameter passed to the script, regardless of how the script is called, was entered this way:

“String1” & “|” & “String2” & “|” & “Number1”

That very simple calculation yielded a script parameter that looked like this:

String1|String2|Number1

Before you make any comment, I realize that I could have entered the calculation exactly as you see the result above. However, I found it easier to determine which parts of the string were the parameters since the chosen separator was set apart from the values and not included as part of a “run-on” text string. (This is a result of my CDO condition. This is similar to OCD, but the letters are arranged in alphabetical order like they are supposed to be.)

To extract the values in the script, I used a series of Set Variable script steps. The calculations for the three variables looked like the following, respectively. (Please note that I have replaced the Get ( ScriptParameter ) function with the text “GSP” to conserve space.):

Left ( GSP ; Position ( GSP ; "|" ; 1 ; 1 ) - 1 )
Middle ( GSP ; Position ( GSP ; "|" ; 1 ; 1 ) + 1 ; Position ( GSP ; "|" ; 1 ; 2 ) - Position ( GSP ; "|" ; 1 ; 1 ) - 1 )
Right ( GSP ; Length ( GSP ) - Position ( GSP ; "|" ; 1 ; 2 ) )

On the plus side of this example, the system easily handles empty or “null” values. On the negative side, adding a fourth value to the parameter requires some additional programming. It is easy enough to add when the parameter is passed. The work begins when modifying the script to parse out this new value. The Middle ( ) functions must remain in the middle and the Right ( ) function must be used for the last value. As part of my process, I would duplicate the next-to-last Set Variable step, and change the variable names and/or the parameters of the functions as needed. Granted, it is not a hard task but one where it is easy to forget to go back and update the “start” and/or “occurrence” values of the Middle ( ) and Position ( ) functions.

The next iteration continued to use a value separator, but I switched to use a carriage return or the “¶” symbol. While it is possible to just replace the “|” in the first example with the “¶”, the calculation of the script parameter is further simplified by using the List ( ) function, which also improves readability. The setup of the parameter passed to the script becomes:

List (
   “String1” ;
   “String2” ;
   “Number1”
)

This calculation returns the following script parameter:

String1
String2
Number1

To extract the values in the script, I again used a series of Set Variable script steps, but I only needed to use the GetValue ( ) function in the calculation. Those calculations for the same steps now look like this:

GetValue ( Get ( ScriptParameter ) ; 1 )
GetValue ( Get ( ScriptParameter ) ; 2 )
GetValue ( Get ( ScriptParameter ) ; 3 )

The advantages of this method are readily apparent. The parameter is easier to read during both the programming and troubleshooting processes, and the parsing of the individual values requires only a single function. Also, adding a new value due to a script change is simply done. The primary disadvantage is with the calculation of the parameter, particularly when using the List ( ) function. Any empty values are ignored so the remaining values in the return-separated list “slide up”. If the script is expecting three values but the second value is empty, the third value becomes the second value when it gets to the script, which in turn causes unexpected behavior as the script is executed. Any empty or “null” value can be handled when using this method but that requires additional programming when both calculating the parameter and extracting the values.

While other methods have been shared at Developer Conferences or through the FileMaker Community or described in blog posts like this one, I continued to use this method for a number of years. First and foremost, it is easy to understand. Also, while these newer techniques addressed the shortcomings listed above, they required custom functions to both calculate the parameter and then parse out the values. Since the disadvantages were only a problem a small amount of the time, I opted to stick with the method that was quick to comprehend.

Then, enter JSON. With the release of FileMaker 16, we were given access to six native functions to manipulate JSON. It took me some time to fully understand the JSON syntax and grasp all the potential use cases for these new functions spread throughout my FileMaker solutions. At its core, JSON is about an array of name / value pairs, and I realized that this behavior can be used to pass information to a script and then parse out the values once the script begins executing. My script parameter calculation now looks like this:

Let ( [
   json = JSONSetElement ( json ; “Value1” ; "String1" ; JSONString ) ;
   json = JSONSetElement ( json ; "Value2" ; "String2" ; JSONString ) ;
   json = JSONSetElement ( json ; "Value3" ; "Number1" ; JSONNumber )
] ;

JSONFormatElements ( json )

)

The parameter received by the script has become:

{
   “Value1” ; “String1”,
   “Value2” ; “String2”,
   “Value3” ; “Number1”
}

While it certainly looks more complex than the other methods, it is easily understood as one becomes more familiar with the JSONSetElement ( ) function. Also, the risk of confusion and error decreases since the names of the “parameters” now accompany the values passed to the script. Empty values and values with multiple lines or special characters are automatically handled by the JSON function used to include them in the script parameter. (Please note that the JSONFormatElements ( ) function is not required to pass the script parameter. It is added to format the elements in the JSON object and make it easier to read when using the Data Viewer for debugging purposes.)

When the values are retrieved from the script parameter using the JSONGetElement ( ) function, the calculations for the Set Variable steps become:

JSONGetElement ( Get ( ScriptParameter ) ; “Value1” )
JSONGetElement ( Get ( ScriptParameter ) ; “Value2” )
JSONGetElement ( Get ( ScriptParameter ) ; “Value3” )

Again, the calculation may initially look intimidating, but any apprehension dissipates as one becomes comfortable with the JSON function.

Much like after I had begun using the return-separated list version of my script parameter, I felt I had arrived at the method I would be using for an extended period of time. However, my final step was taken soon after I switched to the JSONSetElement ( ) / JSONGetElement ( ) technique as I realized what I am able to do, knowing both the name and value for each element included in the JSON object.

It is possible to use a single script step to declare all local script variables as the values passed to the script are parsed out of the script parameter. The first benefit is the portability of the script. I can now copy and paste a single Set Variable script step into a script instead of explicitly declaring each variable with its corresponding value. The script parameter can now include one value or seven values; the same script step will extract them all. The second benefit is found when the script is changed. If another element is included when the script parameter is declared, the value is automatically parsed without having to explicitly declare that local variable in the script. I still need to edit the button or Perform Script step calling my script, but I can just begin using this new piece of information in the script as needed.

As it stands, FileMaker, Inc. has provided the tools to make this a reality. First, the JSONListKeys ( ) function extracts just the keys or names of the elements included in a given JSON object. Then, the Let ( ) function can declare a local script variable (or a “$” variable if you prefer) without using the Set Variable script step. And finally, the Evaluate ( ) function allows the calculation to be written in a dynamic way.

Those three functions handle a single value so the final step is to include them in a custom function. As noted previously, I wanted to refrain from using custom functions since it would need to be added to any file where I wanted to use this new method of parsing a script parameter. However, it allows me to use recursion by calling the custom function repeatedly to parse out all values in a single step. Also, I only had to add one custom function to extract the values instead of adding multiple functions to both calculate and then parse the parameter.

The custom function I created is fnExtractParameter ( json ; index ) where “fnExtractParameter” is just the name I have given my custom function. The first function parameter “json” is the expected JSON object, and “index” is the starting element when the function begins to parse out the values. The custom function is defined as follows:

Let ( [
   keyList = JSONListKeys ( json ; "" ) ;
   keyCount = ValueCount ( keyList ) ;
   key = GetValue ( keyList ; index ) ;
   value = JSONGetElement ( json ; key )
] ;

Evaluate ( "Let ( [ $" & key & " = " & Quote ( value ) & " ] ; 1 )" )
+
If ( index < keyCount ; fnExtractParameter ( json ; index + 1 ) ; "" )

)

In addition to declaring the local script variables included in the script parameter, this function also returns the total number of variables that have been declared by its execution. This resulting number was not required, but I was unable to create a function that didn’t return a result. This has nothing to do with any limitations of the FileMaker platform; I just couldn’t do it due to my CDO condition mentioned previously.

With this function now in my toolbox, the calculation for the single Set Variable script step is now as follows:

fnExtractParameter ( Get ( ScriptParameter ) ; 1 )

When looking at the Set Variable script step, it can’t get much simpler than that. Each value is extracted from the JSON object script parameter, and the variable declared matches the “key” or “name” included in the JSON object. Granted, there is a lot going on behind the scenes but the script is now easily readable, and overall understanding improves since this method is easily portable and reusable between scripts of the same file or even across files.

Also, if a picture is worth a thousand words, a demo file should be worth even more. To aid in your understanding, I have attached a demo file to this post which includes simple examples of all three methods described above. I know I have written more than I expected, and I appreciate you sticking with me until the end. I trust this information will help you in your script writing as much as it has helped me.

Until next time,

TW