Thursday, June 23, 2011

Upgrading your Brain

Here is the presentation that I've given based on the "Pragmatic Thinking & Learning" book by Andy Hunt.

Upgrading your Brian.pptx

Monday, June 20, 2011

GroupingCollection2 not deleting an item while grouped

So here was our issues, when the grid was grouped, an item wouldn't be visually deleted - although it did actually get deleted from the collection and on the server. Everything worked fine when the grid was not grouped.

I discovered that the issue resides in the GroupingCollection2.getChildren() method. This method violates the SRP (Single Responsibility Principle) and not only does it return the children object, but also builds the parentMap.

If you subclass GroupingCollection2, and redirect getChildren to your own method which just returns the children (as the name might suggest). The parentMap fails to get built.

What this means, is the when you delete an item from the source collection, a parent for that item will not be found, and the grouped collection (one that is built internally ) doesn't respond to the delete and therefore the display doesn't need to update.

The solution is in your subclass of GroupingCollection2 to call super.getChildren, even if you don't need use the return results.

public override function getChildren(node:Object):Object // NO PMD
  {
   
   super.getChildren(node); 
   var rtn:Object  = helper.getChildren(node)
   
   /*
   Make sure this return value is not null. 
   A null value will cause the HierarchicalCollectionView to RTE on its internal refresh method
   */
   if (rtn == null)
   {
    rtn = new Object();
   }
   return rtn;
   
  }

HierarchicalCollectionView updateLength discovery

So, in another round of heavy debugging - I came across an an anomaly, which really is just a difference between what the developer thought would happen, and how I chose to implement a feature.

In the HierarchicalCollectionView, on the collectionChange event the length of the collection gets updated for nearly all CollectionChangeKinds except for UPDATE. Now this makes sense, changing a property shouldn't result in a change of length (like adding or removing children).

Unfortunately, the way that I a structured our application was that the items that needed to expand (ie have children) where flat, not hierarchical. So my solution was to toggle a property on the item to indicate that it had children. When I applied the same interface to the grouping collections (so that I could use the same framework for expanding and collapsing), I discovered that the length wasn't being recalculated and thus scrolling was throw off.

Annoyingly, much of the calculate length features in HierarchicalCollectionView are private. So here is a solution that I put in place, but it isn't very pretty:

class HierarchicalCollectionViewWithUpdateListeners extends HierarchicalCollectionView
{
 
 private var myLength:int
 private const NO_LENGTH:int  = -1
 public function HierarchicalCollectionViewWithUpdateListeners(hierarchicalData:IHierarchicalData=null, argOpenNodes:Object=null)
 {
  super(hierarchicalData, argOpenNodes);
 }
 
 
  public override function collectionChangeHandler(event:CollectionEvent):void
  {
   super.collectionChangeHandler(event);
   myLength = NO_LENGTH;
   if (event.kind == CollectionEventKind.UPDATE)
   {
    myLength = calculateLength();
   }
   
  }

  
  public override function get length():int
  {
   var origLen:int = super.length;
   if (myLength != NO_LENGTH)
   {
    return myLength;
   }
   else
   {
    return origLen; 
   }
  }


}

HierarchicalData vs HierarchicalCollectionView as a dataprovider to the AdvancedDataGrid

So I just discovered another issue with the ADG. When you switch dataProviders from a HierarchicalCollectionView to HierarchicalData, the collection doesn't get set properly.

Here is a snippet from the set dataProvider method of the ADG.

if (value is IHierarchicalData)
        {
            _rootModel = value as IHierarchicalData;
        }
        else if (value is IHierarchicalCollectionView)
        {
            // if the dataProvider is IHierarchicalCollectionView,
            // set it to super.dataProvider in commitProperties()
            _rootModel = IHierarchicalCollectionView(value).source;
            hierarchicalCollectionInstance = IHierarchicalCollectionView(value);
        }
        else
        {
            _rootModel = null;
            hierarchicalCollectionInstance = null;
            super.dataProvider = value;
        }

Notice that if you pass in a IHierarchicalCollectionView a private flag called hierarchicalCollectionInstance gets set. It gets nullified on ALL other dataprovider types EXCEPT IHierarchicalData.

This causes a IHierarchicalData following a IHierarchicalCollectionView to essentially not be saved, as the hierarchicalCollectionInstance gets referenced in commitProperties. The old IHierarchicalCollectionView will continue to be the datasource.

The easiest resolution to this is to set the dataProvider to null, then set it to the new source

Thursday, June 16, 2011

Setting up Hudson on Windows Vista

So, I ventured out and for the first time, set up Hudson on my own machine. I've used it many times on a client site, but this was a first time starting it from scratch. I discovered many "slap the head - I was stupid moments" while doing this and need to share.

1) download the hudson.war file, and follow the instructions on installing it as a service under Windows. This means that you need to have java installed on your machine... but it works easily

2) Under Manage Hudson -> Configure, be sure to tell Hudson where your JDK and ANT files are. Note: You can have it automatically install them (This was a critical step that I missed). I found that while it downloaded and tried to install both the JDK and ANT, that it never succeed, so I had to manually install them and point Hudson to the JAVA_HOME and ANT_HOME paths. Also note, that setting the PATH variables inside of windows didn't seem to have that much of an effect.

3) For some reason I found that there is a timer on subversion... that if I committed a file, then immediately went to Hudson and started a build, that it wouldn't have the updated file. If I waited about 30 seconds, it would have the latest.

4) If you are building a Flex library project, you will need to explicitly enumerate all of the classes that you want to include. Here is a snippet that will have ANT do that for you, including removing the .as and the basedir references:


  
  
  
  
  
  
   
    
    
   
  
  
  
  
   

Thanks for all your help google!

Friday, June 3, 2011

Discovering Metrics about Parsley and creating its context

Recently I had a performance tuning need to figure out how long Parsley was taking to instantiate and create its context. Thanks to Marty Pitt for the solution.

First off within the main application have the following when you are defining your context


<parsley:ContextBuilder complete="applicationContextBuilderComplete(event)">
   <debug:TimedObjectSupport />
   <parsley:ViewProcessor type="{ExtendedViewProcessor}" />
</parsley:ContextBuilder>


protected function applicationContextBuilderComplete(event:FlexContextEvent):void
   {
    trace ("Processing Parsley Context: stopTime > " + getTimer());
   }

Where TimedObjectSupport.as is:

import org.spicefactory.parsley.core.bootstrap.BootstrapConfig;
import org.spicefactory.parsley.flex.tag.builder.BootstrapConfigProcessor;

public class TimedObjectSupport implements BootstrapConfigProcessor
 {
  public function processConfig(config:BootstrapConfig):void
  {
   trace ("Processing Parsley Context: startTime > " + getTimer());
  }
 }

and ExtendedViewProcessor.as is:

import org.spicefactory.parsley.core.context.Context;
 import org.spicefactory.parsley.core.view.ViewConfiguration;
 import org.spicefactory.parsley.core.view.processor.DefaultViewProcessor;
 
 public class ExtendedViewProcessor extends DefaultViewProcessor
 {
  public override function init(config:ViewConfiguration, context:Context):void
  {
   trace("Parsley Wiring Start-> " +getTimer());
    super.init(config,context);
   trace("Parsley Wiring Stop-> " +getTimer());
  }


 }