How to enable Tab key on OS X modal dialogs

I’m a key accelerator man. I hate taking my hands out of my keyboard just to press Yes or No in a Modal Dialog. Until last week I thought there was no way to tab through the different buttons of a modal dialog.

Luckily I came across an option in System Preferences to enable the tab key navigation:

  • System Preferences
  • Keyboard
  • Full keyboard access: All Controls

Comments (1)

Some little gems on OSX you might want to know

Here are some little things that might speed you up when working on a Mac.

1. View hidden files in Finder

In terminal type:

1
defaults write com.apple.finder AppleShowAllFiles TRUE

To hide them again:

1
defaults write com.apple.finder AppleShowAllFiles FALSE

2. Manage clipboard from command line

To copy the output of a parameter you can use pbcopy:

1
pwd | pbcopy

The result from pwd will be copied to the clipboard. You can seamlessly use pbpaste

3. Copy directory trees without overrideing

Imagine you’ve got 2 folders: v1 and v2 and you want to copy the contents of v1 inside v2 recursively (merging the contents). With a normal cp command you copy v1 to v2 and there’s a bin folder in both, the bin folder in the destiny will be completely replaced with the one found in v1.

If you were in Linux you would use cp -a, in Mac you can use:

1
cp -pPR v1 v2

Where:
-p = preserve
-R = recursive
-P = no symbolic links are followed — can be added but this is the default

4. Adding some colors to ls

If you want to see some colors used when executing ls you can add the folowing lines to ~/.bash_profile:

1
2
3
4
export TERM=xterm-color
export LSCOLORS=fxhxcxdxbxegedabagacad
alias ls='ls -la'
export CLICOLOR=1

The second line changes the default annoying blue color applied to folders with a more readable red color.

5. VNC client

If you need to connect via vnc to any computer you don’t have to install a vnc client. Open Finder, press command+K and type vnc://host where host is the ip or domain you want to vnc to. A VNC client will automatically pop up.

If you want to allow incoming VNC connections to your MAC you can enable your VNC server open “System Preferences” > “Sharing” and check the “Screen Sharing” option.

6. Remote Login on X server via SSH

There are cases where you have to execute X applications remotely but the server doesn’t have a VNC server neither the possibility.

In these cases you can use:

1
2
xhost host
ssh -X -A userName@host

Note that to be able to execute the commands above you need to have X11. The first line basically allows incoming connections from the server. The ssh command stablishes a connection against the host indicating that any application that nees an X server will be run in the local computer.

Once logged in you can try to execute any visual application. You’ll see that the application pops up locally.

7. Open any document with the default application

You might want to open a random file from the command line in the default desktop application

1
open aFile.ext

To open current directory in finder:

1
open .

8. Open a terminal in current Finder directory

Lots of times you navigate to a certain folder using Finder and then you want to open a Terminal in that folder. To accomplish that you can install openTerminalHere applescript http://www.entropy.ch/software/applescript/

9. You don’t like your dashboard? Disable it!

1
2
defaults write com.apple.dashboard mcx-disabled -boolean NO
killall Dock

10. Show current full path in Finder

1
2
defaults write com.apple.finder _FXShowPosixPathInTitle -bool YES
killall Finder

Do you know any other must-know command or gem?

Tags:

Comments (5)

FlexPMD Eclipse plugin + FlexPMD 1.0 + FlexCPD 1.0 + FlexMetrics released!

We’ve been busy, very busy during the last weeks. But we’re now very happy to announce the release of:

  • FlexPMD 1.0
  • FlexCPD 1.0
  • FlexMetrics 1.0

And our first beta for the new FlexPMD Eclipse plugin that will better integrate FlexPMD into your development workflow!!

You can find all the documentation for FlexPMD on the wiki and for the plugin http://opensource.adobe.com/wiki/display/flexpmd/FlexPMD+Eclipse+plugin

This movie requires Flash Player 9

