Version-safe Monkey Patch on Flex components
What? Why?
Sometimes we want to add some custom behavior to a Flex component and everything you need is private or you find some bug that you can't fix only with inheritance. Or maybe you can, but if you do, you'll have to change all instances to your component. And sometimes you even can't change the instance, like when you are using some closed library.
Today I was trying to fix a bug at the ComboBox component, using the Flex SDK 3.5. This bug was already fixed on the version 3.6, but I can't update if they don't release it as a stable version. This bug is very stupid and a simple method override with 3 lines of code would solve my problem. But, like I said, I can't just change all the ComboBox instances in my application. And, even if I did, maybe some other developer could use the original ComboBox, instead of my FixedComboBox.
So I decided to temporarily Monkey Patch the ComboBox class. To do that, I just had to copy the original ComboBox class and all the includes, fix what I wanted and put it at the same package, but inside my project "src" folder. The compiler will use my patched version and ignore the original class.
The problem:
Okay, but what if we forget to remove the patched version when we update the SDK? Everything is Flex 4 now, so maybe they will only release the SDK 3.6 version in some months. And maybe in some months I will be working in a different project, don't know...
The solution!
So I was thinking... How can I check if the SDK version was updated?
Every Flex component includes a file called "Version.as", that have only one constant with the SDK version. Most people just copy this file when they are "Monkey Patching", but if we do that, it will replace the SDK "Version.as" file and we will never know the correct version.
The trick is: don't copy this "Version.as" file to your project. Then replace the include with the constant inside the file. Find this line:
include "../core/Version.as";
and replace with:
/**
* @private
* Version string for this class.
*/
mx_internal static const VERSION:String = "3.5.0.12683";
Now we don't need to copy the file and we can have different versions for the SDK and the patched components. And you can add this code inside your constructor to check if the version changed:
if( FlexSprite.mx_internal::VERSION != mx_internal::VERSION )
{
setTimeout(function():void{
throw new Error("The SDK version was updated. Please update or remove the Monkey Patched ComboBox from this project.");
}, 1);
}
That's it!
FAQ:
Why FlexSprite? I was thinking about using UIComponent, but maybe you'll Monkey Patch it too. So I think you don't need to change anything inside FlexSprite.
Why throwing an error? You can change it to a trace(), but will you read the console all the time? And don't worry, only people with the debugger Flash Player can read this error anyway.
Why the setTimeout()? If you throw an error inside your constructor, it will break your application. I tried callLater(), but other things in Flex use the callLater() queue and, if you throw an error there, it will break the queue. So I think the setTimeout() is a safe place to put it.
Conclusion:
Monkey Patch is not the best solution. Actually, it's not even a good solution! But it's necessary sometimes and, if you really need it, I think the version check code is a good thing to help you.
Do you have some alternative solution? I would love to read it at the comments!
Cheers!