Getting Started with Cairngorm – Part 4

November 7th, 2007

The basic Cairngorm Event Flow that is handled in Part 3 is essential to any Cairngorm application, but most applications interact with a server. The Service to Worker pattern that was discussed in the previous tutorial is essential to this process. To learn the expanded Cairngorm Flow, you will need to learn a few new Cairngorm elements.

The Server Interaction Elements

There are three new types of classes that you will need to be familiar with to understand the full Service to Worker Pattern.

  • ServiceLocator [Reference] - The ServiceLocator is a singleton that contains references to all of the services that your application will use. It usually includes HTTPService, WebService, or RemoteObject calls.
  • Business Delegate - A Business Delegate has three functions: to locate the service that is needed, to call a method on the service, and to route the result to the specified responder. There is usually one Business Delegate for each service.
  • Value Object - Value Objects are classes that by definition do not require any methods, only properties. They allow for the application to transfer strongly-typed complex data objects to and from the server.

Organizing Your Cairngorm Project (With Server Interaction)

In the last tutorial you saw the standard Cairngorm structure for a project. To complete this structure, you will need to add an additional two folders:

  • /business - This folder typically contains two types of files: the ServiceLocator (named Services.mxml) and Business Delegates.
  • /vo - This folder contains the value objects that are passed to and from the server.

Understanding the Process

Before you begin crafting the server interaction for your project, you need to understand the full Service to Worker pattern. Figure 1 illustrates the entire process.

Full Cairngorm Diagram Final

Figure 1 - Cairngorm Event Flow with Server Interaction

Phase I - Execution Phase

  1. The user is viewing a component that has a login button. When the button is clicked, that component would dispatch an event named LoginEvent that extends CairngormEvent.
  2. The FrontController would receive the LoginEvent and execute the command that is mapped to that event. For this example, it will be named LoginCommand and will implement ICommand.
  3. The Command is executed and received the LoginEvent as an argument. The LoginCommand creates an instance of the Delegate while passing a reference to the Responder. The Command then calls the specified method on the Delegate.
  4. The Delegate gets the service from the ServiceLocator and calls the method on the specific service.
  5. The service defined in the ServiceLocator receives the information that is passed to it from the call in the Delegate.

Phase II - Application Tier Processing

Phase III - Response Phase

  1. The service that is defined in the ServiceLocator returns the result of the server-side processing.
  2. The Delegate receives the result and passes it to the specified responder.
  3. The Responder receives the result indicating a successful login.
  4. The ModelLocator will have a predefined constant for the "Logged in View". The Responder will change workflowState to this value.
  5. Since the view is bound to workflowState, it will automatically update itself to the new view.

Note: Traditionally the Command and Responder were the same class in a Cairngorm application. If you are working with other developers, the applications will probably be coded in this manner. However, most developers (including Adobe Consulting) are now separating these classes into two different classes. For a less complex project, it might not make sense to separate these items, but in a large application it could prove advantageous in organization and practice to have two different classes.

The ServiceLocator

The ServiceLocator is a singleton that contains references to all of the services that the application will be using. These services can be RemoteObjects, HTTPServices, WebServices, or custom services. Like the FrontController, this class is usually instantiated in your main application file. Unlike many of the other Cairngorm project assets that you have created, this class can be defined in MXML (as well as ActionScript). To properly define this in MXML, there must be a namespace that points to the business folder of the Cairngorm package. In this case the namespace cairngorm is typically used. The ServiceLocator in Code Example 1 has one service defined, loginService.

Actionscript:
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <cairngorm:ServiceLocator
  3.     xmlns:cairngorm="com.adobe.cairngorm.business.*"
  4.     xmlns:mx="http://www.adobe.com/2006/mxml">
  5.    
  6.     <!-- Login Service -->
  7.     <mx:RemoteObject id="loginService"
  8.         showBusyCursor="true"
  9.         destination="ColdFusion"
  10.         source="CairngormTest.CairngormLogin">
  11.        
  12.         <mx:method name="login" /> 
  13.        
  14.     </mx:RemoteObject>
  15.    
  16. </cairngorm:ServiceLocator>

Code Example 1 - Sample ServiceLocator

The ServiceLocator is typically named Services.mxml and resides in the business folder of your Cairngorm project.

Commands with Server Interaction

In the last tutorial you hard-coded some values into the LoginCommand to check for a specific username and password. Here was the execute method:

