Friday, December 30, 2011

Refactoring, the clients perspective

I'm in the middle of a refactoring project, but it doesn't involve any code, and I happen to be the client.

In the process of re-insulating the attic, we discovered that the wiring and electricity in our house is FUBAR. So our electrician is redoing all of the wires correctly. This is a refactoring effort, because after tons of effort, hopefully there will be no difference in functionality. The only difference should be less buggy, more maintainable, more consistent electricity, and the ability to renovate.

Let me explain. We had a hunch that there was something wrong with the electricity, has we would often get a short in the master bath light. The bathroom vent fan wasn't working (a known condition when we bought the house), and wanted it fixed before we covered the attic with insulation. Upon inspection, the wiring for the vent was incredibly non-standard and very complex and took about 3 days just to figure out how it was working (sounds familiar so some of the  classes that I've run across). Further inspection showed that most outlets weren't grounded, and most of the junction boxes where overloaded (you can think of this as violations of the single responsibility principle). There was bare wire and extension cords running throughout the attic.

I'm not an electrician, but even I can figure out that this is bad.

So after nearly 6 weeks of banging around the attic, the project is nearly done. And there is barely anything to show for it. Granted, we did add some feature requests (who doesn't?)....
Things like, we could really use an outlet here, or these outlets don't have a ground (known), can we change that. etc. But for 6 weeks of work, we essentially are getting a third hole in an outlet receptacle, and a working vent fan in the bathroom.

So having previously successfully sold a refactoring project, previously failed at selling a refactoring project where it was desperately needed, and now as the recipient of a refactoring project, I feel like I have some interesting insight to share.

Some important background information:

  • I trust the electrician. This is important. I believe him to the be expert and have my best interest first. 
  • He is exceptionally meticulous and honest. 

So here are some of my frustrations about the process.

  • It took about 3x longer then expected
  • Even though I knew work was happening, I had no sense of progress
  • When I did get a progress report, I had no idea what the electrician was talking about 
Here are the things that I found most helpful.
  • There was an "all known" point. This was the point where he had figured out everything: what each wire, junction, and switch did. There were no more surprises after this point. It was at this point that we could start committing to deadlines. (Just as a note, we didn't get to this point until after the original estimate had elapsed). We weren't done, not even close, but we knew everything there was to know and could easily layout the road map for the work to get done.
  • We started a daily list of what he had done that day, what he was planning on getting done the next day, and overall tasks that he knew where still outstanding. For the most part, I didn't understand what most of it meant, but I was able to at least follow along with what he was working on and what he still had left to do. (When did I suddenly become the annoying project manager wanting to check off rows of tasks in Excel? Actually I wasn't applying much pressure, I just wanted to see when I could schedule the insulators, because I was hoping to do it before the end of the year)

    This list became our "Stand up" - what happened yesterday, what is you plan for today. As well as a work in progress (WIP) account. Because the detailed tasks were linked to overall larger tasks and goals.
     
  • There were several mini visual milestones... oh this light is working again, or this outlet is now grounded. It wasn't much, but at least there was something to see / touch.
  • There was something satisfying about seeing large bags of trash being removed from the attic.


  Here are some outcomes to think about:

  • Estimate only up to the "all known" point.  So only plan to work up through the learning part of the code. In terms of refactoring, I think you could rearrange functions within a class, rename private methods and variables, and essentially tag / comment sections and files as to what they do. You could also establish characterizations tests for the pieces that are testable.

    Do not estimate the entire scope of the project until you get to the "all known" point.
  • Figure out beneficial ways to show progress. Some of the metrics that I've used in the past is
    • # of methods under test
    • # public methods and variables (shouldn't change much for a refactoring) 
    • # private methods and variables (maybe should decrease to improve testing and extending)
    • # protected methods and variables (should increase, more methods probably mean smaller, easier to read methods)
    • # of import statements (decreasing shows less dependancy)
    • PMD Errors 
    • PMD Warnings
    • # average method length

      (Note, I'm still looking for some static analysis tool that gets a metric for code readability. Anybody know of any? )
  • Clearly and in detail list all of the work that you've done. Your VCS comments and diff log can help you with this.
  • Engage the customer in a non-time consuming way about what your are your plans large and small (next hour, today, this week). One of the frustrating things about our processes, was the electrician tried to tell me all of the stuff he was doing? This felt like a waste of time, as I kept hearing the same things over and over, I was much happier when I could see it on a sheet of paper, and we saved the discussion for critical decisions (I have to make a change to this fixture or outlet. I'm not able to keep it the same, what would you like me to do). Otherwise, I'd smile and nod, but not really understand  if what he was saying was important to me ( Is there something I need to know) or not.
  • Showing progress on a refactoring effort is difficult. I would say # of classes left to refactor is a mini-visual milestone. I'd gladly take other comments on ways of showing progress for a refactoring effort.
  • I think credibility is important too. Our electrician kept saying how much better it was and that it was good that we were doing this. I found this frustrating. It wasn't until we went up into the attic midway and saw the difference did we recognize the value. But he had to get it to a point where we could see it, we weren't going to crawl back into the depths of the attic (that is why we hired him). So if your refactored code is much easier to understand, even for a non-technical person, there might be value in showing it (if that person even values looking at code. Alot don't - their eyes glaze over if it comes near). If there is another developer nearby (not even from the same language, team or area... a backend dev, or dba, or someone else technical) that can look at the before and after and at some validation. "That looks much better..... I don't even know what I'm looking at, but the newer stuff is much cleaner and more understandable."
  • When we started going over budget, the electrician gave variable rates. There was one rate for the difficult in the attic work (it is uncomfortable and cramped) and a lower rate for the easy on the ground work. I'm not sure if this can apply to coding, but it was a nice gesture on his part. 

So there you have it. I've tried to share as much as I could about this experience as it relates to refactoring. Even when knowing the process, it is hard not to be frustrated. It is hard to not think to yourself, "He is *just* adding a grounded wire to an outlet that is already there. How hard can that be? Why is this taking so long".  I have no idea about how to do what he is doing, but it seems like it would be easy. Obviously it is not.

I'm sure many people say the same thing about coding. "Certainly adding a button in the middle of the datagrid can't be *that* hard. How long does it take to add another page to the site, the menu is already there, just make some more room at the end."

I'm just glad that I trusted our electrician. This might have been a different story had I not. 

Sunday, December 25, 2011

Recycled Keyboard Ornament for Geeks / Programmers

So, as you can tell by my posts, a large part of my year has been working with the Flex DataGrids in their various flavors.

Well, my wife and I have a tradition of choosing an ornament for each of us that represent our year. My wife did a fabulous job creating this christmas ornament for me out of a broken keyboard.

Made from keys, the conductive plastic layer that the keys connect to, hot glue, and play-doh :) (Its great to have to have kids around so all that these supplies are readily on hand :) )


Thursday, December 15, 2011

Finding a mentor - the wish list

So I'm on a mission to find a mentor or mentors. I guess in order to find something, I would need to figure out what it is I'm looking for (which reminds me of the Nanny wish list from Mary Poppins in some ways)

So ideally, a mentor would be someone who:

  • Similarly, but hopefully more, experienced then I am in software development
  • Has a modicum of time to dedicate to me. I don't want it to take up much time, but I also recognize that it takes some
  • Is willing to share their experience, make suggestions, and has an altruistic interest in helping others on their journey
  • Checks in, unprompted, every once in a while, just to maintain connection
Concrete potential ideal examples of interactions from mentee to mentor
  • I have a challenging/critical software / architecture problem and a potential solution or solutions. I could ask the mentor which approach might be best. They could say "Have you thought about this", or "what about this scenario", "Does this take 'x' into account", "Have you checked out 'x', it solves a similar problem", etc.
  • Being able to show someone, a well designed section of code, and get some constructive criticism (if deserved), or a nice job (if deserved).
  • Being able to recommend resources, like conferences, blogs, books, people, etc that are interesting and relevant to  me and my journey.
  • Can connect me to potential people who need to be mentored but are not as far along as I am, so I can be a mentor as well. 
Concrete potential ideal examples of interactions from mentor to mentee
  • Share pieces of their work, that they are proud of, and explain their thought process, challenges, etc, to someone who might understand
  • Ability to talk at a detailed / advanced level to a peer (It is always fun talking about something that you are passionate about to other like minded people
  • Material for blogs, presentations, publications that could be helpful for the mentor's journey.
  • Hopefully a handful of ah-ha moments :)
Please comment about what you would want from a mentor or mentee. 


Thursday, December 8, 2011

Upcoming Presentation @ 360Flex 2012

Title: Decoupling, Refactoring, Testing and Other Improvements to your Craft Topic:

"I'm scared of that file, it does EVERYTHING!"
"That file has changed 77 times in the last month, I have no idea about what it is doing"
"This file touches everything, I'm not changing it" 

Do you ever feel trepidation about modifying a file? Do you fear that making a change might break something and you'd have no idea? This session is about from-the-trenches-experience refactoring, breaking dependencies, and getting tests in place. This is NOT the idealistic Test-Driven Development (TDD) that we all want to do, but how to add characterization tests after the fact - after the code has decayed, and is reeking of code smells. Learn about the essential resources, plug-ins, frameworks, and methodologies that you need to have to regain control of your code and your sanity. 

http://www.360flex.com/   April 15th-18, 2012

Wednesday, November 30, 2011

Journey of a software craftsman - the backstory

Just as a disclaimer, I want to state my definition of a software craftsman, as someone who recognizes that there is an art, science, and craft to programming and I'm striving to constantly improve towards mastery, knowing that I'm only going to asymptotically approach it. 

So with that out of the way, I'm going to start a small series related to my journey as a software craftsman, finding a mentor, and improving my craft. Before I get to the present, I feel it necessary to record many of the milestones and moments that have brought be up to this point. 

This post is not technical nor specifically related to Flash or Flex, but it does (hopefully) describe the initial part of my journey (before I even recognized I was on one)


  • Mid 1990's
    • Context:
      I was fresh out of school working at a multimedia company. One of our clients used Macromedia Director to deliver their quarterly+ shareholders meetings. This client had a franchise model, and as part of their presentation wanted photos of each of the franchise buildings. Specifically in their "slides" there were 4 photos spots available, each of a different franchise, and as you advanced forward, the photos shifted by one, so that it looked like a single new photo got added to each slide. This is all well and good, but the number and order of the slides changed constantly (even up to minutes before they gave the presentation.) (As I side note, they often took us along to the meeting so that we could update the stock price to literally be the current price when they presented it). Sometimes there would be 100 slides, sometimes only 15. Oh and also their was a print option, which included the scrolling photos as well.

      So basically there was about 150 potential slides, any number of which could be in a given presentation in any given order. Some of the slides had dynamic data, like the stock price, which was updated (manually) all the time.
    • Result
      While it didn't have a name to me at the time, through this project I had already separated the functionality (ordering the slides and photos) from the UI (the next frame and printing) and the business rules (externalize everything to text files (no feasible DB or internet available at the time)). It was my discovery of MVC, nearly a decade before I discovered it had a name
  •  Late 1990's
    • Context
      Many of our projects at the time were training animations with mostly linear controls. I quickly realized that testing the latter parts of the projects was very tedious as you'd have to wait for all of the previous parts to finish.
    • Result
      Modular design. I often rigged in temporary navigation panels, debugging tools, and other test harnesses, to allow me to test faster and more accurately. I was able to pull out the pieces from the rest of the application and exercise them. Nothing automated yet though, not quite. About this time I wrote a utility that would crawl the application display list,  find everything with an interaction on it, and force that interaction to fire randomly. This is the equivalent to clicking everything on the screen as fast as you possible can.

      This is close to automated testing, but it wasn't repeatable, as it randomly choose what to interact with. But while watching it, and the logs, I discovered many many bugs before it went to production.
  • Early/Mid 2000's
    • Context
      I had this intense project where the goal was to fully (nearly) simulate a about to be introduced cell phone (with a camera :) ). The project had a one month time frame, and I had absolutely no idea how to do it. Another motivating factor, was that if we could do it, there would potentially be dozens of more phones to simulate. Since there was only a month to deliver the whole thing, there was a demo every Friday. Without having heard of Agile, I was now in my own self defined agile methodology.  At each demo, we did some planning about what would be delivered the following week. It was at this moment, that I realized that the structure that I had put in place didn't scale, and I wouldn't be able to accomplish next week's goal. I would spend the weekend rewriting everything that I had to support the new objectives. This happened EVERY week. Of course each rewrite / refactoring got faster and better
    • Result
      I discovered Agile. Now it wasn't formalized, as I wouldn't put a name to it until many years later. I discovered the power of short iterations, not designing upfront, the value of starting over with leveraged experience, and refactoring. This was probably one of my most powerful experiences.

      After successfully delivering this project, and the next 2 dozens phones*, I researched how might I do simulations better next time. I came across this book called Flash MX for Interactive Simulation, which talked about State Machines and the State Design Pattern.

      This book opened up the world of Design Patterns and UML to me. The coolest thing, was that my solution for the phone WAS INDEED the State Design Pattern. I was ecstatic and amazed that I had "invented" the exact thing that others were practicing.

      This was incredible fuel to me. I read everything I could about Design Patterns and UML
    • *
      • Phone #1 = 1 month, 
      • Phone #2  =  2 weeks, 
      • Phone #3 = 1 week
      • Phone #4+ = 2 days
  • Mid/Late 2000's
    • Context
      I had the opportunity to be on some teams where there was more then me as the one developer on the team. Some were agile, most were not. I learned the value of source control, integration headaches, testing, and working with other peoples' code. I stated recognizing that code readability was critical, as well as the entire team dynamic, philosophies and varying still levels. (What do you mean you are going to reach up from the child, to the parent, to the grandparent, to another descent and change a property!! #@%! Haven't you ever heard of events? !)
    • Result
      Started on the Pragmatic Programmer and Agile philosophies
  • Late 2000 - Recently
    • Context
      I was on a massive project. Two years, 24 developers. It was huge. I was the Flex technical lead and architect. I had laid everything out (architecturally), both code wise and processs wise. We had a 13point definition of done. But when all said and done the team only accomplished 2 of the 13. We were functionally complete, but we weren't done. Because of several factors, team & management mentality, on-boarding and delegation process... we ended up with what can most easily be described as Legacy Code.

      I tried often to make course corrections to the code base and team, but it didn't happen to my satisfaction. This sent me on a massive effort to try to figure out why I couldn't communicate all of my experience to the rest of my team and what I could do to fix it.

      I devoured references like Working Effectively With Legacy CodeClean CodeClean CoderPragmatic Thinking And Learning, and dozens of other books. I was starving for any information about how to communicate better, clean up the code that was there, help my team improve, and figure out what I did wrong, or what I could do better, either immediately or in the future.
    • Result
      Recognized that I am a software craftsman, but I still have a lot to learn to get to where I want to be. I am actively working towards becoming a craftsman, becoming a mentor, finding a mentor, improving my craft and trying to advocate that quality is important for my industry. 


