Jump to content

Best way to pass parameters in FM8?


This topic is 6687 days old. Please don't post here. Open a new topic instead.

Recommended Posts

Has anyone come up with a good way to pass parameters to scripts in FM8?

I have been using the trick where you do this inside the script

Let( Evaluate(GetScriptParameter), X )

for each variable, and you call the script with the following syntax:

X="foo" ; Y="bar"; Z="foobar"

However, there are some things I don't like about this way of doing it.

1. Documentation -- what's the best way to show that a Script has parameters? I find myself having to open the script to double-check I have the parameters named properly.

2. Syntax / Ease of use -- creating the calling parameter string is ugly, lots of escaped-quotes inside quotes, i.e. to make this string:

X="foo" ; Y="bar"; Z="foobar"

you need to write

X="" & TableA::FieldA & "" ; Y="" & TableA::FieldB & "; Z="" & TableA::FieldC & """

Ugly!

3. Missing parameters -- this is an evil one! I just discovered that

"Let (Evaluate(GetScriptParameter), X)"

returns "?" if the parameter wasn't included. I'd much rather it returned the empty string ""

4. Error checking -- how does one easily trap for missing parameters?

So in short, the power is enticing, but I'm finding the use a bit ugly and error-prone.

Some ideas:

A. Naming scripts? Maybe I should start including the parameter names in my script names, e.g. "OpenNewWindow[window,layout,height,width]" This might make it much easier to script as you can get the parameter names w/o having to open the script?

B. Each script that has parameters passed should immediately assign them to local variables of the same name, e.g.

Set Variable $X = Let(Evaluate(GetScriptParameter), X)

Set Variable $Y = Let(Evaluate(GetScriptParameter), Y)

This will probably help with readability and debugging.

C. Creating the parameter string is still a pain. It would be really nice to be able to call a script with a normal parameter list, like this:

Window="MyNewWindow", X=500, Y=Table::FieldA

I wonder if I could create a custom function which would put it together doing all the quoting & escaping for me, so I ended up with this:

"Window="MyNewWindow", X=500, Y="" & Table::FieldA & ""

Link to comment
Share on other sites

You might check out the ideas in this thread:

http://fmforums.com/forum/showtopic.php?tid/120048/post/120048

I use the same CF as Chuck to simplify pulling out the right parameter value.

As for setting the parameters in the script call, I just use the long form. I suppose you could have a CF put the escape characters in there for you, but I don't know if it's worth the trouble.

Link to comment
Share on other sites

Thank you, that is a great discussion thread.

Here's a quick summary of the highlights.

1. There are basically two ways of passing multiple parameters. Either Delimited or Named. Older programming languages tend to use delimited parameters, newer ones tend to use named parameters, but it's basically a matter of preference. However, you should probably choose one method and stick with it throughout a solution.

Named parameters take longer to write, but have the advantage of being more self-documenting, and you can easily omit or re-order the parameters w/o problem.

Named parameters example:

NewWindow(WindowName="NewWindow", Height="47", Width="99")

or

NewWindow(Height="47", WindowName="NewWindow")

Delimited Parameters example:

NewWindow("NewWindow|47|99")

2. No matter which style you use, you may find it handy to put your script parameters in the function (script) name itself. So instead of having a script called "MyFunction" you should name it MyFunction(WindowName,Height,Width)

3. You can create a custom function to extract parameters (either delimited or named). See http://fmforums.com/forum/showtopic.php?tid/120048/post/120048 for more details.

4. In FileMaker 8, your script should immediately assign your parameters to local variables. This makes the script more self-documenting, and also makes subsequent use of the parameters simpler.

Example (uses the "GetParameter" custom function at the above link)

MyFunction(WindowName,Height,Width)

Set Variable $WindowName = GetParameter("WindowName")

Set Variable $Height = GetParameter("Height")

Set Variable $Width = GetParameter("Width")

[... rest of script...]