Actionscript:
  1. public function execute(event:CairngormEvent):void {
  2.     var loginEvent:LoginEvent = event as LoginEvent;
  3.     if( (loginEvent.username == "david") && (loginEvent.password == "password")) {
  4.         modelLocator.workflowState = ViewModelLocator.WELCOME_SCREEN;
  5.     }
  6. }

Code Example 2 - Command Without Delegate

This methodology will hold true for any Command that doesn't have server interaction, but if you do have server interaction, it will need to be modified to include the delegate. First, you will need to make a decision. As stated earlier, you can have separate Commands and Responders, or they can be the same class. For this example, they will be the same class. The Command will now also need to implement the mx.rpc.IResponder class (please note that com.adobe.cairngorm.business.Responder is deprecated and should no longer be used).

The other initial change is that the execute method now instantiates a class named LoginDelegate (which will be created shortly). The LoginDelegate class required one argument in its constructor, the Responder of the service. In this case, the Command will function both as the command and the responder, so you simply need to insert the this keyword inside the parenthesis. If you chose to have a separate responder, you would insert the reference to it here (instead of the this keyword).

Actionscript:
  1. package net.davidtucker.CairngormSample.commands {
  2.    
  3.     import com.adobe.cairngorm.commands.ICommand;
  4.     import com.adobe.cairngorm.control.CairngormEvent;
  5.     import mx.controls.Alert;
  6.     import mx.rpc.IResponder;
  7.     import net.davidtucker.CairngormSample.business.LoginDelegate;
  8.     import net.davidtucker.CairngormSample.events.LoginEvent;
  9.     import net.davidtucker.CairngormSample.model.ViewModelLocator;
  10.    
  11.     public class LoginCommand implements ICommand,IResponder {
  12.    
  13.         public var modelLocator:ViewModelLocator = ViewModelLocator.getInstance();
  14.        
  15.         public function LoginCommand() {
  16.         }
  17.        
  18.         public function execute(event:CairngormEvent):void {
  19.             var loginEvent:LoginEvent = event as LoginEvent;
  20.             var delegate:LoginDelegate = new LoginDelegate( this );
  21.             delegate.login(loginEvent.loginAttempt);
  22.         }
  23.        
  24.         public function result( event:Object ):void {
  25.             if(event.result == true) {
  26.                 modelLocator.workflowState = ViewModelLocator.WELCOME_SCREEN;
  27.             } else {
  28.                 mx.controls.Alert.show("Password Incorrect","ERROR");
  29.             }
  30.         }
  31.        
  32.         public function fault( event:Object ):void {
  33.             trace("Service Error");
  34.         }
  35.        
  36.     }
  37. }

Code Example 3 - Command with Server Interaction Through a Delegate

Value Objects

A Value Object does not extend or implement any Cairngorm class. As stated earlier, it simply is a class that is only required to have properties, but not methods. For example, if you created a Value Object for a Login - it would have a property for username and a property for password.

Actionscript:
  1. package net.davidtucker.CairngormSample.vo {
  2.    
  3.     [RemoteClass(alias="CairngormTest.LoginVO")]
  4.     public class LoginVO {
  5.        
  6.         public var username:String;
  7.         public var password:String;
  8.        
  9.         public function LoginVO(username:String,password:String) {
  10.             this.username = username;
  11.             this.password = password;
  12.         }
  13.        
  14.     }
  15. }

Code Example 4 - Value Object for Login

The RemoteClass metatag is important to note. This will allow the corresponding server-side object (ColdFusion Component, PHP Class, Java Class, etc...) to be mapped to this Value Object. In this case, it is mapped to a ColdFusion component named LoginVO in the CairngormTest folder.

Business Delegates

The final design pattern in the Cairngorm Micro-Architecture is the Business Delegate. A Business Delegate essentially is the abstraction layer between your services and the rest of your application. As stated earlier, it has three functions. First, the Business Delegate will locate the service that is needed in the ServiceLocator. Second, it will call a method on that service. Finally, it will route the response back to the specified responder (usually either a command or separate responder).

A Delegate class doesn't extend or implement and Cairngorm classes, but it generally follows the following guidelines.

  • The Delegate has at least two properties: one named service which is a reference to a service in the ServiceLocator and one named responder which is the responder for the service calls.
  • There is a method for each server-side method that you will be calling.
  • Both the responder and the service variables are set in the constructor.