This movie requires Flash Player 9

If you have any question, problem or suggestion you can use the public forums.

Tags:

Comments (3)

Does an EventDispatcher have subscribed listeners ?

Lately I’ve been reading several discussions around the internals of EventListener, how easy is to create memory leaks, the dependencies between IEventListener and EventListener and onther fancy things. Robert Penner has lots of details around these topics in his 2 part articles:

Even though not a solution I want to add a new parameter to this equation that might help (at least for debugging purposes).

As per the documentaqtion flash.sampler.getMemberNames returns an object containing all members of a specified object, including private members. If we pass a reference to an EventDispatcher instance we will get a private member called listeners. Yes, you’re right, it’s the list of listeners the EventDispatcher has associated.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package {
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.EventDispatcher;
    import flash.sampler.getMemberNames;

    public class testEvents extends Sprite {
        public function testEvents() {
            var myDispatcher:EventDispatcher = new EventDispatcher();
            myDispatcher.addEventListener("myEvent1", handler1);
            myDispatcher.addEventListener("myEvent2", handler2);

            var members:Object = getMemberNames(myDispatcher);

            for each (var name:QName in members) {
                if (name.localName == "listeners") {
                    trace(myDispatcher[name].length);
                    myDispatcher.removeEventListener("myEvent1", myDispatcher[name][0]);
                    myDispatcher.removeEventListener("myEvent2", myDispatcher[name][0]);
                    trace(myDispatcher[name].length);
                }
            }
        }


        private function handler1(event:Event):void {
            trace("handler1");
        }


        private function handler2(event:Event):void {
            trace("handler2");
        }
    }
}

The example above shows how we can get a reference to the different listeners declared in an EventDispatcher and how to remove them without having a direct reference (and knowing the event name arggg).

Even though this can help you to figure out if an EventDispatcher has listeners or not there’re still several problems you might find:

  • You don’t have any information about the listener
  • You don’t know which event the listener is listening to
  • We don’t know which phase the listener is listening to
  • Haven’t done too much testing around this, but I think both weak and strong references are hold in the list
  • The other thing to consider is that flash.sampler.getMemberNames only works in the debugger version of the Flash Player

A very uggly implementation for a removeAllListeners method could be something like:

1
2
3
for each listener in listeners
   for each eventName that the dispatcher can dispatch
      eventDispatcher.removeListener( eventName, listener ) for all phases

If after each removeListener we check the length of the listeners list we could find out the events the listeners were listening to as well as the phase and thean provide some debugging information around it.

Comments (5)

FlexPMD now in labs

FlexPMD is a tool that helps to improve code quality by auditing any AS3/Flex source directory and detecting common bad practices, such as:

  • Unused code (functions, variables, constants, etc.)
  • Inefficient code (misuse of dynamic filters, heavy constructors, etc.)
  • Over-complex code (nested loops, too many conditionals, etc.)
  • Over-long code (classes, methods, etc.)
  • Incorrect use of the Flex component lifecycle (commitProperties, etc.)
  • A report is produced describing the violations of a given rule set. FlexPMD includes a rule set that is broad ranging and continually growing. It is also straightforward to create new rules and users are encouraged to do so.

FlexPMD can currently be invoked from:

  • The command line
  • Maven
  • Ant
  • Automator on Mac OS X
  • Currently, FlexPMD produces a XML report by default, plus an HTML report when invoked by the Maven site plugin. The XML report can then be consumed by any PMD reports pretty printers (like the PMD hudson plugin).

An Eclipse plugin is under consideration.

Some good information on how to start using it can be found in this blog post.

Tags:

Leave a Comment

Logging and catching (async) exceptions during invalidation

Sometimes, due to many different reasons, there’re exceptions that happen during the invalidation/validation cycle of the flex SDK. Most times these exceptions end up being unveiled in the very core of the SDK, being not possible to catch them up because their async nature and the place where they happen.

