Coldbox Developer Training Session 5
bill berzinskas
UNC-CH Office of Research Information Systems
Review last weeks assignment
• Create Unit Tests
• Create Model CFC‟s w/ BaseGateway
Overview
• Talk about the “handler”
• Look at how to use WireBox inside a
handler
• Explore “Integration Testing”
MVC – The Controller (Handler)
• Event handlers are synonymous to the
word Controller in the MVC design
pattern. So every time you hear event
handler, you are taking about a controller.
• These event handlers carry the task of
controlling your application flow, calling
business logic, preparing a display to a
user and pretty much controlling flow.
MVC – The Controller (Handler)
Simple example:
component{
function index(event,rc,prc){
return "Hi from controller land!";
}
}
MVC – The Controller (Handler)
• Handlers reside in the handlers folder and
can be organized by “package” by placing
like CFCs together inside a folder• Handlers/department/roles.cfc
• Handlers/department/
• You can also declare a
HandlersExternalLocation setting in your
Coldbox.cfc• Common.handlers
MVC – The Controller (Handler)
Handler Registration
• At application startup, the framework registers all
the valid event handler CFCs in these locations
(plus handlers inside of modules).
• Two settings are provided to “reload” mappings
in development environments – these may
cause strange errors in high load or ajax
scenarios
coldbox.handlersIndexAutoReload = true;
coldbox.handlerCaching = false;
MVC – The Controller (Handler)Rules and Anatomy of an Event Handler
• They can be simple CFCs or inherit from our
base handler: coldbox.system.EventHandler.
• They must exist in the correct handlers directory
under your application.
• They must NOT contain any business logic, that
is what the model or business layer is for.
MVC – The Controller (Handler)Rules and Anatomy of an Event Handler
• They must have public methods (actions) that
will respond to ColdBox events.
• Private events have an access type of private
and can only be called from within the
application by using the runEvent() method.
MVC – The Controller (Handler)Rules and Anatomy of an Event Handler
• Handlers are cached by default, unless the
handler caching setting is off. You can configure
persistence via metadata.
• You can easily wire up dependencies in your
handler by using the WireBox injection DSL.
MVC – The Controller (Handler)Composed Properties
• It is imperative that you realize that there is a
great object model behind every event handler
that will enable you to do your work more
efficiently.
• The following are the composed properties
every event handler has in their variables scope,
you do not need to do anything to retreive them,
they are already there :)
MVC – The Controller (Handler)Composed Properties
MVC – The Controller (Handler)Featured Properties
• Each event handler can also exhibit
several feature properties that can be
tuned to alter the behavior of the local
AOP interception points, event caching
and HTTP method security.
– aroundHandler_only /aroundHandler_except
– preHandler_only / preHandler_except
– postHandler_only / postHandler_except
– allowedMethods = {Get, POST, Delete}
MVC – The Controller (Handler)Anatomy of an Event Handler Action
• function sayHello(event,rc,prc){
– event : The request context object reference
– rc : A reference to the request collection inside of the request context object
– prc : A reference to the private request collection inside of the request context
object
MVC – The Controller (Handler)Get/Set Request Values
• We all need values in our applications, and we will
interact with the request context
• RC (and PRC) can
– hold data from our model layer so our views can display it
– retreive data from a user's request
MVC – The Controller (Handler)Get/Set Request Values
• You will either interact with the event object to get/set
values OR put/read values directly via the received rc
and prc references.
• We recommend using the references as structures are
much faster than method calls.
– Prefer: Rc.users = userService.get();
• However, the event object should not be discarded as it
has some pretty cool and funky methods of its own.
MVC – The Controller (Handler)“Event” Object
//set a value for views to use
event.setValue("name", "Luis");
// retrieve a value the user sent
event.getValue("name");
// retrieve a value the user sent or give me a default value
event.getValue("isChecked",false);
//param a value
event.paramValue("user_id","");
//remove a value
event.removeValue("name");
MVC – The Controller (Handler)“Event” Object
//check if value exists
if( event.valueExists("name") ){ }
// set a view for rendering
event.setView('blog/index');
// set a layout for rendering
event.setLayout('main');
// set a view and layout
event.setView(view="blog/userinfo",layout="ajax");
MVC – The Controller (Handler)Setting Views / Layouts
The event object is the object that will let you set the views
that you want to render, so please explore its API in the
CFC Docs
Event.setView(„myView‟) -- NO .cfm
Event.setView(„user/detail‟)
While views can be loaded inherently, it is certainly useful
to be able to switch views programaticaly (ex. user A gets
View A and user B gets View B for a given event)
MVC – The Controller (Handler)Setting Views / Layouts
We recommend that you maintain a consistent naming and location
schema between views and your handler and actions, often called
implicit views.
So if you have an incoming event called: users.index then make sure
in your views folder you have:
Views
-users
--index.cfm
This way debugging is much easier and also Implicit Views can be
used. Implicit views means that you won't use a event.setView() to
specify what view to render. It is implied the view to render will be the
same as the executing event.
MVC – The Controller (Handler)Relocating
The framework provides you with a method that you can use to relocate
to other events thanks to the framework super type object
It is extremely important that you use this method when relocating
instead of the native ColdFusion methods as it allows you to gracefully
relocate to other events or external URIs.
By graceful, we mean it does a lot more behind the scenes like making
sure the flash scope is persisted, logging, post processing interceptions
can occur and safe relocations.
MVC – The Controller (Handler)Relocating
setNextEvent
eventThe name of the event or SES pattern to relocate to, if not passed, then it will use the default
event found in your configuration file. (Mutex with URI and URL)
URL The absolute URL to relocate to (Mutex with URI and event)
URI The relative URI to relocate to (Mutex with event and URL)
queryString The query string to append to the relocation. It will be converted to SES if SES is used.
addToken Whether to add the cf tokens or not. Default is false
persistA comma-delimited list of request collection key names that will be flash persisted in the
framework's flash RAM and re-inflated in the next request.
persistStructA structure of key-value pairs that will be flash persisted in the framework's flash RAM and re-
inflated in the next request.
ssl Flag indicating if redirect should be done in ssl mode or not
baseURLIf used, then it is the base url for normal syntax redirection instead of just redirecting to the
index.cfm
postProcessExempt Do not fire the postProcess interceptors
statusCode The status code to relocate with
MVC – The Controller (Handler)Rendering Data
You can also use the event.renderData() method to render and
marshal data directly from an event handler without the need to set a
view for rendering.
Out of the box ColdBox can marshall data
(structs,queries,arrays,complex or even ORM entities) into the following
output formats: – XML, JSON, JSONP, HTML, TEXT, WDDX, PDF,Custom
event.renderData(type="json",data=qUsers)
MVC – The Controller (Handler)Model Integration
As mentioned in some of the early sessions, Wirebox allows us to inject
CFC‟s **NEARLY** anywhere!
component{
// Injection
property name=“model:funkyService" inject;
function index(event,rc,prc){
prc.data = funkyService.getFunkyData();
event.renderData(data=prc.data,type="xml");
}
}
MVC – The Controller (Handler)Model Integration
Coldbox offers a few function to allow us to consume CFC by
REQUESTING them
Via Façade:
prc.data = getModel("FunkyService").getFunkyData();
Directly to Wirebox:
prc.data = wirebox.getInstance("FunkyService").getFunkyData();
Both approaches do exactly the same, in all reality getModel() does a
wirebox.getInstance(), it is a facade method that is easier to
remember.
MVC – The Controller (Handler)Model Integration
Wirebox also offers you the capability to bind incoming
FORM/URL/REMOTE data into your model objects by convention.
populateModel(„myBean‟, rc);
This will try to match incoming variable names to setters or properties
in your domain objects and then populate them for you.
MVC – The Controller (Handler)Validation
Coldbox offers “ValidBox” to allow for annotation based validation
We currently recommend “ValidateThis”, but ValidBox is being
researched!
MVC – The Controller (Handler)Executing Events
Apart from executing events from the URL/FORM or Remote
interfaces, you can also execute events internally, either public or
private from within your event handlers
runEvent(event, prepostExempt, private, eventArguments)
MVC – The Controller (Handler)Testing Controllers
• ColdBox offers two approaches to testing your event handlers:
– Integration Testing : Tests everything top-down in your application
– Handler Testing : Like unit testing for handlers
• Integration testing will virtually create your application and execute
the event you want. Thus, loading everything in a typical request
and simulate it.
• The handler testing just tests the event handler in isolation much like
unit testing does.
MVC – The Controller (Handler)Testing Controllers
• To use “Integration testing”, your test should extend
coldbox.system.testing.baseIntegrationTest
• Events can be executed with the execute() function
function testindex(){
var event = execute("general.index");
assertEquals("Welcome!", event.getValue("welcomeMessage"));
}
• We can then retrieve the event object and thus the requestCollection
to use during our assertions
var event = execute("general.dspLogin");
var prc = event.getCollection(private=true);
assertEquals("general.dspLogin", prc.currentView );
MVC – The Controller (Handler)Questions??
MVC – The Controller (Handler)Assignment
• Add to existing unit test to run GET with a
nominatorPID argumentresults=this.myComp.get(nominatorPID='711933859');
assertTrue(isQuery(results));
• Run – FAIL!
• Add NominatorPID argument to
recommendationGateway
• Run – PASS!
MVC – The Controller (Handler)Assignment
• Create a simpler handler CFC for
recommendation.submission
– Add index function• We don‟t need to “setView” because we‟ve already got an implicit
view in place from last week
• Add an INIT method to recommendationGateway
<cffunction name='init' returnType="any" output="false">
<cfargument name="datasource"
inject="coldbox:datasource:main">
<cfreturn super.init(argumentCollection = arguments)>
</cffunction>
MVC – The Controller (Handler)Assignment
• Autowire recommendationGateway.
– Add the following after the CFCOMPONENT tag in your handler
• <cfproperty name=“recommendations”
inject=“model:recommendation.recommendationGateway”>
• Our “index” handler will call recommendationGateway for data, so
we‟ll add an assertion to
test/integration/recommendation/submissionTest.cfc testIndex
method
Prc = event.getCollection(private=true);
assertTrue(isDefined(„prc.recommendations‟));
assertTrue(isQuery(prc.recommendations));
MVC – The Controller (Handler)Assignment
• Run integration tests – FAIL!!
• Get the data into the handler
<cfset prc.recommendations =
recommendations.get(session.pid)>
• Run Test – PASS!
MVC – The Controller (Handler)Assignment
• Open the recommendation/submission/index.cfm view
– Remove GetFTARs CFINCLUDE
– Begin removal of “getSubmissions_CFCRet” array• Its now a query, so adjust calls like ArrayLen, CFLOOP, [cnt]
• Move the relocation and access checks code into your handler,
using setNextEvent
<cfif !prc.recordcount><cfset setNextEvent(url=„submit.cfm‟)>
</cfif>
<cfif structKeyExists(session,"nominateList") and len(trim(session.nominateList)) eq
0><cfset messageBox.warn(„You do not have access to submit nominations. Please contact Lou Anne
Phelps at the Graduate School. ([email protected]).“)>
<cfset setNextEvent(„general.index‟)>
</cfif>
MVC – The Controller (Handler)Assignment
• We‟ve just “factored up” the call for submission list data and created
/ updated the relevant tests! A small achievement that‟s taken us a
while, but the start of something beautiful!
MVC – The Controller (Handler)Next Week
• FORMS!