├── .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 | } --------------------------------------------------------------------------------