├── .gitignore
├── src
├── example
│ ├── view
│ │ ├── INumberEntry.as
│ │ ├── INumberDisplay.as
│ │ ├── LabelDisplayComponent.as
│ │ ├── SliderInputComponent.as
│ │ ├── DropDownListInputComponent.as
│ │ └── NumericStepperInputComponent.as
│ ├── skins
│ │ ├── SliderInputComponentSkin.mxml
│ │ ├── LabelDisplayComponentSkin.mxml
│ │ ├── NumericStepperInputComponentSkin.mxml
│ │ └── DropDownListInputComponentSkin.mxml
│ ├── signals
│ │ └── NumberUpdatedSignal.as
│ ├── controller
│ │ └── NumberEnteredCommand.as
│ ├── example.css
│ ├── events
│ │ └── NumberEnteredEvent.as
│ ├── model
│ │ └── NumberModel.as
│ ├── mvcs
│ │ ├── NumberDisplayMediator.as
│ │ ├── NumberEntryMediator.as
│ │ └── InterfaceEnabledMediatorMapContextExample.as
│ └── Example.mxml
└── org
│ └── robotlegs
│ └── base
│ └── ViewInterfaceMediatorMap.as
├── tests
├── org
│ └── robotlegs
│ │ ├── mvcs
│ │ └── support
│ │ │ ├── ViewComponentAdvanced.as
│ │ │ ├── TestContextView.as
│ │ │ ├── IViewComponent.as
│ │ │ ├── ViewMediatorAdvanced.as
│ │ │ ├── ITestContextView.as
│ │ │ ├── ViewComponent.as
│ │ │ ├── TestContextViewMediator.as
│ │ │ ├── ViewMediator.as
│ │ │ ├── ViewInterfaceMediator.as
│ │ │ ├── TestViewInterfaceMappingContext.as
│ │ │ └── TestViewInterfaceMappingSignalContext.as
│ │ ├── InterfaceMappingTestSuite.as
│ │ └── base
│ │ ├── BaseTestSuite.as
│ │ └── InterfaceEnabledMediatorMapTests.as
└── InterfaceMappingTests.mxml
└── readme.textile
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | *.iml
3 | out/
4 | .idea/
5 |
--------------------------------------------------------------------------------
/src/example/view/INumberEntry.as:
--------------------------------------------------------------------------------
1 | package example.view
2 | {
3 | import org.osflash.signals.ISignal;
4 |
5 | public interface INumberEntry
6 | {
7 | function get numberEntered():ISignal;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/example/view/INumberDisplay.as:
--------------------------------------------------------------------------------
1 | package example.view
2 | {
3 | import org.osflash.signals.ISignal;
4 |
5 | public interface INumberDisplay
6 | {
7 | function set numberUpdated(value:ISignal):void;
8 | }
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/src/example/skins/SliderInputComponentSkin.mxml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/example/skins/LabelDisplayComponentSkin.mxml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/tests/org/robotlegs/mvcs/support/ViewComponentAdvanced.as:
--------------------------------------------------------------------------------
1 | package org.robotlegs.mvcs.support
2 | {
3 | public class ViewComponentAdvanced extends ViewComponent
4 | {
5 | public function ViewComponentAdvanced()
6 | {
7 | super();
8 | }
9 |
10 | }
11 | }
--------------------------------------------------------------------------------
/src/example/skins/NumericStepperInputComponentSkin.mxml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/tests/org/robotlegs/mvcs/support/TestContextView.as:
--------------------------------------------------------------------------------
1 | package org.robotlegs.mvcs.support
2 | {
3 | import mx.containers.Canvas;
4 |
5 | public class TestContextView extends Canvas implements ITestContextView
6 | {
7 | public function TestContextView()
8 | {
9 | super();
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/tests/org/robotlegs/InterfaceMappingTestSuite.as:
--------------------------------------------------------------------------------
1 | package org.robotlegs
2 | {
3 |
4 | import org.robotlegs.base.BaseTestSuite;
5 |
6 | [Suite]
7 | [RunWith("org.flexunit.runners.Suite")]
8 | public class InterfaceMappingTestSuite
9 | {
10 | public var baseTestSuite:BaseTestSuite;
11 | }
12 |
13 | }
--------------------------------------------------------------------------------
/src/example/signals/NumberUpdatedSignal.as:
--------------------------------------------------------------------------------
1 | package example.signals
2 | {
3 | import org.osflash.signals.Signal;
4 |
5 | public class NumberUpdatedSignal extends Signal
6 | {
7 | public function NumberUpdatedSignal()
8 | {
9 | super(Number);
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/tests/org/robotlegs/mvcs/support/IViewComponent.as:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by IntelliJ IDEA.
3 | * User: conrad
4 | * Date: Oct 6, 2010
5 | * Time: 7:30:10 PM
6 | * To change this template use File | Settings | File Templates.
7 | */
8 | package org.robotlegs.mvcs.support
9 | {
10 | public interface IViewComponent
11 | {
12 | }
13 | }
--------------------------------------------------------------------------------
/tests/org/robotlegs/mvcs/support/ViewMediatorAdvanced.as:
--------------------------------------------------------------------------------
1 | package org.robotlegs.mvcs.support
2 | {
3 | public class ViewMediatorAdvanced extends ViewMediator
4 | {
5 | [Inject]
6 | public var viewAdvanced:ViewComponentAdvanced;
7 |
8 | public function ViewMediatorAdvanced()
9 | {
10 | super();
11 | }
12 |
13 | }
14 | }
--------------------------------------------------------------------------------
/tests/org/robotlegs/mvcs/support/ITestContextView.as:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by IntelliJ IDEA.
3 | * User: conrad
4 | * Date: 13/10/2010
5 | * Time: 06:59
6 | * To change this template use File | Settings | File Templates.
7 | */
8 | package org.robotlegs.mvcs.support
9 | {
10 | public interface ITestContextView
11 | {
12 |
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/tests/org/robotlegs/base/BaseTestSuite.as:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2009 the original author or authors
3 | *
4 | * Permission is hereby granted to use, modify, and distribute this file
5 | * in accordance with the terms of the license agreement accompanying it.
6 | */
7 |
8 | package org.robotlegs.base
9 | {
10 | [Suite]
11 | [RunWith("org.flexunit.runners.Suite")]
12 | public class BaseTestSuite
13 | {
14 | public var mediatorMapTests:InterfaceEnabledMediatorMapTests;
15 | }
16 | }
--------------------------------------------------------------------------------
/tests/org/robotlegs/mvcs/support/ViewComponent.as:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2009 the original author or authors
3 | *
4 | * Permission is hereby granted to use, modify, and distribute this file
5 | * in accordance with the terms of the license agreement accompanying it.
6 | */
7 |
8 | package org.robotlegs.mvcs.support
9 | {
10 | import mx.core.UIComponent;
11 |
12 | public class ViewComponent extends UIComponent implements IViewComponent
13 | {
14 | public function ViewComponent()
15 | {
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/src/example/controller/NumberEnteredCommand.as:
--------------------------------------------------------------------------------
1 | package example.controller
2 | {
3 | import example.events.NumberEnteredEvent;
4 | import example.model.NumberModel;
5 |
6 | public class NumberEnteredCommand
7 | {
8 |
9 | [Inject]
10 | public var event:NumberEnteredEvent;
11 |
12 | [Inject]
13 | public var numberModel:NumberModel;
14 |
15 | public function execute():void
16 | {
17 | numberModel.currentNumber = event.number;
18 | }
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/src/example/example.css:
--------------------------------------------------------------------------------
1 | @namespace "example.view.*";
2 |
3 | LabelDisplayComponent
4 | {
5 | skin-class: ClassReference("example.skins.LabelDisplayComponentSkin");
6 | }
7 |
8 | NumericStepperInputComponent
9 | {
10 | skin-class: ClassReference("example.skins.NumericStepperInputComponentSkin");
11 | }
12 |
13 | SliderInputComponent
14 | {
15 | skin-class: ClassReference("example.skins.SliderInputComponentSkin");
16 | }
17 |
18 | DropDownListInputComponent
19 | {
20 | skin-class: ClassReference("example.skins.DropDownListInputComponentSkin");
21 | }
22 |
--------------------------------------------------------------------------------
/tests/org/robotlegs/mvcs/support/TestContextViewMediator.as:
--------------------------------------------------------------------------------
1 | package org.robotlegs.mvcs.support
2 | {
3 | import flash.events.Event;
4 |
5 | import org.robotlegs.mvcs.Mediator;
6 |
7 | public class TestContextViewMediator extends Mediator
8 | {
9 | public static const MEDIATOR_IS_REGISTERED:String = "MediatorIsRegistered";
10 |
11 | public function TestContextViewMediator()
12 | {
13 | super();
14 | }
15 |
16 | override public function onRegister() : void
17 | {
18 | eventDispatcher.dispatchEvent(new Event(MEDIATOR_IS_REGISTERED));
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/src/example/events/NumberEnteredEvent.as:
--------------------------------------------------------------------------------
1 | package example.events
2 | {
3 | import flash.events.Event;
4 |
5 | public class NumberEnteredEvent extends Event
6 | {
7 |
8 | public static const NUMBER_ENTERED:String = "numberEntered";
9 |
10 | private var _number:Number;
11 |
12 | public function NumberEnteredEvent(number:Number)
13 | {
14 | super(NUMBER_ENTERED);
15 | _number = number;
16 | }
17 |
18 | public function get number():Number
19 | {
20 | return _number;
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/example/model/NumberModel.as:
--------------------------------------------------------------------------------
1 | package example.model
2 | {
3 | import example.signals.NumberUpdatedSignal;
4 |
5 | public class NumberModel
6 | {
7 |
8 | [Inject]
9 | public var numberUpdated:NumberUpdatedSignal;
10 |
11 | private var _currentNumber:Number;
12 |
13 | public function NumberModel()
14 | {
15 |
16 | }
17 |
18 | public function set currentNumber(value:Number):void
19 | {
20 | if ( _currentNumber == value ) return;
21 |
22 | _currentNumber = value;
23 | numberUpdated.dispatch(_currentNumber);
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/example/mvcs/NumberDisplayMediator.as:
--------------------------------------------------------------------------------
1 | package example.mvcs
2 | {
3 | import example.signals.NumberUpdatedSignal;
4 | import example.view.INumberDisplay;
5 |
6 | import org.robotlegs.mvcs.Mediator;
7 |
8 | public class NumberDisplayMediator extends Mediator
9 | {
10 |
11 | [Inject]
12 | public var view:INumberDisplay;
13 |
14 | [Inject]
15 | public var numberUpdated:NumberUpdatedSignal;
16 |
17 | public function NumberDisplayMediator()
18 | {
19 | }
20 |
21 | [PostConstruct]
22 | public function init():void
23 | {
24 | view.numberUpdated = numberUpdated;
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/tests/org/robotlegs/mvcs/support/ViewMediator.as:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2009 the original author or authors
3 | *
4 | * Permission is hereby granted to use, modify, and distribute this file
5 | * in accordance with the terms of the license agreement accompanying it.
6 | */
7 |
8 | package org.robotlegs.mvcs.support
9 | {
10 | import org.robotlegs.mvcs.Mediator;
11 |
12 | public class ViewMediator extends Mediator
13 | {
14 | [Inject]
15 | public var view:ViewComponent;
16 |
17 | public function ViewMediator()
18 | {
19 | }
20 |
21 | override public function onRegister():void
22 | {
23 |
24 | }
25 |
26 | override public function onRemove():void
27 | {
28 |
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/tests/org/robotlegs/mvcs/support/ViewInterfaceMediator.as:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2009 the original author or authors
3 | *
4 | * Permission is hereby granted to use, modify, and distribute this file
5 | * in accordance with the terms of the license agreement accompanying it.
6 | */
7 |
8 | package org.robotlegs.mvcs.support
9 | {
10 | import org.robotlegs.mvcs.Mediator;
11 |
12 | public class ViewInterfaceMediator extends Mediator
13 | {
14 | [Inject]
15 | public var view:IViewComponent;
16 |
17 | public function ViewInterfaceMediator()
18 | {
19 | }
20 |
21 | override public function onRegister():void
22 | {
23 |
24 | }
25 |
26 | override public function onRemove():void
27 | {
28 |
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/src/example/mvcs/NumberEntryMediator.as:
--------------------------------------------------------------------------------
1 | package example.mvcs
2 | {
3 | import example.events.NumberEnteredEvent;
4 | import example.view.INumberEntry;
5 |
6 | import org.robotlegs.mvcs.Mediator;
7 |
8 | public class NumberEntryMediator extends Mediator
9 | {
10 |
11 | [Inject]
12 | public var view:INumberEntry;
13 |
14 | public function NumberEntryMediator()
15 | {
16 | }
17 |
18 | [PostConstruct]
19 | public function init():void
20 | {
21 | view.numberEntered.add(onViewNumberEntered)
22 | }
23 |
24 | private function onViewNumberEntered(number:Number):void
25 | {
26 | dispatch(new NumberEnteredEvent(number));
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/example/view/LabelDisplayComponent.as:
--------------------------------------------------------------------------------
1 | package example.view
2 | {
3 |
4 | import org.osflash.signals.ISignal;
5 |
6 | import spark.components.Label;
7 | import spark.components.supportClasses.SkinnableComponent;
8 |
9 | public class LabelDisplayComponent extends SkinnableComponent implements INumberDisplay
10 | {
11 |
12 | [SkinPart(required="true")]
13 | public var numberDisplay:Label;
14 |
15 | public function LabelDisplayComponent()
16 | {
17 | }
18 |
19 | public function set numberUpdated(value:ISignal):void
20 | {
21 | value.add(onNumberUpdated)
22 | }
23 |
24 | private function onNumberUpdated(number:Number):void
25 | {
26 | numberDisplay.text = number.toString();
27 | }
28 |
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/example/skins/DropDownListInputComponentSkin.mxml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 | 1
10 | 2
11 | 3
12 | 4
13 | 5
14 | 6
15 | 7
16 | 8
17 | 9
18 | 10
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/tests/InterfaceMappingTests.mxml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/tests/org/robotlegs/mvcs/support/TestViewInterfaceMappingContext.as:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2009 the original author or authors
3 | *
4 | * Permission is hereby granted to use, modify, and distribute this file
5 | * in accordance with the terms of the license agreement accompanying it.
6 | */
7 | package org.robotlegs.mvcs.support
8 | {
9 | import flash.display.DisplayObjectContainer;
10 |
11 | import org.robotlegs.core.IInjector;
12 | import org.robotlegs.mvcs.ViewInterfaceMappingContext;
13 |
14 | public class TestViewInterfaceMappingContext extends ViewInterfaceMappingContext
15 | {
16 | public var startupComplete:Boolean = false;
17 |
18 | public function TestViewInterfaceMappingContext(contextView:DisplayObjectContainer = null, autoStartup:Boolean = true)
19 | {
20 | super(contextView, autoStartup);
21 | }
22 |
23 | override public function startup():void
24 | {
25 | startupComplete = true;
26 | super.startup();
27 | }
28 |
29 | public function getInjector():IInjector
30 | {
31 | return this.injector;
32 | }
33 |
34 | public function get isInitialized():Boolean
35 | {
36 | var initialized:Boolean = true;
37 | initialized = commandMap && initialized;
38 | initialized = mediatorMap && initialized;
39 | return initialized;
40 |
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/tests/org/robotlegs/mvcs/support/TestViewInterfaceMappingSignalContext.as:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2009 the original author or authors
3 | *
4 | * Permission is hereby granted to use, modify, and distribute this file
5 | * in accordance with the terms of the license agreement accompanying it.
6 | */
7 | package org.robotlegs.mvcs.support
8 | {
9 | import flash.display.DisplayObjectContainer;
10 |
11 | import org.robotlegs.core.IInjector;
12 | import org.robotlegs.mvcs.ViewInterfaceMappingContext;
13 | import org.robotlegs.mvcs.ViewInterfaceMappingSignalContext;
14 |
15 | public class TestViewInterfaceMappingSignalContext extends ViewInterfaceMappingSignalContext
16 | {
17 | public var startupComplete:Boolean = false;
18 |
19 | public function TestViewInterfaceMappingSignalContext(contextView:DisplayObjectContainer = null, autoStartup:Boolean = true)
20 | {
21 | super(contextView, autoStartup);
22 | }
23 |
24 | override public function startup():void
25 | {
26 | startupComplete = true;
27 | super.startup();
28 | }
29 |
30 | public function getInjector():IInjector
31 | {
32 | return this.injector;
33 | }
34 |
35 | public function get isInitialized():Boolean
36 | {
37 | var initialized:Boolean = true;
38 | initialized = commandMap && initialized;
39 | initialized = mediatorMap && initialized;
40 | return initialized;
41 |
42 | }
43 | }
44 | }
--------------------------------------------------------------------------------
/src/example/Example.mxml:
--------------------------------------------------------------------------------
1 |
2 |
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 | stepper
34 | slider
35 | dropdown
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/src/example/mvcs/InterfaceEnabledMediatorMapContextExample.as:
--------------------------------------------------------------------------------
1 | package example.mvcs
2 | {
3 | import example.controller.NumberEnteredCommand;
4 | import example.events.NumberEnteredEvent;
5 | import example.model.NumberModel;
6 | import example.signals.NumberUpdatedSignal;
7 | import example.view.INumberDisplay;
8 | import example.view.INumberEntry;
9 |
10 | import org.robotlegs.base.ViewInterfaceMediatorMap;
11 | import org.robotlegs.core.IMediatorMap;
12 | import org.robotlegs.mvcs.Context;
13 |
14 | public class InterfaceEnabledMediatorMapContextExample extends Context
15 | {
16 |
17 | public function InterfaceEnabledMediatorMapContextExample()
18 | {
19 | }
20 |
21 | //
22 | // Override the default mediator map with one that can map to interfaces
23 | //
24 | override protected function get mediatorMap():IMediatorMap
25 | {
26 | return _mediatorMap ||= new ViewInterfaceMediatorMap(contextView, createChildInjector(), reflector);
27 | }
28 |
29 |
30 | override public function startup():void
31 | {
32 | injector.mapSingleton(NumberModel);
33 | injector.mapSingleton(NumberUpdatedSignal);
34 |
35 | mediatorMap.mapView(INumberEntry,NumberEntryMediator);
36 | mediatorMap.mapView(INumberDisplay,NumberDisplayMediator);
37 |
38 | commandMap.mapEvent(NumberEnteredEvent.NUMBER_ENTERED,NumberEnteredCommand);
39 | }
40 |
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/example/view/SliderInputComponent.as:
--------------------------------------------------------------------------------
1 | package example.view
2 | {
3 |
4 | import flash.events.Event;
5 |
6 | import org.osflash.signals.ISignal;
7 |
8 | import org.osflash.signals.Signal;
9 |
10 | import spark.components.HSlider;
11 | import spark.components.supportClasses.SkinnableComponent;
12 |
13 | public class SliderInputComponent extends SkinnableComponent implements INumberEntry
14 | {
15 |
16 | [SkinPart(required="true")]
17 | public var numberSlider:HSlider;
18 |
19 | private var _numberEntered:Signal;
20 |
21 | public function SliderInputComponent()
22 | {
23 | _numberEntered = new Signal(Number);
24 | }
25 |
26 | public function get numberEntered():ISignal
27 | {
28 | return _numberEntered;
29 | }
30 |
31 | override protected function partAdded(partName:String, instance:Object):void
32 | {
33 | super.partAdded(partName, instance);
34 | switch( instance )
35 | {
36 | case numberSlider:
37 | numberSlider.addEventListener(Event.CHANGE,onNumberSliderChange);
38 | break;
39 | }
40 | }
41 |
42 | override protected function partRemoved(partName:String, instance:Object):void
43 | {
44 | switch( instance )
45 | {
46 | case numberSlider:
47 | numberSlider.removeEventListener(Event.CHANGE,onNumberSliderChange);
48 | break;
49 | }
50 | super.partAdded(partName, instance);
51 | }
52 |
53 | private function onNumberSliderChange(event:Event):void
54 | {
55 | _numberEntered.dispatch(numberSlider.value);
56 | }
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/src/example/view/DropDownListInputComponent.as:
--------------------------------------------------------------------------------
1 | package example.view
2 | {
3 | import flash.events.Event;
4 |
5 | import org.osflash.signals.ISignal;
6 | import org.osflash.signals.Signal;
7 |
8 | import spark.components.DropDownList;
9 | import spark.components.supportClasses.SkinnableComponent;
10 |
11 | public class DropDownListInputComponent extends SkinnableComponent implements INumberEntry
12 | {
13 |
14 | [SkinPart(required="true")]
15 | public var numberList:DropDownList;
16 |
17 | private var _numberEntered:Signal;
18 |
19 | public function DropDownListInputComponent()
20 | {
21 | _numberEntered = new Signal(Number);
22 | }
23 |
24 | public function get numberEntered():ISignal
25 | {
26 | return _numberEntered;
27 | }
28 |
29 | override protected function partAdded(partName:String, instance:Object):void
30 | {
31 | super.partAdded(partName, instance);
32 | switch( instance )
33 | {
34 | case numberList:
35 | numberList.addEventListener(Event.CHANGE,onNumberSliderChange);
36 | break;
37 | }
38 | }
39 |
40 | override protected function partRemoved(partName:String, instance:Object):void
41 | {
42 | switch( instance )
43 | {
44 | case numberList:
45 | numberList.removeEventListener(Event.CHANGE,onNumberSliderChange);
46 | break;
47 | }
48 | super.partAdded(partName, instance);
49 | }
50 |
51 | private function onNumberSliderChange(event:Event):void
52 | {
53 | _numberEntered.dispatch(numberList.selectedItem);
54 | }
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/src/example/view/NumericStepperInputComponent.as:
--------------------------------------------------------------------------------
1 | package example.view
2 | {
3 |
4 | import flash.events.Event;
5 |
6 | import org.osflash.signals.ISignal;
7 |
8 | import org.osflash.signals.Signal;
9 |
10 | import spark.components.NumericStepper;
11 | import spark.components.supportClasses.SkinnableComponent;
12 |
13 | public class NumericStepperInputComponent extends SkinnableComponent implements INumberEntry
14 | {
15 |
16 | [SkinPart(required="true")]
17 | public var numberStepper:NumericStepper;
18 |
19 | private var _numberEntered:Signal;
20 |
21 | public function NumericStepperInputComponent()
22 | {
23 | _numberEntered = new Signal(Number);
24 | }
25 |
26 | public function get numberEntered():ISignal
27 | {
28 | return _numberEntered;
29 | }
30 |
31 | private function onNumberStepperChange(event:Event):void
32 | {
33 | _numberEntered.dispatch(numberStepper.value);
34 | }
35 |
36 | override protected function partAdded(partName:String, instance:Object):void
37 | {
38 | super.partAdded(partName, instance);
39 | switch ( instance )
40 | {
41 | case numberStepper:
42 | numberStepper.addEventListener(Event.CHANGE,onNumberStepperChange);
43 | break;
44 | }
45 | }
46 |
47 |
48 | override protected function partRemoved(partName:String, instance:Object):void
49 | {
50 | switch ( instance )
51 | {
52 | case numberStepper:
53 | numberStepper.removeEventListener(Event.CHANGE,onNumberStepperChange);
54 | break;
55 | }
56 | super.partRemoved(partName, instance);
57 | }
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/readme.textile:
--------------------------------------------------------------------------------
1 | h2. View Interface Mediator Map
2 |
3 | A pure view interface enabled MediatorMap for use in a Robotlegs context
4 |
5 | h3. Rationale
6 |
7 | When using Robotlegs you sometimes you want to inject a view into a mediator as an interface. For example
8 |
9 | pre. public class MyViewMediator extends Mediator
10 | {
11 | [Inject]
12 | public var view:IMyView;
13 | }
14 |
15 | The normal mechanism for mapping this in your context is to add a third argument to the mapView command telling the mediatorMap what you would like to inject the view 'as'
16 |
17 | pre. override public function startup():void
18 | {
19 | mediatorMap.mapView(MyViewClass,MyViewMediator,IMyView);
20 | }
21 |
22 | The problem with this is that it adds a concrete reference to a concrete class in the context - MyViewClass. It would be nice to simply specify the view interface and mediator.
23 |
24 | Enter the "ViewInterfaceMediatorMap":http://github.com/piercer/robotlegs-extensions-ViewIntefaceMediatorMap/blob/master/src/org/robotlegs/base/ViewInterfaceMediatorMap.as. It is a drop in replacement for the current 1.3.0 Robotlegs MediatorMap with all of the same functionality, but with the added bonus that you can map mediators directly to view interfaces. The above piece of code could then be written
25 |
26 | pre. override public function startup():void
27 | {
28 | mediatorMap.mapView(IMyView,MyViewMediator);
29 | }
30 |
31 | This has a number of advantages:
32 |
33 | * Your context only cares about what your view does not how it does it.
34 | * A context swc can be produced that has no dependencies on actual view implementations.
35 | * The concrete view classes do not have to be baked in at compile time. You can swap views in and out at runtime, even from loaded modules, and as long as they implement the interface, the correct mediator will be created for them even though the context will never have seen the actual concrete classes before.
36 |
37 | h3. Usage
38 |
39 | In order to use this new mediator map simply extend the Robotlegs Context as normal and override the get mediatorMap property like so
40 |
41 | pre. public class MyContext extends Context
42 | {
43 | //
44 | // Override the default mediator map with one that can map to interfaces
45 | //
46 | override protected function get mediatorMap():IMediatorMap
47 | {
48 | return _mediatorMap ||= new ViewInterfaceMediatorMap(contextView, createChildInjector(), reflector);
49 | }
50 | }
51 |
52 | With this done you can map mediators to view interfaces.
53 |
54 | h3. Performance
55 |
56 | Mapping mediators to views is the least performant bit of Robotlegs. Adding the concrete class to the mapView call allows it to be faster. However this mediator map has been benchmarked with the software "here":http://http://github.com/piercer/robotlegs-mediatormap-benchmark and it has been found to be only fractionally slower than the normal mediator map.
57 |
58 | h3. Signals
59 |
60 | This extension really benefits from the use of as3-signals since they enable a view to impement an interface, something that simply does not fit into the way events work. Please see the example.
61 |
62 | h3. Example
63 |
64 | In the repository is a very simple example that shows how mapping to an interface can enable multiple view mechanisms and components to 'plug in' to the same underlying business logic.
65 |
66 | The example is written as if it were a big application and so it is massively over engineered. The code is however intended to show a very clean approach to application and component development. It is like the application and views that control it are two separate things - which is good.
67 |
68 | The application consists of a simple context
69 |
70 | pre. package example.mvcs
71 | {
72 | public class InterfaceEnabledMediatorMapContextExample extends Context
73 | {
74 | override protected function get mediatorMap():IMediatorMap
75 | {
76 | return _mediatorMap ||= new ViewInterfaceMediatorMap(contextView, createChildInjector(), reflector);
77 | }
78 | override public function startup():void
79 | {
80 | injector.mapSingleton(NumberModel);
81 | injector.mapSingleton(NumberUpdatedSignal);
82 | mediatorMap.mapView(INumberEntry,NumberEntryMediator);
83 | mediatorMap.mapView(INumberDisplay,NumberDisplayMediator);
84 | commandMap.mapEvent(NumberEnteredEvent.NUMBER_ENTERED,NumberEnteredCommand);
85 | }
86 | }
87 | }
88 |
89 | Where we can see that two mediators are mapped to two different interfaces. The interfaces use signals to represent the transmission of information between the application and its view, for example the INumberEntry interface looks like this
90 |
91 | pre. package example.view
92 | {
93 | import org.osflash.signals.ISignal;
94 | public interface INumberEntry
95 | {
96 | function get numberEntered():ISignal;
97 | }
98 | }
99 |
100 | If a view that implements this interface is added to the stage then a NumberEntryMediator is created for it which should understand the signal that it dispatches. When a number is chosen by the view it dispatches a signal to show that the selected number has changed, this causes a command to be triggered inside the application (NumberEnteredCommand) that updates a model which updates the view.
101 |
102 | The application actually has three views that implement the INumberEntry interface and which one is currently in use can be chosen by a dropdownlist. Even though there is only one mapping in the context, and the context has never seen these classes the application still works and each input mechanism works as it should.
--------------------------------------------------------------------------------
/src/org/robotlegs/base/ViewInterfaceMediatorMap.as:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2009 the original author or authors
3 | *
4 | * Permission is hereby granted to use, modify, and distribute this file
5 | * in accordance with the terms of the license agreement accompanying it.
6 | */
7 |
8 | package org.robotlegs.base
9 | {
10 | import flash.display.DisplayObject;
11 | import flash.display.DisplayObjectContainer;
12 | import flash.display.Sprite;
13 | import flash.events.Event;
14 | import flash.utils.Dictionary;
15 | import flash.utils.describeType;
16 | import flash.utils.getQualifiedClassName;
17 |
18 | import org.robotlegs.core.IInjector;
19 | import org.robotlegs.core.IMediator;
20 | import org.robotlegs.core.IMediatorMap;
21 | import org.robotlegs.core.IReflector;
22 |
23 | /**
24 | * An abstract IMediatorMap implementation
25 | */
26 | public class ViewInterfaceMediatorMap extends ViewMapBase implements IMediatorMap
27 | {
28 | /**
29 | * @private
30 | */
31 | protected static const enterFrameDispatcher:Sprite = new Sprite();
32 |
33 | /**
34 | * @private
35 | */
36 | protected var mediatorByView:Dictionary;
37 |
38 | /**
39 | * @private
40 | */
41 | protected var mappingConfigByView:Dictionary;
42 |
43 | /**
44 | * @private
45 | */
46 | protected var mappingConfigByViewClassName:Dictionary;
47 |
48 | /**
49 | * @private
50 | */
51 | protected var mediatorsMarkedForRemoval:Dictionary;
52 |
53 | /**
54 | * @private
55 | */
56 | protected var unmappedViews:Dictionary;
57 |
58 | /**
59 | * @private
60 | */
61 | protected var hasMediatorsMarkedForRemoval:Boolean;
62 |
63 | /**
64 | * @private
65 | */
66 | protected var reflector:IReflector;
67 |
68 | //---------------------------------------------------------------------
69 | // Constructor
70 | //---------------------------------------------------------------------
71 |
72 | /**
73 | * Creates a new MediatorMap object
74 | *
75 | * @param contextView The root view node of the context. The map will listen for ADDED_TO_STAGE events on this node
76 | * @param injector An IInjector to use for this context
77 | * @param reflector An IReflector to use for this context
78 | */
79 | public function ViewInterfaceMediatorMap(contextView:DisplayObjectContainer, injector:IInjector, reflector:IReflector)
80 | {
81 | super(contextView, injector);
82 |
83 | this.reflector = reflector;
84 |
85 | // mappings - if you can do it with fewer dictionaries you get a prize
86 | this.mediatorByView = new Dictionary(true);
87 | this.mappingConfigByView = new Dictionary(true);
88 | this.mappingConfigByViewClassName = new Dictionary(false);
89 | this.mediatorsMarkedForRemoval = new Dictionary(false);
90 | this.unmappedViews = new Dictionary(false);
91 | }
92 |
93 | //---------------------------------------------------------------------
94 | // API
95 | //---------------------------------------------------------------------
96 |
97 | /**
98 | * @inheritDoc
99 | */
100 | public function mapView(viewClassOrName:*, mediatorClass:Class, injectViewAs:* = null, autoCreate:Boolean = true, autoRemove:Boolean = true):void
101 | {
102 | var viewClassName:String = reflector.getFQCN(viewClassOrName);
103 |
104 | if (mappingConfigByViewClassName[viewClassName] != null)
105 | throw new ContextError(ContextError.E_MEDIATORMAP_OVR + ' - ' + mediatorClass);
106 |
107 | if (!reflector.classExtendsOrImplements(mediatorClass, IMediator))
108 | throw new ContextError(ContextError.E_MEDIATORMAP_NOIMPL + ' - ' + mediatorClass);
109 |
110 | unmappedViews = new Dictionary(false);
111 | var config:MappingConfig = new MappingConfig();
112 | config.mediatorClass = mediatorClass;
113 | config.autoCreate = autoCreate;
114 | config.autoRemove = autoRemove;
115 | if (injectViewAs)
116 | {
117 | if (injectViewAs is Array)
118 | {
119 | config.typedViewClasses = (injectViewAs as Array).concat();
120 | }
121 | else if (injectViewAs is Class)
122 | {
123 | config.typedViewClasses = [injectViewAs];
124 | }
125 | }
126 | else if (viewClassOrName is Class)
127 | {
128 | config.typedViewClasses = [viewClassOrName];
129 | }
130 | mappingConfigByViewClassName[viewClassName] = config;
131 | if (autoCreate || autoRemove)
132 | {
133 | viewListenerCount++;
134 | if (viewListenerCount == 1)
135 | addListeners();
136 | }
137 |
138 | // This was a bad idea - causes unexpected eager instantiation of object graph
139 | //
140 | // If
141 | //
142 | // 1) autoCreate is true and we already have a contextView
143 | // 2) A config exists for the contextView, and
144 | // 3) the config we have just added is that config
145 | //
146 | // Then we should autowire the contextView now.
147 | //
148 | if (autoCreate && contextView)
149 | {
150 | var config2:MappingConfig = getMappingConfig(contextView);
151 | if (config == config2)
152 | {
153 | createMediatorUsing(contextView, viewClassName, config);
154 | }
155 | }
156 | }
157 |
158 | /**
159 | * @inheritDoc
160 | */
161 | public function unmapView(viewClassOrName:*):void
162 | {
163 | var viewClassName:String = reflector.getFQCN(viewClassOrName);
164 | var config:MappingConfig = mappingConfigByViewClassName[viewClassName];
165 | if (config && (config.autoCreate || config.autoRemove))
166 | {
167 | viewListenerCount--;
168 | if (viewListenerCount == 0)
169 | removeListeners();
170 | for each (var mappedConcreteViewClassName:String in config.mappedConcreteClasses)
171 | {
172 | delete mappingConfigByViewClassName[mappedConcreteViewClassName];
173 | }
174 | }
175 | delete mappingConfigByViewClassName[viewClassName];
176 | unmappedViews = new Dictionary(false);
177 | }
178 |
179 | /**
180 | * @inheritDoc
181 | */
182 | public function createMediator(viewComponent:Object):IMediator
183 | {
184 | return createMediatorUsing(viewComponent);
185 | }
186 |
187 | /**
188 | * @inheritDoc
189 | */
190 | public function registerMediator(viewComponent:Object, mediator:IMediator):void
191 | {
192 | injector.mapValue(reflector.getClass(mediator), mediator);
193 | mediatorByView[viewComponent] = mediator;
194 | mappingConfigByView[viewComponent] = getMappingConfig(viewComponent);
195 | mediator.setViewComponent(viewComponent);
196 | mediator.preRegister();
197 | }
198 |
199 | /**
200 | * @inheritDoc
201 | */
202 | public function removeMediator(mediator:IMediator):IMediator
203 | {
204 | if (mediator)
205 | {
206 | var viewComponent:Object = mediator.getViewComponent();
207 | delete mediatorByView[viewComponent];
208 | delete mappingConfigByView[viewComponent];
209 | mediator.preRemove();
210 | mediator.setViewComponent(null);
211 | injector.unmap(reflector.getClass(mediator));
212 | }
213 | return mediator;
214 | }
215 |
216 | /**
217 | * @inheritDoc
218 | */
219 | public function removeMediatorByView(viewComponent:Object):IMediator
220 | {
221 | return removeMediator(retrieveMediator(viewComponent));
222 | }
223 |
224 | /**
225 | * @inheritDoc
226 | */
227 | public function retrieveMediator(viewComponent:Object):IMediator
228 | {
229 | return mediatorByView[viewComponent];
230 | }
231 |
232 | /**
233 | * @inheritDoc
234 | */
235 | public function hasMapping(viewClassOrName:*):Boolean
236 | {
237 | var viewClassName:String = reflector.getFQCN(viewClassOrName);
238 | return (mappingConfigByViewClassName[viewClassName] != null);
239 | }
240 |
241 | /**
242 | * @inheritDoc
243 | */
244 | public function hasMediatorForView(viewComponent:Object):Boolean
245 | {
246 | return mediatorByView[viewComponent] != null;
247 | }
248 |
249 | /**
250 | * @inheritDoc
251 | */
252 | public function hasMediator(mediator:IMediator):Boolean
253 | {
254 | for each (var med:IMediator in mediatorByView)
255 | if (med == mediator)
256 | return true;
257 | return false;
258 | }
259 |
260 | //---------------------------------------------------------------------
261 | // Internal
262 | //---------------------------------------------------------------------
263 |
264 | /**
265 | * @private
266 | */
267 | protected override function addListeners():void
268 | {
269 | if (contextView && enabled)
270 | {
271 | contextView.addEventListener(Event.ADDED_TO_STAGE, onViewAdded, useCapture, 0, true);
272 | contextView.addEventListener(Event.REMOVED_FROM_STAGE, onViewRemoved, useCapture, 0, true);
273 | }
274 | }
275 |
276 | /**
277 | * @private
278 | */
279 | protected override function removeListeners():void
280 | {
281 | if (contextView)
282 | {
283 | contextView.removeEventListener(Event.ADDED_TO_STAGE, onViewAdded, useCapture);
284 | contextView.removeEventListener(Event.REMOVED_FROM_STAGE, onViewRemoved, useCapture);
285 | }
286 | }
287 |
288 | /**
289 | * @private
290 | */
291 | protected override function onViewAdded(e:Event):void
292 | {
293 | if (mediatorsMarkedForRemoval[e.target])
294 | {
295 | delete mediatorsMarkedForRemoval[e.target];
296 | return;
297 | }
298 | var viewClassName:String = getQualifiedClassName(e.target);
299 | var config:MappingConfig = getMappingConfig(e.target);
300 | if (config && config.autoCreate)
301 | createMediatorUsing(e.target, viewClassName, config);
302 | }
303 |
304 | /**
305 | * @private
306 | */
307 | protected function createMediatorUsing(viewComponent:Object, viewClassName:String = '', config:MappingConfig = null):IMediator
308 | {
309 | var mediator:IMediator = mediatorByView[viewComponent];
310 | if (mediator == null)
311 | {
312 | config ||= getMappingConfig(viewComponent, viewClassName);
313 | if (config)
314 | {
315 | for each (var claxx:Class in config.typedViewClasses)
316 | {
317 | injector.mapValue(claxx, viewComponent);
318 | }
319 | mediator = injector.instantiate(config.mediatorClass);
320 | for each (var clazz:Class in config.typedViewClasses)
321 | {
322 | injector.unmap(clazz);
323 | }
324 | registerMediator(viewComponent, mediator);
325 | }
326 | }
327 | return mediator;
328 | }
329 |
330 | /**
331 | * Flex framework work-around part #5
332 | */
333 | protected function onViewRemoved(e:Event):void
334 | {
335 | var config:MappingConfig = mappingConfigByView[e.target];
336 | if (config && config.autoRemove)
337 | {
338 | mediatorsMarkedForRemoval[e.target] = e.target;
339 | if (!hasMediatorsMarkedForRemoval)
340 | {
341 | hasMediatorsMarkedForRemoval = true;
342 | enterFrameDispatcher.addEventListener(Event.ENTER_FRAME, removeMediatorLater);
343 | }
344 | }
345 | }
346 |
347 | /**
348 | * Flex framework work-around part #6
349 | */
350 | protected function removeMediatorLater(event:Event):void
351 | {
352 | enterFrameDispatcher.removeEventListener(Event.ENTER_FRAME, removeMediatorLater);
353 | for each (var view:DisplayObject in mediatorsMarkedForRemoval)
354 | {
355 | if (!view.stage)
356 | {
357 | removeMediatorByView(view);
358 | }
359 | delete mediatorsMarkedForRemoval[view];
360 | }
361 | hasMediatorsMarkedForRemoval = false;
362 | }
363 |
364 | private function getMappingConfig(viewComponent:Object, viewClassName:String = ''):MappingConfig
365 | {
366 | viewClassName ||= getQualifiedClassName(viewComponent);
367 | if (unmappedViews[viewClassName])
368 | {
369 | return null;
370 | }
371 | var config:MappingConfig = mappingConfigByViewClassName[viewClassName];
372 | if (!config)
373 | {
374 | //
375 | // This stuff should be handled by the Reflector
376 | //
377 | var classXML:XML = describeType(viewComponent);
378 | for each (var implementedInterface:XML in classXML.implementsInterface)
379 | {
380 | var interfaceName:String = implementedInterface.@type;
381 | config = mappingConfigByViewClassName[interfaceName];
382 | //
383 | // We can't cache the config by throwing into mappingConfigByViewClassName
384 | // as there is no way to invalidate that action
385 | //
386 | if (config)
387 | {
388 | mappingConfigByViewClassName[viewClassName]=config;
389 | config.mappedConcreteClasses.push(viewClassName);
390 | return config;
391 | }
392 | }
393 | //
394 | // If we get here the no matching interface mapping was
395 | // found so we add this className to the list of unmapped Views
396 | //
397 | unmappedViews[viewClassName]=1;
398 | }
399 | return config;
400 | }
401 |
402 | }
403 | }
404 |
405 | class MappingConfig
406 | {
407 | public var mediatorClass:Class;
408 | public var typedViewClasses:Array;
409 | public var autoCreate:Boolean;
410 | public var autoRemove:Boolean;
411 | public var mappedConcreteClasses:Array=[];
412 | }
413 |
--------------------------------------------------------------------------------
/tests/org/robotlegs/base/InterfaceEnabledMediatorMapTests.as:
--------------------------------------------------------------------------------
1 | package org.robotlegs.base
2 | {
3 | import flash.display.DisplayObjectContainer;
4 | import flash.events.Event;
5 | import flash.events.EventDispatcher;
6 | import flash.events.IEventDispatcher;
7 |
8 | import mx.core.UIComponent;
9 |
10 | import org.flexunit.Assert;
11 | import org.flexunit.async.Async;
12 | import org.fluint.uiImpersonation.UIImpersonator;
13 | import org.robotlegs.adapters.SwiftSuspendersInjector;
14 | import org.robotlegs.adapters.SwiftSuspendersReflector;
15 | import org.robotlegs.core.IEventMap;
16 | import org.robotlegs.core.IInjector;
17 | import org.robotlegs.core.IMediator;
18 | import org.robotlegs.core.IMediatorMap;
19 | import org.robotlegs.core.IReflector;
20 | import org.robotlegs.mvcs.support.ITestContextView;
21 | import org.robotlegs.mvcs.support.IViewComponent;
22 | import org.robotlegs.mvcs.support.TestContextView;
23 | import org.robotlegs.mvcs.support.TestContextViewMediator;
24 | import org.robotlegs.mvcs.support.ViewComponent;
25 | import org.robotlegs.mvcs.support.ViewComponentAdvanced;
26 | import org.robotlegs.mvcs.support.ViewInterfaceMediator;
27 | import org.robotlegs.mvcs.support.ViewMediator;
28 | import org.robotlegs.mvcs.support.ViewMediatorAdvanced;
29 |
30 | public class InterfaceEnabledMediatorMapTests
31 | {
32 | public static const TEST_EVENT:String = 'testEvent';
33 |
34 | protected var contextView:DisplayObjectContainer;
35 | protected var eventDispatcher:IEventDispatcher;
36 | protected var commandExecuted:Boolean;
37 | protected var mediatorMap:ViewInterfaceMediatorMap;
38 | protected var injector:IInjector;
39 | protected var reflector:IReflector;
40 | protected var eventMap:IEventMap;
41 |
42 | [BeforeClass]
43 | public static function runBeforeEntireSuite():void
44 | {
45 | }
46 |
47 | [AfterClass]
48 | public static function runAfterEntireSuite():void
49 | {
50 | }
51 |
52 | [Before(ui)]
53 | public function runBeforeEachTest():void
54 | {
55 | contextView = new TestContextView();
56 | eventDispatcher = new EventDispatcher();
57 | injector = new SwiftSuspendersInjector();
58 | reflector = new SwiftSuspendersReflector();
59 | mediatorMap = new ViewInterfaceMediatorMap(contextView, injector, reflector);
60 |
61 | injector.mapValue(DisplayObjectContainer, contextView);
62 | injector.mapValue(IInjector, injector);
63 | injector.mapValue(IEventDispatcher, eventDispatcher);
64 | injector.mapValue(IMediatorMap, mediatorMap);
65 |
66 | UIImpersonator.addChild(contextView);
67 | }
68 |
69 | [After(ui)]
70 | public function runAfterEachTest():void
71 | {
72 | UIImpersonator.removeAllChildren();
73 | injector.unmap(IMediatorMap);
74 | }
75 |
76 | [Test]
77 | public function mediatorIsMappedAndCreatedForView():void
78 | {
79 | mediatorMap.mapView(ViewComponent, ViewMediator, null, false, false);
80 | var viewComponent:ViewComponent = new ViewComponent();
81 | contextView.addChild(viewComponent);
82 | var mediator:IMediator = mediatorMap.createMediator(viewComponent);
83 | Assert.assertNotNull('Mediator should have been created ', mediator);
84 | Assert.assertTrue('Mediator should have been created for View Component', mediatorMap.hasMediatorForView(viewComponent));
85 | }
86 |
87 | [Test]
88 | public function mediatorIsMappedAndCreatedForViewInterface():void
89 | {
90 | mediatorMap.mapView(IViewComponent, ViewInterfaceMediator);
91 | var viewComponent:ViewComponent = new ViewComponent();
92 | contextView.addChild(viewComponent);
93 | var mediator:IMediator = mediatorMap.createMediator(viewComponent);
94 | Assert.assertNotNull('Mediator should have been created ', mediator);
95 | Assert.assertTrue('Mediator should have been created for View Component', mediatorMap.hasMediatorForView(viewComponent));
96 | }
97 |
98 | [Test]
99 | public function mediatorIsMappedAndCreatedWithInjectViewAsClass():void {
100 | mediatorMap.mapView(ViewComponent, ViewMediator, ViewComponent, false, false);
101 | var viewComponent:ViewComponent = new ViewComponent();
102 | contextView.addChild(viewComponent);
103 | var mediator:IMediator = mediatorMap.createMediator(viewComponent);
104 | var exactMediator:ViewMediator = mediator as ViewMediator;
105 | Assert.assertNotNull('Mediator should have been created', mediator);
106 | Assert.assertTrue('Mediator should have been created of the exact desired class', mediator is ViewMediator);
107 | Assert.assertTrue('Mediator should have been created for View Component', mediatorMap.hasMediatorForView(viewComponent));
108 | Assert.assertNotNull('View Component should have been injected into Mediator', exactMediator.view);
109 | Assert.assertTrue('View Component injected should match the desired class type', exactMediator.view is ViewComponent);
110 | }
111 |
112 | [Test]
113 | public function mediatorIsMappedAndCreatedWithInjectViewAsArrayOfSameClass():void {
114 | mediatorMap.mapView(ViewComponent, ViewMediator, [ViewComponent], false, false);
115 | var viewComponent:ViewComponent = new ViewComponent();
116 | contextView.addChild(viewComponent);
117 | var mediator:IMediator = mediatorMap.createMediator(viewComponent);
118 | var exactMediator:ViewMediator = mediator as ViewMediator;
119 | Assert.assertNotNull('Mediator should have been created', mediator);
120 | Assert.assertTrue('Mediator should have been created of the exact desired class', mediator is ViewMediator);
121 | Assert.assertTrue('Mediator should have been created for View Component', mediatorMap.hasMediatorForView(viewComponent));
122 | Assert.assertNotNull('View Component should have been injected into Mediator', exactMediator.view);
123 | Assert.assertTrue('View Component injected should match the desired class type', exactMediator.view is ViewComponent);
124 | }
125 |
126 | [Test]
127 | public function mediatorIsMappedAndCreatedWithInjectViewAsArrayOfRelatedClass():void {
128 | mediatorMap.mapView(ViewComponentAdvanced, ViewMediatorAdvanced, [ViewComponent, ViewComponentAdvanced], false, false);
129 | var viewComponentAdvanced:ViewComponentAdvanced = new ViewComponentAdvanced();
130 | contextView.addChild(viewComponentAdvanced);
131 | var mediator:IMediator = mediatorMap.createMediator(viewComponentAdvanced);
132 | var exactMediator:ViewMediatorAdvanced = mediator as ViewMediatorAdvanced;
133 | Assert.assertNotNull('Mediator should have been created', mediator);
134 | Assert.assertTrue('Mediator should have been created of the exact desired class', mediator is ViewMediatorAdvanced);
135 | Assert.assertTrue('Mediator should have been created for View Component', mediatorMap.hasMediatorForView(viewComponentAdvanced));
136 | Assert.assertNotNull('First Class in the "injectViewAs" array should have been injected into Mediator', exactMediator.view);
137 | Assert.assertNotNull('Second Class in the "injectViewAs" array should have been injected into Mediator', exactMediator.viewAdvanced);
138 | Assert.assertTrue('First Class injected via the "injectViewAs" array should match the desired class type', exactMediator.view is ViewComponent);
139 | Assert.assertTrue('Second Class injected via the "injectViewAs" array should match the desired class type', exactMediator.viewAdvanced is ViewComponentAdvanced);
140 | }
141 |
142 |
143 | [Test]
144 | public function mediatorIsMappedAddedAndRemoved():void
145 | {
146 | var viewComponent:ViewComponent = new ViewComponent();
147 | var mediator:IMediator;
148 |
149 | mediatorMap.mapView(ViewComponent, ViewMediator, null, false, false);
150 | contextView.addChild(viewComponent);
151 | mediator = mediatorMap.createMediator(viewComponent);
152 | Assert.assertNotNull('Mediator should have been created', mediator);
153 | Assert.assertTrue('Mediator should have been created', mediatorMap.hasMediator(mediator));
154 | Assert.assertTrue('Mediator should have been created for View Component', mediatorMap.hasMediatorForView(viewComponent));
155 | mediatorMap.removeMediator(mediator);
156 | Assert.assertFalse("Mediator Should Not Exist", mediatorMap.hasMediator(mediator));
157 | Assert.assertFalse("View Mediator Should Not Exist", mediatorMap.hasMediatorForView(viewComponent));
158 | }
159 |
160 | [Test]
161 | public function interfaceMediatorIsMappedAddedAndRemoved():void
162 | {
163 | var viewComponent:ViewComponent = new ViewComponent();
164 | var mediator:IMediator;
165 |
166 | mediatorMap.mapView(IViewComponent, ViewInterfaceMediator, null, false, false);
167 | contextView.addChild(viewComponent);
168 | mediator = mediatorMap.createMediator(viewComponent);
169 | Assert.assertNotNull('Mediator should have been created', mediator);
170 | Assert.assertTrue('Mediator should have been created', mediatorMap.hasMediator(mediator));
171 | Assert.assertTrue('Mediator should have been created for View Component', mediatorMap.hasMediatorForView(viewComponent));
172 | mediatorMap.removeMediator(mediator);
173 | Assert.assertFalse("Mediator Should Not Exist", mediatorMap.hasMediator(mediator));
174 | Assert.assertFalse("View Mediator Should Not Exist", mediatorMap.hasMediatorForView(viewComponent));
175 | }
176 |
177 | [Test]
178 | public function mediatorIsMappedAddedAndRemovedByView():void
179 | {
180 | var viewComponent:ViewComponent = new ViewComponent();
181 | var mediator:IMediator;
182 |
183 | mediatorMap.mapView(ViewComponent, ViewMediator, null, false, false);
184 | contextView.addChild(viewComponent);
185 | mediator = mediatorMap.createMediator(viewComponent);
186 | Assert.assertNotNull('Mediator should have been created', mediator);
187 | Assert.assertTrue('Mediator should have been created', mediatorMap.hasMediator(mediator));
188 | Assert.assertTrue('Mediator should have been created for View Component', mediatorMap.hasMediatorForView(viewComponent));
189 | mediatorMap.removeMediatorByView(viewComponent);
190 | Assert.assertFalse("Mediator should not exist", mediatorMap.hasMediator(mediator));
191 | Assert.assertFalse("View Mediator should not exist", mediatorMap.hasMediatorForView(viewComponent));
192 | }
193 |
194 | [Test]
195 | public function interfaceMediatorIsMappedAddedAndRemovedByView():void
196 | {
197 | var viewComponent:ViewComponent = new ViewComponent();
198 | var mediator:IMediator;
199 |
200 | mediatorMap.mapView(IViewComponent, ViewInterfaceMediator, null, false, false);
201 | contextView.addChild(viewComponent);
202 | mediator = mediatorMap.createMediator(viewComponent);
203 | Assert.assertNotNull('Mediator should have been created', mediator);
204 | Assert.assertTrue('Mediator should have been created', mediatorMap.hasMediator(mediator));
205 | Assert.assertTrue('Mediator should have been created for View Component', mediatorMap.hasMediatorForView(viewComponent));
206 | mediatorMap.removeMediatorByView(viewComponent);
207 | Assert.assertFalse("Mediator should not exist", mediatorMap.hasMediator(mediator));
208 | Assert.assertFalse("View Mediator should not exist", mediatorMap.hasMediatorForView(viewComponent));
209 | }
210 |
211 | [Test]
212 | public function autoRegister():void
213 | {
214 | mediatorMap.mapView(ViewComponent, ViewMediator, null, true, true);
215 | var viewComponent:ViewComponent = new ViewComponent();
216 | contextView.addChild(viewComponent);
217 | Assert.assertTrue('Mediator should have been created for View Component', mediatorMap.hasMediatorForView(viewComponent));
218 | }
219 |
220 | [Test]
221 | public function autoRegisterWithInterface():void
222 | {
223 | mediatorMap.mapView(IViewComponent, ViewInterfaceMediator, null, true, true);
224 | var viewComponent:ViewComponent = new ViewComponent();
225 | contextView.addChild(viewComponent);
226 | Assert.assertTrue('Mediator should have been created for View Component', mediatorMap.hasMediatorForView(viewComponent));
227 | }
228 |
229 | [Test(async, timeout='500')]
230 | public function mediatorIsKeptDuringReparenting():void
231 | {
232 | var viewComponent:ViewComponent = new ViewComponent();
233 | var mediator:IMediator;
234 |
235 | mediatorMap.mapView(ViewComponent, ViewMediator, null, false, true);
236 | contextView.addChild(viewComponent);
237 | mediator = mediatorMap.createMediator(viewComponent);
238 | Assert.assertNotNull('Mediator should have been created', mediator);
239 | Assert.assertTrue('Mediator should have been created', mediatorMap.hasMediator(mediator));
240 | Assert.assertTrue('Mediator should have been created for View Component', mediatorMap.hasMediatorForView(viewComponent));
241 | var container:UIComponent = new UIComponent();
242 | contextView.addChild(container);
243 | container.addChild(viewComponent);
244 |
245 | Async.handleEvent(this, contextView, Event.ENTER_FRAME, delayFurther, 500, {dispatcher: contextView, method: verifyMediatorSurvival, view: viewComponent, mediator: mediator});
246 | }
247 |
248 | private function verifyMediatorSurvival(event:Event, data:Object):void
249 | {
250 | var viewComponent:ViewComponent = data.view;
251 | var mediator:IMediator = data.mediator;
252 | Assert.assertTrue("Mediator should exist", mediatorMap.hasMediator(mediator));
253 | Assert.assertTrue("View Mediator should exist", mediatorMap.hasMediatorForView(viewComponent));
254 | }
255 |
256 | [Test(async, timeout='500')]
257 | public function mediatorIsRemovedWithView():void
258 | {
259 | var viewComponent:ViewComponent = new ViewComponent();
260 | var mediator:IMediator;
261 |
262 | mediatorMap.mapView(ViewComponent, ViewMediator, null, false, true);
263 | contextView.addChild(viewComponent);
264 | mediator = mediatorMap.createMediator(viewComponent);
265 | Assert.assertNotNull('Mediator should have been created', mediator);
266 | Assert.assertTrue('Mediator should have been created', mediatorMap.hasMediator(mediator));
267 | Assert.assertTrue('Mediator should have been created for View Component', mediatorMap.hasMediatorForView(viewComponent));
268 |
269 | contextView.removeChild(viewComponent);
270 | Async.handleEvent(this, contextView, Event.ENTER_FRAME, delayFurther, 500, {dispatcher: contextView, method: verifyMediatorRemoval, view: viewComponent, mediator: mediator});
271 | }
272 |
273 | [Test(async, timeout='500')]
274 | public function interfaceMediatorIsRemovedWithView():void
275 | {
276 | var viewComponent:ViewComponent = new ViewComponent();
277 | var mediator:IMediator;
278 |
279 | mediatorMap.mapView(IViewComponent, ViewInterfaceMediator, null, false, true);
280 | contextView.addChild(viewComponent);
281 | mediator = mediatorMap.createMediator(viewComponent);
282 | Assert.assertNotNull('Mediator should have been created', mediator);
283 | Assert.assertTrue('Mediator should have been created', mediatorMap.hasMediator(mediator));
284 | Assert.assertTrue('Mediator should have been created for View Component', mediatorMap.hasMediatorForView(viewComponent));
285 |
286 | contextView.removeChild(viewComponent);
287 | Async.handleEvent(this, contextView, Event.ENTER_FRAME, delayFurther, 500, {dispatcher: contextView, method: verifyMediatorRemoval, view: viewComponent, mediator: mediator});
288 | }
289 |
290 | private function verifyMediatorRemoval(event:Event, data:Object):void
291 | {
292 | var viewComponent:ViewComponent = data.view;
293 | var mediator:IMediator = data.mediator;
294 | Assert.assertFalse("Mediator should not exist", mediatorMap.hasMediator(mediator));
295 | Assert.assertFalse("View Mediator should not exist", mediatorMap.hasMediatorForView(viewComponent));
296 | }
297 |
298 | private function delayFurther(event:Event, data:Object):void
299 | {
300 | Async.handleEvent(this, data.dispatcher, Event.ENTER_FRAME, data.method, 500, data);
301 | delete data.dispatcher;
302 | delete data.method;
303 | }
304 |
305 | [Test]
306 | public function contextViewMediatorIsCreatedWhenMapped():void
307 | {
308 | mediatorMap.mapView( TestContextView, TestContextViewMediator );
309 | Assert.assertTrue('Mediator should have been created for contextView', mediatorMap.hasMediatorForView(contextView));
310 | }
311 |
312 | [Test]
313 | public function contextViewMediatorIsCreatedWhenMappedToInterface():void
314 | {
315 | mediatorMap.mapView( ITestContextView, TestContextViewMediator );
316 | Assert.assertTrue('Mediator should have been created for contextView when mapped to interface', mediatorMap.hasMediatorForView(contextView));
317 | }
318 |
319 | [Test]
320 | public function contextViewMediatorIsNotCreatedWhenMappedAndAutoCreateIsFalse():void
321 | {
322 | mediatorMap.mapView( TestContextView, TestContextViewMediator, null, false );
323 | Assert.assertFalse('Mediator should NOT have been created for contextView', mediatorMap.hasMediatorForView(contextView));
324 | }
325 |
326 | [Test]
327 | public function contextViewMediatorIsNotCreatedWhenMappedToInterfaceAndAutoCreateIsFalse():void
328 | {
329 | mediatorMap.mapView( ITestContextView, TestContextViewMediator, null, false );
330 | Assert.assertFalse('Mediator should NOT have been created for contextView', mediatorMap.hasMediatorForView(contextView));
331 | }
332 |
333 | [Test]
334 | public function unmapView():void
335 | {
336 | mediatorMap.mapView(ViewComponent, ViewMediator);
337 | mediatorMap.unmapView(ViewComponent);
338 | var viewComponent:ViewComponent = new ViewComponent();
339 | contextView.addChild(viewComponent);
340 | var hasMediator:Boolean = mediatorMap.hasMediatorForView(viewComponent);
341 | Assert.assertFalse('Mediator should NOT have been created for View Component', hasMediator);
342 | }
343 |
344 | [Test]
345 | public function unmapViewInterface():void
346 | {
347 | mediatorMap.mapView(IViewComponent, ViewInterfaceMediator);
348 | mediatorMap.unmapView(IViewComponent);
349 | var viewComponent:ViewComponent = new ViewComponent();
350 | contextView.addChild(viewComponent);
351 | var hasMediator:Boolean = mediatorMap.hasMediatorForView(viewComponent);
352 | Assert.assertFalse('Mediator should NOT have been created for View Component', hasMediator);
353 | }
354 |
355 | [Test]
356 | public function autoRegisterUnregisterRegister():void
357 | {
358 | var viewComponent:ViewComponent = new ViewComponent();
359 |
360 | mediatorMap.mapView(ViewComponent, ViewMediator, null, true, true);
361 | mediatorMap.unmapView(ViewComponent);
362 | contextView.addChild(viewComponent);
363 | Assert.assertFalse('Mediator should NOT have been created for View Component', mediatorMap.hasMediatorForView(viewComponent));
364 | contextView.removeChild(viewComponent);
365 |
366 | mediatorMap.mapView(ViewComponent, ViewMediator, null, true, true);
367 | contextView.addChild(viewComponent);
368 | Assert.assertTrue('Mediator should have been created for View Component', mediatorMap.hasMediatorForView(viewComponent));
369 | }
370 |
371 | [Test]
372 | public function autoRegisterUnregisterRegisterWithInterfaces():void
373 | {
374 | var viewComponent:ViewComponent = new ViewComponent();
375 |
376 | mediatorMap.mapView(IViewComponent, ViewInterfaceMediator, null, true, true);
377 | mediatorMap.unmapView(IViewComponent);
378 | contextView.addChild(viewComponent);
379 | Assert.assertFalse('Mediator should NOT have been created for View Component', mediatorMap.hasMediatorForView(viewComponent));
380 | contextView.removeChild(viewComponent);
381 |
382 | mediatorMap.mapView(IViewComponent, ViewInterfaceMediator, null, true, true);
383 | contextView.addChild(viewComponent);
384 | Assert.assertTrue('Mediator should have been created for View Component', mediatorMap.hasMediatorForView(viewComponent));
385 | }
386 |
387 | }
388 | }
--------------------------------------------------------------------------------