The stacktrace of these exceptiosn, normally, looks like the lines below:

1
2
3
4
5
6
7
...
at mx.core::UIComponent/validateProperties()[C:\autobuild\3.x\frameworks\projects\framework\src\mx\core\UIComponent.as:5807]
at mx.managers::LayoutManager/validateProperties()[C:\autobuild\3.x\frameworks\projects\framework\src\mx\managers\LayoutManager.as:539]
at mx.managers::LayoutManager/doPhasedInstantiation()[C:\autobuild\3.x\frameworks\projects\framework\src\mx\managers\LayoutManager.as:689]
at Function/http://adobe.com/AS3/2006/builtin::apply()
at mx.core::UIComponent/callLaterDispatcher2()[C:\autobuild\3.x\frameworks\projects\framework\src\mx\core\UIComponent.as:8633]
at mx.core::UIComponent/callLaterDispatcher()[C:\autobuild\3.x\frameworks\projects\framework\src\mx\core\UIComponent.as:8582]

Today, snooping around the UIComponent class, I’ve found a nice unexpected thing. Basically the invalidation pattern in the SDK is built around the LayoutManager and the UIComponent classes. The callLater method is the responsible to delay the execution of certain functionalities to the next frame. callLater implementation relies on the RENDER and ENTER_FRAME frames. The handler for these events is the method callLaterDispatcher.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// At run-time, callLaterDispatcher2() is called
// without a surrounding try-catch.
if (!UIComponentGlobals.catchCallLaterExceptions)
{
    callLaterDispatcher2(event);
}

// At design-time, callLaterDispatcher2() is called
// with a surrounding try-catch.
else
{
    try
    {
        callLaterDispatcher2(event);
    }
    catch(e:Error)
    {
        var callLaterErrorEvent:DynamicEvent = new DynamicEvent("callLaterError");
        callLaterErrorEvent.error = e;
        systemManager.dispatchEvent(callLaterErrorEvent);
    }
}

Here’s the surprise. If UIComponentGlobals.catchCallLaterExceptions is true, the execution of the validation cycle will be wrapped with a try..catch and a DynamicEvent will be dispatched through the systemManager with information about the error.

This opens the doors for at least catch the error and log it. In some cases, depending on the severity of the error and if the application is not too corrupted, we’ll even be able to display a message to the user notifying him the error. This is not a fix to FP-1499 nor FP-444 but under some circumstances it can be really helpful if combined with great logging practices as explained by Tom.

I’ve created a simple class that hooks into the systemManager, listens for the callLaterError event and logs it using the standard SDK logging mechanism. The AsyncErrorLogger is MXML friendly so that it can be instantiated in the root application. It provides an enable property to switch the behavior on and off. Additionally the property redispatchErrorEvent, which by default is false, redispatches the error causing the traditional flash player error dialog to appear (in debugger player versions) and not fail silently.

Here’s an example on how to use it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?xml version = "1.0" encoding = "utf-8"?>
<mx:WindowedApplication xmlns:mx = "http://www.adobe.com/2006/mxml" xmlns:utils = "com.adobe.ac.utils.*">

    <mx:Script>
        <![CDATA[
           import mx.logging.LogEventLevel;
           
           //---------------------------------------------
           // THAT'S THE WAY TO SIMULATE THE ASYNC ERROR
           //---------------------------------------------
           
           private var doItChange : Boolean = false;
           
           private function doIt() : void
           {
               doItChange = true;
               invalidateProperties();
           }
           
           override protected function commitProperties() : void{
               super.commitProperties();
               
               if( doItChange )
               {
                   doItChange = false;
                   throw new Error("CallLater exception");
               }
           }
       ]]>
    </mx:Script>

    <mx:TraceTarget level = "{ LogEventLevel.ALL }"/>

    <utils:AsyncErrorLogger/>

    <mx:Button click = "doIt()" label = "Throw exception"/>
