So I ran into an odd issue with Model-Glue a few weeks ago and forgot to blog about it at the time. Luckily the issue cropped up today in training. First, let me ask you what you think the following code would do in your controller:
var theval = arguments.event.getValue("dharma");
var thevalagain = arguments.event.getValue("dharma","blackrock");
Assuming dharma did not exist, I thought the result would be:
(empty string)
blackrock
By default the getValue method of the event object returns an empty string if the value didn't exist. If you specify a value to use for the default it will return that instead. So in simpler terms, the code block above should work like so:
Ray: Model-Glue, grab me the value for dharma.Model-Glue: It doesn't exist, Ray, old buddy, so I'll give you a blank string.
Ray: Model-Glue, I know I just asked for dharma, but would you mind getting it again, and if it doesn't exist, return 'blackrock'. Thanks my good man, I appreciate it it.
Model-Glue: Well, Ray, it still doesn't exist, so I'll return 'blackrock' to you.
Ray: Thanks. We really need to do lunch some time. Say hi to Marge and the kids.
Unfortunately that isn't what happens. Instead, Model-Glue will set the default value to the event if it doesn't exist. So I get this back instead:
(empty string)
(empty string)
Why? Because behind the scenes, this is what really is going on:
Ray: Model-Glue, grab me the value for dharma.Model-Glue: It doesn't exist, Ray, old buddy, so I'll give you a blank string since you didn't specify a default. I'm also going to add dharma to the event object with that default. I do this because of my unending respect and love for my developers.
Ray: Model-Glue, I know I just asked for dharma, but would you mind getting it again, and if it doesn't exist, return 'blackrock'. Thanks my good man, I appreciate it it.
Model-Glue: Well, Ray, it does actually exist now. You asked for it before and since I didn't have it, I created a default value for it of an empty string.
Ray: Thanks. We really need to do lunch some time. Say hi to Marge and the kids.
So I don't know about you - but that was totally unexpected. To me, a "get" shouldn't set anything. It should just fetch. However, if you look at the docs...
Default (optional) - If the value does not exist, a default value to set into the viewstate and then return
Ok, so it's documented. But did you expect it to work that way? But wait - it gets better. Do the same code with the viewState object in a view and it works as I expected it!
<cfset x = viewState.getValue("dharma")>
<cfset x2 = viewState.getValue("dharma","blackrock")>
But the docs for the viewState.getValue match what you see for the Event object. Bug!
Archived Comments
Ray, there's a reason for this.
Model-Glue creates in instance of ModelGlue.util.GenericCollection which is what it passes around and eventually becomes the ViewState object against which you call ViewState.getValue("foo"). Within the controller, however, the instance of the GenericCollection is wrapped with an instance of ModelGlue.unity.eventrequest.EventContext which is what you actually call event.getValue("foo) against within the controller. Internally it calls viewState.getValue(argument.name,argument.default) and, since the method gives a default of "", the value is always created if you call getValue() in the controller.
However in a view you're calling getValue() directly against the raw GenericCollection and, therefore, it only sets a default if you ask it to by calling ViewState.getValue("foo","bar")... and no, this isn't necessarily obvious. And, yes, I think we have, at least, a documentation bug. Personally it's neverr caused me any issues, but that's probably because I do all my defaulting in the controller and generally make sure my data is all there in the View (or I call it with a default at the top of the page, effectively using it like a cfparam call).
I'll talk to Joe and see if we can't shore up this behavior a bit... it shouldn't be too hard to make it work one way or the other.
Thanks!
Jared
You might should make the function parameter default something like "[[default]]" so you'll know if a true "default" value has been passed in or not. If the value is "[[default]]" you know the user hasn't specified a real default value yet, and the function can just return an empty string instead.
I'm more inclined to make the ViewState's behavior fall in line with the EventContext's behavior than to break the current behavior of the EventContext. The EventContext is doing what it's supposed to be doing... it's the ViewState that's not behaving in line with the documentation and expected behavior.
In general that would seem to me to be the course least likely to break existing applications. Either way, this could be a situation where "beta software" really means "beta software" and some stuff gets broken because it exploited a bug in the framework.
One other option is to throw an exception of type "MissingDefaultInput" where the EventContext throws an error if a getValue() method call happens *without* a default passed in (but only if the value in question doesn't exist). The problem with this is that it introduces all sorts of potential conflicts.
I ran into this bug is v1 of Model-Glue as well.
@Michael, the *correct* way to handle this is to *not* specify default= but instead simply specify required="false" and then you test structKeyExists(arguments,"argName") instead of testing the actual value against the "magical" default value.
It is extremely bad practice - albeit very common, unfortunately - to specify a default value when what you really want to do is test whether an argument was actually passed in.
I posted a quick solution to the problem since all the original article did was point out a bug in someone else's code... without proposing a solution in turn. But thanks for illustrating the "correct" method.
@Michael: To be clear, the bug here is a doc bug for the viewstate.getvalue method. The MAIN reason for the article was to point out something that could be confusing to users - as it got me AND the client I was training. I could follow up this post with a "btw, here is how you check for the existence"
@Sean - I'm not sure it is fair to say it is _extremely_ bad practice, although maybe I'm being defensive. ;) I DO stand by my belief that this is both unexpected and odd that a get would set anything, but isn't the end of the world.