If you made it this far, thanks for reading my story and sticking around :) I plan on actively contributing to this topic as I continue on my journey. 

Wednesday, November 9, 2011

Flash is NOT Dead - communicating to lay people

So, there is lots of chatter around Adobe's announcement today, that I want to add my 2 cents on. I happen to be teaching a class on Flash tonight, so I thought about how to explain this announcement to my class.

While I agree with Adobe's decision (somewhat), I think they royally screwed up their communication of their message.

They've spent years training people to recognize the Flash brand, and failed to realize that most people can't distinguish all of the pieces that make up that brand. Nobody outside of the community knows that there is even a Flash Pro, Flash Builder, Flash Player, AIR, Flex... it is all Flash to them. Adobe has done a good job in that regard. So today's message that says Flash {insert ANY other words here} is not going to be supported, gets interpreted as the entire brand is not going to be supported.

Here is a snippet of their message:

We will no longer continue to develop Flash Player in the browser to work with new mobile device configurations (chipset, browser, OS version, etc.) following the upcoming release of Flash Player 11.1 for Android and BlackBerry PlayBook.

And here is what the world read:

 We will no longer continue to develop Flash Player


That is akin to reading a hypothetical message that says:
"Continental Airlines will close its Houston hub" as
"Continental will stop flying"

This is only a small piece of the whole equation. But this is what the lay person hears... and hence all of the panic today.

I came up with the non-technology analogy to explain it to my class.
(Bare with me, taking the analogy away from technology requires some leeway) :

Imagine that you are a company, called StoreYourStuffCompany, that produces magic spells (Harry Potter references to follow). Your main product is an enchantment, StuffYourStuff, that makes all of your stuff, like books, clothes, furniture, etc, fit into a given space.

Now, the enchantment has been around for a while, and actually StoreYourStuffCompany has partners including home builders, so that your home comes designed with this StuffYourStuff enchantment. Hence all of your stuff fits into your house or apartment.

Recently, people want to carry more stuff with them, and so StoreYourStuffCompany has partnered with backpack and purse manufacturers, like the following videos.


Harry Potter's Undetectable Extension Charm
and Mary Poppins Carpet Bag:


Now the problem, when you have all of your stuff in one of these bags, is that it is poor experience as Mary Poppins shows looking for her tape measure. It is difficult to get to the thing that you are looking for, and you often have to touch everything to find the thing you want.

