Light weight scaffolding for FW/1 - coldfusion

I am trying to create a light weight scaffolding facility for FW/1. Right now I have a file called scaffold.cfc in the controller which looks like
<cfcomponent hint="this is expected to be extended, and never used directly">
<cfscript>
function init(fw) { variables.fw = fw; }
void function home (required struct rc) output="false" {
/* TODO: Generic load */
setView("scaffold.home");
}
void function create (required struct rc) output="false" {
/* TODO: Generic create */
setView("scaffold.create");
}
void function show (required struct rc) output="false" {
/* TODO: Generic show */
setView("scaffold.show");
}
...
</cfscript>
</cfcomponent>
I want to make sure that index.cfm?action=scaffold.* or index.cfm/scaffold/* can never ran.
Where is the best place to do this?

You could place the scaffold.cfc outside the controllers folder so FW/1 doesn't consider it to be a controller CFC (since you would only extend this, not use it directly as a controller). You could also place it in a subfolder of the controllers folder and, again, FW/1 would ignore it.

Related

global variable is empty

I'm using Lucee CFML to create a website.
I have a global variable stored in application.cfc like this:
component {
application.globalquery;
}
On the second page of the website, I assign a value to that variable:
<cfscript>
myquery = ["object1", "object2", "object3", "object4", "object5"];
application.globalquery = myquery;
</cfscript>
On a third page, I dump the globalquery variable:
<cfscript>
dump(var="#application.globalquery#");
</cfscript>
But the result of this dump is string: empty
The expected behavior should be, that it contains the 5 objects of myquery.
Why isn't that the case?
You're resetting the value on every request.
By putting the definition at the root of the component:
component {
application.globalquery;
}
You're essentially telling CF to run that code on every request, like this:
component {
function onRequestStart() {
application.globalquery;
}
}
You need to define that variable only once, when the application starts, like this:
component {
function onApplicationStart() {
application.globalquery;
}
}
From then on, the value should only change when you tell it to change.
Here's more info on Application.cfc. There may be a few differences between Adobe CF and Lucee.

Mocking dependencies inside of an AOP object using Testbox and AOP/1

I am curious if there is an easier way to mock the dependencies inside a component that uses AOP.
For example I have a component that uses CRUD methods to persist changes to the database. For audit logging I figured I could use AOP so I didn't have to put my logging calls inside of the methods. The issue that I am running into is during unit testing when I want to mock the database and session storage used inside the object that provides the AOP. The only way I have been able to figure out how to fake the database and session is to create a new mock component file that extends the original and then overrides the init method. Using this method is not very flexible as I have to create several extra files for a simple unit test. Below is some of the test code:
This is the bean that gets created with the di/1 bean factory:
//customerBean.cfc
component accessors="true" extends="models.abstracts.AbstractModel" displayname="Customer" {
property name="ID" type="numeric" default="0";
property name="Name" type="string" length="100" default="<Unknown>" logEvents="update";
property name="Type" type="string" default="Homeowner" logEvents="update";
property name="Status" type="string" default="Active" logEvents="update";
public any function init(customerDAO) {
variables.DAO = arguments.customerDAO;
}
}
Here is the component that the customerBean extends:
//abstractModel.cfc
component accessors="true" {
public any function doCreate(required struct data) {
setID(save());
return getID();
}
}
This is the AOP component that has two dependencies that need to be mocked:
//customerLogger.cfc
component accessors="true" {
property name="tableToLogTo" default="CustomerLogs" setter="false";
public any function init(LoggerServiceDAO, scopeStorage) {
variables.loggerServiceDAO = arguments.loggerServiceDAO;
variables.scopeStorage = arguments.scopeStorage;
}
public any function after(targetBean, methodName, args, result) {
variables.loggerServiceDAO.insertLog(method=arguments.methodName, user=variables.scopeStorage.get('session.user.id'));
}
}
Here is the code I use to get the CustomerBean:
bf = new framework.aop(
folders="/models,/system,/tests/Mocks", config={transients=["entity", "process", "transients", "report", "forms"],transientPattern="Bean$"}
);
bf.intercept('CustomerBean', 'CustomerLoggerMock', 'doCreate');
customerBean = bf.getBean('CustomerBean', {customerDAO: customerDAOMock});
Ideally what I would like to do is use Testbox to mock the two objects and use the nice built in verification methods. Is this possible or am I stuck creating all of these mock files?

How do you capture errors for an entire Application in ColdFusion?

I am currently trying to capture all errors in my application by including the following code in Application.cfc:
<cffunction name="onError">
<!--- The onError method gets two arguments:
An exception structure, which is identical to a cfcatch variable.
The name of the Application.cfc method, if any, in which the error
happened. --->
<cfargument name="Except" required=true/>
<cfargument type="String" name = "EventName" required=true/>
<!--- Log all errors in an application-specific log file. --->
<cflog file="#THIS.NAME#" type="error" text="Event Name: #Eventname#" >
<cflog file="#THIS.NAME#" type="error" text="Message: #Except.message#">
<!--- Throw validation errors to ColdFusion for handling. --->
<cfif Find("coldfusion.filter.FormValidationException", Arguments.Except.StackTrace)>
<cfthrow object="#Except#">
<cfelse>
<cfoutput>
<h1>#Eventname#</h1>
</cfoutput>
<cfdump var="#Except#">
</cfif>
</cffunction>
Some of that is borrowed from other examples I have seen (which I don't fully understand). I ultimately want to show some kind of graceful error page to solicit feedback from the user and then log/email the error. This seems to catch a lot of errors, but not all. I don't want to use try/catch everywhere if I don't have to either. Any suggestions?
There is also an overall ColdFusion error handler that you can define in the ColdFusion administrator. Under the Server Settings > Settings, scroll down to the bottom and set the option for "Site-wide Error Handler".
Check this in the docs as well About error handling in ColdFusion
Using shared hosting shouldnt be an issue, just ask your hosr what the error templates are, if they are clued up about cf then they will have them setup.
Wr use error.cfm and 404.cfm fot example which go in the root of every customers site.
The "OnError" method within the Application.cfc will only catch the errors where they have not previously been caught by a user defined try/catch statement.
http://livedocs.adobe.com/coldfusion/8/htmldocs/help.html?content=appFramework_13.html
With this said, I think it to be a good idea to have try catch statements within your code where necessary (situations where you cannot degrade gracefully). What I like to do is instantiate a cfc that wraps all exception handling. This cfc can then contain the actual error handling logic and all that the OnError method will need to do is instantiate the correct component and "control" the error.
A very simple illustration:
<cfscript>
/** Application.cfc **/
public function onError(required exception, required string eventName)
{
var factory = new App.ExceptionFactory();
var e = factory.getNewException(arguments.eventName, arguments.exception);
if (e.logError()) {
/** we cauld also have a logging cfc etc **/
var loggingFile = new App.SomeLoggingCfc(arguments.eventName, arguments.exception);
loggingFile.commitLog();
}
if (e.debugError()) {
// show developer info here
}
/** Throw the exception **/
e.throwException();
}
/** App.ExceptionFactory **/
public ExceptionFactory function getNewException(required string eventName, required exception)
{
return new "App.#exception.type#"(argumentCollection = arguments);
}
/** App.Exception.CustomException **/
public boolean function logError()
{
/** log the error **/
}
public boolean function debugError() {}
public function throwException()
{
/** do what you want here **/
}
</cfscript>

