(and functions with arguments can take more than the defined amount)
This may be well known, but I haven't seen a lot (or anything) on it. Of course, as always,
I may just be missing something.
In any case, the other day as I was looking over some old code,
I (re)discovered this marvelous fact. Now, you may be wondering why on Earth I'd want
to use arguments in a function where none were defined. But, I have at least one case
where I think it's valuable: suppose you are following the
Active Record Pattern, or really
writing any
ORM. Basically, you want to
abstract the query process. Now, that's certainly a noble goal. But, what happens when
you want to provide a filter? For instance, you might have a function
find_by_id()
,
a which finds a record based on the
id
you pass in. That's easy enough.
You might even provide methods to
find_by_other_columns_even_in_combinations()
.
But surely you can't provide every combination! That's where you'd want to
provide a
filter
argument. Here it comes again: another but! You don't want
to simply provide
filter="where column1=#form.column1#"
- you want to parameterize
the query for the developer. Now, one way to do that would be to parse the
filter argument and reconstruct it using your
cfqueryparam
s in all the right
places. But, an easier way would be to something like this:
find_records("where age=? and (name=? or name=?)", 9, "blockhead", "charlie brown")
Note: I would probably move the "where" into the abstraction, and just let the programmer provide the clause,
but for illustration purposes, I left it in there.
For illustration purposes, my function only does this:
<cfcomponent>
<cffunction name="find_records"
output="true"
>
<cfargument name="filter"
>
<cfloop collection="#arguments#"
item="key"
>
#key# = #arguments[key]#<br/>
</cfloop>
</cffunction>
</cfcomponent>
But it would be easy enough to modify it to do something useful. Right now, if you run it, you see
all the arguments are available in the
arguments
structure. The first one comes under the key
"filter", and the rest come under numeric keys, in the order they were passed (of course, looping over it won't
show you that order, but the number of the key represents the position in which it was passed. Now, you can
parameterize the arguments, first checking more restrictive and moving to less restrictive parameters
(for instance, you'd want to check
isDate()
,
isNumeric()
, and fall back on string).
You could name the arguments too, but I haven't found that useful for this example, since they will have their
names as keys in the
arguments
structure, and I know of no good way to figure out the order
in which they were passed.
Finally, I haven't yet implemented this in cfrails, so I don't know how feasible basing parameterization on
the
CF type is, but I'll try to remember to post it when
I do implement it. In any case, I'd be willing to bet there are other uses for this - it's just
that this is the most important one on my mind recently.
Update: It did occur to me as I was writing this post that you could accomplish the same thing by using an array as a
defined argument, with the added benefit of keeping your interface well defined. In all honesty, I would prefer to do it
that way if you could define an array inline like:
[9, "blockhead", "charlie brown"]
. But the way you define arrays in
Coldfusion really makes for too much code in what I'd like to accomplish with this, so in this case I'd provide a hint in the
function to let a developer know it expects you to pass those arguments. This way, I get cleaner syntax and a decent solution to the
interface problem.
Hey! Why don't you make your life easier and subscribe to the full post
or short blurb RSS feed? I'm so confident you'll love my smelly pasta plate
wisdom that I'm offering a no-strings-attached, lifetime money back guarantee!
Leave a comment
Nice post. The one "argumentless" function I like to throw in a lot when developing is:
<cffunction name="Debug">
<cfdump var="#ARGUMENTS#" />
<cfabort />
</cffunction>
I have never tried mixing named and unnamed arguments, but from you have shown, it works. Sweeeeet!
Posted by
Ben Nadel
on Jan 15, 2007 at 01:53 PM UTC - 6 hrs
Ooops, that didnt come through:
[cffunction name="Debug"]
[cfdump var="#ARGUMENTS#" /]
[cfabort /]
[/cffunction]
Posted by
Ben Nadel
on Jan 15, 2007 at 01:54 PM UTC - 6 hrs
Quite alright. =) I'll need to fix that... hadn't really thought about code being nixed. I'm sure this thing might be buggy... so forgive me if you find any (and let me know!).
Also, I didn't mean to imply you could mix named and unnamed arguments... well, at least not calling it that. You can use defined arguments and undefined arguments together, but of course if you name them when calling the function, all of them must be named, or else CF throws the "you can't mix named and unnamed arguments" exception.
Posted by
Sam
on Jan 15, 2007 at 02:07 PM UTC - 6 hrs
Sam, sorry about misunderstanding part of the post. I am reading at about a mile a minute (have crazy deadlines today).
Posted by
Ben Nadel
on Jan 15, 2007 at 02:29 PM UTC - 6 hrs
Two of the more used UDFs I use in CF loop over the arguments scope. I have an arrayCreate() and structCreate() UDF that I use to emulate the [] and {} shortcut syntax in Javascript.
I find it much more convient to do:
arrayCreate("one", "two", "three");
This allows me to pass an on-the-fly array to a function as well.
The most common use of this functions is like:
arrayCreate(
structCreate(id:1, label:"one"),
structCreate(id:2, label:"two"),
structCreate(id:3, label:"three")
);
This would leave me with a 3 row array, each a struct w/an "id" and "label" key.
Posted by
Dan G. Switzer, II
on Jan 15, 2007 at 03:54 PM UTC - 6 hrs
Dan, I love it! I can't tell you how useful those would be.
Posted by
Ben Nadel
on Jan 15, 2007 at 04:43 PM UTC - 6 hrs
Ben - no worries at all!
Dan - Thanks for that idea. I might have to alias those functions to something shorter to achieve the readability/clarity I desire, but that is a most excellent idea. The ability to define arrays and structs inline are two of the top 3 things I'd like to see in Coldfusion. The other is a methodMissing or basically a function that gets called in case of an error.
Peter- Cool. I'll have to check that out. And clearly I need to add something to convert URLs to links and shorten them... if that address was any longer, the page would be broken =).
Posted by
Sam
on Jan 16, 2007 at 02:04 PM UTC - 6 hrs
"a most excellent idea"
Is that something Bill and Ted should be saying, and not me?
Posted by
Sam
on Jan 16, 2007 at 02:05 PM UTC - 6 hrs
Leave a comment