Yet, in your house finding your stuff is a relatively good experience (assuming a base level of home organization).

So StuffYourStuff comes up with an additional enchantment, GetYourStuff, which in Harry Potter language is the summoning charm, Accio. This charm allows you to simply say what you want and it snaps to your hand if it is nearby.

The GetYourStuff is hugely successful, so much so that people don't carry the backpacks and purses, instead use StuffYourStuff to keep all of their stuff in their pockets, and GetYourStuff brings them the exact items that they need when they need it.

StoreYourStuffCompany decides to discontinue partnering with the backpack and purse makers to heavily focus on improving the GetYourStuff charm, as well as the StuffYourStuff enchantment.

Rephrased for techology this story reads like this:

StoreYourStuffCompany = Adobe
StuffYourStuff = Flash
Home / Apartment = DESKTOP browsers
Backpacks and purses  = MOBILE browsers
GetYourStuff = AIR for desktop, mobile, other

When you are on your phone, do you go to facebook.com or the facebook app? twitter.com or TweetDeck? gmail.com or the email app?

I'm guessing you said the app. That is because the experience is better. You get the information that you need/want faster without touching everything. You go to the web to look up information that you can't get from an app. You are there to accomplish a specific task, when you are browsing on your phone, and HTML5 might be the technology that will allow you to succeed faster?  

Here are some other good insights into the matter:
http://mattgemmell.com/2011/11/09/adobe-communication/
http://forta.com/blog/index.cfm/2011/11/9/Some-Thoughts-On-Flash-And-Devices

My biggest fear is that for the lay people and business people who only hear that "Flash is not to be supported" / "Flash is dead", will base their financial decisions on that little and incorrect sound bite.

Adobe will need to do some serious marketing to get the right message across.

Friday, November 4, 2011

Working effectively with novices


So I have two books that I highly recommend.

First let me explain where I'm coming from: First I'm a professor, mentor, and trainer, and I always have to try to express my knowledge about Flash and Flex to people who are brand new.

Second, as a consultant, architect, and developer, I frequently need to express the value of TDD, Agile, CI, and lots of other really good things, that are not quite development.

These books highlight the differences between a novice's brain and an expert's, and how to start bridging that gap.


Pragmatic Thinking & Learning by Andy Hunt, talks about the novice versus expert brain. How the brain interprets and responds to signals. This is written by programmers, and is incredibly useful.

For me, this book answered the question of why did my project managers always seem to make decisions that I didn't agree with and I urged against.






Made To Stick by Chip & Dan Heath, is a fantastic book about what makes an idea or concept "sticky". Why do we remember certain stories, advertisments, urban legands after hearing them once, yet things critical to our job we forget the moment we leave the meeting.

I feel like this book would have been helpful for me to persuade the aforementioned managers of why some of the things I was staying was important.




For me, these two books are game changers. I re-written my entire University of Houston, "Intro to Multimedia" course to target the novice brain better, and I'm working on incorporating more sticky ideas too.

Because I'm so excited by these books, I've had to share.

Thursday, November 3, 2011

Migrating from Flex 3 to Flex 4 - A bulleted list

I know that there are lots of migration guides, but I thought I post a bulleted list. The intent of this migration is NOT to move to spark components, but to keep as much the same as the flex 3 build. So for example if you have a flex 3 dashboard, that you now want to load in flex 4 content.

1) Fix name spaces:
Search: xmlns:mx="http://www.adobe.com/2006/mxml"
Replace xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:mx="library://ns.adobe.com/flex/mx"

2) fix MetaData
Search: mx:Metadata
Replace: fx:Metadata

3) fix Script

Search: mx:Script
Replace: fx:Script

4) fix Component

Search: mx:Component
Replace: fx:Component

5) fix Bindings

Search: mx:Binding
Replace: fx:Binding

6) Declarations wrapping
All non-visual elements need to be wrapped into the Declarations Tag.
Create a Flex Template, to make this process really easy.
Declarations_multiline
<fx:Declarations>
${line_selection}
</fx:Declarations>



Declarations_singleLine
<fx:Declarations>
${word_selection}
</fx:Declarations>

Then it is just a matter of selecting the non-visual elements, hitting cntl-space and choosing the correct Declarations template

7) Adding the halo skin back into the workspace

  • Copy the Halo theme into the workspace into a folder called "themes"
    c:\p.f\adobe\Flashbuilder\sdk\framework\theme\Halo.swf
  • Flex Build Path -> Source Path -> Add Folder -> ${DOCUMENTS}\themes
  • update the compiler settings to include:
     -theme=Halo/halo.swc
8) Migrate the states.
This takes the most work, because there is not much automation that you can do

  1. Select all of the states and duplicate using cntl-alt-down
  2. cntl-shift-c, to comment out the duplicated code
  3. delete all of the nested nodes (AddChild, SetStyle, etc)
  4. Create a 2nd Editor View (Window -> new Editor), and adjust them so you can see both
  5. Keep the commented states on one side, and the version you are editing on the other
  6. Systematically add the state properties
    1. On the adds / removes, be sure to include the "includeIn" property
    2. Use the Flex4 state syntax for the properties and styles
  7. Be careful with the basedOn property of the states, you will need to make sure that your includeIn and properties reflect that as well. 
*********** At this point we should be just down to warnings ***********

9) Remove warnings about the styleManager
Manually replace "StyleManager.getStyleDeclaration" with "FlexGlobals.topLevelApplication.styleManager.getStyleDeclaration"

You need to do this manually, as you will also need FlexGlobals imports. Also if you are within a visual element, you can just use styleManager instead of the static FlexGlobals property

10) Manually replace "Application.application" with "FlexGlobals.topLevelApplication"
You need to do this manually, as you will also need FlexGlobals imports.

11) You might still have some CSS warnings left, if so, you can select the Flex 3 compatibility mode within the Flex Compiler options



Some useful resources I found along the way:

Tuesday, October 25, 2011

Multiple initializers for property 'dataprovider'

So, while migrating from Flex 3 to Flex 4, I'm going through the

  • change namespaces
  • move mx: to fx:
  • wrap non-UIComponents into <fx:Declarations>
The next round of compiler errors is
"Multiple initializers for property 'dataProvider'. (note: 'dataProvider' is the default property of 'mx.controls.ComboBox').

Super useful error message here. 
Thanks to the hint at http://www.freeflowingcode.com/blog/flex/flex-multiple-initializers I noticed that there where still non-visual elements inside these components. 

Wrapping these elements into <fx:Declarations> made the error go away. 

So I guess that the take-away is, for components that use a dataProvider, and whose default property is dataProvider, you get a cryptic error message when your <Declarations> are not inside the correct tag. 

Showing more errors in Flash Builder / Eclipse

Have you had a task, like repackaging, migrating from Flex 3 to Flex 4, or anything that generated 100s of errors. Did you ever notice that eclipse had a "convenient" way of displaying 100 of (x+100) errors? I just discovered the simple way to show ALL of your errors.

Problems View -> Preferences (via down arrow on right hand side) -> Use Marker Limit = false;

Tuesday, October 4, 2011

Focusing on the spark Datagrid

From my presentation at 360Flex Unconference at Max.

The Slide Deck

Here is the blog that talks about the demo and has the source code:
The demo and source code


Tuesday, September 13, 2011

Precision focus control within the Spark DataGrid

