500 likes | 732 Views
About Me. Explosive Ordnance Disposal. About Me. Technically Speaking…. SQL (MSSQL, Sybase, Oracle, Access) ColdFusion OOP/CFCs Webservices Ajax Model-Glue Coldspring JavaScript Visual Basic. (www.dougboude.com/blog). What I’ll be Talking About. OOP Essentials
E N D
About Me Explosive Ordnance Disposal
Technically Speaking… • SQL (MSSQL, Sybase, Oracle, Access) • ColdFusion • OOP/CFCs • Webservices • Ajax • Model-Glue • Coldspring • JavaScript • Visual Basic (www.dougboude.com/blog)
What I’ll be Talking About • OOP Essentials • Adding to your personal Lexicon • Learning to see what is invisible
Why OO in the first place? • Code Segregation and logic isolation / encapsulation • Reduced Development Time • Code Re-Use • Job Security/opportunities
CFCs as Objects • A coldfusion ‘object’ is simply a persistent instance of a CFC. This… <CFINVOKE component="model.user" method="getUser" returnvariable="stUser" argumentcollection=“stArgs" /> As opposed to… <CFSET objUser = CreateObject("component","model.user") /> <cfset stUser = objUser.getUser(argumentCollection = stArgs)/>
CFCs as Objects SCOPE within a CFC • THIS – visible internally and externally to the CFC Within the component… <cfset this.GlobalVal= “See me now?”/> Within our calling template… <cfscript> myTestObj = CreateObject(“component”,”myTest”); myTestObj.GlobalVal = “I set you from outside of the object!”; </cfscript> • Variables • Var
CFCs as Objects SCOPE within a CFC • THIS • Variables – available to any method within the CFC, but not directly visible externally <cfset variables.internalValue = "See me now?" /> <cffunction access="public" name="sampleMethod" output="false" returntype="string"> <cfreturn variables.internalValue /> </cffunction> • Var
CFCs as Objects SCOPE within a CFC • THIS • Variables • Var – Ensures that a variable will be available local to the method, and thread safe <cffunction access="public" name="sampleMethod" output="false" returntype="void"> <cfargument name="sourceID" type="numeric" required="yes“/> <cfset variterations = 0/> <cfset varurlitems = "“/> <cfset varnewInsertItems = arraynew(1)/> </cffunction>
CFCs as Objects Constructors • Typically named ‘INIT’ • Used for the same reasons we use application.cfm/cfc or an app_globals.cfm file • Always should return ‘This’ <cffunction access="public" name="init" output="false" returntype="COM.RSS"> <cfargument name="dsn" type="string" required="yes" default=""> <cfset variables.dsn = arguments.dsn> <cfreturn this> </cffunction> <!--- take note of returntype and access values…--->
CFCs as Objects CFPROPERTY Tag • Used for informational purposes only…no functional value. • Webservices will read them; • Some CFC Documenters will read them
CFCs as Objects Anatomy of an Object • Has properties (static values, such as DSN) • Can ‘DO’ things (has methods that can be called) That’s it.
CFCs as Objects When Objects Use Objects! The CFC content… <cffunction access="public" name="init" output="false" returntype=“model.userService"> <cfargument name="dsn" type="string" required="yes" default=""> <cfset variables.dsn = arguments.dsn /> <cfset variables.objUser =CreateObject("component","model.user") /> <cfset variables.objAddress =CreateObject("component","model.address") /> <cfreturn this /> </cffunction> <cffunction name="insertUser" access="public" output="false" returntype=“void"> <cfargument name="firstname" type="string" required="true" /> <cfargument name="address1" type="string" required="true" /> <cfset var userid = "" /> <cfset var addressid = "" /> <cfset userid = variables.objUser.insertUser(firstname=arguments.firstname, DSN=variables.DSN)/> <cfset addressid = variables.objAddress.insertAddress(userid = userid, address1 = arguments.address1, DSN=variables.DSN)/> </cffunction>
CFCs as Objects When Objects Use Objects! Calling the CFC… <!--- Instantiate the userService object...note that we're calling INIT upon instantiation and passing in our DSN value from a persistent scope ---> <cfset objUserService = CreateObject("component","model.userService").init(DSN = application.dsn) /> <!--- make our call to the insertUser method. insertUser returns ‘void’, so we don’t set the call equal to any variable. ---> <cfset objUserService.insertUser(firstname ="Doug Boude",address1 ="109 Main St")/>
Adding to Your Personal Lexicon… Method Injection Event-Handler DAO Accessor Transfer Object Controller Mutator Service Layer ViewStack Listener Scaffolding Model Event Message Broadcast ViewState Implicit Invocation Auto-Wiring
Adding to Your Personal Lexicon… • Just a few… • Abstraction • Façade • Service Layer • Model • View • Controller Doug Boude’s Personal OO Lexicon: http://www.dougboude.com/documents/dougboudeslexicon.cfm Or Go to www.dougboude.com and search for ‘lexicon’
Adding to Your Personal Lexicon… Service Layer
BREAK POINT (Time for Model-Glue!)
Model-Glue: Unity • What is this trinity? • Model-Glue • Reactor • Coldspring • Where does it live? • Anywhere globally accessible. One instance of the framework drives N apps
Model-Glue: Unity Typical Model-Glue Application directory Structure
Model-Glue: Unity Configuration • Application.cfc • For me and my team, we typically only use this to name the app, not much else. • Coldspring.xml • Where a lot of the meaty app config takes place. Coldspring manages objects…ModelGlue is an object…so here is where we configure our “model glue bean”. • ModelGlue.xml • The heart and soul of your application. Equate it with the index file in Fusebox 2, or the CF_Switch file in FB3. • Reactor.xml • Reactor manages relationships among your backend data tables. Configure those relationships here if you opt to use Reactor.
Model-Glue: Unity COLDSPRING.XML SNIPPET… <beans> <!-- This is your Model-Glue configuration --> <bean id="modelGlueConfiguration" class="modelglue.unity.framework.ModelGlueConfiguration"> <!-- Be sure to change reload to false when you go to production! --> <property name="reload"><value>true</value></property> <property name="rescaffold"><value>false</value></property> <!-- Be sure to change debug to false when you go to production! --> <property name="debug"><value>false</value></property> <property name="defaultEvent"><value>page.home</value></property> <property name="reloadPassword"><value>true</value></property> <property name="viewMappings"><value>/views</value></property> <property name="generatedViewMapping"><value>/views/generated</value></property> <property name="configurationPath"><value>/config/ModelGlue.xml</value></property> <property name="scaffoldPath"><value>/config/scaffolds/Scaffolds.xml</value></property> <property name="statePrecedence"><value>form</value></property> <property name="reloadKey"><value>init</value></property> <property name="eventValue"><value>event</value></property> <property name="defaultTemplate"><value>index.cfm</value></property> <property name="defaultExceptionHandler"><value>exception</value></property> <property name="defaultCacheTimeout"><value>5</value></property> <property name="defaultScaffolds"><value>list,edit,view,commit,delete</value></property> </bean>
Model-Glue: Unity REACTOR.XML SNIPPET… <objects> <object name="User"> <hasMany name="UserOrganization"> <relate from="UserId" to="UserId" /> </hasMany> </object> <object name="UserOrganization"> <hasOne name="user"> <relate from="userId" to="userId" /> </hasOne> <hasOne name="Organization" > <relate from="OrganizationId" to="OrganizationId" /> </hasOne> </object> <object name="Organization" > <hasMany name="User"> <link name="UserOrganization" /> </hasMany> </object> </objects>
Model-Glue: Unity • <modelglue> • <controllers> • <controller name="AuthenticationController" type="controller.AuthenticationController"> • <message-listener message="OnRequestStart" function="SetCurrentUser" /> • <message-listener message="Logout" function="logoutUser" /> • <message-listener message="Login" function="authenticateUser" /> • </controller> • </controllers> • <event-handlers> • <event-handler name="user.login"> • <broadcasts> • <message name="Login" /> • </broadcasts> • <views> • <include name="personalInfo" template="dspPersonalInfo.cfm" /> • <include name="body" template="dspLanding.cfm" /> • </views> • <results> • <result name="valid" do="user.loggedin" redirect="true" /> • <result name="invalid" do="page.home" redirect="true" /> • </results> • </event-handler> • </event-handlers> • </modelglue>
Model-Glue: Unity Modes of Operation • Development • Production In order to toggle between development and production modes, edit the "reload" property in Coldspring.xml, setting the value to "true" for development or "false" for production. In addition, edit the "debug" property, setting the value to "true" for development or "false" for production.
Model-Glue: Unity MAKING CHANGES DURING DEVELOPMENT Because MG:Unity maintains some objects in persistent scopes like Application and Session, certain types of changes made during development require certain actions in order to see those changes. If you make a change to: • ModelGlue.xml - Make sure the "reload" property in Coldspring.xml has a value of "true"; initialize the app by going to the url [app url]/?init=true • Coldspring.XML - If reload=true, then there's nothing to do; otherwise, set reload=true, then add init=true parameter to the url (www.myapp.com/index.cfm?init=true) • a controller cfc - same as above • a model cfc - all changes should be visible real time...nothing to do • database - Reactor needs reset. In Coldspring.XML, in the Reactor Bean, make sure mode is development and make sure reload=true. if reload=false, follow steps for Coldspring.xml changes.
Model-Glue: Unity Seeing What is Invisible: Visualizing the Model-Glue Event Lifecycle
Model-Glue: Unity Web page Visualizing the Model-Glue Event Lifecycle (index.cfm?event=user.login) 1. A request is made! ModelGlue.XML <modelglue> <event-handlers> <event-handler name="user.login"> <broadcasts> <message name="Login" /> </broadcasts> … </event-handler> </event-handlers> </modelglue> MG FRAMEWORK 2. MG Framework reads the description of the requested event. Event bucket is created and Incoming variables (URL, FORM) are placed into it. Messages are broadcast as defined by the xml… Event
Model-Glue: Unity Visualizing the Model-Glue Event Lifecycle Controller “hears” the ‘login’ message being broadcast… ModelGlue.XML • <modelglue> • <controllers> • <controller name=“UserController" type="controller.UserController"> • <message-listener message="Logout" function="logoutUser" /> • <message-listener message="Login" function=“UserLogin" /> • </controller> • </controllers> • </modelglue> Controller CFC 3. Listening controller executes the method associated with the message… Any Results, data, or other relevant information resulting from the method execution is placed into the event bucket… Event Event bucket is passed back to the framework.
Model-Glue: Unity Visualizing the Model-Glue Event Lifecycle Event bucket passed back from the controller to MG… Event ModelGlue.XML <modelglue> <event-handlers> <event-handler name="user.login"> <results> <result name="valid" do="user.loggedin" /> <result name="invalid" do="page.home" /> </results> … </event-handler> </event-handlers> </modelglue> MG FRAMEWORK 4. Any relevant ‘Results’ value placed into the event bucket by the controller are evaluated and events are called as appropriate. (in this way, you can “chain” events…) If there are no relevant ‘Results’, any defined Views for that event are then evaluated…
Model-Glue: Unity Visualizing the Model-Glue Event Lifecycle Web page 5. The views defined in the <VIEWS> tag are rendered by MG and returned to the browser PersonalInfo body Values needed for display purposes are gotten out of the event bucket, which is now known by the views as the “ViewState” bucket. #PersonalInfo# (This stack of rendered views is called the Viewstack) <modelglue> <event-handlers> <event-handler name="user.login"> <views> <include name="personalInfo" template="dspPersonalInfo.cfm" /> <include name="body" template="dspLanding.cfm" /> </views> … </event-handler> </event-handlers> </modelglue> ViewState ModelGlue.XML MG FRAMEWORK
Model-Glue: Unity Commonly used MG functions to interact with the Event Bucket (within a controller): • GetValue([name of formfield]) • ValueExists([name of formfield]) • SetValue([variable name],[value]) • Trace([variable name],[value]) Outstanding, simple documentation at http://docs.model-glue.com/
Model-Glue: Unity Views! • Live in the Views directory(see coldspring.xml, modelglue bean definition) • Contain all of your html and layout • Get their dynamic data from the ViewState bucket • Rendered in the order defined in Modelglue.xml • Last template rendered is it! • Rendered views are all placed on the “viewstack” • Rendered views can be included within one another.
Model-Glue: Unity Views! • Retrieve all needed values out of the viewstate first thing (neatness, readability) <cfset lex = viewstate.getValue("lexicon")/> <cfset linkQuery = viewstate.getValue("linkQuery")/> • Retrieve all needed rendered views out of the ViewStack <cfset NavView = viewcollection.getView("nav") /> • Use the values/views within your template! <span class="contentTitle"> <cfoutput>#lex.getTerm("pod.reports.title")#</cfoutput> </span> <div id="nav"> <cfoutput>#NavView#</cfoutput> </div>
Model-Glue: Unity Commonly used MG functions to interact with the Viewstate and ViewCollection (within a display template): • Viewstate.GetValue([name of value]) • Viewstate.Exists([name of value]) • ViewCollection.Exists([name of rendered template, as defined in modelglue.xml event]) • ViewCollection.getView([name of view]) Outstanding, simple documentation at http://docs.model-glue.com/
Model-Glue: Unity Controllers! • Live in the Controller directory • Perform any needed communication with Model-Glue • Methods will typically receive the Event bucket object as an argument, and nothing more (see modelglue.xml for exception) • Role is to facilitate exchange of data between the model and the view via the Event bucket…that’s it. • KISS it when writing controller functions…should be short and sweet, with all business logic/rules in the model.
Model-Glue: Unity Segregating Functionality: Rules of Thumb Controller • The controller’s job is nothing more than facilitating the exchange of information between the view and the model. • The role of the controller should always be minimized as much as possible. • No business logic or rules should ever be found in the controller • Sample controller function: <cffunction name="makeOrgTree" access="public" output="false" returntype="void" hint="I'll return you an org tree in whatever form you request it, for the currently selected org"> <cfargument name="event" type="any" required="yes"/> <cfset varformat = arguments.event.getArgument("format")/> <cfset varorgID = session.currentUserOrg.getOrganizationID() /> <cfset varORM = getModelGlue().getORMService()/> <cfset varretTree = getOrganization().makeOrgTree(orgID,ORM,format)/> <cfset arguments.event.setValue("orgTree",retTree)/> <cfset arguments.event.trace("org tree result",replace(retTree,"""","\""","all"))/> </cffunction> (where the ‘argument’ value came from in the actual event doing the broadcast…) <message name="makeOrgTree" > <argument name="format" value="STRING" /> </message>
Model-Glue: Unity Segregating Functionality: Rules of Thumb Generally Speaking… • If coordination between two or more objects is required, you should probably handle that by creating a service layer object in your model • A View template must be self-sufficient…never talks to anyone but the viewstate or viewcollection (no ‘session’, no ‘application’, no nothing else!) • Consider using a “persistence façade” to handle communication with session, client, or whatever ‘sticky’ scope you choose. • When architecting/designing objects for the model, do so completely without the framework in mind at all! They should be stand alone and self-sufficient. • Consider using a configuration object to hold application configuration data, such as DSN, security considerations, etc. Sample configuration object bean definition with coldspring.xml: <bean id="configuration" class="model.configuration"> <property name="forceLogin"><value>false</value></property> <property name="publicEvents"> <value>page.about,page.faq,page.contact,page.login</value> </property> <property name="loginEvent"><value>home</value></property> <property name="dsn"><value>cwes_prod</value></property> </bean>
Model-Glue: Unity Model-Glue Application Architecture: How my team does it • Define basic functionality/data sets • Perform “Ontology” for each area • Write stubbed out CFCs (with CFCDocs in mind to make it self-documenting) • Assemble coded components • Supplement with Canvas Wiki
Model-Glue: Unity Model-Glue Application Architecture: How my team does it
Model-Glue: Unity Model-Glue Application Architecture: How my team does it ONTOLOGY • Ask the questions (for instance, regarding a lexicon object): • What do I know about myself? • I know what language is currently selected; • I know the ID of the current user’s employer • What can I do? • I can retrieve a lexicon term based on incoming term key, either a default value or the employer’s override value; • I can create a new lexicon term • I can edit an existing term
Model-Glue: Unity Model-Glue Application Architecture: How my team does it Stub out CFC(s) • Based on ontology exercise: • Write a CFC that contains methods and variables to cover all points • Determine what other objects this CFC will be dependent upon (if any) and inject them • If coordination between objects is needed for this CFC, make it a service layer object and move the real functionality down to lower level objects • Write methods to include only incoming arguments. Comment heavily to explain what functionality will take place within that method. • ALWAYS populate ‘HINT’ attributes of all tags! This enhances self-documentation and assists the programmer.
Model-Glue: Unity Model-Glue Application Architecture: How my team does it CODING • Divvy work out to programmers! • Assemble components as completed • Utilize MG’s ability to use includes within Modelglue.xml in order to alleviate work overwriting. • <modelglue> • <include template="/config/AdminEvents.xml" /> • … • </modelglue>
Model-Glue: Unity COLDSPRING! • What does it do? • Manages your objects for you • Instantiates/creates them; • Automagically injects needed objects within other objects; • Provides “one stop shopping” point at which to manage configuration settings for objects • Allows you to automagically inject functionality on a global scale (logging, for instance) • Probably several other things I’m not aware of
Model-Glue: Unity COLDSPRING! • Two ways to put an object inside of another object: • Without Coldspring: • <cfcomponent> • <cffunction name=“init” returntype=“any”> • <cfset variables.objActivityCenter = createObject(“component”,”model.activitycenter”).init(url=http://www.fiservhealth.com/webservices.cfm?wsdl) /> • </cffunction> • … • </cfcomponent> • (at this point, any method within the cfc can get at the ActivityCenter object) • ActivityCenter object used in multiple other objects…interface changes…what then? • Coldspring provides the answer.
Model-Glue: Unity COLDSPRING! • Two ways to put an object inside of another object: • WITH Coldspring(step 1): • Define the object inside of Coldspring.XML; • <bean id="activitycenter" class="model.activitycenter"> • <property name="url"> • <value>http://www.wausau.com/activitycenter.jsp?wsdl</value> • </property> • </bean> • Notice that we are setting the value of the required ‘URL’ parameter here, within the bean definition, so any object using ActivityCenter will never have to pass it in…Coldspring does that for us when it creates the Activitycenter bean!
Model-Glue: Unity COLDSPRING! • Two ways to put an object inside of another object: • WITH Coldspring (step 2): • Create obligatory getters and setters inside the components that need another object injected. • <cfcomponent> • <CFFUNCTION access="public" name=“setActivityCenter" output="false" returntype="void" HINT="I set the activitycenter obj to a local variable"> • <CFARGUMENT name=“ActivityCenter" type="model.ActivityCenter" required="true" /> • <CFSET variables._ ActivityCenter = arguments. ActivityCenter /> • </CFFUNCTION> • <CFFUNCTION access="public" name="getActivityCenter" output="false" HINT="I retrieve the ActivityCenter obj from a local variable"> • <CFRETURN variables._ ActivityCenter /> • </CFFUNCTION> • </cfcomponent> • Coldspring sees the getter and setter, knows it has a bean named ActivityCenter, and will automatically inject it for you! If the URL or other ActivityCenter parameters EVER change, you need only change it once within the Coldspring.XML file. SWEET!
EXTRAS Selecting a Framework Why? • App needs to remain flexible and growth oriented; • High degree of customization potentially needed; How? • Scope out a small but relevant app and build it with each of the flavors being considered.
EXTRAS OTHER LINKS: CFCDoc http://www.spike.org.uk/projects/cfcdoc/index.cfm MGDoc http://ray.camdenfamily.com/index.cfm/2006/3/30/MGDoc-a-ModelGlue-documentor Canvas Wiki http://ray.camdenfamily.com/index.cfm/Canvas-Wiki