This is a strange article, this one. It started off just being a feature request which I was typing into the Adobe bug tracker, but decided to write my thoughts up here, by way of soliciting other people's thoughts too. On re-read just before pressing "publish", I realise the framing for the point is about five times longer than the point itself. Oops. Oh well. It's a slow news day here. It's Monday after all.
ColdFusion has a function getMetadata(), which can be called on a variety of objects: an CFC instance, a user-defined function, a property, a query. I'm just focusing on on its usage with "objects".
Here's an example of getMetadata() being called on an object:
// Bloggable.cfc
interface {
public void function g(required numeric x);
}
// Parent.cfc
/**
@hint Parent component
*/
component {
/**
* @hint Method defined in Parent
*/
public void function g(required numeric x){
return;
}
}
// Sub.cfc
/**
@hint Demonstrating getMetadata() results
*/
component extends="Parent" implements="Bloggable" {
/**
* @hint Method defined in Sub
* @x A number
*/
public void function f(required numeric x){
return;
}
}
<!--- test.cfm --->
<cfdump var="#getMetadata(new Sub())#">
Output:
struct | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
EXTENDS |
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
FULLNAME | cf.cfml.functions.system.getMetadata.blog.Sub | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
FUNCTIONS |
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
HINT | Demonstrating getMetadata() results | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
IMPLEMENTS |
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
NAME | cf.cfml.functions.system.getMetadata.blog.Sub | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
PATH | D:\websites\www.scribble.local\cf\cfml\functions\system\getMetadata\blog\Sub.cfc | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
TYPE | component |
Lovely: everything we'd want to know about that object.
Despite being called on an object, getMetadata() returns the metadata for the CFC, not the actual object. Changes made to the object after it's been instantiated are not reflected in the returned metadata, eg:
<!--- test.cfm --->
<cfscript>
o = new Sub();
function h(){
writeOutput("Hi!");
}
o.h = h; // stick it in the object
o.h(); // prove it works
writeDump(getMetadata(o)); // but where is it?
</cfscript>
Output:
Hi!
struct | |||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
EXTENDS | |||||||||||||||||||||||||||||||
FULLNAME | cf.cfml.functions.system.getMetadata.blog.Sub | ||||||||||||||||||||||||||||||
FUNCTIONS |
| ||||||||||||||||||||||||||||||
HINT | Demonstrating getMetadata() results | ||||||||||||||||||||||||||||||
IMPLEMENTS | |||||||||||||||||||||||||||||||
NAME | cf.cfml.functions.system.getMetadata.blog.Sub | ||||||||||||||||||||||||||||||
PATH | D:\websites\www.scribble.local\cf\cfml\functions\system\getMetadata\blog\Sub.cfc | ||||||||||||||||||||||||||||||
TYPE | component |
(I've collapsed a bit of it so as to minimise repetitive clutter).
This - to me - is a bug: we've got a function getComponentMetadata(), and that should be getting the metadata for a component. getMetadata(), when having an object passed to it, should get metadata on that object. Not on the object's underlying component. Anyway, I've already dwelt on that and I have nothing new to add.
What would work around this bug would be to have a getObjectMetadata(), which simply does what it claims it does: gets the metadata of the actual object passed to it, rather than the CFC it was an instance of.
That in itself is not so interesting, but I'd also envisage the metadata to be writable. For example, if I push another function into the metadata, this is reflected in the object. We can already push functions into objects - as demonstrated above - but there's other stuff we can't do. How about pushing functions into the object so that it fulfills an interface contract, and then also setting the object's implements metadata to tell it it now implements that interface; so it can then be used in situations in which we need that interface. Or - and god knows the ramifications of this - changing the extends metadata of the object so it extends some different parent component?
TBH, the most useful thing would be for it to return the metadata of the actual object, not the component the object is an instance of. But I've had situations in which being able to make an existing object fulfill an interface contract would have been useful. But then again I bring this upon myself because I use interfaces. Maybe I just shouldn't.
Anyway, any thoughts on this sort of thing? I've raised it as E/R 3594771.
Update:
Ooh. I forgot... what does Railo do about all this? Well Railo does actually include some metadata for the injected function, h():
functions |
|
Although it's clearly not being considered in quite the same way that the compile-time-present functions are. The metadata here is a bit sparse (and not correct, either). For the purposes of this example, I changed h() to be:
public void function h() hint="Hi!"{
writeOutput("Hi!");
}
So in that dump, Railo is omitting the hint, and reporting the returntype incorrectly. If I dump output on the function itself, rather than the object, I get a more pleasing (if not aesthetically) result:
Struct | |||
access |
| ||
closure |
| ||
description |
| ||
hint |
| ||
name |
| ||
output |
| ||
owner |
| ||
parameters |
| ||
returnFormat |
| ||
returntype |
|
So this is something. Good ole Railo.
--
Adam