5. Passing parameters. You may want to create a custom function to properly quote the parameter string.

6. Gotchas:

6A. Beware of missing parameters! These can sometimes evaluate to "?" rather than "". You may want to modify your "GetParameter" script to handle this situation, for example.

6B. Delimiters. If you are using a delimiter (such as comma, the pipe symbol (|) etc, be sure you properly handle the case where such a symbol occurs inside a string.

Hope this was helpful!

Link to comment
Share on other sites

I don't know where you got 2. and 4. They seems unnecessary to me. A simple #comment at the top of the script should be sufficient for documenting what parameters the script expects. For me, I'm either working closely with the script anyway, so I know the parameters, or I have to review how the script works, so I'll see the parameters then.

Link to comment
Share on other sites

I still don't see why I should squeeze multiple variables into a single script parameter, then hurry up and parse them out into individual script variables (in version 8).

I believe I can just as well eliminate the middleman and set those script variables directly from the source?

Link to comment
Share on other sites

There's a few reasons I can think of.

1. The script is called from a button.

2. The script is called from another file.

3. You're trying to stay compatible with FM7 clients.

4. You're just trying to keep the control of the values that get passed exclusively within the script parameters (kind of an object oriented design approach.)

Link to comment
Share on other sites

1.

I believe most scripts are called from a button - and?

Either the button gets its variables from fields, or they are hard-coded.

In the first case, a script can get them from the fields directly.

In the second case, they can be MUCH more conveniently hard-coded in the script itself, e.g.;)

Set Variable [ $VariableA; Choose ( Get (ScriptParameter) ; "valueA1" ; "valueA2" ) ]

Set Variable [ $VariableB; Choose ( Get (ScriptParameter) ; "valueB1" ; "valueB2" ) ]

so the buttons can have simple parameters, like 0, 1, and so on.

2.

Yes - although IMHO it would be more convenient - and less error-prone - to set globals in the target file before calling the script.

3.

In such case, you shouldn't try and set script variables...

4.

Didn't get this one.

Link to comment
Share on other sites

1. Perhaps this is a matter of preference, or perhaps it's because I'm still thinking in terms of FM7 compatability, but I would rather send those hard-coded things as parameters.

2. I would disagree about the convienience and error proneness of script parameters vs. globals. Although either could work, my preference would be to reduce the number of globals and use the script parameters when sending values to subscripts in another file.

3. I never suggested setting script variables from the parameters (I said this seemed unnecessary, remember.)

4. The idea is to treat scripts like a function or a black box, where values are passed in the parameters, then the script does what it needs to from those inputs and returns a result or performs an action. Obviously this doesn't work for all types of scripts. For me, this is mostly a theoretical example, but it does have some practical uses (again, mostly when dealing with FM7 compatability or external subscripts.)

Hope that clarifies.

Link to comment
Share on other sites

I don't think there's much disagreement between us. The title of the topic is "Best way to pass parameters in FM8". The advice given was:

"...your script should immediately assign your parameters to local variables."

I questioned the combination of the two, i.e. concatenating multiple variables into a script parameter, followed by immediate parsing into local variables. As soon as you mention version 7 compatability, my remark gets out of context and becomes irrelevant.

Regarding point 1, imagine say 20 buttons x 4 variables. Now let's say variable 3 has to be changed slightly, for example instead of Date ( x ; y ; z ) it needs to be Date ( x ; y - 1 ; z ), or GetAsNumber ( Date ( x ; y ; z ) ). It takes three steps to get to the definition of script parameter in a single button - need I say more?

Link to comment
Share on other sites

Regarding point 1, imagine say 20 buttons x 4 variables. Now let's say variable 3 has to be changed slightly, for example instead of Date ( x ; y ; z ) it needs to be Date ( x ; y - 1 ; z ), or GetAsNumber ( Date ( x ; y ; z ) ). It takes three steps to get to the definition of script parameter in a single button - need I say more?