Actionscript:
  1. package net.davidtucker.CairngormSample.business {
  2.    
  3.     import mx.rpc.IResponder;
  4.     import net.davidtucker.CairngormSample.vo.LoginVO;
  5.     import com.adobe.cairngorm.business.ServiceLocator;
  6.    
  7.     public class LoginDelegate {
  8.         private var responder : IResponder;
  9.         private var service : Object;   
  10.        
  11.         public function LoginDelegate( responder:IResponder ) {
  12.             this.responder = responder;
  13.             this.service = ServiceLocator.getInstance().getRemoteObject("loginService");
  14.         }
  15.        
  16.         public function login(login:LoginVO):void {
  17.             var call:Object = service.login( login );
  18.             call.addResponder( responder );
  19.         }
  20.        
  21.     }
  22. }

Code Example 5 - Service Delegate

There are many benefits to having this layer. If coded correctly, you should be able to change out the server interaction (going from PHP to ColdFusion for example) and only have to change the code in your ServiceLocator and your Delegate. You also can easily insert "stub code" to simulate the actual server interaction during the early stages of development.

The Application Tier

In this example, the Application Tier will be handled by a ColdFusion 8 installation. It will contain two Coldfusion components. These components are purposefully simple.

  1. LoginVO.cfc - This component will correspond to the ActionScript class LoginVO that you created earlier.
  2. CairngormLogin.cfc - This component will perform the actual login processing.

In this example, the Flex application will pass a LoginVO ActionScript object to the CairngormLogin.cfc's login method through a RemoteObject call. This will be mapped to a LoginVO.cfc object. If this LoginVO.cfc object has the username "david" and the password "password" the method will return true. If not, it will return false.

Coldfusion:
  1. <cfcomponent displayname="LoginVO" hint="Login VO For CairngormTest" output="false">
  2.  
  3.     <cfset this.username = "" />
  4.     <cfset this.password = "" />
  5.    
  6. </cfcomponent>

Code Example 6 - LoginVO.cfc

Coldfusion:
  1. <cfcomponent displayname="CairngormLogin" hint="CFC to Test Cairngorm Service Interaction" output="false">
  2.  
  3.     <cffunction name="login" displayname="login" access="remote" output="false" returntype="boolean">
  4.         <cfargument name="loginAttempt" type="LoginVO" required="true" />
  5.            
  6.             <cfif (loginAttempt.username EQ "david") AND (loginAttempt.password EQ "password")>
  7.                 <cfreturn true />
  8.             <cfelse>
  9.                 <cfreturn false />
  10.             </cfif>
  11.        
  12.     </cffunction>
  13.    
  14. </cfcomponent>

Code Example 7 - CairngormLogin.cfc

Application Code
Download (11 Kb)

Looking Ahead

There are only two tutorials remaining in the Cairngorm series. In the next tutorials you will learn about code generation options for Cairngorm, and you will also build an actual application using Cairngorm.

Reference

The following resouces should assist you in getting Flex connected to your application server.

Cairngorm, Flex | Comments | Trackback | Del.icio.us | Digg | Technorati Jump to the top of this page

