I'm calling a web service through ColdFusion which returns an object, and I want to verify if one of the methods of this object exists as it won't always exist.
I found this source which seemed promising however based on my tests I can see the results are always negative and the method is never found when it's clearly there.
<cfif structKeyExists("#Result.getNotifications().getValidationResult(0)#","getField")>
Result is my underlying object, and my end goal is to verify if the method getField() exists.
Is there a clean way to do this as opposed to a try/catch?
Update:
Unfortunately, I am not sure IsInstanceOf() works with web services, due to the fact that CF uses a Proxy object to "wrap" the underlying web service class. If not, another simple option is to check class name. That avoids the ambiguity of checking for method name only (which could potentially exist in many different classes). Plus I suspect it may be more light-weight than IsInstanceOf() anyway.
<cfif compare(yourObject.getClass().name, "org.tempuri.ValidationResultField") eq 0>
Found ValidationResultField. do something
</cfif>
It looks like the dump contains several different types of objects/classes: ArrayOfValidationResult, ValidationResultField, etecetera. It sounds like what you are really trying to determine is which of those classes you are working with, so you know exactly what fields and methods will be available, per the web service definitions. Given that, I think IsInstanceOf() would be a more appropriate test, than checking for method names. More accurate as well. Nothing prevents two different classes from having the same method name. So even if method X or Y exists, there is still a possibility it may be a different class than expected.
<cfif IsInstanceOf(yourObject, "org.tempuri.ValidationResultField")>
do something
</cfif>
As far as I know, the mentioned structKeyExists approach only works if CF wraps the class internally, e.g. all instances of cfcomponent.
The only option left is to actually reflect the class:
<cftry>
<cfset Result.getNotifications().getValidationResult(0).getClass().getMethod("getField", javaCast("null", ""))>
<!--- method does exist --->
<cfcatch type="coldfusion.runtime.CfJspPage$UnsupportedBaseTypeException">
<!--- method does not exist --->
</cfcatch>
</cftry>
If the method doesn't exist, it throws UnsupportedBaseTypeException, which seems to be a follow-up of NoSuchMethodException.
Honestly, you might as well just invoke the method and catch it. Reflection comes with an additional overhead and you have to catch it anyway.
Like Miguel-F, I think this is something for getMetadata(). The following should return an array containing the respective functions of the object:
<cfset funcs = getmetadata(nameOfObj).functions>
The names of the functions are then funcs[1].name, funcs[2].name, and so on.
In general, you may obtain the metadata of all the functions of a webservice, given the URL of the WSDL, with something like
<cfhttp method="get" url="http://www.webservicex.net/globalweather.asmx?WSDL" result="res">
<cfset wsXml=xmlparse(res.filecontent)>
<cfset wsOperations = xmlsearch(wsXml,"//wsdl:operation")>
<cfdump var="#wsOperations#">
Another method you could look at (perhaps undocumented) is to get the method names from the class names in the stubs directory.
The code to run is:
<cfscript>
wsargs = structnew();
wsargs.savejava="yes";
</cfscript>
<cfset convert=createobject("webservice","url_of_wsdl",wsargs)>
Then figure out how to fish out the names from the stubs directory, {CF_INSTALL}/stubs. In my case, CF_INSTALL is C:/ColdFusion2016/cfusion/
Related
Sorry about the question phrase. I couldn't find better way to describe it. But my problem is as follows:
I have 3 cfc's namely settings.cfc, prices.cfc and helpers.cfc. These cfc's extend 4th cfc controller.cfc.
The helper.cfc is as follows:
<cfcomponent extends="Controller">
<cffunction name="formatCurrency">
<cfset formattedCurrency = 1 />
<cfreturn formattedCurrency>
</cffunction>
<cffunction name="processTemplateVariables">
<cfargument name="templateText" default="defaultText" >
<cfset formatCurrency() />
<cfreturn formattedCurrency >
</cffunction>
</cfcomponent>
The settings.cfc has a setApplicationVariables method which we use to set the application level variables. In this cfc, i have created an object of helpers.cfc and put that object into the application scope. The settings.cfc is as follows:
<cfcomponent extends="Controller">
<cffunction name="setApplicationVariables">
<cfset application.helpers = createObject("component","controllers.Helpers") />
</cffunction>
</cfcomponent>
The settings.cfc gets invoked on application start which in turn creates a object of helpers.cfc and put it into the application scope.
We create a reference to the method ProcessTemplateVariables in the controller.cfc as follows:
<cfcomponent extends="Wheels">
<cfset getFormattedCurrency = application.helpers.processTemplateVariables >
</cfcomponent>
In the prices.cfc, we use this reference to call the function processTemplateVariables, which it does. But it does not call the function formatCurrency that is called inside from the processTemplateVariables and it throws error "variable formatCurrency is undefined".
But if i use the application.helpers.processTemplateVariables(templateText="someText"), it works.
It also works, when i use cfinvoke as bellow:
<cfinvoke method="processTemplateVariables" component="controllers.helpers" templateText="someText" returnvariable="content">
The prices.cfc is as follows:
<cfcomponent extends="Controller">
<cffunction name="index">
<!--- does not work, throws 'the formatCurrency() variable is undefined' --->
<cfdump var="#getFormattedCurrency("someText")#"><cfabort>
<!--- works --->
<cfinvoke method="processTemplateVariables" component="controllers.helpers" templateText="someText" returnvariable="content">
<!--- works --->
<cfset application.helpers.processTemplateVariables("someText") />
</cffunction>
</cfcomponent>
I am not sure why using reference is not working.
Sorry about the earlier confusion but your comments made me dig deeper and i could found out that it was reference that was culprit. Is there any way to make this work with reference, that would be cool?
Update:
This blog entry (by Adam Cameron) has a better description. To summarize:
.. it pulls the method out of the CFC, so it will be running in the
context of the calling code, not the CFC instance. Depending on the
code in the method, this might or might not matter.
In your specific case it does matter. The function has a dependency on formatCurrency, which does not exist in the "context" of the calling page, and that is why you get an "undefined" error.
(From comments)
Yeah, I am pretty sure you cannot do that. Each function is compiled into an individual class: specifically a static inner class. (You can see the inner class names if you dump out the function name without parenthesis ie #application.helpers.formatCurrency#) In other words, it is disconnected from any specific instance - and by extension - the other functions.
When you create an instance of the component, all of the functions are stored its variables scope. So when you invoke "processTemplateVariables" - from within the instance - it has access to the other functions via the component's variables scope. When your code creates a reference to that function, what you are actually getting is completely disconnected from the parent instance ie application.helpers. So it won't have access to any of the other functions. Hence why you get an "undefined" error.
The limitation is understood well from the answer by Leigh.
To focus on your requirement here: using a short name alias for the functions, You can still use your original code, with a little trick of adding all the dependent functions as a reference, so that they are available inside the controller.cfc scope similar to the getFormattedCurrency.
Edit 1:
<cfcomponent extends="Wheels">
<cfset getFormattedCurrency = application.helpers.processTemplateVariables />
<cfset formatCurrency = application.helpers.formatCurrency />
</cfcomponent>
Now, for the fun part, it's quite possible to access a function from another cfc and still preserve all the dependencies. I was surprised as well when I recalled an amazing post by Bennadel here. This is simply amazing. But to warn you this practice is discouraged. So I took up with your setup and went ahead anyway. And it all worked like a charm with no issues encountered so far(but I'm quite sure there could arise some complications).
The point of problem with original usage was that the function has a dependency on formatCurrency, which does not exist in the "context" of the calling page as educated by Leigh in his answer.
So what if you can just copy scoped objects or even the functions from another component to you controller.cfc, sounds weird and amazing at the same time but it's possible using the <cfinclude> tag inside the controller.cfc (note: not encouraged) based upon the idea what Bennadel used for his example.
Edit2:
Your controller.cfc should look something like this:
<cfcomponent extends="Wheels">
<!--- Placed inside the controller's scope itself, outside every other function --->
<cfinclude template="helpers.cfc" />
....<rest of your code>....
...........
</cfcomponent>
Note that you don't even need to create a short-hand alias for the functions now. All the components and views can directly use the function name as you intended.
No need to point out that there would be chances of Naming collision if any other component extending the Controller.cfc has a function with the same name as any of the function inside the component library that is being imported. But those can be solved by following more specialization for the functions into multiple components OR as simple as using a prefix as a part of the coding standard to the function names to avoid any such future scenarios.
I have a “best-practices” question in regards to the correct way to instance CFCs that all need to talk to each other in a given project.
Let’s say for example you have a web application that has a bunch of different modules in it:
Online Calendar
Online Store
Blog
File Manager (uploading/downloading/processing files)
User Accounts
Each of these modules is nicely organized so that the functions that pertain to each module are contained within separate CFC files:
Calendar.cfc
Store.cfc
Blog.cfc
Files.cfc
Users.cfc
Each CFC contains functions appropriate for that particular module. For example, the Users.cfc contains functions pertaining to logging users on/off, updating account info etc…
Sometimes a CFC might need to reference a function in another CFC, for example, if the store (Store.cfc) needs to get information from a customer (Users.cfc). However, I'm not sure of the correct way to accomplish this. There are a couple ways that I've been playing with to allow my CFC's to reference each other:
Method 1: Within a CFC, instance the other CFC’s that you’re going to need:
<!--- Store.cfc --->
<cfcomponent>
<!--- instance all the CFC’s we will need here --->
<cfset usersCFC = CreateObject("component","users") />
<cfset filesCFC = CreateObject("component","files") />
<cffunction name="storeAction">
<cfset var customerInfo = usersCFC.getUser(1) />
This approach seems to work most of the time unless some of the instanced CFC’s also instance the CFC’s that instance them. For example: If Users.cfc instances Files.cfc and Files.cfc also instances Users.cfc. I’ve run into problems with occasional dreaded NULL NULL errors with this probably because of some type of infinite recursion issue.
Method 2: Instance any needed CFCs inside a CFC’s function scope (this seems to prevent the recursion issues):
<!--- Store.cfc --->
<cfcomponent>
<cffunction name="storeAction">
<!--- create a struct to keep all this function’s variables --->
<cfset var local = structNew() />
<!--- instance all the CFC’s we will need here --->
<cfset local.usersCFC = CreateObject("component","users") />
<cfset local.filesCFC = CreateObject("component","files") />
<cfset var customerInfo = local.usersCFC.getUser(1) />
My concern with this approach is that it may not be as efficient in terms of memory and processing efficiency because you wind up instancing the same CFC’s multiple times for each function that needs it. However it does solve the problem from method 1 of infinite recursion by isolating the CFCs to their respective function scopes.
One thing I thought of based on things I've seen online and articles on object oriented programming is to take advantage of a “Base.cfc” which uses the “extends” property of the cfcompontent tag to instance all of the CFC's in the application. However, I've never tested this type of setup before and I'm not sure if this is the ideal way to allow all my CFCs to talk to each other especially since I believe using extends overwrites functions if any of them share a common function name (e.g. "init()").
<!--- Base.cfc --->
<cfcomponent extends="calendar store blog users files">
What is the correct "best-practices" method for solving this type of problem?
If each of your CFC instances are intended to be singletons (i.e. you only need one instance of it in your application), then you definitely want to looking into Dependancy Injection. There are three main Dependancy Injection frameworks for CF; ColdSpring, WireBox and DI/1.
I'd suggest you look at DI/1 or WireBox as ColdSpring hasn't been updated for a while.
The wiki page for DI/1 is here:
https://github.com/framework-one/di1/wiki/Getting-Started-with-Inject-One
Wirebox wiki page is here:
http://wiki.coldbox.org/wiki/WireBox.cfm
Essentially what these frameworks do is to create (instantiate) your CFCs (beans) and then handles the dependancies they have on each other. So when you need to get your instantiated CFC it's already wired up and ready to go.
Dependancy Injection is also sometimes called IoC (inversion of control) and is a common design pattern used in many languages.
Hope that helps and good luck!
If your cfcs not related to each other the base.cfc concept does not fit. The inheritance is for classes have common things that can inherit from each other. For example if you have User.cfc and you want to added new cfc called customer.cfc I would inherit from User and override some functionality or add some without touching the actual user.cfc.
So, back to your question, since the CFC are not related or have common between each other and to avoid cross referencing, I will create serviceFactory holds instances of cfcs like this
component name="ServiceFactory"
{
function init(){
return this;
}
public User function getUserService(){
return new User();
}
public Calendar function getCalendar(){
return new Calendar();
}
}
and referencing it by
serviceFactory= new ServiceFactory();
userService = serviceFactory.getUserService();
Keep in mind this approach works only if you have sort of another CFC to manage your logic
you can do the same for all other services. If your functions are static you can save your services in application scope and instantiate it only one time (like singleton).
The third option you have is DI(dependency Injection) framework
As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 11 years ago.
In the spirit of my other questions regarding "common programming mistakes ... to avoid"
What are some common programming mistakes for a ColdFusion programmer to avoid?
set <cffile> upload path to a web accessible, CF-enabled directory!!!
isStruct() before isObject() in a series of <cfif>'s expecting isStruct only catches struct (cfc component returns True from isStruct() as well)
no HtmlEditFormat() when displaying user-generated content (XSS)
forgot to add output=false on CFC methods
not using <cfqueryparam> inside <cfquery>
not scoping not-so-evident variables like cfquery name or loop index in a method
use <cfform> when all they need is plain-vanilla HTML <form>
forgot to UrlEncodedFormat() user-defined URL
use <cffeed> without sanitizing the content
trust isDate() too much (any number would return true)
expect string comparison to be case-sensitive (IS and EQ operators are case-insensitive)
sending strings "yes" or "no" to SerializeJSON() without appending a whitespace to preserve the string (otherwise SerializeJSON() or DeserializeJSON() will translate them to "true" and "false")
not putting singletons services in application scope
blindly create as much CFCs as one wants like one would do in JAVA
putting complex value/object into a list (can't, list is just a string of comma-seperated values)
writing functions that takes array as an argument and modify that array expecting that array will be modified (array in CFML is passed by value)
blindly changes access="remote" on a method and expect it to work (when remote proxy is generally more appropriate)
use a lot of WriteOutput() in cfscript when CFML is more appropriate
blindly uses IsDefined() when StructKeyExists() can generally do it more efficiently
blindly uses Iif() and De() without knowing they're as nasty as Evaluate()
update some code in onApplicationStart() and not seeing the difference on refresh (restart the app!)
<cfloop> or '' outside of <cfquery> causing multiple new query connections to be opened. 99% of the time it's better to have multiple statements inside of one cfquery to perform multiple actions, or to UNION data together.
hardcoding absolute path when ExpandPath() is generally better
forgot to turn on Unicode support in DSN (Unicode becomes '????')
not upgrading to the latest JRE and Hotfixes
misusing Client scope and blow up Windows registry...
uses depreciated/obsolete functions/features (i.e. flash form aka flex 1.x alpha, cftable, Verity full-text search, etc...)
passing CFCATCH to a function as argument type Struct (CFCATCH behaves like a Struct, but it is not. Just pass it as type 'Any').
Not reading CFC Best Practices from ColdBox wiki.
buying in the mindset of .ASP(X) or .JSP or [insert web technology] are always better.. ;)
not use PrecisionEvaluate() and getting all sort of floating point rounding error especially when calculating money.
Inappropriate use of #
SELECT *
Not scrubbing URL/form inputs
Debugging on in production environment (even if output is suppressed)
Shamelessly stealing Henry's formatting...
it is faster and more accurate to check explicit boolean instead of implied; use <cfif query.recordCount GT 0> instead of <cfif query.recordCount>
do not use evaluate(), de(), or iif()... ever. there is always a way around these slow functions
understand structures, keys, values and how to access query and structure data using array notation. (this will usually get around your need for evaluate())
do not use pound signs unless you are outputting data or otherwise creating a string (don't do this: myFunction(arg = #myVar#) )
read and understand the difference between THIS and VARIABLES scope in a CFC
avoid extreme overuse of <cfsilent> when you probably need to use a <cfcontent reset="true"> just before you begin your output (before doctype, xml declaration, or <html>)
don't blindly drop ColdFusion values into an HTML script block (javascript) without using jsStringFormat()
if you are not using <CDATA> text in your XML, you may want to use xmlFormat() when creating an XML document
don't use Windows registry for Client scope data. Use the database.
if your IT architecture permits, use Session data instead of Client data.
use <cflock> correctly and consistently; shared data will leak in your Application.
if you are going to use Java objects, understand the Java error messages (for example, 'method cannot be found' may not mean the method does not exist at all, it means the method does not exist for the arguments you've supplied)
if you have to read large files, either use the new CF8 "File" functions or hand off the task to Java on CF6 & 7. <cffile> is inefficient for large files.
understand pass-by-reference and pass-by-value, and how those concepts work in CF; specifically when using functions to modify XML documents
as Henry stated, always use <cfqueryparam>; also ensure that you are using the correct CFSQLType parameter for your DBMS (for date, time, timestamp, etc)
don't chain together a series of <cfif> and <cfelseif> logic blocks, use <cfswitch> and <cfcase> if you have more than three conditions you need to handle
more of an architecture note: always do some sort of server side validation to catch the nasty data the wolf-shirt-wearing user may be passing you
last architecture note: let CF do your middle layer of data retrieval and display and let your webserver do webserver things like SEO URLs (I'm looking at you ColdCourse)
SQL Injection Attacks. It seems like cfquery is just made to allow them. So you should use cfqueryparams.
In Coldfusion, all variables are global by default, unless they are declared with the var keyword. (Somewhat similar to the situation in Javascript.)
So you either have to remember to var every variable used in a function, including things like names that are using in a cfquery name, or you can just use this trick:
<cffunction name="MyFunction">
<cfset var Local = StructNew()>
<!--- Now anything Local. is automatically local --->
<cfset Local.x = 42>
<!--- Including cfquery name="" --->
<cfquery name="Local.Customers" datasource="some_datasource">
SELECT C.ID, C.Name
FROM Customers C
</cfquery>
</cffunction>
There's nothing magic about the name Local, it's just convention. Although Coldfusion 9 will add an explict local scope, so if you use Local it will probably ease upgrading to CF9 when the time comes.
Note that the situation is a tad different for CFCs: In CFCs, the variables scope (the "default" scope) isn't global like it is for normal functions but rather exists per instance of your CFC. So while forgetting to use var is not quite as dangerous in a CFC as it is in a top-level function, the best practice is still to use var all the time.
Failing to prevent users from seeing coldfusion errors.
Add a onError method to a top level Application.cfc to prevent users from seeing those all to detailed dump messages exposing your inner workings(and failings).
<cffunction name="onError" returntype="void" output="true">
<cfargument name="exception" type="any" required="true" />
<cfargument name="eventname" type="string" required="true" />
varscoper is also a great tool for automating the check for variable scoping omissions in components.
http://varscoper.riaforge.org/
Overuse of 'query of query'. That is, further filtering or sorting of query results using the cfquery tag.
This type of work is often done better by the database itself, particularly if the dataset is large.
One of the biggest mistakes would be not using cfqueryparam
Very bad:
SELECT UserName
FROM Customers
WHERE CustomerID = #URL.custid#
Very good:
SELECT UserName
FROM Customers
WHERE CustomerID = <cfqueryparam value="#URL.custid#" cfsqltype="cf_sql_integer">`
Making that mistake will cost you a website.
Putting variables in the wrong scope; even if you don't blow up the registry or crash the server, it's easy to slowly drain performance from your application by bumping variables up to the highest scope in which you think you might need them, or to lose information because you stored it in one scope and tried to access them in a different scope.
Using cfcatch without capturing and/or transmitting some information about the error so that it can be found and fixed. (It's difficult to find an error that doesn't tell you it occurred.)
Using listcontains() when you want listfind(). Especially if the list contains numbers. listfind() matches only an entire item in a list; listcontains() matches part of an item. (Yes, we made this mistake once.)
With administrator access:
Leaving the defaults for a data source set up on the server. "Least privileges" applies on the CF side as well; don't give it any more permissions than it specifically needs. (GRANT, ALTER, REVOKE, DROP ... you don't really want those checked.)
Not checking the boxes for retrieving all the contents from a CLOB/BLOB field when that's what you're expecting. (It was really interesting seeing that applied to a field in which we were storing PDFs.)
I have written a gateway to get a result set from my database. How do i store every row in a separate dao so that i can manipulate every record further? Or can i access the result set directly to get the records?
This is my Gateway (btw, should i write the conditional logic within the cfquery into a seperate cfc that extends this one?)
<cfcomponent name="MaterialDao" hint="data access object" output="false">
<cffunction name="init" hint="constructor" access="public" output="false" returntype="MaterialDao">
<cfargument name="dsn" type="String" required="true" hint="datasource" />
<cfset variables.instance.dsn = arguments.dsn />
<cfreturn this />
</cffunction>
<cffunction name="readMaterial" hint="read" access="public" output="false" returntype="Query">
<cfargument name="district" type="String" />
<cfset var qReadMaterial = "" />
<cfquery name="qReadMaterial" datasource="#variables.instance.dsn#">
<cfif StructKeyExists(arguments,"district")>
SELECT A.NR, A.BEZ, D.BES, D.STA
<cfelse>
SELECT A.NR, A.BEZ
</cfif>
FROM DEK AS D INNER JOIN ART AS A
ON D.NR = A.NR
WHERE 0=0
<cfif StructKeyExists(arguments,"district")>
AND D.BEZ = #arguments.district#
</cfif>
ORDER BY A.BEZ
</cfquery>
<cfreturn qReadMaterial />
</cffunction>
</cfcomponent>
I have already read a lot of articles and it seems that there are different opinions about this matter (DAO vs. Gateway, DAO & Gateway etc.). What is the best practice, what do the pros do?
The pros use just one pattern for the database access layer. The use of both a DAO and Gateway is a misnomer that I'm not really sure where it got started, but seems to only exist in the ColdFusion crowd. The DAO and Gateway patterns can pretty much serve the same function, but I think the DAO fits the bill more when talking about database interaction.
DAOs should include functionality for CRUD methods plus returning sets of records. Since CRUD and basic sets of records is highly repetitive, I use a code generator to create the code for this interaction and then customize what I need. This is a good place for conditional logic for selecting the records you need.
As Aaron mentioned, returning an array of objects for a set of records in your database is unfeasible in ColdFusion due the the performance overhead of creating objects. I typically just use the basic query returned from the DAO in my views. However, if the thing I'm modeling needs some behavior in a view, then I will put the query in an object using something similar to what Peter Bell does.
Peter Bell had a great presentation some months ago on his release of the Iterating Business Object CFC which allows you to take multiple records and iterate over one record at a time using this simple framework: http://ibo.riaforge.org/. Until CF is a little faster at generating objects, recycling a single instance of an object and repopulating the properties is likely your best best. Perhaps this can help you load one record at a time into your DAO.
Conditional logic can go in the Gateway or in a Manager CFC. Typically, I would include logic that is simple like the logic outlined in your post directly in the CFC.
A bit of advice, you may wish to make the arguments.distinct NOT required and do a simple check with if (structKeyExists(arguments, "distinct") ) { do something }.
Regards,
-Aaron Greenlee
At our company we thought long and hard about this stuff for a few months, trying the Adobe CF DAO creator via RDS and some other older ones (anyone remember CFPowerTools?).
We decided in the end to write our own DAO code generator and I thought I'd share our thoughts here. The reason we decided was because we needed to add locking hints to out SQL, we wanted to make it more efficient, more secure and cleaner.
The setup we decided on was to create a pre-defined base DAO object (called DAO.cfc) which all generated 'table' DAOs extended. All it had was a few utility methods, but the key thing thing is we can add any other functions in there that we need all our generated DAOs to have access to.
So we auto-generate code by selecting a table from the database (using the CF admin API) and create the [TableName].cfc DAO with the usual init, setters and getters, so the basic CRUD stuff.
In addition to this, we also generate a [TableName]GatewayBase.cfc and a [TableName]Gateway.cfc. [TableName]Gateway.cfc extends [TableName]GatewayBase.cfc.
So for a sample DAO run on a table called 'Customers', the files created are:
Customers.cfc /* extends DAO.cfc [not created, already exists] */
CustomersGateway.cfc
CustomersGatewayBase.cfc /* extends CustomersGateway */
So, the idea is that the gateway provides a way to deal with many 'Customer' records - the DAO is used when dealing with one and only one. All methods in the gateway will generally return a CF query object. CF is too inefficient to create massive arrays of DAO objects and in our mind the query object in CF is really flexible, so we're happy to use it.
When coding, the sub-class CustomerGateway.cfc is the only one instantiated and used. However, the base class it extends has some very useful generic functions that come for free, such as getFieldListByProperty() which based on passed parameters will return certain fields (i.e. table columns) by a certain property (i.e. a column value), so for example:
myGateway.getFieldListByProperty(property="status", value="1", fieldList="customerName,customerID", orderBy="createdOn") />
That call will return the 'customerName' and 'customerID' values for all customers with a status of 1, ordered by the date they were created on. The code is also hardened against SQL injection and validated so sensible exceptions are thrown.
This function will provide 99% (we hope!) of the multi-record querying you do on a table. If you need a more sophisiticated query then the CustomerGateway.cfc is there for you to add functions to.
Finally, we allow you add functions to CustomerGateway CFC only because if you change the customers table (say add a column), you will need to recreate the table, and tha will overwrite the Customers.cfc and the CustomersGatewayBase.cfc. However, your custom code (if any) is safe in the sub-class.
Anyway, this might be slightly off topic, but sure I thought someone might find our experience useful.
Update: Based on the answers I initially went the route of using IsInstanceOf() which was designed for this need. However it turned out to be extremely inefficient for some unknown reason. In debugging the app later I ended up just setting some properties on the object to use instead of IsInstanceOf resulting in orders of magnitude speed improvement.
What I am trying to do is test an object in ColdFusion to see what type of component it is. Something like...
<cfif isValid( "compath.dog", currentObj)>
...do something specific with dog objects...
</cfif>
I thought this was possible but receive an error saying the type I am passing does not correspond to one in the valid list of types...
Valid type arguments are: any, array, Boolean, date, numeric, query, string, struct, UUID, GUID, binary, integer, float, eurodate, time, creditcard, email, ssn, telephone, zipcode, url, regex, range , component, or variableName.
Is there a way to accomplish this in ColdFusion?
You could also use IsInstanceOf(). Though you must still use the full path, it can also be used to determine inheritance or identify components that implement a particular interface.
<cfif IsInstanceOf(obj, "compath.Dog")>
yes. it is a dog component {woof}
<cfelse>
some other type of component
</cfif>
<cfif IsInstanceOf(obj, "compath.AnimalInterface")>
yes. it implements the animal interface
<cfelse>
no. it must be vegetable or mineral ...
</cfif>
You could use GetMetaData to find the type. Some quick code:
<cfif GetMetaData(currentObj).type eq "compath.dog">
you could use name or fullname from the getmetadata() function.
<cfif GetMetaData(currentObj).name eq "compath.dog">
...do something specific with dog objects...
</cfif>
or
<cfif GetMetaData(currentObj).fullname eq "compath.dog">
...do something specific with dog objects...
</cfif>
docs are here getmetadata() on what getmetadata() returns depending on the object type.
Dan, feel free to rip the code out of MXUnit which does exactly what you need to do. We do it in our assertIsTypeOf() assertion. See here for details: http://code.google.com/p/mxunit/source/browse/mxunit/trunk/framework/MXUnitAssertionExtensions.cfc
The reason you're seeing the performance hit with isInstanceOf() most likely has to do with setting this.customTagPaths in your Application.cfc. I hit this myself and filed a bug on it recently. Hopefully it'll get fixed in CF10, whenever that is.