A grid of buttons is a good example. I have a Curriculum database that lets one program Activities for 10 Activity periods for each weekday. The overview screen has the grid of Activity Titles with buttons to bring the user to a selection screen where the Activity for the selected day & period can be changed. To do this with a Choose statement would require some 50 (10x5) index numbers to be assigned to the buttons and then entered into the choose statement in the script. This is doable, but a little awkward to remember which index was for which day & period. With a script parameter with two values, I instead directly assign each day & period value to the script parameter. I know exactly which values to put in each parameter from which button definition is opened. The script itself doesn't need any logic to choose the right values, it just need to knw that there's two parameters, "day" and "period".

Now suppose the client wants to add another activity period (this actually happened in my case.) With the index number, you either need to reassign some of the index numbers in the choose and the buttons, then add the new ones, or append new index numbers for the new buttons, making the new numbers out of sequence. With multi-value parameters, the new buttons are added with the new period value and the appropriate day.

As I said, I think this is more a matter of preference.

Link to comment
Share on other sites

First, let me say that I appreciate everyone weighing in. That's why I started this topic -- to stimulate discussion and see if we can come up a "best" way of doing it.

To Comment -- I agree that in the degenerate case of a single-line script with several parameters, using my suggestions is a pain in the ass and may hurt, rather than help, readability.

Passing parameters by Globals (fields or variables) in any other contexts is bad OO design. (google on "function call Side effects") However, since FM8 just barely handles OO design, I think it's not immediately clear what the right solution is.

However, I find that scripts rarely stay simple, and a one-line script which relies on globals now, may become a 10 or 50 line script someday. If you do my suggestions (putting all passed variables into the script parameter, then immediately assigning them to local variables in the script), I argue that you will find it much easier to understand and debug somewhere down the road.

It's too bad that the "Perform Script" command doesn't automatically support multiple passed parameters. Imagine how much clearer things would be if, when you choose "perform script", a dialog box popped up showing that this particular scripot has 4 parameters, and they are named "WindowName" "Layout" "Height" and "Width".

It's also too bad that FM8's Data Viewer doesn't have a feature to automatically show all local variables. This would make script debugging much easier, and bring FM more into line with other modern 4GL.

Link to comment
Share on other sites

If you do my suggestions (putting all passed variables into the script parameter, then immediately assigning them to local variables in the script), I argue that you will find it much easier to understand and debug somewhere down the road.

We are going around in circles. Where are your variables coming from? What is the advantage of cramming them together in a single script parameter, then immediately parsing them out, against assigning them to local variables directly?

Link to comment
Share on other sites

We are going around in circles. Where are your variables coming from? What is the advantage of cramming them together in a single script parameter, then immediately parsing them out, against assigning them to local variables directly?

That would be silly, I agree. I'm talking about non-trival cases, in which you have a complex action that will need to be called from more than one context. In that case, there is a significant advantage in making your code generic, and re-usable.

Your way of doing things results in scripts that only work when (A) called from a particular button (;) on a particular layout © that has a particular base table (D) with the right record selected.

The method I'm describing (in which you pass the relevant data as parameters) can be called from anywhere in the solution, with any record showing.

Perhaps a real-life example will make this more clear.

I have a script which is called "Log". This script creates a new record in the "Log" table which contains some useful debugging or audting info. As such, I find myself calling this Log script from nearly every layout and script in my system. It has 4 parameters (Kind, Message, Severity, Username).

Under FM7/8, to add an entry to this log file, I just call the Log script with the four parameters.