So, in preparation for my MAX 360Flex Unconference presentation on Tues Oct 4th at 2pm (http://www.360flex.com/blog/2011/08/360max-the-schedule-2/) I've put together this demo about how to get precision focus control within the Spark Datagrid.

In this example you can see:

  • Set focus to first editable item
  • Tab through occasionally editable fields
  • Focus with a mixture of multiple occasionally editable fields within a record
  • Focus on a particular data item
  • Focus using Tab, Shift+Tab, Arrows, and Page-Up and Page-Down
  • Remove Focus
  • Focus by clicking on a row
(Note, I've left the "disabled" cells editable, as I'm only changing the alpha. This is so that you can see that even if you click in to the non-editable fields directly, that the editing sessions (via the "Dashboard") doesn't get set)

view source

The slide deck for the preso will be coming soon.

Wednesday, September 7, 2011

DataGrids performance tests

 So my mission is to try to performance test the 3 Flex grids. mx:DataGrid, s:DataGrid and mx:AdvancedDataGrid.

I feel like my tests are incorrect and looking to you to point out my flaws.

Each grid has the same dataprovider and columns defined. I'm only using the default renderers
I have a StopWatch class, that will getTimer() from a start() method, until the next Event.RENDER from the grid under test.
I remove the data provider by setting it to null.
Once the screen is blank, I start the stopwatch and add the dataprovider.

The average of these tests over ten samples is:

Spark DataGrid: 207 ms
mx DataGrid: 176ms
AdvancedDataGrid: 171ms

I feel like my testing method is invalid, as I don't trust that

   1. The ADG is the fastest at rendering
   2. Spark is the worst at rendering
   3. That the difference in rendering between the 3 grids is negligible

Anyone to offer any insight?

Tuesday, August 9, 2011

HierarchicalCollectionView and Sorting Discovery

More woes around HierarchicalCollectionView. This was discovered while trying sort from the columns in the AdvancedDataGrid.

The usual case is that you click on a column and the sorting gets applied based on that column's datafield. In our case though, the datafield was a placeholder, as the item renderer took care of collecting the needed data out of the local cache based on the materialId.

Soooo... for sorting, we applied a sortCompareFunction to the column to account for this - so far so good.... until.....

HierarchicalCollectionView
 private function sortCanBeApplied(coll:ICollectionView):Boolean
    {
        if (sort == null)
            return true;
        
        // get the current item
        var obj:Object = coll.createCursor().current;
        
        if (!obj || !sort.fields)
            return false;
        
        // check for the properties (sort fields) in the current object
        for (var i:int = 0; i < sort.fields.length; i++)
        {
            var sf:SortField = sort.fields[i];
            if (!obj.hasOwnProperty(sf.name))
                return false;
        }
        return true;
    }
Notice line 16: if (!obj.hasOwnProperty(sf.name)) Well, if you trace everything back, it is essentially saying that the data HAS to have the property of the datafield of the column, otherwise no sorting.

Drat... now we have to add all of these [Transient] property placeholders to our vos, just to get sorting to work. (While messy, the other alternatives seemed even more messy and risky. Please comment if you have suggestions).

Here is a list of ideas that we considered but decided that it would make the code base less readable and understandable

  • Override hasOwnProperty on the VO
  • Override AdvancedDataGrid.addSortField() to change the sortField.name

  • Wednesday, July 27, 2011

    Spark Data Grid Tab Focus Control

    After spending so many months customizing the AdvancedDataGrid, I'm now interested to see if I can migrate many of those customizations to the Spark DataGrid. One of my first challenges is to see if I can improve my focus control - specifically tab navigation.

    To review, here is the use case I'm trying to achieve (BTW it works in spark, and only partially works in ADG). My data is a product, and I have two occasionally editable fields related to this product: Quantity and Price (my role is a sales associate - so I can negotiate on prices). The product as a whole is only editable if it is in stock; I cannot change quantity or price if it is out of stock. Price is only editable if and handful of business rules apply (margin, vendor, etc). I want to be able to tab to the editable fields.

    The problem with the ADG was that in it's "findNextEditableItemRenderer" loop, it only checked the data (ie, inStock). There was no way to tell what column it was looking at.

    I was quite pleased with the spark DataGrid and its nice separation of concerns. I love that there is a separate DataGridEditor. And I was happy to discover that there was a simple PUBLIC method on DataGrid that I could tap into to do everything that I wanted (So far).

    That public method is startItemEditorSession(rowIndex:int, columnIndex:int):Boolean. SWEET!

    That problem is resolved in the spark dataGrid.
    You can see the example here. In this example, the top grid is the regular one and the bottom grid is the modified one. I'm using the alpha property instead of enabled to prove that focus is indeed under my control (MUH HA HA HA - <evil laugh>).

    The key is that within the startItemEditorSession, you
  • convert the rowindex to the data via the getItemAt on the dataprovider
  • convert the columnIndex to the column
  • validate the data as well as the data[column.datafield] properties
  • return the boolean if editing should happen.

    And here is the code

    public class FocusSparkDataGrid extends DataGrid 
     {
      private var _isDataEditableHelper:IIsDataEditable
      
      public function FocusSparkDataGrid()
      {
       super();
      }
      
    
      public function get isDataEditableHelper():IIsDataEditable
      {
       return _isDataEditableHelper;
      }
    
      public function set isDataEditableHelper(value:IIsDataEditable):void
      {
       _isDataEditableHelper = value;
      }
    
      override public function startItemEditorSession(rowIndex:int, columnIndex:int):Boolean
      {
       var dataIsEditable:Boolean = isDataEditable(rowIndex,columnIndex);
       if (dataIsEditable)
       {
        var editingStarted:Boolean = super.startItemEditorSession(rowIndex,columnIndex);
        return editingStarted
       }
       return false;
      }
    
      
      protected function isDataEditable(rowIndex:int, columnIndex:int):Boolean
      {
       if (isDataEditableHelper != null)
       {
        var dataItem:Object = dataProvider.getItemAt(rowIndex);
        var column:GridColumn = GridColumn(columns.getItemAt(columnIndex));
        var dataField:String = column.dataField;
        return isDataEditableHelper.isDataEditableForProperty(dataItem,dataField)
       }
       return false;
      }
    }
    
    

    public class IsProductVOEditable implements IIsDataEditable
     {
      public function IsProductVOEditable()
      {
      }
      
      public function isDataEditableForProperty(data:Object, property:String=""):Boolean
      {
       var vo:ProductVO = ProductVO(data);
       if (vo.inStock)
       {
        if (property == "price")
        {
         return vo.priceEditable;
        }
        return true;
       }
       return false;
      }
     }
  • 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());
      }
    
    
     }
    
    

    Thursday, April 21, 2011

    Taming the Beast - AdvancedDataGrid Code

    I refactored and cleaned up my code from my 360Flex unconference presentation. I've posted an example and the code with unit tests at:

    http://www.squaredi.com/presentations/adgFocus/ADGLibrary.html

    the swf has view source enabled, so you can download it that way.

    Features:
    • External focus control. None, First, Next, Previous or any data item
    • Skipping data via a given condition ( in this case it is when the product.inStock == false)
    • Using arrow keys and page up and page down to move focus
    • Using tab in a normal mouse-less workflow
    • Clicking anywhere in a row causes focus to cell
    Goals (as a note to those that didn't attend my session)
    • Very easy to read (Compose, delegate, and very small functions)
    • Swappable functionality on a an instance level
    • Unit Testable
    • 100% decoupled from Data, Models, and Frameworks
    • Still  *is* an AdvancedDataGrid to the developers

    Monday, April 11, 2011

    Taming the beast - AdvancedDataGrid

    Here is my slide deck from the 360Flex unconference presentation... I'm not ready to post the code yet (let me clean it up and comment it)...

    http://www.squaredi.com/presentations/AdvancedDataGrid%20-%20Taming%20the%20beast.pptx

    Wednesday, March 16, 2011

    Stump the Evangelists

    So back at MAX 2010, there was a stump the evangelists game. There was a call for questions. I submitted a whole bunch, but most weren't used. These questions and their answers are useful to share, because they were intended to stump the people in the know. They all came about because there was significant debugging effort involved in understanding them.

    Q1) Which kind of COLLECTION_CHANGE Event (as specified by CollectionKindEvent), does the HierarchicalCollectionView ignore, when dispatched from its hierarchicalData source.
    A1) CollectionKindEvent.REFRESH
    Reference: HierachicalCollectionView.
    collectionChangeHandler() method   -> line 1413; if-else statement
    Discovered when trying to apply filtering to an array collection that gets mapped to hierachicalData




    Q2)  Why do you see columns A&B and not just A in the following example:

    <?xml version="1.0" encoding="utf-8"?>
    <mx:AdvancedDataGrid xmlns:fx="http://ns.adobe.com/mxml/2009"
                         xmlns:s="library://ns.adobe.com/flex/spark"
                         xmlns:mx="library://ns.adobe.com/flex/mx"
                         currentState="A"
                         >
        <mx:states>
            <s:State name="A" />
            <s:State name="B" />
        </mx:states>
       
        <mx:columns>
            <mx:AdvancedDataGridColumn dataField="A" />
            <mx:AdvancedDataGridColumn dataField="B" />
        </mx:columns>
       
        <mx:columns.A>
            <mx:AdvancedDataGridColumn dataField="A" />
        </mx:columns.A>
       
        <mx:columns.B>
            <mx:AdvancedDataGridColumn dataField="B" />
        </mx:columns.B>
    </mx:AdvancedDataGrid>

    A2) The problem is with commitProperties. currentState gets set in UIComponent's commitProperties, but  in AdvancedDataGridBaseEx.commitProperties(), createDisplayableColumns is called BEFORE super.commitProperties(). This means that the columns get set before the state, which SHOULD cause them to reset, but since we are currently within the commitProperties method, the invalidateProperties flag does NOT get triggered again (to avoid an infinite loop), even though the columnChanged flag gets set. CommitProperties doesn't run again until an external event which causes invalidateProperties to be set.





    Q3) Within a single collection displayed in an editable column of an editable AdvancedDataGrid, you have a property that is sometimes editable and sometimes not (for example, you cannot purchase quantity to an out of stock item, even though it still shows up on the catalog). What tricks can you employ to have the ADG skip over the non-editable cells when using keyboard navigation?

    A3) There is nothing you can do on the renderer/editor to prevent this. This cell will still receive focus from keyboard navigation, because of the findNextItemRenderer() method within AdvancedDataGridBase. Even if there are no focusable elements within the cell. Nor can you pass focus to the next element via the focusManager as the grid doesn't respond to non-keyboard or mouse focus events. The ONLY point of entry (ie: public or protected properties or methods) to which you have access to cause focus to skip over this renderer is the  protected function isDataEditable(data:Object):Boolean defined on AdvancedDataGridBase. You MUST extend the ADG and override this method in order to be able to set up your grid to skip particular cells.

    Q4.1) Not using Flash Player 10.1, how can you catch a majority of thrown errors within the Flex component lifecycle.
    A4.1) UIComponentGlobals.catchCallLaterExceptions = true

    Q4.2) With UIComponentGlobals.catchCallLaterExceptions set to true, what errors do NOT get caught?
    A4.2) Errors that happen within EventListeners

    Q4.3) With Flash Player 10.1, how can you catch all errors
    A4.3) loaderInfo["uncaughtErrorEvents"].addEventListener("uncaughtError",globalExeptionCatcher) //at the time being, there is / should be a way with strongly typed values to do this later.


    Q5) How do I see a traceLog of all of the Bindings that are firing?
    A5) BindingManager.debugBinding()

    Q6) What is the value of the updateCount from the following code?
    <?xml version="1.0" encoding="utf-8"?>
    <s:myComp xmlns:fx="http://ns.adobe.com/mxml/2009"
                   xmlns:s="library://ns.adobe.com/flex/spark"
                   xmlns:mx="library://ns.adobe.com/flex/mx"
                   creationComplete="onCreationComplete()"
                   >
        <fx:Script>
            <![CDATA[
                private var _myProp:Number = 1;
                private var updateCount:Number = 0;
               
                [Bindable]
                public function get myProp():Number {return _myProp;}
                public function set myProp(v:Number):void
                {
                    _myProp = v;
                    updateCount++;
                }
               
                private function onCreationComplete():void
                {
                    myProp = 1;
                }
            ]]>
        </fx:Script>
       
    </s:myComp>
    A6) 0. The binding doesn't fire if the getter returns the same value as what you are setting. You would need to manually dispatch an event within the setter and set the event metadata within the binding to get the updateCount value to change to 1

    Q7) How must you create Bindings on implict getters and setters within an interface?
    package
    {
        interface IExample
        {
            function get propA():String;
            function set propA(v:String):void;
        }
    }

    A7) You must use explict events within the bindings tag, otherwise bindings will never fire in the implementations
    package
    {
        interface IExample
        {
            [Bindable(event="propAChanged")]
            function get propA():String;
            function set propA(v:String):void;
        }
    }


    Q8) What are any of the non-standard practices (related to the flex framework as a whole) regarding the hover state in the MxAdvancedDataGridItemRenderer that make it extremely difficult to extend this class and modify the hover functionality?

    A8)        * hovered property is being set in the "invalidateProperties" method instead of the "commitProperties" method.
                 * actually it is super.hovered that is being set, elminating the ability to override it
                 * when hovered IS being set, the state change happends via "setCurrentState()" method instead of "currentState" property
                 * hovered is determined from "listBase.isItemHighlighted(data);"... ie the renderer asks the grid. this supercedes any value that is passed in manually
                 * hovered property doesn't actually get set (in the super clases) if the currentState is already "hovered".

    Thursday, March 10, 2011

    Getting below the minimum height for the spark combo box

    Did you know that the spark combobox has a built in minimum height of 16? Trying to change that is a little tricky.

    <?xml version="1.0" encoding="utf-8"?>
    <s:ComboBox xmlns:fx="http://ns.adobe.com/mxml/2009" 
       xmlns:s="library://ns.adobe.com/flex/spark" 
       xmlns:mx="library://ns.adobe.com/flex/mx"
       >
     <fx:Declarations>
      <!-- Place non-visual elements (e.g., services, value objects) here -->
     </fx:Declarations>
     <fx:Script>
      <![CDATA[
       import mx.core.IUIComponent;
       import mx.core.UIComponent;
       import mx.events.FlexEvent;
       
       private var partArray:Array = new Array();
    
    
    
    
       protected override function childrenCreated():void
       {
        //TODO Auto-generated method stub
        super.childrenCreated();
        var len:int = partArray.length;
        var comp:UIComponent;
        //reset those pesky minimum heights
        for (var i:int = 0 ; i< len; i++)
        {
         comp = UIComponent(partArray[i])
         comp.explicitMinHeight = Math.min(height,comp.minHeight);
         comp.explicitMinWidth = Math.min(width, comp.minWidth);
        }
       }
    
       protected override function partAdded(partName:String, instance:Object):void
       {
        //TODO Auto-generated method stub
        super.partAdded(partName,instance);
        partArray.push(instance);
       }
    
       
      ]]>
     </fx:Script>
     
    </s:ComboBox>
    
    

    Generating fake data

    Fake data? Why would you ever need fake data?

    Well the backend isn't ready? You're doing a POC? You're offline (airplane) while trying to develop.

    I've found myself creating collections of vo's just to fill a list or combo box to test my interactions and events. I use to write for loops to do this and found that I could simplify the process somewhat.

    So I created a little generator script, that you input the class (VO), the number that you want, and optionally any properties that you don't want to generate and it will return an Array of sequentially described VOs.

    Basically what this means is that all of your vo string properties will be A,B,C,etc. Your vo number properties will be the index that it is, ie first vo = 0, 2nd vo = 1, etc. boolean vo properties will be random t/f. Date properties will be new Date().

    This is not set up for complex nested datatypes

    
    package com.squaredi.core.data.generators
    {
     /**
      *
      * Context:
      *  During development (unit testing, POC's, and deployment before the backend)
      *  We may want to quickly create a series of temporary objects to fill a grid or to play with
      * 
      * Therefore:
      *   We can generate a series of objects where string properties will be "A,B,C..."  
      *   Number properties will be the number of the item that is generated
      *   Boolean properties will be random. 
      * 
      * Forces:
      *   The intent is to create a filled VO quickly... the data is not ment to be accurate
      * 
      * But:
      *   This doesn't include any additional datatypes then the intrinsic ones listed
      */
     
     import flash.utils.describeType;
     
     import mx.utils.ObjectUtil;
    
     public class MockGenerator
     {
      private static function createStub(cls:Class,index:int = 0, ignoreProperties:Array /*String*/ = null):Object // NO PMD
      {
       if (ignoreProperties == null) {ignoreProperties = [];}
       
       // First grab all of our writeable getter/setters and variables
       var info:XML =  describeType(cls);
       var setters:XMLList = info..accessor.(@access == "readwrite");
       var vars:XMLList = info..variable;
       var merge:XMLList = setters + vars;
       
       //Figure out what letter in the sequence this is going to be
       //If we go above 26, then start repeating letters (AA, BB) (AAA,BBB)
       var lettersInAlphabet:Number = 26; //0 based
       var repeatCount:int = Math.max(1,Math.ceil(index / lettersInAlphabet));
       if (index % lettersInAlphabet ==0 && index >0) {repeatCount++;}
       var asciiNum:Number = 65 + (index % lettersInAlphabet)
       var asciiChar:String = String.fromCharCode(asciiNum);
       var stringVal:String = ""
       for (var i:int = 0 ; i < repeatCount; i++)
       {
        stringVal += asciiChar
       }
       
       //Figure out what Numbers and Booleans are going to be
       var numVal:Number = index;
       var boolVal:Boolean = Boolean(Math.round(Math.random()));
       
       //Create the new Object, loop over each property and apply value
       var obj:Object = new cls();
       var prop:String;
       var type:String;
       for each (var propXml:XML in merge)
       {
        prop = propXml.@name;
        type= propXml.@type;
        
        //skip over namespaced properties (internal managed data for example)
        if (propXml.attribute("uri").length() >0)
        {
         break;
        }
        
        //Skip over uid
        if (prop == "uid")
        {
         continue;
        }
        
        //Allow for the developer to ignore setting some of the properties (as that might be the thing that they are testing)
        if (ignoreProperties.indexOf(prop) == -1)
        {
         switch(type)
         {
          case "String": obj[prop] = stringVal;break;
          case "Number":
          case "int":
           obj[prop] = numVal;
           break;
          case "Boolean":
           obj[prop] = boolVal;
           break;
          case "Date":
           obj[prop] = new Date();
           break;
         }
        }
       }
       
       return obj;
      }
      
      /**
       * Generate a given number of mock objects with some unique data
       * */
      public static function createStubs(cls:Class, total:int, ignoreProperties:Array /*String*/ = null ):Array /*Objects of type Class*/
      {
       var rtn:Array = new Array();
       for (var i:int = 0; i < total; i++)
       {
        rtn.push(createStub(cls,i,ignoreProperties));
       }
       return rtn;
      }
      
     }
    }
    

    Conversion to TitleCase

    So I discovered in livedocs (Using Regular Expressions ) that you can pass a function as the 2nd parameter to a regular expression replace.

    When a function name is used as the second parameter of the replace() method, the following are passed as parameters to the called function:
    • The matching portion of the string.
    • Any captured parenthetical group matches. The number of arguments passed this way varies depending on the number of captured parenthetical group matches. You can determine the number of captured parenthetical group matches by checking arguments.length - 3 within the function code.
    • The index position in the string where the match begins.
    • The complete string. 
    therefore, to make a really easy convert to TitleCase method:


    public static function toTitleCase(value:String):String
      {
       if (value)
       {
        //Convert to title case
        var titleCaseStr:String = value.toLowerCase();
        
        //http://livedocs.adobe.com/flex/3/html/help.html?content=12_Using_Regular_Expressions_10.html
        
        var toUpperFxn:Function = function (...parameters):String 
        {
         return parameters[1].toUpperCase()
        }
    
        //Find all word boundarys and then the first letter of the word... then call the function
        titleCaseStr = titleCaseStr.replace(/\b(\w)/g, toUpperFxn);
        
        return titleCaseStr;
       }
    
       return ""; 
      }
    

    Copying Related Properties into Objects

    Lets say that you have a hierarchy of VO's that share some properties....
    for example:
    • OrderedItemVO extends ProductItemVO
    • PriceExtendedVO extends PriceVO
    and the issue is that you did a search (or whatever) and you have a collection of products, that you then want to add to an order (in which they need to be upgraded to an ordered item vo).

    The brute force solution would be to create a new OrderedItemVo() and copy a property one by one into it like:
    var copy: OrderedItemVO = new OrderedItemVO();
    copy.id = source.id
    copy.description = source.description
    ...
    

    but here is an alternative method that will do this a little more elegantly:

    public static function copy(source:Object, destination:Object):void // NO PMD
      {
       // First grab all of our writeable getter/setters and variables
       var info:XML =  describeType(source);
       var setters:XMLList = info..accessor.(@access == "readwrite");
       var vars:XMLList = info..variable;
       var merge:XMLList = setters + vars;
       
       //loop over everything adding the properties
       var prop:String;
       var type:String;
       var isDynamic:Boolean = info.@isDynamic == "true"
       for each (var propXml:XML in merge)
       {
        prop = propXml.@name;
        
        //skip over uid... a good idea to exclude this one.
        if (prop == "uid")
        {
         continue;
        }
        
        if (destination.hasOwnProperty(prop) || isDynamic )
        {
         destination[prop] = source[prop]
        }
        
       } 
      }
    

    Wednesday, March 9, 2011

    Cancelling inflight remote calls

    So here is a couple of scenarios:
    1. User clicks search, then immediately clicks logout, a couple of moments later the search results come back and the user is taken back into search results screen, resulting in an unstable app now that the user is null
    2. Sales Rep is working on a quote for customer A, in the middle of an operation (search, save, quote), they get a phone call from customer B and swithes accounts to handle the phone call. Want to make sure that customer A result events don't corrupt or popuplate customer B's data model.
    So how to cancel a remote operation?

    mx.rpc.Operation does have a cancel method, but that won't work if you are using a Responder pattern based on a token. Besides, it requires knowledge of all of the operations that are inflight.

    Here is another approach. Add a static variable to AsyncToken. When a new AsyncToken is created, copy that static variable into a local variable. Before calling our responders compare the local variable to the static variable before proceeding. If you need to cancel a response, change the static variable on the AsyncToken.

    So that is the solution... now how to implement it.
    You need to extend 3 classes to get this work. AsyncToken, Operation, RemoteObject.
    AsyncToken is extended as described above.
    Operation is extended to return the new AsyncToken,
    RemoteObject is extended to use the new Operation.

    AsyncTokenWResponderValidation.as
    package com.squaredi.remoting.cancelable
    {
     import mx.core.mx_internal;
     import mx.messaging.messages.IMessage;
     import mx.rpc.AsyncToken;
     import mx.rpc.IResponder;
     import mx.rpc.events.FaultEvent;
     import mx.rpc.events.ResultEvent;
     
     use namespace mx_internal;
     /**
      * Context: You need to cancel an inflight message
      * Issue: While you can cancel an mx.rpc.Operation, it doesn't actually cancel responders assigned to the AsyncToken
      *     Since many of the frameworks relay on the Responder pattern, which applies the responder to the token,
      *     this is how we can not respond to an inflight message
      * 
      * Usage: if needing to cancel an inflight message -> AsyncTokenWResponderValidation.resetValidationToken();
      *       
      * 
      * 
      * */
     public dynamic class AsyncTokenWResponderValidation extends AsyncToken
     {
      
      private static var globalValidationToken:String;
      protected var validationToken:String;
      
      public static function resetValidationToken():void
      {
       globalValidationToken = String(new Date().time);
      }
      
      public static function getGlobalValidationToken():String
      {
       return globalValidationToken;
      }
      
      public function AsyncTokenWResponderValidation(message:IMessage=null)
      {
       super(message);
       if (getGlobalValidationToken() == null)
       {
        resetValidationToken()
       }
       validationToken = getGlobalValidationToken();
      }
      
      
      mx_internal override function applyFault(event:FaultEvent):void
      {
       if (isTokenValid())
       {
        super.applyFault(event);
       }
      }
    
      mx_internal override function applyResult(event:ResultEvent):void
      {
       if (isTokenValid())
       {
        super.applyResult(event);
       }
      }
    
      protected function isTokenValid():Boolean
      {
       return this.validationToken == getGlobalValidationToken() 
      }
    
     }
    }
    

    CancelableOperation.as
    package com.squaredi.remoting.cancelable
    {
     import mx.core.mx_internal;
     import mx.messaging.messages.IMessage;
     import mx.rpc.AbstractService;
     import mx.rpc.AsyncToken;
     import mx.rpc.remoting.Operation;
     
     use namespace mx_internal;
     
     public class CancelableOperation extends Operation
     {
      public function CancelableOperation(remoteObject:AbstractService=null, name:String=null)
      {
       super(remoteObject, name);
      }
      
      mx_internal override function invoke(message:IMessage, token:AsyncToken=null):AsyncToken
      {
       if (token == null)
       {
        token = new AsyncTokenWResponderValidation(null);
       }
       return super.invoke(message,token);
      }
     }
    }
    

    CancelableRemoteObject.as
    package com.squaredi.remoting.cancelable
    {
     import mx.core.mx_internal;
     import mx.rpc.AbstractOperation;
     import mx.rpc.remoting.RemoteObject;
     
     use namespace mx_internal
     public class CancelableRemoteObject extends RemoteObject
     {
      public function CancelableRemoteObject(destination:String=null)
      {
       super(destination);
      }
      
      override public function getOperation(name:String):AbstractOperation
      {
       var op:AbstractOperation = super.getOperation(name);
       if (!( op is CancelableOperation))
       {
        op = new CancelableOperation(this, name);
        this._operations[name] = op;
        op.asyncRequest = this.asyncRequest;
       }
       return op;
      }
     }
    }
    

    Tuesday, March 8, 2011

    Caingorm as a plugin

    Context: When developing a large app, we often want to make modules, or even fully self contained subsections of the app. The implemention of  classic cairngorm (< version 3.0) doesn't really allow for that.
    I wanted to be able to create a userPreference package, a product package, and order package, etc that I could then easily merge together into the main app.

    so this is what I want.

    package examples.cairngorm
    {
     import com.squaredi.frameworks.cairngorm.FrontControllerPlugin;
    
     public class MainFC extends FrontControllerPlugin
     {
      public function MainFC()
      {
       super("main");
       addController(new PlugInFC());
      }
     }
    }
    
    package examples.cairngorm
    {
     import com.squaredi.frameworks.cairngorm.FrontControllerPlugin;
     
     public class PlugInFC extends FrontControllerPlugin
     {
      public function PlugInFC(id:String=null)
      {
       super("plugin");
       addCommand(MyCoolThingEvent.EVENT, MyCoolThingCommand);
      }
     }
    }
    

    So here is the class that I created to do that.

    FrontControllerPlugIn.as
    package com.squaredi.frameworks.cairngorm
    {
     import com.adobe.cairngorm.CairngormError;
     import com.adobe.cairngorm.CairngormMessageCodes;
     import com.adobe.cairngorm.commands.ICommand;
     import com.adobe.cairngorm.control.CairngormEvent;
     import com.adobe.cairngorm.control.CairngormEventDispatcher;
     import com.adobe.cairngorm.control.FrontController;
     
     import flash.events.EventDispatcher;
     import flash.utils.Dictionary;
     
     import mx.logging.ILogger;
     import mx.logging.Log;
     import mx.utils.ObjectUtil;
    
     /**
      * This file is not likely to be edited. Please extend it for the various projects
      * 
    
      * It also has the ability to add other Controllers into itself to make a plugin type architecture via the addController method
      * 
      * As a plugin type architecture, there are use cases when a developer of a subcomponent or subproject might have a default implementation for and event 
      * (like show an alert for isolation testing), and know that the real implementation needs to be different. 
      * 
      * They should add the command via the addCommandToOverride method
      * 
      * */
     public class FrontControllerPlugin extends FrontController
     {
      protected static var logger:ILogger = Log.getLogger("com.squaredi.frameworks.cairngorm.FrontControllerPlugin")
      private var pluginControllers:Array /*FrontControllerPlugin*/ = new Array();
      
      private var cmdToPluginMap:Dictionary = new Dictionary(true);
      
      protected var overrideCommands:Dictionary = new Dictionary(true);
      
      private var id:String
      
      public function FrontControllerPlugin(id:String = null)
      {
       super();
       this.id = id;
       if (Log.isInfo())
       {
        logger.info("FrontControllerPlugin constructor: " + this.toString());
       }
      }
      
      
      
      /**
       * Adds all of the event command pairs already assocated with another frontController
       * This will throw an error if there are duplicated event names.
       * The expection to this is that if the duplicated event name is intended to be override
       * 
       * @param p
       * 
       */
      public function addController(p:FrontControllerPlugin):void
      {
       /*
       1) Recurse through all subcontrollers
       2) Add only new controllers
       3) Registger commands and deregister from added controllers
       */
       
       //Do subcontrollers
       if (p.pluginControllers.length >0)
       {
        //Register sub controllers first
        var len:int = pluginControllers.length;
        for (var i:int = 0; i < len; i++)
        {
         addController(p.pluginControllers[i]);
        }
        
       }
       
       //Find new Controllers
       if (findPlugin(p) < 0)
       {
        //Have not registered the controller yet
        pluginControllers.push(p);
        
        //Copy Override Items
        for (var clz:String in p.overrideCommands)
        {
         overrideCommands[clz] = p.overrideCommands[clz]; 
        }
        
        //Copy the commands in and remove them from the plugin
        var localcommands:Dictionary = p.commands;
        for (var cmd:String in localcommands)
        {
         //Make sure not to add commands twice either
         cmdToPluginMap[cmd] = p //Make sure to map the command before it gets added
         addCommand(cmd,localcommands[cmd],true);
         p.removeCommand(cmd);
        }
       }
       else
       {
        if (Log.isInfo()){logger.info(p.toString() + " has already been added");}
       }
      }
      
      protected function findPlugin(p:FrontControllerPlugin):int
      {
       var rtn:int = -1;
       var len:int = pluginControllers.length;
       for (var i:int = 0; i < len; i++)
       {
        if (pluginControllers[i].toString() == p.toString())
        {
         rtn = i;
         break;
        }
       }
       return rtn;
      }
      
      /**
       * 
       * Overrides the super class to allow for an error message that includes which plugin the conflict already exists in
       * 
       * @param commandName
       * @param commandRef
       * @param useWeakReference
       * 
       */
      public override function addCommand(commandName:String, commandRef:Class, useWeakReference:Boolean=true):void
      {
       //Add usefull infomation for the alert
       var pluginRef:FrontControllerPlugin = cmdToPluginMap[commandName];
       var pluginName:String = this.toString();
       if (pluginRef != null) { 
        pluginName = pluginRef.toString();
        cmdToPluginMap[commandName] = this;
       }
       
       var commandReference:Class = commands[ commandName ]
       if( commandReference != null)
       {
        //Verify that the command is not in the override list
        var overrideCommandReference:Class = overrideCommands[ commandName ]
        if (overrideCommandReference == null)
        {  
         throw new Error( "Command already registered for " + commandName +" in " + pluginName);
        }
        else
        {
         //We are indeed overriding a command
         if (Log.isInfo()) { logger.info(this.toString + " overriding a command")};
         
         //lets remove the old reference and add the new
         overrideCommands[ commandName ] = null;
         delete overrideCommands[ commandName ];
         
         cmdToPluginMap[commandName] = this;
         
        }
       }
       commands[ commandName ] = commandRef;
       CairngormEventDispatcher.getInstance().addEventListener( commandName, executeCommand, false, 0, useWeakReference );
      }
      
      /**
       * * As a plugin type architecture, there are use cases when a developer of a subcomponent or subproject might have a default implementation for and event 
       * (like show an alert for isolation testing), and know that the real implementation needs to be different. 
       * 
       * Adding command via this method will throw an error if this frontController gets added to another one AND the command is not redefined in the new one
       * 
       * */
      public function addCommandToOverride(commandName:String, commandRef:Class, useWeakReference:Boolean=true):void
      {
       var commandReference:Class = overrideCommands[ commandName ]
       if( commandReference != null)
       {
        var pluginRef:FrontControllerPlugin = cmdToPluginMap[commandName];
        var pluginName:String = this.toString();
        if (pluginRef != null) { 
         pluginName = pluginRef.toString();
         cmdToPluginMap[commandName] = this;
        }
        
        throw new Error( "Command already registered for override " + commandName +" in " + pluginName);
       }
       
       overrideCommands[ commandName ] = commandRef;
       
       addCommand(commandName, commandRef, useWeakReference);
       
      }
      
      protected function checkForOverrideCommands():void
      {
       //Copy Override Items
       var needsToBeOverridden:Array = new Array();
       for  (var clz:String in overrideCommands)
       {
        needsToBeOverridden.push(clz);
       }
       
       if (needsToBeOverridden.length >0)
       {
        throw new Error (this.toString() + " needs to override the following events: " + needsToBeOverridden.toString());
       }
       
      }
      
      internal function forceCheckForOverrideCommands():void
      {
       checkForOverrideCommands();
      }
      /**
       * 
       * Creates the command and temporarily adds it to the context so that we get dependancy injection
       * 
       * 
       */
      override protected function executeCommand( event : CairngormEvent ) : void
      {
       var commandToInitialise : Class = getCommand( event.type );
       var commandToExecute : ICommand = new commandToInitialise();
       
       if (Log.isInfo()) {logger.info("EVENT:" + event.type);}
       commandToExecute.execute( event );
       
      }
      
      internal function getCommandForTesting(commandName:String):Class
      {
       try
       {
        var cmd:Class = getCommand(commandName)
       }
       catch (e:Error)
       {
        //Ignore Cairngorm Errors for testing
       }
       return cmd;
      }
      
      /**
       * 
       * Returns the className for debugging purposes
       * 
       * @return 
       * 
       */
      public function toString():String
      {
       var classInfo:Object = ObjectUtil.getClassInfo(this);
       return classInfo.name + " (" + id + ")"
      }
      
      
     }
    }
    

    Using Palettes with CSS

    Context: Often times when working on a project, I find that there is a limited number of colors, or a palette, that we are going to be using. But in the CSS each color is specified independently. I would love to be able to say use "corporateGreen" or "corporateBrown" in my css rather then litter the entire css or skins with hex values. This will improve readability, consistency and maintenance.

    So here is the solution that I have:

    1. Define a Palette Class based on Proxy that can translate names (String) to colors(uint)
    2. Have this Palette Class read in an embeded text file that defines the colors
    3. Pass the palettes to a stylesheetMixin class that parses the css to replace the names in the css with the hexdecimal values before applying the styles.

    Palette.as
    package com.squaredi.styles
    {
     /**
      * 
      * @author Drew Shefman 
      * dshefman at squaredi dot com
      * 
      */
     import flash.utils.Dictionary;
     import flash.utils.Proxy;
     import flash.utils.flash_proxy;
     
     import mx.core.ByteArrayAsset;
     
     /**
      * Read name value pairs of color names from an "external file."
      * "external file" is quoted because to get it to sync with CSS we actually embed the external files.
      * Here is an example of the external file 
    * 
      * _companyRed=0xFF0000
      * _companyGreen=0x00FF00
      * _companyBlue=0x0000FF
      * _columnGreen=_companyGreen
      * 
      * Note: Watch out for spaces in the file
    */
     dynamic public class Palette extends Proxy
     {
      public static var throwErrorsForMissingColors:Boolean = true;
      public static const MISSING_COLOR:int = -1
      
      private  var dict:Dictionary;
      public var paletteName:String;
      
      public function Palette(p_name:String)
      {
       dict = new Dictionary();
      }
      
      /**
       * Create an instance of the embedded text file, 
       * Process the file and split it into name value pairs
       * Save off the names into the palette
       **/ 
      public  function addColorsViaEmbededTextClass(theClass:Class):void
      {
       //Convert the embeded text file to a string
       var byteArray:ByteArrayAsset = ByteArrayAsset(new theClass());
       var str:String = byteArray.readUTFBytes(byteArray.length);
       
       //Clean up extra characters
       str = str.split(" ").join(""); //remove white spaces
       
       
       //Loop over name/value pairs
       var split:Array = str.split("\r\n");
       var obj:Object = this;
       var key:String;
       var value:String;
       var pair:Array;
       var pairStr:String;
       var len:int = split.length;
       for ( var i:int =0 ; i < len ; i++ )
       {
        pairStr = split[i];
        pair = pairStr.split("=");
        key = pair[0];
        value = pair[1];
        obj[key] = value;
       }
      }
      
      
      flash_proxy override function getProperty(name:*):*
      {
       if (isValid(name))
       {
        if (flash_proxy::hasProperty(name))
        {
         return Number(dict[name]);
        }
        else
        {
         if (throwErrorsForMissingColors)
         {
          throw new Error(name + " is an invalid color. Please look in the external colors file to figure out the name.");
         }
         else
         {
          return MISSING_COLOR;
         }
        }
       }
       return null;
      }
      
      flash_proxy override function hasProperty(name:*):Boolean
      {
       return dict[name] != null;
      }
      
      flash_proxy override function setProperty(name:*, value:*):void
      {
       
       //Make sure the name/values are not blank
       if (name=="" || value == "") {return;}
       
       //name comes in as a QName, so we need to getString to get the value out of it.
       name = name.toString();
       
       //Allow for colors to reference previously defined colors
       if (isValid(value)) { value = flash_proxy::getProperty(value)};
       
       //Verify that the name is correct and save it 
       if (isValid(name))
       {
        dict[name] = value;
       }
       else
       {
        throw new Error("Palette colors should start with a '_' to allow for easy distinction from other css variables");
       }
      }
      
      /**
       * Force that color variables start with and "_" 
       * */
      private function isValid(value:String):Boolean
      {
       if (value.indexOf("_") == 0)
       {
        return true;
       }
       
       return false;
      }
      
     }
    }
    

    StyleSheetMixin.as:
    StyleSheeMixin.as
    usage: http://stackoverflow.com/questions/2292127/how-to-have-constants-in-flex-css-files
    // =================================================================
    /* 
    *  http://stackoverflow.com/questions/2292127/how-to-have-constants-in-flex-css-files** *  Copyright (c) 2010 viatropos http://www.viatropos.com/
     *  Lance Pollard
     *  lancejpollard at gmail dot com
     *  
     *  Permission is hereby granted, free of charge, to any person
     *  obtaining a copy of this software and associated documentation
     *  files (the "Software"), to deal in the Software without
     *  restriction, including without limitation the rights to use,
     *  copy, modify, merge, publish, distribute, sublicense, and/or sell
     *  copies of the Software, and to permit persons to whom the
     *  Software is furnished to do so, subject to the following
     *  conditions:
     * 
     *  The above copyright notice and this permission notice shall be
     *  included in all copies or substantial portions of the Software.
     * 
     *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
     *  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
     *  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
     *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     *  OTHER DEALINGS IN THE SOFTWARE.
     */
    // =================================================================
    
    package com.squaredi.styles
    {
     import flash.display.Sprite;
     import flash.events.Event;
     import flash.utils.getDefinitionByName;
     
     import mx.core.IMXMLObject;
     import mx.core.Singleton;
     import mx.styles.CSSStyleDeclaration;
     import mx.styles.IStyleManager2;
     import mx.styles.StyleManager;
     
     public class StylesheetMixin implements IMXMLObject
     {
      private var _palettes:Array;
      /**
       *  Classes of static constants storing values for css
       */
      public function get palettes():Array
      {
       return _palettes;
      }
      public function set palettes(value:Array):void
      {
       _palettes = value;
      }
      
      public function StylesheetMixin()
      {
      }
      
      public function setStyles():void
      {
       // get all selectors in the application
       var styleManager:IStyleManager2 = getStyleManager();
       var selectors:Array = styleManager.selectors;
       var declaration:CSSStyleDeclaration;
       var i:int = 0;
       var n:int = selectors.length;
       for (i; i < n; i++)
       {
        declaration = styleManager.getStyleDeclaration(selectors[i]);
        // set palette properties to each declaration
        setProperties(declaration);
       }
      }
      
      protected function getStyleManager():IStyleManager2
      {
       var application:*;
       try {
        application = flash.utils.getDefinitionByName("mx.core::FlexGlobals")["topLevelApplication"];
        if (application)
         return application.styleManager as IStyleManager2;
       } catch (error:Error) {
        application = flash.utils.getDefinitionByName("mx.core::Application")["application"];
        if (application)
         return IStyleManager2(Singleton.getInstance("mx.styles::IStyleManager2"));
       } 
       return null;
      }
      
      protected function setProperties(declaration:CSSStyleDeclaration):void
      {
       var selector:Object = getDeclarationToken(declaration);
       var property:String;
       for (property in selector)
       {
        setProperty(declaration, property, selector[property]);
       }
      }
      
      public function getDeclarationToken(declaration:CSSStyleDeclaration):Object
      {
       var selector:Object = {factory:declaration.factory};
       // maybe your selector has a "factory" property which we should avoid?
       if (!(typeof(selector.factory) == "function") || selector.factory == null)
        return null;
       selector.factory();
       delete selector.factory;
       return selector;
      }
      
      public function setProperty(declaration:CSSStyleDeclaration, property:String, value:*):*
      {
       var paletteValue:*;
       var changed:Boolean = false;
       if (value is Array)
       {
        var i:int = 0;
        var n:int = value.length;
        for (i; i < n; i++)
        {
         paletteValue = getPaletteItem(value[i]);
         if (paletteValue)
         {
          changed = true;
          value[i] = paletteValue;
         } 
        }
        
       }
       else if (value is String)
       {
        paletteValue = getPaletteItem(value);
        if (paletteValue)
        {
         value = paletteValue;
         changed = true;
        }
       }
       if (changed)
       {
        declaration.setStyle(property, value);
       }
      }
      
      public function getPaletteItem(targetId:String):*
      {
       var i:int = 0;
       var n:int = palettes.length;
       var PaletteClass:Object;
       for (i; i < n; i++)
       {
        PaletteClass = palettes[i];
        if (PaletteClass[targetId])
         return PaletteClass[targetId];
       }
       return null;
      }
      
      private var timer:Sprite = new Sprite();
      // have to wait a frame for styles to be initialized
      public function initialized(document:Object, id:String):void
      {
       var handler:Function = function(event:Event):void
       {
        timer.removeEventListener(Event.ENTER_FRAME, handler);
        timer = null;
        setStyles();
       }
       timer.addEventListener(Event.ENTER_FRAME, handler);
      }
     } 
    }
    
    So then in your Application, the way that you would use it would be:
    Application.mxml
    <?xml version="1.0" encoding="utf-8"?>
    <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
          xmlns:s="library://ns.adobe.com/flex/spark" 
          xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600"
          xmlns:styles="com.squaredi.styles.*"
          
          initialize="onInitialize(event)" 
          >
     <fx:Script>
      <![CDATA[
       import com.squaredi.styles.Palette;
       
       import mx.events.FlexEvent;
       [Embed(source="examples/styles/palette.txt", mimeType="application/octet-stream")]
       public  var paletteClass:Class
       
       [Bindable] private var allPalettes:Array
       
       protected function onInitialize(event:FlexEvent):void
       {
        var pal:Palette = new Palette("main");
        pal.addColorsViaEmbededTextClass(paletteClass);
        
        allPalettes = new Array(pal);
        
       }
    
    
      ]]>
     </fx:Script>
     
     <fx:Style>
      @namespace s "library://ns.adobe.com/flex/spark";
      @namespace mx "library://ns.adobe.com/flex/mx";
      
      
      
      .example
      {
       backgroundColor: _corpRed;
       backgroundAlpha:1.0;
       
      }
      </fx:Style>
     
     <fx:Declarations>
      <!-- Place non-visual elements (e.g., services, value objects) here -->
      <styles:StylesheetMixin palettes="{allPalettes}" />
     </fx:Declarations>
     <s:SkinnableContainer styleName="example" width="200" height="200" >
      
      <s:Label text="Hello world" />
     </s:SkinnableContainer>
    </s:Application>
    
    
    and your embeded text file would look like:
    _corpRed=0xFF0000