</mx:WindowedApplication>

Here’s the code. You can download the class from here as well.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package com.adobe.ac.utils
{
    import flash.events.ErrorEvent;
    import flash.events.EventDispatcher;

    import mx.core.Application;
    import mx.core.UIComponentGlobals;
    import mx.events.DynamicEvent;
    import mx.events.FlexEvent;
    import mx.logging.ILogger;
    import mx.logging.Log;
    import mx.managers.ISystemManager;

    [Event(name="error",type="flash.events.ErrorEvent")]
    public class AsyncErrorLogger extends EventDispatcher
    {
        //------------------------------------------------------------------------
        //
        //  Constants
        //
        //------------------------------------------------------------------------
        private static const CALL_LATER_ERROR:String = "callLaterError";

        private static const LOG:ILogger = Log.getLogger("com.adobe.cairngorm.AsyncErrorLogger");


        //------------------------------------------------------------------------
        //
        //  Properties
        //
        //------------------------------------------------------------------------

        //-------------------------------
        //  systemManager
        //-------------------------------
        private var systemManager:ISystemManager;

        //-------------------------------
        //  logStackTrace
        //-------------------------------

        public var logStackTrace:Boolean = true;

        //-------------------------------
        //  redispatchError
        //-------------------------------
        public var redispatchError:Boolean = false;

        //-------------------------------
        //  enabled
        //-------------------------------

        [Bindable]
        public function get enabled():Boolean
        {
            return UIComponentGlobals.catchCallLaterExceptions;
        }

        public function set enabled(value:Boolean):void
        {
            UIComponentGlobals.catchCallLaterExceptions = value;
        }


        //------------------------------------------------------------------------
        //
        //  Constructor
        //
        //------------------------------------------------------------------------

        public function AsyncErrorLogger(systemManager:ISystemManager = null)
        {
            enabled = true;

            init(systemManager);
        }


        //------------------------------------------------------------------------
        //
        //  Private Methods
        //
        //------------------------------------------------------------------------

        private function init(systemManager:ISystemManager):void
        {
            if (systemManager)
            {
                initializeListener(systemManager);
            }
            else
            {
                var application:Object = Application.application;

                if (!application.initialized)
                {
                    application.addEventListener(FlexEvent.CREATION_COMPLETE, creationCompleteHandler);
                }
                else
                {
                    initializeListener(application.systemManager);
                }
            }
        }

        private function creationCompleteHandler(event:Event):void
        {
            var application:Object = Application.application;
            application.removeEventListener(FlexEvent.INIT_COMPLETE, creationCompleteHandler);

            initializeListener(application.systemManager);
        }

        private function initializeListener(systemManager:ISystemManager):void
        {
            this.systemManager = systemManager;

            if (this.systemManager)
            {
                LOG.debug("AsynErrorLogger initialized.");
                this.systemManager.addEventListener(CALL_LATER_ERROR, errorHandler);
            }
        }

        private function errorHandler(event:DynamicEvent):void
        {
            var error:Error = event.error as Error;

            LOG.error("{0}", error.message);

            if (logStackTrace)
            {
                LOG.error(error.getStackTrace());
            }

            if (redispatchError)
            {
                var errorEvent:ErrorEvent = new ErrorEvent(ErrorEvent.ERROR, false, false, error.getStackTrace(), error.errorID);
                dispatchEvent(errorEvent);
            }
        }
    }
}

Tags: , ,

Leave a Comment

Debugging production ready AIR applications

One of the biggest problems around testing installed AIR applications is that testers can’t see when a runtime exception is triggered. Lots of times the symptom of a problem will be something completely different to the real cause and developers will have to spend much more time trying to figure out what was the root cause of the problem. Even though you have a good logging policy if there’s an uncaught exception there’s not too much you can do.

Here are some techniques I’ve came across to make testing less painful for everybody.

The debug flag way