This is not the only way to do it -- Other methods would be (A) set up 4 global variables, set the variables and then call the Log script (this is how I did it in FM5/6). (: Have a relationship between the table you are on and the logging table, and use SetField (with an appropriate timestamp key) to create the record.

(A) requires 5 script steps to operate (vs. one). (: requires only 4, but requires you to add this Log table to every other TO in your solution.

In summary, I'll claim that in the non-trivial cases, it is by far the best practice to write a generic script which can accept multiple parameters. If you accept that argument, then the question becomes, what is the best/cleanest/most elegant way to do this.

Link to comment
Share on other sites

Your way of doing things results in scripts that only work when (A) called from a particular button (;) on a particular layout © that has a particular base table (D) with the right record selected.
Sounds like the best idea to me!!! That is PRECISELY what I want to control. Oh, all except for 'the right record.' Who cares about that? :giggle:

Sorry everyone ... sometimes things just strike me as SillyDegree10 and I can't help myself ... I mean, who would care about controlling B, C or D when a script fires?? I wasn't making fun ... I just got home and read this sentence and almost dropped my shorts! I realize I 'sort of' took it out of context but I couldn't stop myself ... :wink2:

I would daresay that the example you cited (Log file) would be the exception here (is this the non-trivial you mentioned?). In normalized business databases, B, C and D would need to be critically controlled and specific.

LaRetta

Link to comment
Share on other sites

Sounds like the best idea to me!!! That is PRECISELY what I want to control. Oh, all except for 'the right record.' Who cares about that? :giggle:

Part of FileMaker's greatness is that it integrates Form & Function, which makes it very easy for beginners to learn and get going. This is also one of its major weaknesses, as it leads to (assuming you are being serious) comments like yours, which seem to imply that having an atomic operation that is wed to and inseparable from the user interface which operates it is a good thing. Most professional OO programmers / theorists would probably disagree with you, and say that it's actually a Bad Thing.

I would daresay that the example you cited (Log file) would be the exception here (is this the non-trivial you mentioned?). In normalized business databases, B, C and D would need to be critically controlled and specific.

LaRetta

It's easy to come up with other examples. Consider a room reservation solution in which the act of "reserving a room" is an atom. Now, one could design a UI that has a bunch of user interface fields on it, with a final one which says "Reserve this room!". Clicking the button then goes to a script which does all the work (perhaps it fills in some join tables, sends a confirmation email, whatever). The script assumes that's being called from the context of a particular layout (such that any fields or global fields values are correct). It's quick & easy to write. No problem...yet.

Now, imagine your boss comes up one day and says "Hey, we now are getting email reservations from a third party website, and we need to process them automatically. I checked and there's no way to have them use our database, we must use the emails. You already wrote the "reserve a room" script, so it should be easy, right?

You start to think about this. Hmm. Uh oh, we have a problem.. Since you wrote the "reserve a room" script with a User Interface context in mind, there is no way to "call" it with a different set of parameters.

So, what you'll probably end up doing is writing a "Reserve a room by parsing an email" script. Now, you have 2 scripts, both of which at the core level do the same thing, but both of which are wed to a particular user interface. Had you written the original "reserve a room" script with parameters, you could reuse it.

This is just one example, I could think of a dozen more.

The principles here are (A) generalizability, (;) encapsulation / data hiding © reusability and (D) abstraction.

Have you read much OO theory?

Edited by Guest
typo it's for its
Link to comment
Share on other sites

I don't think the amount of script steps is the most important consideration, but since you are setting 4 variables anyway, you can set them conditionally based on the current layout or whatever, so it comes out the same in script steps - and no script parameter is required.

I agree with LaRetta that your example is rather esoteric. I agree with you (as I did with Ender) that everything depends.

Link to comment
Share on other sites

I follow you xochi. I have several situations like your Log and Room Reservation, where the same action could be called from more than one file. For these cases it makes sense to me to use multi-value parameters.

But unlike your 'variable setting' approach, I don't see the need. Since the parameters already have the values, they can be referenced as needed.

As for the general applicability of object oriented design in FileMaker, I just don't think it's practical for most situations, since scripts are usually working with various fields in any number of records. But I do feel it works for external script calls and modular operations, like your Log creation.

Link to comment
Share on other sites

Good points, all.

I'll say this -- after years with FM5 and 6, and even 7, one gets used to a "certain way of doing things", some good some bad. I'm still trying to figure out what's possible in FM8, and what's not only possible but actually practical and useful.

Re: Variables. I suppose my point is that once you've passed your variables into a script all squished together in the script parameter, it's nice to be able to refer to them by name. So, while you could do GetScriptParameter(...) whenever you need to refer to that parameter, it seems like in the long run a better idea to have them all assigned to local variables.

Now, to be fair, I'm at the tail end of redoing in FM8, a solution that was built in a hurry for Y2k in FM5 by someone who just barely knew what they were doing. It's been a trying experience.

So if I sound hyper paranoid about having crystal clear documentation etc., that's probably why. ;)

Link to comment
Share on other sites

"Have you read much OO theory?"

Actually I have and I see no contradiction in my statement whatsoever. I've been working with Users and databases for 38 years. You are implying that, since I disagree with that paragraph, I must be incapable of fluid, intuitive design as opposed to your option; which you admit is "... enticing, but I'm finding the use a bit ugly and error-prone." I'm not on opposite sides from you here (on passing parameters) and I've actually used some of the formulae discussed (before realizing it's overkill in most instances). But that PARAGRAPH proposed that script point-of-view and context makes no difference. Any Developer that doesn't care about the layout (or table occurrence or record) upon which a script fires should take up another profession. :wink2:

Aside from the poor paragraph, I've found this thread quite interesting. But, as you've admitted in [spoilerq:1] your Gotchas above[/spoilerq][spoilera:1]

6A. Beware of missing parameters! These can sometimes evaluate to "?" rather than "". You may want to modify your "GetParameter" script to handle this situation, for example. 6B. Delimiters. If you are using a delimiter (such as comma, the pipe symbol (|) etc, be sure you properly handle the case where such a symbol occurs inside a string.
[/spoilera] and other potential unidentified gotchas, combining then splitting carries greater risk. So unless it's really necessary why put it together only to take it apart when you can access the information directly?

It's your "abstraction" vs. safety and common sense. Yep, I'm an atomic Developer and the businesses I design for are glad also. And I remain unconvinced from your examples but that's okay ... I've gained a lot from the discussion anyway. And, best of all, now I know who to contact if I want to know what all other OO programmers and theorists think. :shocked:

LaRetta

Link to comment
Share on other sites

"But that PARAGRAPH proposed that script point-of-view and context makes no difference. Any Developer that doesn't care about the layout (or table occurrence or record) upon which a script fires should take up another profession. :wink2:

[..]

LaRetta

;) Heh. Methinks that what I implied, and what you inferred, were not the same? This couuld explain why you thought it was funny, and I was also amused by your reaction. I think we are mainly in agreement here...

In any case, I never meant to say that context is not important, rather I meant to say that writing scripts that are too dependent on context (layout, table, globals) can be a PITA to debug and maintain dow the road, etc, especially if what they are doing is more of a transaction-type operation.

One might be well served to separate the UI scripting (collecting & validating user input) from the scripts that do all the table & field updating and assorted mucky-muck (that's my new term of the day).

Link to comment
Share on other sites

  • 3 weeks later...

From the FM Pro mailing list, another suggestion (thanks to Steve Harley!). This one has some really useful features, such as auto-assigning to local variables, and better error checking.

in your calls, use local variable names ("$" prefix) within

all your parameter strings

define a custom function with no arguments:

get_params ()

Evaluate ("let ([" & Get(ScriptParameter) & "]; 1)")

this converts all the parameters to local variables and

returns 1 as an indicator of success (useful if you Set

Error Capture [On], and there is an error during parameter

evaluation)

in the called script, simply invoke the custom function via

a "stub" if:

Set Variable [$got_params; get_params()]

now all your parameters are available as local variables;

if you want a parameter to have a default value when it isn't

supplied, just set it as a local variable prior to the call

to get_params()

Link to comment
Share on other sites

  • 2 weeks later...

This topic is 6687 days old. Please don't post here. Open a new topic instead.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.