October 20, 2009
It took me a day to figure this out, live and learn right. I used 'do' as the event name in one of my Coldbox applications as opposed to 'event' given as the default. If you're considering what to name your events in Coldbox, don't. Just use the default 'event' name given. I was just trying to be unique but it backfired.
The 'do' term is a reserved keyword in Flex, ActionScript actually. The lesson I got from this is to not ever use any keywords from any language for which you may mix. In this case I was using Flash remoting from my Flex application to to fire remote ColdFusion functions from the Coldbox framework remote proxy.
Here is the code I was using in my Flex actionscript. Notice the 'event' object param passed to the proxy process function, you can't use 'do', well technically you can but don't because it won't work.
private function authenticateClient():void {
var cProxy:RemoteObject = getColdBoxProxy();
cProxy.addEventListener(FaultEvent.FAULT,faultHandler);
cProxy.process.addEventListener(ResultEvent.RESULT,authenticateHandler);
cProxy.process({event:"general.doRemoteLogin"});
}
/* UTILITY METHOD TO GET A CBPROXY OBJECT, You can separate all this to a delegate class */
public function getColdBoxProxy():RemoteObject{
var cProxy:RemoteObject = new RemoteObject("ColdFusion");
cProxy.source = _model.cbProxyPath;
cProxy.showBusyCursor = true;
return cProxy;
}
June 20, 2009
Here it is in a nutshell. I have a controller cfc in my framework that directs execution instructions to the business model cfc's. But I'm wandering if I should pass whole objects, like a user object, and get values as needed from it's methods within the business model? Or should I just pass the object's properties to the business model classes as needed?
Another way to put it is this(model/securityService.cfc):
instance.securityGateway.login(User.getUsername(),User.getUserpassword());
or
instance.securityGateway.login(User);
The business model gateway in this example would be something like(model/data/gateway/securityGateway.cfc):
<cffunction name="login"..>
<cfargument name="username"../>
<cfargument name="password"../>
...
</cffunction>
or
<cffunction name="login"..>
<cfargument name="User" type="model.data.gateway.User"../>
..
</cffunction>
Maybe because I'm not a seasoned OO veteran but I'm looking at this and trying to figure out the best course and can't decide which one to take. From the service cfc do I pass in the user object or it's properties (username & password)?
For now I'm passing the object's properties if the gateway method only requires data and doesn't need to access any of the objects other methods like setters. If the gateway method here needed to use the object's methods instead of just it's properties, then I'd pass the entire object. So what do you think is best practice?
May 20, 2009
I got tired of trying to get cfUnit working and all I wanted to do was test component methods. I wanted to be able to simply point to it, test it, and enter arguments if required. And I wanted this set up somewhere easy I could use quickly, repeatedly, effortlessly, and painlessly (word?).
In almost all my applications I have a 'webmaster' directory where I run test. Lately I've found myself getting fancy with setting up a web development control panel of sorts. Whereas web sites generally have administration control for admin users, I've been building in webmaster control for developers. Mostly things like server and framework info, links to documentation, framework and site functionality examples, and etc.
Now I'm adding a little CFC Test form where I enter my cfc path, the method name, and arguments. It will pass the event argument so you can also test cfc handlers.
Warning, I just created this a few hours ago. It's tested to work but I haven't cleaned it up or put in validation and error handling yet.
views/webmaster/index.cfm (webmaster control panel)
<cfform name="cfctestform" action="#cgi.SCRIPT_NAME#" method="post" format="html">
<cfinput name="do" type="hidden" value="webmaster.panelcontrol.testmethod" />
<cfinput name="sbIsEnabled" type="hidden" value="0" />
Enter Component Path:<br /><cfinput name="cfcpath" type="text" />
<br />
Enter Method to Test:<br /><cfinput name="cfcmethod" type="text" />
<br />
Arguments:(comma seperated list)<br /><cfinput name="methodargs" type="text" width="80" />
<br />
<cfinput type="submit" name="submit" value="Run Test" />
</cfform>
events/webmaster/panelcontrol.cfc (Coldbox handler)
<cffunction name="testmethod" access="public" returntype="void" output="true">
<cfargument name="Event" type="coldbox.system.beans.requestContext">
<cfset var rc = arguments.event.getCollection()>
<cfset var ret = "">
<cfset var cfcObj = createObject("component",rc.cfcpath)>
<cfif listLen(rc.methodargs)>
<cfloop from="1" to="#listLen(rc.methodargs)#" index="i" step="2">
<cfset evaluate("arguments['#listGetAt(rc.methodargs,i)#'] = '#listGetAt(rc.methodargs,i+1)#'")>
</cfloop>
</cfif>
<cfinvoke component="#rc.cfcpath#" method="#rc.cfcmethod#" argumentcollection="#arguments#" returnvariable="ret">
<cfscript>
//Display event.setValue("cfctestresults",ret);
event.setValue("author","Clint Willard");
event.setView("webmaster/dspCfcTestResult");
</cfscript>
</cffunction>
Basically it loops through the method arguments list you entered in the form setting them as part of the arguments struct. That arguments struct is then passed to the invoked component and method, along with the event object. Then the view is set to output with the test results, whatever the method is set to return.
views/webmaster/dspcfcTestResult.cfm
<cfscript>
cfcresult = event.getValue("cfctestresults","NA");
</cfscript>
<cfoutput>
#dump(cfcresult)#
</cfoutput>
KISS output.
Let me know if this might be a good idea to expand on or should I just LOL learn cfUnit? And the webmaster information and control panel stuff?
May 17, 2009
Posted At : 9:10 PM
| Posted By : clint317
Related Categories:
Coldbox 2.6
Tired of keeping your modelMappings.cfm file straight everytime you add or delete a model? What you need is a script that'll do it all automatically. This code isn't mine and I can't remember where I found it but it's very useful. Mind the OS slashes '/' and '\', change as needed for your system, Windows or *nix, which can probably be parameterized too.
<cfscript> /**
* Deletes the n leftmost elements from the specified list.
* Modified by RCamden
*
* @param list The list to modify.
* @param numElements The number of elements to delete from the left hand side.
* @param delimiter The delimiter to use. Defaults to a comma.
* @return Returns a string.
* @author Shaun Ambrose (
shaun.ambrose@arcorsys.com)
* @version 1, April 24,
2002 **/
function ListDeleteLeft(list, numElements) {
var i=0;
var delimiter=
",";
if (Arraylen(arguments) gt
2) {
delimiter=arguments[3];
}
if (numElements gt ListLen(list, delimiter)) return
"";
for (i=1; i lte numElements; i=i+1) {
list=listDeleteAt(list,
1, delimiter);
}
return list;
}
</cfscript> <!--- Add model mappings dynamically for all cfcs found in the /model directory --->
<cfdirectory action="list" directory="#expandPath('/model')#" name="files" recurse="true" /> <cfloop query="files"> <cfif right(files.name,4) is ".cfc"> <!--- rip extension off component --->
<cfset filename = left(files.name,len(files.name)-4)> <cfset thisPath = listAppend(listDeleteLeft(files.directory,listFindNoCase(files.directory,"model","\"),"\"),filename,"\") /> <cfset addModelMapping(path=listChangeDelims(thisPath,".","\")) /> </cfif> </cfloop>
May 14, 2009
Posted At : 2:50 PM
| Posted By : clint317
Related Categories:
Coldbox 2.6
Coldbox has a really cool bean factory that can speed up your development time. Through it's methods you can manage your bean objects with one-liners. Like, instead of having your handler get a bean form the gateway then filling or updating that bean with it's getters, you can simply tell the bean factory to do it for you.
UserBean = getPlugin('beanFactory').getModel('data.bean.User');
getPlugin('beanFactory').populateBean(UserBean);
The example you see above gets a bean and populates or updates its properties via its setters automatically. Your next question is "With what?"
Let's imagine a form that a user fills out to update his address. Nevermind the code I'm typing from my head, it's short to be to the point.
<form method="POST" action="#cgi.SCRIPT_NAME#">
<cfinput type="hidden" name="do" value="general.editAddress"/>
<cfinput type="text" name="address"/>
<cfinput type="submit" name="submit" value="Submit"/>
</form>
Notice the name property for the text input where the address goes, 'address', case isn't important. It gets handled by the general handler in the events directory as an editAddress event, which runs the editAddress method of the general handler object.
And in the general handler is the editAddress event.
<cffunction name="editAddress" access="public" returntype="void" output="false">
<cfargument name="Event" type="coldbox.system.beans.requestContext"/>
<cfset rc = event.getCollection()>
<cfscript>
getPlugin('beanFactory').populateBean(variables.UserBean);
getPlugin('beanFactory').getModel('data.gateway.usersGateway').saveBean(variables.UserBean);
</script>
</cffunction>
With just this much code, after the form was submitted, my handler, with the bean factory, has updated the bean property 'address' and used the gateway to update it in the database.
The populateBean method takes the request context, which contains the submitted form items such as 'address=1732 Main St.' and finds the setter method matching the form names, or 'address' in this case. Then it runs the setAddress() method of the bean, updating it.
You don't have to call out the bean's actual setter method in your code which is fantastic when your form has many more fields. You can also set a value in the request context that matches a bean's property name and then just use populateBean() to update it. The following code will include an update to the name properties of the user bean. If the user bean object has a setFirstName() and setLastName() method, then it's first and last name properties will also be updated.
<cffunction name="editAddress" access="public" returntype="void" output="false">
<cfargument name="Event" type="coldbox.system.beans.requestContext"/>
<cfset rc = event.getCollection()>
<cfscript>
rc.firstName = "Clint";
rc.lastName = "Eastwood";
getPlugin('beanFactory').populateBean(variables.UserBean);
getPlugin('beanFactory').getModel('data.gateway.usersGateway').saveBean(variables.UserBean);
</script>
</cffunction>
After the code above runs, UserBean.getFirstName() will return 'Clint' now. As long as the property is spelled the same, 'firstname'. You want to keep your object property names the same as in your database.
Maybe some time I'll go over the bean factory's model mapping and autowire methods but for now I'm not using them and haven't yet found a reason to.
May 13, 2009
Posted At : 5:31 PM
| Posted By : clint317
Related Categories:
Coldbox 2.6
I found another way in ColdBox to inject dependencies within my model and data layer. I'm not sure if it's newer or older than my current IOC only injection
<cfcomponent name="general" extends="coldbox.system.eventhandler" output="false" autowire="true">
...
<!------------------------------------------ DEPENDENCIES -------------------------------------->
<!--- Get User Service --->
<cffunction name="getUsersService" access="public" output="false" returntype="any" hint="Get usersService">
<cfreturn instance.usersService/>
</cffunction>
<cffunction name="setUsersService" access="public" output="false" returntype="void" hint="Set usersService">
<cfargument name="usersService" type="any" required="true"/>
<cfset instance.usersService = arguments.usersService/>
</cffunction>
<!--- Get User Bean --->
<cffunction name="getUsersBean" access="public" output="false" returntype="any" hint="Get usersBean">
<cfreturn instance.usersBean/>
</cffunction>
<cffunction name="setUsersBean" access="public" output="false" returntype="void" hint="Set usersBean">
<cfargument name="usersBean" type="any" required="true"/>
<cfset instance.usersBean = arguments.usersBean/>
</cffunction>
...
</cfcomponent>
but it sure seems easier, cleaner, and coder intuitive. I'm still using ColdSpring IOC for my data layer but now I'm using ColdBox's own model injection. It (beanFactory) uses a modelMappings.cfm file in the config directory to list the metadata that's like model mappings.
cfscript>
/* Add all the model mappings you want */
/* addModelMapping(alias="",path="") */
addModelMapping(path='UsersService');
addModelMapping(path='data.bean.Users');
</script>
Its intuitive enough to map the model name from the path or you can use an alias instead. Whether or not a huge application with a few thousand classes would bloat this file and slow things down would be an interesting question. But for now what makes this way better to me would be the more intuitive feeling of the metadata style dependency listings using the cfproperty tag in all my models
<cfcomponent name="testarea" extends="coldbox.system.eventhandler" output="false" autowire="true">
<!----------------------------------- DEPENDENCIES -------------------------------------->
<cfproperty name="UsersService" type="model" scope="instance" />
...
<cffunction name="index" access="public" returntype="void" output="true">
<cfargument name="Event" type="coldbox.system.beans.requestContext">
<cfscript>
//Logic userName = instance.usersService.getUserName();
//Display Event.setValue('userName',userName);
Event.setView("test");
</cfscript>
</cffunction>
...
</cfcomponent>
and frankly the modelMappings meta listing style actually helps with mental organization.
I'm not sure if this way is supposed to be better, worse, or just different but I love it.
May 12, 2009
Posted At : 8:15 AM
| Posted By : clint317
Related Categories:
Coldbox 2.6
One of the newest enhancements to Coldbox (2.6) is an environments.xml.cfm file in the config directory you can use to override application settings depending on what box your on. Even if you work on a DEV server, test on a QA server, and publish to the PRODUCTION server, you can set settings like caching and auto reloading for each box and forget it.
For example you can set in the coldbox.xml.cfm for production but override that in environments.xml.cfm with <Setting name="DebugMode" value="true" />. So if your dev environment url parameter in the environment.xml.cfm is 'localhost', then when the cgi.http_host equals 'localhost' instead of 'mysite.com' the debugmode value will be 'true'. And without changing a thing, it will be 'false' in production. There is no need to set a production environment in environments.xml.cfm as CB will use coldbox.xml.cfm settings only if cgi.http_host isn't found.
October 16, 2008
After working in Coldbox for a while I know your first instinct is to make a cfajaxproxy call to a cfc event just like all the other Coldbox events. However, using cfajaxproxy is just a little different. Here is an example to show you one simple way of doing it, not the only way but one of them.
First let me note that I am using Coldspring version of IOC. It should work the same though with any form of IOC. Mostly we need the IOC to provide the bean to the services your Ajax call requires from within the Coldbox proxy.
After having provided a service in Coldspring like so:
<bean id="usersService" class="model.usersService">
<constructor-arg name="privatemessagesGateway">
<ref bean="privatemessagesGateway"/>
</constructor-arg>
<constructor-arg name="ColdBoxController">
<ref bean="ColdBoxController"/>
</constructor-arg>
</bean>
we can use this "usersService" bean in the coldboxproxy.cfc like this:
<cffunction name="ajxSetPMRead" access="remote" output="false" returntype="void">
<cfargument name="messageid" type="String" required="true"/>
<cfset var pm = getBean('usersService').getPrivateMessageByID(int(arguments.messageid))>
<cfset pm.setRead(1)>
<cfset getBean('usersService').savePrivateMessage(pm)>
</cffunction>
The code above is what the Ajax, cfcajaxproxy, will call as a method for Coldbox to perform. It fetches a private message object bean, sets or updates some properties, then uses the gateway to save the object bean. I don't have a return or output but you can do that also here if desired. And here is the actual view and Ajax calling code:
<cfajaxproxy cfc="coldboxproxy" jsclassname="cbProxy">
<a href="##" onClick="showMessageDetails('#privateMessagesQuery.privatemessageid#','#privateMessagesQuery.read#','#privateMessagesQuery.subject#','#privateMessagesQuery.fromUserName#','#privateMessagesQuery.message#')">#privateMessagesQuery.subject#</a>
<$cript>
function showMessageDetails(messageid,read,subject,from,message){
var e = new cbProxy();
if(read == 0){
e.setCallbackHandler(setPrivateMessageRead);
e.setErrorHandler(ajaxErrorHandler);
e.ajxSetPMRead(messageid);
}
document.getElementById('pmReply').style.display = "none";
document.getElementById('pmDetails').style.display = "inline";
document.getElementById('pmDetails_Subject').innerHTML=subject;
document.getElementById('pmDetails_From').innerHTML=from;
document.getElementById('pmDetails_Message').innerHTML=message;
}
var setPrivateMessageRead = function(res){
alert('set to read');
}
var ajaxErrorHandler = function(statusCode, statusMsg){
alert('Status: ' + statusCode + ', ' + statusMsg);
}
</script>
The cfc attribute is dot notation to the location of your coldboxproxy.cfc, mine here is at the root. Basically I'm showing the private message details in a div but needing to set the read flag in the database to 1 to indicate it has been read. And using Ajax I can set this in the database for each message read without a page refresh. The alerts in the javascript are for demonstration and testing purposes.
The code I provided is a cut down version of actual code and does not include the view event, form code, queries, or other behind the scenes code. I hope this helps to give you a step ahead on how to use Ajax with the Coldbox framework.
June 6, 2008
Coldbox plugins are a really useful aspect of the framework when you want to create reusable interfunctions. Interfunction is a term for a function within an application that can be used in any application within any one specific framework.
Coldbox plugin interfunctions are, to me, like UDFs, almost. Except that they are OO capable components. But enough with talk, read more about plugins here.
And here's an example of a custom plugin I created called "MyUtilities" that has a function utilizing CF8's new image resizing abilities. It's not expanded yet as this is just what I needed for the moment on a project.
This goes into a cfc (MyUtilities.cfc) file in your plugins directory, I called this plugin "MyUtilities" for now:
<cfcomponent name="MyUtilities"
hint="This is a Utilities CFC"
extends="coldbox.system.plugin"
output="false"
cache="true"
cachetimeout="5">
<cffunction name="resizeImage" access="public" returntype="Any">
<cfargument name="fileLocation" type="String" required="true"/>
<cfargument name="destination" type="String" required="false" default="#arguments.fileLocation#">
<cfargument name="xWidth" type="String" required="false" default="100"/>
<cfargument name="yHeight" type="String" required="false" default="100"/>
<cfscript>
if(IsImageFile("#arguments.fileLocation#")){
myImage = ImageNew(arguments.fileLocation);
ImageSetAntialiasing(myImage,"on");
ImageScaleToFit(myImage,arguments.xWidth,arguments.yHeight,"highestQuality");
ImageWrite(myImage,arguments.destination,1);
return ImageInfo(myImage);
}else{
return false;
}
</cfscript>
</cffunction>
</cfcomponent>
And this is how you'd use it in your code:
imageThumb = getPlugin("MyUtilities",true).resizeImage(fileLocation,thumbLocation,width,height);
You can see from the resizeImage function that I hard coded resize quality and antialiasing, as that is true anywhere in my project for all image sizing. But you can take this and customize it further for your needs. I just mostly wanted to share with you an example of using a custom Coldbox plugin.
June 4, 2008
I have to say I love this automatic bean population thing going around. Model-Glue does it also but since I mostly use Coldbox, that's what I'm showing here.
WTF, you may ask? Well, you know how when you submit a form to edit a user account for example, and then you have to pass all those form elements to your user bean object's getters and setters one by one. userObj.setUsername(form.username), userObj.setUserpassword(form.password) and so on.. well you can forget about ever doing this again.
HOW!?!? By using the Coldbox beanfactory populatebean method. You can play with form elements first if you need to, then simply call the populate method passing in the current userbean, and then Coldbox will match form elements to the bean's getter and do the heaving lifting for you returning an updated bean ready for saving.
2 simple lines, get bean and populate bean. Then do whatever else you prefer from there to save it wherever etc..
<cffunction name="doEditAccount" access="public" returntype="void" output="false">
<cfargument name="Event" type="coldbox.system.beans.requestContext">
<!--- Logic --->
<cfscript>
userBean = getPlugin("sessionstorage").getVar("User");
getPlugin("beanFactory").populateBean(userBean);
getUsersService().saveUser(userBean);
getPlugin("sessionstorage").setVar("User",userBean);
</cfscript>
<!--- Display --->
<cfset setNextEvent("myaccount.general.dspAccountHome")>
</cffunction>
The most important thing to remember is that your actual bean cfc object must use an init method with setters. And the form element names should be the same as the bean names.
A bean's init:
<cffunction name="init" returntype="User">
<cfargument name="username">
<cfset setUsername(arguments.username)>
</cffunction>
So that:
<input type="text" name="username">
Matches:
<cffunction name="setUsername">
<cfargument name="var">
<cfset variables.instance.username = arguments.var>
</cffunction>
Hope that helps. :)
More Entries