One thing I’ve been thinking for a while is that there are not two different versions of the AIR runtime; one with debugging enabled and the other without it (the way Flash Player has). This means that there might be a way to enable and disable debugging in the same runtime. This would allow, at least in some controlled environments, to see Runtime Exceptions and make everybody’s life easier. At least until we have global error handling implemented directly on AIR or Flash Player. Yes, I agree, It’s unbelievable that on its 10th version we still are not able to capture global exceptions: FP-444 and FP-1499

Using Procmon I realized that when an AIR application is launched it tries to read a file in {INSTALLATION_FOLDER}/META-INF/AIR/debug. See the screenshot. What surprised me was the result “NAME NOT FOUND”. Basically the file didn’t exist in the app I was playing with. After some more trial / error, yes, you’re right, this is the flag that switches on and off debugging capabilities.

If an AIR application is launched and an empty file with name debug is found in {INSTALLATION_FOLDER}/META-INF/AIR/debug then, any runtime exception will be displayed in a system modal window! In OS the path for this file will be {APPLICATION_FOLDER}.app/Contents/Resources/META-INF/AIR/debug

So if testers (or users during the earlier versions of your app) are using your application, make sure they create the debug file and attach any stack trace they see to their bug reports.

The adl way

This is not as easy as the previous solution but it gives the testers more options.

Once installed, an AIR application, is not more than the swf file produced from FB, its application descriptor and a native launcher. Given that we have access to the main application swf file and its corresponding descriptor we can use ADL to launch the installed application instead of the native launcher.

Here’s how:

1. Installed the SDK you want to use from http://opensource.adobe.com/wiki/display/flexsdk/Downloads
2. Uncompress the zip in the location you prefer. I’ll refer to this folder as {FLEX_HOME}

In Windows
3. Right click on “My Computer” > Properties
4. Go to “Advanced” tab
5. Click in the option “Environment Variables”
6. In the “System variables” find the “Path” definition and add this to it (double clicking on it): “;{FLEX_HOME}/bin”
7. Make sure your replace {FLEX_HOME} with the path to the folder where you’ve uncompressed the SDK

In OS X
3. Open a terminal
4. Execute this command: open ~/.bash_profile
5. Add the following line to the opened file:
PATH=${PATH}:{FLEX_HOME}/bin
6. Make sure your replace {FLEX_HOME} with the path to the folder where you’ve uncompressed the SDK

In Linux
3. Open a terminal
4. Edit your profile file ~/.profile
5. Add the line: export PATH=$PATH:$FLEX_HOME

Then to launch the application you will have to:

In Windows
1. Open a command line window. Start > “Run …” > cmd
2. Navigate to the folder where the application is installed: cd “Program Files”/MyApp
3. Execute this command: adl META-INF/AIR/application.xml .
4. Don’t forget the dot at the end of the command

In OS X and Linux
1. Open a terminal
2. Navigate to the folder where the application is installed: cd /soft/testAirApp.app/Contents/Resources where testAirApp is the name of your application
3. Execute the following command: adl META-INF/AIR/application.xml .
4. Don’t forget the dot at the end of the command

If during the execution process there’s an exception, ADL will show it.

Tags: ,

Comments (8)

Code hinting filtering… a little gem

Flash Builder now includes the ability to filter code hints depending on whether the the hint is a property, an style, an event or an effect. Simply press ctrl+space to cycle through them all.

Comments (3)

Assert( Gumbo )

Here are the slides I used during my presentation around testing, FlexUnit4 and RIATest.

This movie requires Flash Player 9

Tags: , ,

Leave a Comment

Hello Gumbo presentation

Here are the slides and sample files I used for the Hello Gumbo presentation I did for the Spanish Flex User group in the context of the Global AUG tour.

This movie requires Flash Player 9

This movie requires Flash Player 9

This movie requires Flash Player 9

Here’s the source code for the examples.

Tags: , , ,

Comments (1)