Thursday, December 25, 2008

An overly simple Model-Glue app

So there is good documentation on Model-glue framework. I followed along with their “Pig Latin Translator” but decided I need something more realistic. Who uses a translator that translates to “Pig Latin”? So here is a more realistic Login –> Validate Login –> show view on correct login || show error and login screen on bad login info type application in Model Glue.
If you understand this and follow, you’d have known the most of model glue in its basic form.
model
So here is the directory structure. Simply copy the skeleton app that they give you, then change the application-name (I use it same as the dir name) in 3 places:
1. Application.cfc
2. {app}/config/Coldspring.xml (find & replace)
3. {app}/config/ModelGlue.cfm (find & replace)
This should set you up for a basic MG app… run this and see if you get “Model Glue is running” page without errors

The following files are of consequence:
ModelGlue.xml file:
   1: <?xml version="1.0" encoding="UTF-8"?>
   2: <modelglue>
   3:   <controllers>
   4:     <controller name="MyController" type="bookstoremgcs.controller.Controller">
   5:       <message-listener message="OnRequestStart" function="OnRequestStart" />
   6:       <message-listener message="OnQueueComplete" function="OnQueueComplete" />
   7:       <message-listener message="OnRequestEnd" function="OnRequestEnd" />
   8:     </controller>
   9:     <controller name="UserController" type="bookstoremgcs.controller.userController">
  10:       <message-listener message="LoginInfoSubmitted" function="validateLoginInfo" />
  11:     </controller>
  12:   </controllers>
  13:   
  14:   <event-types>
  15:     <event-type name="extra-template">
  16:         <after>
  17:              <views><include name="main" template="templates/main.cfm" /></views>
  18:         </after>
  19:     </event-type>
  20:   </event-types>
  21:   
  22:   <event-handlers>
  23:     <event-handler name="page.index">
  24:       <broadcasts />
  25:       <results>
  26:         <result do="view.template" />
  27:       </results>
  28:       <views>
  29:         <include name="body" template="dspIndex.cfm" />
  30:       </views>
  31:     </event-handler>
  32:     <event-handler name="view.template">
  33:       <broadcasts />
  34:       <results />
  35:       <views>
  36:         <include name="template" template="dspTemplate.cfm" />
  37:       </views>
  38:     </event-handler>
  39:     <event-handler name="exception">
  40:       <broadcasts />
  41:       <views>
  42:         <include name="body" template="dspException.cfm" />
  43:       </views>
  44:     </event-handler>
  45:     <event-handler name="showLogin" type="extra-template">
  46:       <broadcasts />
  47:       <views>
  48:         <include name="loginForm" template="userLogin/frmLogin.cfm" append="false">
  49:           <value name="tryLogin" value="handleLogin" />
  50:         </include>
  51:         
  52:         <include name="body" template="userLogin/frmLogin.cfm" append="false" />
  53:       </views>
  54:       <results />
  55:     </event-handler>
  56:     <event-handler name="handleLogin" type="extra-template">
  57:       <broadcasts>
  58:         <message name="LoginInfoSubmitted" />
  59:       </broadcasts>
  60:       <views>
  61:         <include name="body" template="userLogin/dspLoginResult.cfm" append="false">
  62:           <value name="BackToLogin" value="showLogin" />
  63:         </include>
  64:       </views>
  65:       <results>
  66:         <result name="validationError" do="showLogin" redirect="true" append="" preservestate="true" />
  67:       </results>
  68:     </event-handler>
  69:   </event-handlers>
  70: </modelglue>



I have a custom “UserController.cfc” which you see is referred in the Controller Section:



   1: <cfcomponent name="userController" extends="ModelGlue.unity.controller.Controller" output="false">
   2:     <cffunction name="validateLoginInfo" access="remote" output="false" returntype="void">
   3:         <cfargument name="event" type="any" />
   4:         <cfset var result = "" />
   5:         <!--cfset var user = createObject("component", "bookstoremgcs.model.Users").init() /-->
   6:         <cfif arguments.event.getvalue("txt_username") eq "anang" and arguments.event.getvalue("txt_password") eq "abc123">
   7:             <cfset result = true />
   8:             <cfset arguments.event.addTraceStatement("User", "Login Information is valid", result) />
   9:         <cfelse>
  10:             <cfset result = false />
  11:             <cfset arguments.event.addTraceStatement("User", "Login Information is valid", result) />
  12:             <cfset arguments.event.setValue("loginError", "Error in Username or Password") />
  13:             <cfset arguments.event.addResult("validationError") />
  14:         </cfif>
  15:         
  16:         <cfset arguments.event.setValue("LoginValidationResult", result) />
  17:     </cffunction>
  18: </cfcomponent>



The form page looks like this. The copyToScope() is worth mentioning as it will allow you to copy certain variabled from the “event” structure to a “variables” (or other) scope.


   1: <cfset event.copyToScope(variables, "tryLogin, username,loginError")>
   2: <cfset onSubmitNavigate = event.linkTo(tryLogin) />
   3:  
   4: <cfif event.exists("loginError")>
   5: <h4><cfoutput>#loginError#</cfoutput></h4>
   6: </cfif>
   7:  
   8: <cfform action="#onSubmitNavigate#">
   9: <table>
  10: <tr>
  11:     <td>Username:</td>
  12:     <td><cfinput type="text" name="txt_username" value="#username#" /></td>
  13: </tr>
  14: <tr>
  15:     <td>Password:</td>
  16:     <td><cfinput type="password" name="txt_password" value="" /></td>
  17: </tr>
  18: <tr>
  19:     <td></td>
  20:     <td><cfinput type="submit" name="btn_submit" value="Login" /></td>
  21: </tr>
  22: </table>
  23: </cfform>

The showLoginResult file:


   1: <cfset event.copyToScope(variables, 'BackToLogin,LoginValidationResult') />
   2:  
   3: <cfoutput>
   4:     Login Result: #LoginValidationResult#<br />
   5:     <a href="#event.LinkTo(BackToLogin)#">back</a>
   6: </cfoutput>

The workflow theory is simply this:

  • The user submits login info
  • The form submission is rcvd. by MG, MG consults the MG xml and decides that the event (based on the URL param event) is “handleLogin” and passes control
  • MG sees that the event-handler declares a broadcast “LoginInfoSubmitted” message.
  • MG consults the MG.XML to see who is listening, finds the UserController.cfc and invokes the listener method,
  • MG passes control to the controller method which (in this case) validates the login and puts the result in the “event” structure
  • At this point MG gets control, consults the MG.xml to see if there are any “results” expected.
  • If one is expected, MG consults the event structure to evaluate result (in our case this is “validationError” which is coded in the controller as <cfset arguments.event.addResult("validationError") />)
  • If no result variable is found the MG proceeds as per the instruction, in our case, to show a page that says “login success”
This is it. Simple, and pretty cool !
There are some other topics on how to wrap the generated content in templates using viewCollection which is shown in the xml under “event-type” tag.
That’s it… that’s Model-Glue in it’s most basic form.

No comments: