Monday, February 11, 2013

Flex GridColumn, complex objects, and default sorting

I recently discovered something about the spark GridColumn. You can pass in a complex type for the dataField.

Say for example that your dataProvider is a collection of Users:

public class User
{
    public var name:String;

    public var id:String;

    public var manager:User;

}


You can specify the gridColumn.datafield as "manager.name"
<s:GridColumn dataField="manager.name" />

This is really cool. Can you can see it in the source code down where the datafield property is set. There is an internal dataFieldPath variable.

Here is the problem that I ran into. I had several columns that had these nested values and I wanted to set up each column to have case insensitive sorting, which is not the default. Using a shared sortFunction was difficult, because I also didn't have direct acces to the dataFieldPath (the nested property), and it would have too much work (in my opinion) to build the object within the sort and then grab the string and then lowercase and compare. It was especially too much work, because it would have negated any label functions that were already in place for these columns.

So here is my solution:


package com.workbook.core.view.datagrid
{
    import mx.formatters.IFormatter;

    import spark.collections.SortField;
    import spark.components.gridClasses.GridColumn;
    import spark.components.gridClasses.GridSortField;

    public class CaseInsensitiveComplexDataPathGridColumn extends GridColumn
    {

        public function CaseInsensitiveComplexDataPathGridColumn()
        {
            super()
        }



        override public function get sortField():SortField
        {
            var sf:SortField = super.sortField;
            const isComplexDataField:Boolean = sf is GridSortField;
            var cF:Function = null;
            if (isComplexDataField && sortCompareFunction == null)
            {
                sf.compareFunction = dataFieldPathSortCompareCaseInsensitive;
            }
            return sf;
        }

        protected function dataFieldPathSortCompareCaseInsensitive(obj1:Object,
                                                                 obj2:Object):int
       {
            if (!obj1 && !obj2)
                return 0;

            if (!obj1)
                return 1;

            if (!obj2)
                return -1;

            const obj1String:String = itemToLabel(obj1).toLowerCase();
            const obj2String:String = itemToLabel(obj2).toLowerCase();

            if ( obj1String < obj2String )
                return -1;

            if ( obj1String > obj2String )
                return 1;

            return 0;
        }


    }

}

What this does, is that when you click on the column header, it grabs the sortfield from the gridColumn (that was the easiest public property to get at). Fortunately if the dataField is complex, it builds a GridSortField object instead of a SortField.

I check for the GridSortField and if there is not already a sortCompareFunction, I assign it to my case insensitive sort function.

Then within the sort function, I call itemToLabel, which fortunately does all of the heavy lifting to determine the nested object's label, calling any labelFunctions as needed.

Then I force to lowercase, and done.

To use, I replace my GridColumn in my dataGrid definition, with CaseInsensitiveComplexDataPathGridColumn.

Wednesday, January 2, 2013

Drive - Book Quotes

I'm starting a new trend, that as I read books, I'm going to list out significant or relevant quotes that I'd like to remember.

Drive

by Daniel Pink:

Something cool about this book is that at the end, there is several levels of summary, from a 140 character twitter summary, an elevator and cocktail party summary, as well as a couple of paragraphs on each chapter.

External motivation ( rewards and punishments) == bad

  1. Extinguish intrinsic motivation
  2. Diminish Performance
  3. Crush Creativity
  4. Crowd out good behavior
  5. Encourages cheating, shortcuts, and unethical behavior
  6. Addictive
  7. Fosters short - term thinking

The Three Elements of intrinsic motivations

  • Autonomy

    • Autonomy over the task
    • Autonomy over their time
    • Autonomy over their technique
    • Autonomy over their team
"The most autonomy crushing mechanism imaginable: the billable hour. Most lawyers must track time, often in six minute increments. If they fail to bill enough hours, their jobs are in jeopardy. As a result their focus inevitably veers from output of their work (solving the clients problem) to its input (piling up as many hours as possible). "
The billable hour makes sense for routine tasks, but for non-routine tasks, the link between how much time somebody spends and what that somebody produces is irregular and unpredictable.
The antithesis of the billable hour is the ROWE (Results-only work environment) 

  • Mastery

    • Mastery is a Mindset
      • It is more like strength then height. You can work to improve it. It is in your control
    • Mastery is a Pain
      • "The mundanity of excellence".. spending the most time and effort on the little details that show the tiniest of improvements
    • Mastery is an Asymptote
      • Mastery is impossible to realize fully, because you can always get better. The joy is in the pursuit more than the realization. In the end, mastery attracts precisely because master eludes. 

  • Purpose

    • The yearning to do what we do in the service of something larger than ourselves

Toolkits

Intrinsic Motivations for Parents:


  • Offer praise... the right way
    • Praise effort and strategy, not intelligence
    • Make praise specific
    • Praise in private: Praise is feedback, not an award ceremony.
    • Offer praise only when there is good reason
  • Help kids see the big picture

Thursday, November 15, 2012

Always linking Amazon to the school rewards


Our elementary school has a link off the PTO website, where proceeds from the sale on Amazon will be donated to the school. That's great, but I always forget to click the link first before I go shopping.

I checked with Amazon tech support and the feature to save / manage that within my account is not a current feature (although I requested it).

So now what.

Chrome Extensions: Redirector

This will take a url, like www.amazon.com, and redirect it to the one that has the school links.
Note, that I'm sure there are similar tools for the other browsers, but this one is for Chrome.

Here are the steps that you will need to follow to set this up.
  1. From Chrome, download Redirector 
  2. Click on Rules Manager
  3. Click on the red plus (+) next to the word "Name"
  4. There are four text fields:
Name: (Whatever you want)
Amazon to KolterAmazon


Match
www.amazon.com


Substitution
(www.amazon.com)/?([^\?]*)\??(.*)


Replacement
$1/$2?$3&tag=kolelepto-20&camp=212677&creative=384117&linkCode=ur1&adid=0EN8T40BVQFZ0NCS99MQ&


What this will do is it will find
  • www.amazon.com
  • www.amazon.com/
  • www.amazon.com/anything_else_without_a_question_mark
  • www.amazon.com/anything?anything
and it will append the school information on the end. 
The school information is the part that says:
&tag=kolelepto-20&camp=212677&creative=384117&linkCode=ur1&adid=0EN8T40BVQFZ0NCS99MQ&

And now, whenever you navigate to www.amazon.com, it should automatically think that you clicked on the support the school link. 

Friday, October 26, 2012

On Being a Senior Engineer, Estimating

So, I'm moving from being a freelance consultant to a "technical lead". While I feel that I have a lot of skills in that area already, I'm always looking to grow.

So with that I'm going to be adding some posts about what it means to be a technical lead.

Also, I'm thinking about designing a presentation around estimating, because that is something that I want to learn more about. So I'm also going to be adding some estimation research up here.

And of course, other technical discoveries, AS3, flex, and others will always be here.

So I just came across this blog, On Being a Senior Engineer which I think is fantastic, and here are the bullets:

  •  Mature engineers seek out constructive criticism of their designs.
  • Mature engineers understand the non-technical areas of how they are perceived.
  • Mature engineers do not shy away from making estimates, and are always trying to get better at it.
  • Mature engineers have an innate sense of anticipation, even if they don’t know they do.
  • Mature engineers understand that not all of their projects are filled with rockstar-on-stage work.
  • Mature engineers lift the skills and expertise of those around them.
  • Mature engineers make their trade-offs explicit when making judgements and decisions.
  • Mature engineers don’t practice CYAE (“Cover Your Ass Engineering”)
  • Mature engineers are empathetic.
  • Mature engineers don’t make empty complaints.
  • Mature engineers are aware of cognitive biases


And some of the points about estimation:
Estimation is really about responsibility

(Quoted directly from the blog)

From the Unwritten Laws:
Promises, schedules, and estimates are necessary and important instruments in a well-ordered business. Many engineers fail to realize this, or habitually try to dodge the irksome responsibility for making commitments. You must make promises based upon your own estimates for the part of the job for which you are responsible, together with estimates obtained from contributing departments for their parts. No one should be allowed to avoid the issue by the old formula, “I can’t give a promise because it depends upon so many uncertain factors.”
Avoiding responsibility for estimates is another way of saying, “I’m not ready to be relied upon for building critical pieces of infrastructure.” All businesses rely on estimates, and all engineers working on a project are involved in Joint Activity, which means that they have a responsibility to others to make themselves interpredictable. In general, mature engineers are comfortable with working within some nonzero amount of uncertainty and risk.

And there is a cognitive biases (one of my weaknesses) around estimating

Planning Fallacy – (related to the point about making estimates under uncertainty, above) basically: being more optimistic about forecasting the time a particular project will take.



Thursday, October 25, 2012

Remote Debugging a swf on Android

So I recently had a very thorny challenge of debugging on android.

Because of business / technical reasons, we HAD to serve the swf via HTML through a specific server. The problem that I was having was that the at full screen the video went blank, although we could hear the audio.

Now just as some background, the project was inherited and while there was a decent MVC architecture in place, it was possible that any element on the screen had access to any other element and could modify its properties.... which happened quite a bit.

My challenge was to try to figure out how to get *some* information out of the device so I could have a clue as to what was going on.

I tried to reverse tether and proxy, but neither of those I could do without rooting, which I wasn't willing to do.

Then I tried setting up a hotspot from my laptop (http://www.connectify.me/) to see if I could get Charles in place to then do a local mapping which I could leverage for remote debugging in the IDE, but my version of Android didn't like ad-hoc networks.

Finally I discovered that Monster Debugger had a P2P version. This version allowed me to run demonster on my desktop and the app on the device and as long as they were on the same wifi network, I could get some telemetry. ROCK ON!

So I created a MonsterTraceTarget to go with my logging, and I got trace statements. I could also drill into the visual hierarchy and validate and set properties.

After much searching I found the video element about 16 levels deep with a width and height set to 0,0. This explains the audio but no video. Traces confirmed that somewhere in the code we were hitting the video api (not AS3, but a wrapper to the 3rd party video provider) and actually setting these crazy properties.

"Find Uses" (thank you IntelliJ), led me to half a dozen places where this could happen. More trace statements to determine which one was the culprit. Iterating on the "find uses" and trace statements, I finally discovered that it was an omission that had happened *months* earlier (so much for test early, test often), where there was a conditional test within the configuration startup for touch device and full screen (duh), where the stage rectangle was supposed to be calculated, but wasn't - hence the zeros.

Huge thanks to Demonster and IntelliJ for the tools needed to solve this 2-days-worth-of research thorny issue.


Tuesday, July 10, 2012

Column Spanning with Flex Spark DataGrid

This post is long overdue, but this was something that I was playing around with for a potential requirement for a project many months back.

So here is the problem: Can we get a column span happening on the spark data grid. Yes.

Either scripts and active content are not permitted to run or Adobe Flash Player version 11.1.0 or greater is not installed.

Get Adobe Flash Player


The link to the source code is here: https://github.com/dshefman/FlexSparkDataGridSpannableColumns


And here are the key steps

1) Setup a new Skin with an added GridLayer (in this case called "rendererOverlayLayer") as the last entry
ColumnSpanningSparkDatagridSkin.mxml
<s:Grid id="grid" itemRenderer="spark.skins.spark.DefaultGridItemRenderer">
                        <s:GridLayer name="backgroundLayer"/>
                        <s:GridLayer name="selectionLayer"/>
                        <s:GridLayer name="editorIndicatorLayer"/>                            
                        <s:GridLayer name="rendererLayer"/>
                        <s:GridLayer name="overlayLayer"/>
                        <s:GridLayer name="rendererOverlayLayer" />
                    </s:Grid>

2) Create your custom cell renderer, extending ColumnSpanningGridItemRenderer.as
3) Create your custom spannableItemRendererAccessor, implement ISpannableGridItemRenderer.
Within this class, you will need to fill out the following interface:
public interface ISpannableGridItemRenderer
 {
  function getElementThatSpans():UIComponent
  function getSpanningRendererLayerNameInDataGridSkin():String
  function getNumofSpannedColumns(data:Object):int
  function doesDataSpanColumns(data:Object):Boolean
  function isCurrentCellHiddenBeneathASpan(data:Object,columnIndex:int):Boolean
 }
