The generic bean is very tightly tied to our Clojure data mapper and CFML / Clojure interop library but here's onMissingMethod() which automatically handles getFoo() and getBarIterator() (for a lookup in the foo table via this bean's fooId, and a lookup in the bar table for this bean's id as a foreign key) - it may not make any sense out of context but it might give you some ideas. It also supports getRelated("foo") and getIterator("bar") so that case sensitive table names can be used explicitly if the lowercase default doesn't work. I'll answer the other questions in a separate email.
public any function onMissingMethod( string missingMethodName, struct missingMethodArgs ) {
var stem = left( missingMethodName, 3 );
if ( stem == 'get' ) {
var root = right( missingMethodName, len( missingMethodName ) - 3 );
var n = len( root );
var itPost = "iterator";
var itLen = len( itPost );
if ( n >= itLen && right( root, itLen ) == itPost ) {
var startIndex = 1;
if ( n == itLen ) {
// getIterator("foo") or getIterator("foo",{ col = "asc|desc"}*)
if ( structKeyExists( missingMethodArgs, 1 ) ) {
var join = missingMethodArgs[ 1 ];
root = join & "iterator";
startIndex = 2;
} else {
throw "getIterator() requires the table name as an argument";
}
} else {
// getFooIterator() or getFooIterator({ col = "asc|desc"}*)
var join = lCase( left( root, n - itLen ) );
}
var orderInfo = { ordering = [] };
var key = join & "iterator";
if ( structKeyExists( missingMethodArgs, startIndex ) ) {
orderInfo = variables._collectOrderBy( missingMethodArgs, startIndex );
var key &= "|" & orderInfo.orderKey;
}
// #3607: we can cache the _data_ but must create the
// iterator afresh on every "get" operation:
if ( !structKeyExists( variables.relatedCache, key ) ) {
var fk = { };
variables.relatedCache[ key ] = variables.orm.findByKeys( join, fk, "query", orderInfo.ordering );
}
return variables.orm.createIterator( join, variables.relatedCache[ key ] );
}
var join = root;
var relatedConvention = true;
if ( root == "related" ) {
if ( structKeyExists( missingMethodArgs, 1 ) ) {
// override default all lowercase name
join = missingMethodArgs[ 1 ];
relatedConvention = false;
}
}
if ( !this._has( join ) ) {
// not a known property, check for possible related object
if ( this._has( join & "id" ) ) {
var key = join & "related";
if ( !structKeyExists( variables.relatedCache,key ) ) {
var relatedId = this._get( join & "id" );
if ( relatedConvention ) {
join = lCase( root );
}
variables.relatedCache[ key ] = variables.orm.create( join ).load( relatedId );
}
return variables.relatedCache[ key ];
}
}
return this._get( join );
} else if ( stem == 'has' ) {
var root = right( missingMethodName, len( missingMethodName ) - 3 );
return this._has( root );
} else if ( stem == 'set' ) {
var root = right( missingMethodName, len( missingMethodName ) - 3 );
if ( structKeyExists( missingMethodArgs, 1 ) ) {
return this._set( root, missingMethodArgs[ 1 ] );
} else {
throw "#missingMethodName#() called without an argument";
}
} else if ( stem == "nil" ) {
var root = right( missingMethodName, len( missingMethodName ) - 3 );
return this.nil( root );
} else {
var message = "no such method (" & missingMethodName & ") in " & getMetadata(this).name & "; [" & structKeyList(this) & "]";
throw "#message#";