What is Tequila

Tequila is Flex/Air/Flash MVC framework based on PureMVC multicore version( extended with Fabrication utility ). It takes all the best from base, simplifies various areas and adds new functionality.

download tequila
download tequila docs
date version comment
02.03.10 9.0.1
  • extended properties logging for proxies and mediators ( Tequila Console )
  • fixed wrong actions counting when logging ( Tequila Console )

Tequila Console is AIR application to debug notification flow, framework issues and user log messages.

Please upgrade your Flash Player This is the content that would be shown if the user does not have Flash Player 9.0.115 or higher installed.

Why Tequila was created?

PureMVC is awesome, I think. But, as many other things, has its drawbacks. Pipes are great in theory, but using them can be very hard for someone that is new to framework and all this “junctions” terminology scares him a lot. Here comes Fabrication – great utility wich handles pipes management behind easier solutions, introduces reactions, interceptors ( those are great, believe me ) reflexive notification/mediators registrations ( with support for flex components deffered instantation ). But still, couple of things are not as I want them to be so I’ve decided to introduce some other, new solutions.

look at example code:

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
package com.moi.tequila.example.model {
 
    import org.puremvc.as3.multicore.utilities.fabrication.patterns.mediator.FlexMediator;
    import spark.components.WindowedApplication;
 
    public class AppMediator extends FlexMediator {
 
        static public const NAME:String = "AppMediator";
 
        public function AppMediator(viewComponent:WindowedApplication)
        {
 
            super(NAME, viewComponent);
        }
 
        override public function onRegister():void
        {
            super.onRegister();
            var someProxy:SomeProxy = retrieveProxy(SomeProxy.NAME) as SomeProxy;
            var anotherProxy:AnotherProxy = retrieveProxy(AnotherProxy.NAME) as AnotherProxy;
        }
 
        public function get view():WindowedApplication
        {
            return viewComponent as WindowedApplication;
        }
 
    }
}

19th and 20th line is what I’m not fan of when creating stuff in PureMVC. Using static constants and casting maybe is not wrong idea, but writing the same code over and over through many places in code may drive you crazy. I thought that simple dependency injection mechanism would be very useful in this place. Check this out, the same class but instead FabricationMEdiator we use TequilaMediator:

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 com.moi.tequila.example.model {
 
    import com.moi.tequila.puremvc.patterns.mediator.TequilaFlexMediator;
    import spark.components.WindowedApplication;
 
    public class AppMediator extends TequilaFlexMediator {
 
        [InjectProxy]
        public var someProxy:SomeProxy;
 
        [InjectProxy]
        public var anotherProxy:AnotherProxy;
 
        static public const NAME:String = "AppMediator";
 
        public function AppMediator(viewComponent:WindowedApplication)
        {
 
            super(NAME, viewComponent);
        }
 
        override public function onRegister():void
        {
            super.onRegister();
            trace( someProxy );
            trace( anotherProxy );
        }
 
        public function get view():WindowedApplication
        {
            return viewComponent as WindowedApplication;
        }
 
    }
}

These two proxies are injected behind the scene, in TequilaMediator registration action. You can do the same by using ‘InjectMediator’ tag to inject early registered mediators. Every command in Tequila has the same possibilities. What’s more – when injecting proxy wich can be created on-the-fly ( that means no params in constructor ), that is NOT registered before, it is automatically created, and added to facade. And what if you have couple instances of the same proxy, or you want to type your proxy as an interface? no problem:

1
2
[InjectProxy(name='anyInstanceOfSomeProxy')]
public var someProxy:SomeProxy;

Extended reaction registration

Fabrication does marvelous job introducing reactions and their automatic creation/dispose during mediator lifetime cycle. Using reacTo.. patterns has one drawback – in mediator there has to be present getter fo event dispatcher instance ( for 99% cases it will be viewComponent itself or its public property ). So, each item we want to register eaction fro has to have its getter in mediator body, as in here:

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
package com.moi.tequila.example.view {
 
    import flash.events.MouseEvent;
    import mx.core.UIComponent;
    import org.puremvc.as3.multicore.utilities.fabrication.patterns.mediator.FlexMediator;
    import spark.components.Application;
 
    public class SomeMediator extends FlexMediator {
 
        static public const NAME:String = "SomeMediator";
 
        public function SomeMediator(viewComponent:Application)
        {
 
            super(NAME, viewComponent );
        }
 
        public function reactToBtnClick( event:MouseEvent ):void {
 
            // do something on event
 
        }
 
        public function get btn():UIComponent {
 
            return view.button;
 
        }
 
        public function get view():Application
        {
            return viewComponent as Application;
        }
 
    }
}