36 comments on “Getting Started with Cairngorm – Part 4”

  1. 01

    David - I’m really enjoying this series. Keep up the good work - you’re helping a lot of people!

    Jim Rutherford at November 7th, 2007 around 1:03 pm
    Jump to the top of this page
  2. 02

    Thanks Jim! It is always encouraging to hear that people are benefiting from these tutorials.

    David Tucker at November 7th, 2007 around 1:08 pm
    Jump to the top of this page
  3. 03

    David,

    This is a great series! Thanks for the note about folks separating the Command and the IResponder used. It’s bugged the heck out of me for a long time that they’re often one and the same, because it destroys a good deal of portability (a responder to a PHP-based XML service is likely to have to do different things than a responder to a CF-based AMF service!).

    One approach I’ve taken (posting to ask for your thoughts, not to suggest a reference model) that cuts down a bit on the number of classes is to go ahead and implement IResponder in the command (keeping the “unit of work” on both side of an sync request in one class), but to require it to only deal in domain object / vos of the application.

    In the delegate’s service invocation, the delegate creates its own Responder instance pointing to result/fault methods for the operation invoked. They’re responsible for handling the raw response (such as morphing XML results to VOs) then passing the (massaged) response back up the chain to the command itself. Any thoughts on that?

    Joe rinehart at November 7th, 2007 around 1:39 pm
    Jump to the top of this page
  4. 04

    It is the best tutorial for cairngorm in the net.Thanks a lot.

    T

    Tony Chang at November 7th, 2007 around 1:39 pm
    Jump to the top of this page
  5. 05

    @Joe - Just to give a heads up, one of the next two tutorials will deal with “Best Practices”. This is one of the items that I will cover - and I will use a very similar method to what you are describing.

    Specifically, I create a Factory (generally one per data source) that has methods to transform my server side data into the VO or a Model object of the application. These factories are called by the response methods inside of the Delegate. This “massaged” data is passed to the Command responder (as you described). This is the best way that I have found to create an application where you truly can switch the server-side implementation without recoding both the Commands and Delegates.

    @Tony - Thanks! As I said earlier, it is great to hear that people enjoy (and can use) these tutorials.

    David Tucker at November 7th, 2007 around 3:43 pm
    Jump to the top of this page
  6. 06

    [...] Tucker has a 4 part series Flash video on the topic Getting Started with [...]

    Cairngorm Pointers « Kaushik’s Blog at November 7th, 2007 around 6:54 pm
    Jump to the top of this page
  7. 07

    could you explain the ViewHelper and ViewLocator classes in the PART 5 ?

    Nshen at November 7th, 2007 around 7:27 pm
    Jump to the top of this page
  8. 08

    [...] Part 4 [...]

    THE EYE OF RIA » Getting Started with Cairngorm at November 8th, 2007 around 1:57 am
    Jump to the top of this page
  9. 09

    @Nshen - I will address both ViewHelpers and ViewLocators in the next tutorial. The point I will make is that they both have fallen out of favor with most Cairngorm developers. The both are considered “bad practice” to use (quoting Jesse Warden on this one). However, if you have a specific question regarding these - feel free to leave a comment.

    David Tucker at November 8th, 2007 around 1:47 pm
    Jump to the top of this page
  10. 10

    Another solid tutorial. Thanks soo much for putting these together.

    On the topic of ServiceLocator, I was wondering how an AS3 lib such as flickrlib from Adobe could be used as a service in a Cairngorm app. Any info on that would be great.

    Thanks again for the outstanding work!!!!

    Sean Moore at November 8th, 2007 around 3:54 pm
    Jump to the top of this page
  11. 11

    @Sean - I am glad that you like the tutorials! I will probably use a Web API in the next tutorial. I am looking at several to determine what would work best.

    David Tucker at November 8th, 2007 around 5:00 pm
    Jump to the top of this page
  12. 12

    Hi David,

    On the ServiceLocator section of this article, it says:
    “Like the FrontController, this class is usually instantiated in your main application file. ”

    However ServiceLocator is a singleton class and can’t by instanciated, due to it’s singleton enforcer.

    Best,
    Iuliu B.

    iuliub at November 9th, 2007 around 12:38 am
    Jump to the top of this page
  13. 13

    @iuliub - Thanks for the good ear! Normally, you would be right. However, in Cairngorm, the ServiceLocator uses a different type of Singleton enforcement(which allows it be instantiated in MXML). When you first instantiate the ServiceLocator it creates the instance, but if you try to do it again - it will throw an error.

    David Tucker at November 9th, 2007 around 4:03 am
    Jump to the top of this page
  14. 14

    I loved this tutorial. Only problem is, I’m using amfphp 1.9 and cannot get it to return anything using Cairngorm 2.2.1 and FlexBuilder 2.1. I’ve managed to use it just fine without the Cairngorm framework.

    Anybody like to show how this would work with amfphp 1.9 as the remoteobject and CairngormLogin.php and LoginVO.php?

    Matt at November 9th, 2007 around 4:32 pm
    Jump to the top of this page
  15. 15

    @Matt - I would first ask a few questions. Have you tested your service with the ServiceBrowser in AMFPHP (to ensure that there are no errors on the PHP side)? Have you set the “_explicitType” for your LoginVO.php class? Please let me know if you are still having problems.

    David Tucker at November 9th, 2007 around 4:51 pm
    Jump to the top of this page
  16. 16

    Thanks for getting back so fast. Haven’t set “_explicitType”, I’ll try that. I get errors in the service browser when I include (”LoginVO.php”) in the CairngormLogin.php file

    Matt at November 9th, 2007 around 5:30 pm
    Jump to the top of this page
  17. 17

    If you get errors, then there is probably an error in your LoginVO.php file - you can email it to me at david [at] davidtucker.net

    David Tucker at November 9th, 2007 around 5:41 pm
    Jump to the top of this page
  18. 18

    Finally figured out what I was doing wrong. The AS LoginVO is being parsed as an associative array by php. Tried placing it in the vo path specified by AMFPHP but don’t seem to be having much luck there. I guess I’ll stick with it being read by php as an array for now as that’s working.

    Matt at November 12th, 2007 around 6:41 pm
    Jump to the top of this page
  19. 19

    Hello David,

    Do you have any experience with a .net webservice?
    I’ve got a simple test service in .net (it just returns an xml). without cairngorm I just set an mx:WebService and in a DataGrid I set the dataProvider and the datafields. I wan’t to test it with cairngorm, but so far, I don’t know how.
    I hope you can help me.

    Laurens van der Plaat at November 14th, 2007 around 1:50 am
    Jump to the top of this page
  20. 20

    Hi David,

    Let me say THANK YOU THANK YOU for these tutorials. I am fairly new to Cairngorm and have struggled until I found your site.

    I have a couple of questions that Im hoping you can offer advice on. First, I am wanting to use e4x with my Cairngorm webservices and am having quite the time getting my command, delegates, and service locator to play nice. I would appreciate any advice you can offer.

    Second, I am wanting to create modules within my cairngorm application and was wondering if you had any tips or tutorials on this topic.

    And lastly, I want to make my projects localizable. Is there anything that I need to do different than a regular Flex project to ensure this works in Cairngorm?

    Thank you for taking the time to read and hopefully answer some or all of my questions.

    Peace,
    Shawn

    Shawn at November 15th, 2007 around 7:34 am
    Jump to the top of this page
  21. 21

    @ shawn

    I’m also new with cairngorm and flex. I’ve tried to use cairngorm with an .net webservice. And after 3 days it finally works :D!
    I also wanted to use e4x so I did the following:

    ——————————
    the service locator:

    now you can use e4x in your command/responder. If you use event.result in your result function you get the xml

    ——————————

    I hope this will help you

    Laurens van der Plaat at November 16th, 2007 around 2:57 am
    Jump to the top of this page
  22. 22

    ow I can’t type mxml tags here, I didn’t know that… I can email you the code if you like…

    Laurens van der Plaat at November 16th, 2007 around 3:00 am
    Jump to the top of this page
  23. 23

    @Laurens - Feel free to email it to me at david [at] davidtucker.net

    David Tucker at November 16th, 2007 around 3:52 am
    Jump to the top of this page
  24. 24

    Hi David,

    First of all, thanks for this great set of tutorials!

    I’d like to port an old app to cairngorm. The app uses NetConnection() to communicate to Java via amf. I’m not quite sure where to go… I don’t think I can use RemoteObject (could be wrong). Should I just skip the ServiceLocator bit (seems a bit wrong), or try to extend the ServiceLocator to support some “custom AMFConnection”, like “getAMFService()” or so… I’m sure enough people have done this, I just can’t find any documentation.

    thanks again!

    Sakri

    sakri at November 20th, 2007 around 7:59 am
    Jump to the top of this page
  25. 25

    Hi David,

    How do you assure that a event has finished updating a vo before using the data from the vo that has to be updated?

    morten at November 28th, 2007 around 1:18 pm
    Jump to the top of this page
  26. 26

    [...] Part 4 you saw the full Service to Worker pattern demonstrated. However, the method discussed in the last [...]

    Jump to the top of this page
  27. 27

    Great job. I really enjoy your tutorials. It helps me and my team a lot.

    Just one question. I did try to run your sample app on my local machnine and I found out the ColdFusion returns “true” as a string instead of boolean. I had to change the checkin of the “event.result” in loginCommand as following. Why did CF return the string of “true” instead of boolean true? I thought the cfc login method should return boolean.

    ===================================
    if (event.result == “true”) {
    //Change view
    modelLocator.workflowState = ModelLocator.WELCOME_SCREEN;
    ===================================================================

    Joe at December 5th, 2007 around 2:39 pm
    Jump to the top of this page
  28. 28

    @Joe - I have had that happen before (only in Flex 2 if I remember correctly). Are you using Flex 2 of Flex 3 Beta?

    David Tucker at December 6th, 2007 around 9:43 am
    Jump to the top of this page
  29. Jump to the top of this page
  30. 30

    David,
    I am using Flex 2. Is that a bug ?

    Thanks

    Joe at December 7th, 2007 around 7:23 am
    Jump to the top of this page
  31. 31

    Hi,

    Firstly, many many thanks, great series, and has taught me a lot of CFusion and Flex, and saved me a lot of time.

    Just for fun :) and to test my understanding, I thought I’d extend this example to return the LoginVO object after validation, rather than boolean.

    I hit a few problems which I’d like to share with everyone here, hopefully it will help someone.

    When I modified the CFC to return the loginAttempt rather than the boolean, I got a strange error in the Flex Builder console.

    ArgumentError: Error #1063: Argument count mismatch on com.divinecreations.ViewManager.vo::LoginVO(). Expected 2, got 0.

    I could see my object in the event.result.objectProxy, but I couldn’t coax it back out and into my waiting variable. I either got the above error, or null.

    Turns out, just like C# (so I should have spotted this much sooner), the serializer can not work with parameterized constructors.

    In the example on this tutorial, the LoginVO must be instantiated with 2 parameters, which means I could’nt use LoginVO to accept a returning CFC VO.

    Furthermore, to my suprise AS3 can not handle multiple constructors, so I had to ditch the elegant parameterized constructor in this example for a default parameterless one.

    Once I’d done this (and a few other subsequent modifications) I was able to extract the waiting VO from the result.event

    var user:LoginVO = event.result as LoginVO;

    A summary of my changes are:

    – LoginVO.as — (created a default constructor)
    public function LoginVO()
    {
    //init
    }

    – LoginScreen.mxml — (modified the login event raiser)

    var loginEvent:LoginEvent = new LoginEvent(new LoginVO(username.text, password.text, false));

    Had to be replaced with a less elegant:

    var loginUser:LoginVO = new LoginVO();
    loginUser.username = username.text;
    loginUser.password = password.text;

    var loginEvent:LoginEvent = new LoginEvent(loginUser);

    If anyone knows how I can acheive the same effect, without having to revert to default constructors, I’d love to hear from you.

    Thanks,

    Matt

    Matt Law at March 16th, 2008 around 1:02 pm
    Jump to the top of this page
  32. 32

    Hi David,

    I have got requirement of changing source in services.mxml on fly, is it possible to define it in action script?

    I tried like this, but it doesn’t compile.

    import mx.rpc.remoting.RemoteObject;

    public var sourcePath:String = “com.affino.showcasedesignelement.cfcs.ShowcaseDesignElement”;
    private var remoteObject:RemoteObject = new RemoteObject(’ColdFusion’);
    remoteObject.destination = ‘ColdFusion’;
    remoteObject.source = sourcePath;

    Please let me know if this can be done?

    Thanks

    Philip

    Philip Bedi at March 25th, 2008 around 5:45 am
    Jump to the top of this page
  33. 33

    [...] Most Commented Post (32 Comments): Getting Started with Cairngorm - Part 4 [...]

    Jump to the top of this page
  34. 34

    David it was a great series. I am using drupal as a backend and flex as frontend but i am not able to connect my service using the cairngorm. I am using amfphp to connect drupla service to flex any idea please let me know how to connect the drupla model using cairngorm in flex.

    Mohamed at April 8th, 2008 around 10:38 pm
    Jump to the top of this page
  35. 35

    Hi David, great series, totally got me through the first steps of cairngorm, and i now see why, for flex, it might have some advantages to PureMVC. Great work man!
    @Matt, well not sure if this is what you want but a colleague of mine just described how to dynamically parse mxml, http://www.dreaminginflash.com/2008/04/24/the-mxmlloader-class-dynamic-load-mxml/
    @David, sorry for the link, if it’s wrong feel free to remove it.

    Keep it up!

    Pedro at April 30th, 2008 around 5:12 am
    Jump to the top of this page
  36. 36

    Hi,.. david this is gr8 series of tutorials to understand Cairngorm. Actually i tried to learn Cairngorm lots of times, but each and every time i felt that it is a very confusional matter. but when i gone through your tutorial series,… NOW i m feeling COOL…… thnx for such a cool tutorials……

    Expecting more good tutorials for you.

    And One more thing…….. in video tutorials…….. your style of saying “davidtucker” is so nice…….. :)

    shrikant at May 5th, 2008 around 11:06 pm
    Jump to the top of this page

Leave a Reply

  •  
  •  
  •  

You can keep track of new comments to this post with the comments feed.

Badges

View David Tucker's profile on LinkedIn
Inside RIA Badge

Community Posts