What's the best way to unit test a cfc that uses a Java object for a lot of its functionality?

I have a cfc that relies heavily on a Java object (created via JavaLoader) for a lot of its core functionality that I'd like to write some tests for and I'm not sure what the best way to do this is. Here is an example of a method I'd like to write a test for with instance.note being a java object.
<cffunction name="getNotes" returntype="Array" access="public" output="false" hint="I return a list of a users notebooks" >
<cfargument name="maxCount" type="numeric" required="false" default="9999" hint="The maximum number of notes to get" />
<cfscript>
if(arguments.maxCount)
return instance.note.listNotes(maxCount);
else
return instance.note.listNotes();
</cfscript>
</cffunction>
One thing I thought of doing is creating a stub CFC that has the same method names and similar return values and then mocking that stub and injecting it?
Can't you just write meaningful assertions on the result, i.e. on the array of notes? Looking at that code, the only things I'd test are a) when you pass a maxCount, does your resultant array honor that size? b) without maxCount, is the list of notes the length that you'd expect? Because that's all your code does. I'd test your code, not the code of the underlying java object.
When we needed to unit test CF functions that relied upon Java objects (which we did a LOT of), we used Mockito to mock the Java objects.
So, hoping this code snippet makes sense, its been almost a year since I've done this:
<cfcomponent displayname="TestWhatever" extends="mxunit.framework.TestCase" output="false">
<cffunction name="setUp" access="public" returntype="void">
<cfscript>
// named it mk for keeping it short
variables.mk = createObject("java","org.mockito.Mockito");
//Create the mock object
variables.mockNote = mk.mock(createObject("java","com.company.whatever.note").getClass());
// Mock Data
fullList = {whatever listNotes() returns}
partialList3 = {whatever listNotes(3) returns}
//some common mocking
mk.when(variables.mockNote.listNotes()).thenReturn(fullList);
mk.when(variables.mockNote.listNotes(mk.eq(3))).thenReturn(partialList3);
mk.when(variables.rootOrgObj.guid()).thenReturn("root");
// Assign the mock object to where your CFC expects it.
instance.note = variables.mockNote
</cfscript>
</cffunction>
</cfcomponent>
Having said that, if your sample function is real, there's really no point in unit testing it. Its simply not doing anything but being a proxy to the java object. There is no significant logic in there to test.
Since you have a default on the cfargument, it will always exist (again, I think so, its been a year since I've done CF), so your guard statement isn't even required - the first code path will always be called, with the passed maxCount if specified, or with 9999 if not.
I took Edward's answer and implemented it like so:
I used the JavaLoader library to create my mockito object.
variables.cfEvernote = "";
variables.classLoader = createObject("component", "resources.JavaLoader").
init(["#expandPath('../lib/mockito-all-1.8.5.jar')#",
"#expandPath('../lib/CFEvernote.jar')#",
"#expandPath('../lib/libthrift.jar')#",
"#expandPath('../lib/evernote-api-1.18.jar')#"]);
variables.mockito = variables.classLoader.create("org.mockito.Mockito").init();
Then in the setup method of my munit test I create my new mock java object:
<cffunction name="setUp" access="public" output="false" returntype="void">
<cfscript>
variables.cfEvernote = createObject("component","com.714studios.cfevernote.CFEvernote").
Init(variables.configArray[1],variables.configArray[2],
"sandbox.evernote.com",
"http://localhost/cfevernote/callback.cfm"
"#ExpandPath('../lib')#");
variables.mockCFEvernote = variables.mockito.mock(variables.classLoader.create("com.sudios714.cfevernote.CFEvernote").
Init("123","S1","232","sandbox.evernote.com","mock").getClass());
variables.cfEvernote.setCFEvernote(mockCFEvernote);
</cfscript>
Then in my tests I create my mock behavior like so.
<cffunction name="test..." returntype="void" access="public" output="false" >
<cfscript>
var notebooks = "";
var expected = 12;
var i = 0;
var retArray = createObject("Java","java.util.ArrayList");
var actual = "";
for(i = 1; i lte 12; i = i + 1){
retArray.Add("");
}
variables.mockito.when(mockCFEvernote.listNotebooks(12)).thenReturn(retArray);
notebooks = variables.cfEvernote.getNotebooks(12);
actual = arrayLen(notebooks);
assertEquals(expected,actual);
</cfscript>
I've also blogged about it in a bit more detail here - http://blog.bittersweetryan.com/2011/07/unit-testing-coldfusion-components-that.html.

cflogin in cfscript

I'm trying to learn the new cfscript syntax, as well as use cflogin.
I'm guessing I can't mix cf tags in cfscript, and I don't see a script equivalent to cflogin, cflogout, cfloginuser.
Should I call a component that is written in the CF8 syntax in order to use cflogin?
public void function onRequest(required string Page) output="true" {
if (StructKeyExists(url,"logout")) {
<cflogout>
}
<cflogin>
local.qryUsr = new Components.Usr.Login(form);
if (local.qryUsr.Recordcount) {
<cfloginuser name="#form.UsrName#" password="#form.UsrPassword#" roles="#local.qryUsr.Roles#">
} else {
request.errorMessage = "Incorrect login";
include login/login.cfm;
return;
}
</cflogin>
include arguments.Page;
}
You cannot directly mix tags and scripts. However, you can fake it by writing function wrappers around the tags:
<cffunction name="logout">
<cflogout />
</cffunction>
and call like:
logout();
Obviously, this is a trivial example. You'd want to specify your arguments, your return value, etc. in your actual code.
Note one: Do not do this for a generic query function that accepts user input, as you won't be able to use cfqueryparam.
Note two: I generally don't do this. If I'm writing code that depends on tag-only operations, I use the tag syntax.
as a side note, there is a small effort going on over at CFLib.org to create functions for all CF Tags.
check out CFMLLib for more details