As we see getter in 24th line is needed to register reaction ( via reactToBtnClick method on 18th line ) in mediator registration phase. Without getter no error is raised, but simply no event is registered. So, idea is to provide easy way to register reaction directly on viewComponent property. in Tequila this can be done by using reactTo_ reaction pattern. See example:

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
package com.moi.tequila.example.view {
 
    import com.moi.tequila.puremvc.patterns.mediator.TequilaFlexMediator;
    import flash.events.MouseEvent;
    import spark.components.Application;
 
    public class SomeMediator extends TequilaFlexMediator {
 
        static public const NAME:String = "SomeMediator";
 
        public function SomeMediator(viewComponent:Application)
        {
 
            super(NAME, viewComponent );
        }
 
        public function reactToButton_Click( event:MouseEvent ):void {
 
            // do something on event
 
        }
 
        // this is not needed anymore :)
 
        /*public function get btn():UIComponent {
 
            return view.button;
 
        }*/
 
        public function get view():Application
        {
            return viewComponent as Application;
        }
 
    }
}

In registration phase injection mechanism is checking fo ‘button’ property on mediator itself. If it is not found it tries to register reaction on ‘button’ property of viewComponent instead. Of course you can use fabrication way, too but in Tequila you have another way to achive the same but with less code to write :).

.. And there is more – you can use addReaction method on TequilaFlex/FlashMediator to add reaction manually. In all cases, when mediator is removed ( unregistered ) from facade all reactions are automatically disposed.

Strong typed notification interests

When using reflexive notification interests provided by Fabrication, you probably do it this way:

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
// first, you create notification name constant. Let's make it in separate class:
package com.moi.tequila.example.notifications {
    public class MyNotifications {
 
        public static const MY_NOTE:String = "MY_NOTE";
 
    }
}
 
// then, create mediator( or command ) that will handle this notification ( for example ):
package com.moi.tequila.example.view {
 
    import org.puremvc.as3.multicore.interfaces.INotification;
    import org.puremvc.as3.multicore.utilities.fabrication.patterns.mediator.FlexMediator;
    import spark.components.Application;
 
    public class SomeMediator extends FlexMediator {
 
        static public const NAME:String = "SomeMediator";
 
        public function SomeMediator(viewComponent:Application)
        {
 
            super(NAME, viewComponent );
        }
 
        public function respondToMyNote( notification:INotification ):void {
 
            // act on notification
        }
 
        public function get view():Application
        {
            return viewComponent as Application;
        }
 
    }
}

Sometimes, probably during some refactoring, you want to change your notification name. Let’s say you changed it to:

1
public static const MY_SPECIAL_NOTE:String = "MY_SPECIAL_NOTE";

If you forget to change all respondTo.. methods that uses this notifications – upps, you have got a problem. Believe me – in big project it is not so rare and can be very confusing. So there is my idea ( bit strange ) how to handle that kind of problem.. Let say, we will keep notification names as a namespaces :)! Try to think about notifying observers as triggering some actions for all of them in predefined namespace. Every notification has its own namespace. Look at the example below, firs we declare a notification as a namespace:

1
2
3
4
5
6
// com.moi.tequila.notifications.MY_NOTE.as
package com.moi.tequila.console.notifications {
 
    public namespace MY_NOTE = "MY_NOTE";
 
}

Then, let’s define mediator to act as an observer for this notification:

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
package com.moi.tequila.view {    
 
    import com.moi.tequila.puremvc.patterns.mediator.TequilaFlexMediator;
    import mx.core.UIComponent;
    import org.puremvc.as3.multicore.interfaces.INotification;
 
    public class SomeMediator extends TequilaFlexMediator {
        static public const NAME:String = "SomeMediator";
        ;
        public function SomeMediator(viewComponent:UIComponent)
        {
            super(NAME, viewComponent);
        }
 
        MY_NOTE function processNotification(notification:INotification):void
        {             //act on notification 
 
        }
 
        override protected function invokeNamespaceNotificationHandler(namespace:Namespace, note:INotification):void
        {
            ( namespace::processNotification ).apply(this, [ note ]);
        }
    }
}

Pros:

Cons:

The last thing is not a big problem when you can use template mechanism :).

So, this is it.

P.S.

Big, big thanks for Mateusz MaƂczak ( komixo, homepage) ) for support, feedback and testing my tequila. I know that Mateusz is working on some cool stuff ( as always he does ) using tequila, so maybe I can provide you the first usecase in short time.