These methods are used by ColumnSpanningGridItemRenderer to determine and relayer the span based on the data that comes in
4) Attach your spannableItemRendererAccessor to the cellRenderer within preInitialize. (Note, if you attach it during creationComplete, you will need to force an update before it takes effect.)

How it works:

The ColumnSpanningGridItemRenderer does all of the heavy lifting. It checks to see if the data has spanning enabled. If it does, then it reparents the itemrenderer to the rendererOverlayLayer within the skin and resizes it to fit the cell bounds defined. If it doesn't it reparents the itemrenderer back to the original rendererLayer and resizes back to the original size.
There is a tricky part within the code, as I discovered, renderers are not added / removed from stage, but instead their visibility is toggled.



*** Tight Coupling Warning ****

Within the ISpannableGridItemRenderer, I expect that you will need implicit knowledge of the data / datatype coming in. Please remember that the data that is fed in is the rowData, and you will likely need to convert to to cellData to figure out individual spans. This could be through separate helper / utility classes. 

My example is a little extreme, as it is unlikely each cell would be comprised of its own value object. But it was easiest for this post. 

Anyways, this is purpose of the "convertRawDataToSpannableData()" method. 
Then once you have your cellData, you will need to determine if it spans via "doesDataSpanColumns()" which is probably related to "getNumofSpannedColumns()"

That's the easy part. The hard part is determining that the following cells are hidden beneath the span. To make this a little easier, if there is some condition within the cell data that indicates that they are hidden, it isn't too bad (null / empty values/ constants). Otherwise, you might have to do some preprocessing of the data to compare expected column indices to actual column  indices  and base your conditionals on that. 

Thursday, June 14, 2012

PureMVC, amendments to best practices

I recently inherited a very well written PureMVC project, following the recommended best practices to the letter - and it gave me a lot of headaches. So I'm making some amendments.

First let me give some context. This project was a video player project, the client orginally had their own in house video player, (ie NetStream based), but are now moving to a service for their videos. They wanted to keep the UI the same, just switch out the video player.

This is perfect, this is exactly what an MVC framework should be about, I could keep the view part and just replace the video player and model to where the data was coming from.

While trying to decode the source code, I started by just seeing if I could instantiate just one of the mediators, to see what the view looked like. Compiler Error.

Turns out, that the mediator had referenes to the ApplicationFacade to get the notification names. Well that means that the entire ApplicationFacade had to be compiled. That meant that all of the registered commands, mediators and proxies, had to be compiled. So somewhere deep within the framework, I was missing a bunch of classes.

So my first step was to comment out all of the proxies and commands, as I only wanted the views. Still no joy. Not wanting to remap all of the notifcation declarations, I made a MediatorNameConstants. This file had a list of all of the mediator names, but without references to the mediator classes. I updated all of the NAME values of all of the mediators to point to this file as well as all of the references.

Now when I wanted a mediator, I only needed that mediator and the facade, not all of the framework.

As I worked through the code and added stuff back in here is the list of best practices regarding PureMVC that I came up with.


  • While tedious, keep all mediator and proxy names in a separate constants file.
    I did this so any mediators or proxies that were referenced in mediators, weren't required to be compiled in, only their names.
  • Then within a mediator, command, or proxy, instead of creating local variables for other mediators or proxies, declare them as explicit getters
    Like this:
    public function get myProxy():IMyProxy { return facade.retrieveProxy(MyProxyConstants.NAME) as IMyProxy) }

    That way the class can be overwritten for extension and testing. This would also allow for some dependancy injection, if so desired.
  • Make the proxies into interfaces, at least the major ones, like ApplicationProxy or ConfiguationProxy.
    Now there is a theory that says if you only have one, than an interface isn't needed, and I agree. Although I think for something that is major and widespread like the two mentioned above, that an interface of one does make sense.

If these things were in place in my inherited project, it would have saved me days worth of debugging and code searching efforts.