Table of Contents
This document should be considered to be a work in progress. It will almost certainly change as we learn more and get more feedback. We are trying to document our current practice as best we can, but there are still lots of unanswered questions. Please leave comments below if you have some feedback.
The Modular FileMaker (“mFM”) specification is intended to make it easier to discover, share, and maintain focused bits of FileMaker code. It has no opinion on any issues that do not directly impact that goal. We are only describing a light-weight guideline for how to code some aspects of what happens inside a module. What happens outside the module is not relevant.
The overriding directive of mFM is to drive adoption. Everything else takes a back seat. The benefits of module design only start to accrue when lots of people are writing and using lots of different modules. Therefore we want to get rid of anything that stands in the way of that goal. We try to write our code in such a way that it can be more easily installed, maintained, and used. We also try to not let super opinionated style spill outside the module in such a way that it will negatively impact adoption.
A developer should be able to use an mFM module without adopting the entire FileMaker mindset of the developer of the module.
We do not want to force people who want to use our module to have to use anything unnecessary to the module’s focus and purpose. We don’t want to include a bunch of dependencies out of convenience. If there are a bunch of dependencies that you need, perhaps consider breaking them out into their own modules. Then include those modules as dependencies. In general, dependencies are kept to a minimum, and are clearly documented.
Custom functions get their own entry here because they can quickly get out of hand. It is a really good idea to not force your users to take on your convenience custom functions. There is no good way of managing and grouping them besides namespacing them. Asking people to put your set of convenience custom functions into their solutions is a sure way to decrease adoption. Even very useful custom functions that do things like make it easy to pass multiple parameters or parse them should be avoided when coding inside your module. See the section below on multiple parameters for how to best pass parameters into scripts in your modules.
You can often just use regular script steps with the same code that work the same way. Here is a FileMaker file with some example script steps showing how to do things like parse multiple parameter passing and and script trigger enabling and disabling without custom functions.
Custom Function Modules
We are not suggesting that you do away with custom functions entirely. Sometimes they are absolutely necessary. But if you don’t need to include them please don’t. If you have a set of custom functions that you use in all your modules, and you simply MUST use them, consider breaking them out into a separate module. That will make them a little easier to manage. We have some examples of custom function modules already.
What it’s Not
This is not about building solutions. This is about writing modules, not about how you might choose to write the code that glues those modules together into a larger solution. This specification says nothing about that. Zero. Zip. Nada. Do it in anyway that seems right to you.
Modular FileMaker modules are defined in a script folder. The scripts and folders within that folder are laid out in a simple pattern that tries to make it clear how to configure and use the module.
Modules Folder In Each File
Each FileMaker file must contain a folder at the top level called “Modules”. That folder must contain a folder for each module in the FileMaker file. Even modules that are don’t have scripted functionality get a script folder. The folder must have the same name as the module. This makes it easy to see what modules are in every file.
Individual Module Folder
Each module lives in a folder of the same name. It is placed in the “Modules” folder as described above. It is helpful to avoid naming conflicts by prefixing folder and script names with the name of the module. For example, the required README script for a module named “HyperList” could be “HyperList: README”
It includes a README script as the first named “script” and up to three subfolders. In the image below, the MasterDetail module’s folders and README are prefixed with the exact same name as the module, with the same case. This is fine. Something like “masterdetail:” would be fine as well. Just be consistent. Don’t change from one format to another in your module.
There must be a script called ” README” as the first named script visible in that folder. Reading the contents of that script should give the user basic information about what the module does, who made it, and what version it is. It should either spell out exactly how to install the module or point to resources on the internet or elsewhere that describe how to do it. It should also list every dependency, or point to a script in the folder that does.
Each mFM Module folder can contain up to four subfolders.
- Configuration and Settings – If ANY scripts in the module allow or require user modification, they must go in this folder. These might include scripts that do setup or configuration, or provide outbound hooks or callbacks for the module. If there are no such scripts then the folder may be omitted.
- Examples and Tests – if you have example scripts and tests that you want to distribute along with your module they should go there
- Public – All API scripts or scripts that are called from outside the module or from buttons or triggers need to go in here. This is the public face of your module. Any inbound calls must go here. Any script triggers that your module needs to use must go in their own subfolder inside this folder. Script triggers are one of the areas where modules can class, so we want to call out if your module is using them. See below for more info on script triggers. If there are no public scripts then the folder may be omitted.
- Private – Any scripts that are internal to the module go in this folder. This is where you might want to do the big heavy ugly work of your module. You can then put a nice face on it by placing “wrappers” or “API” scripts in the public folder that call scripts in here. This is also a good place to store shared utility scripts that are called by more than one API script.
None of these folders is required. If you have no scripts to go inside them then don’t bother putting an empty Folder in there if you don’t want to.
Installation and Configuration Tests
It’s really helpful if you can include a script or two that can test if your module is installed and setup correctly. Here we show a couple of extra scripts providing more explanation and a test.
The image above shows a module called “SQL Helper”. It has nothing other than custom functions in it. It does include a script that can test to make sure that each custom function is installed correctly. More on how to do that soon…
Organizing Other FileMaker Elements ( not scripts )
Layouts are pretty obvious. Any layouts that belong to a module should be in a folder with the same name as the module it belongs to. A layout folder containing layouts for a fictitious module called “MailMaster” would be called “MailMaster”. For other kinds of elements you will need to use namespacing to group things. If the MailMaster module had custom functions, they should be prefixed with “MailMaster” or “mailmaster”
There are few areas where FileMaker modules need to communicate to one another. For example, if one module is signaling that it needs to disable script triggers, it is important that other modules are aware of this. Passing parameters and Error Capture State are two more areas where its helpful if modules from different developers are all on the same page. What follows are recommendations for handling these areas.
Enabling and Disabling Script Triggers
There is a set of custom functions for enabling and disabling script triggers by Jeremy Bante that handle this task pretty well. While we DON’T recommend that you force your module user’s to use any custom functions unless absolutely necessary, we do recommend that you write your script trigger scripts so they are compatible with this set of custom functions. This is pretty simple. All we have to agree on is what the $$Global variable is named that indicates that script triggers are currently disabled. We have chosen this
If GetAsBoolean ($$~DISABLETRIGGERS ) is True (1) then script triggers should not run. This variable was chosen because there is already a set of custom functions and a standard over at FileMakerStandards.org. You do not need to use custom functions, and if you are coding inside of your module then we recommend that you don’t.
Error Capture State
Its helpful to your fellow mFM module developers to leave the Error Capture State set the same way in which you found it. In other words, before you change it, save the current state using the Get ( ErrorCaptureState ) function, then return it to that state before your scripts return control back to the script that called them.
Another really good idea is to respect the Error Capture State when you are displaying your own custom error dialogs. If the person using your Module wants to supress your error dialogs, he/she can set the Error Capture State on before calling your module’s scripts.
Error handling is a slightly different topic, and one which we haven’t reached anything close to consensus on. But the general idea is that you pass back errors in the script result in some documented and consistent manner.
If you need to pass multiple parameters into public scripts, you must do so using the very common Let Variables Notation pattern. Sometimes we refer to these as “Let Variables” or “Let Vars”. This technique has been widely used and documented in various forms, including in FileMaker’s own help document. There are other techniques, we know, and they all have their various pros and cons, but Let Vars seem to have the most support. The format can be easily written and parsed without custom functions. The format specification is as follows:
Multiple named parameters should be passed as a string that can be evaluated as the variable section of a Let Statement. Variable names are to be prefixed with one or two “$”. Trailing semi-colons “;” are allowed and your unpacking or parsing function should support them.
In practice, you are often going to write them like this example where the values are being pulled from fields. Notice you must quote everything except numbers.
Strings written in the above format can easily be parsed with a function similar to this one, by Jeremy Bante. This is exactly the same code as used by the #Assign custom function from FileMakerStandards.org
This example file has some scripts that contain the above code.