Skip to content

Instantly share code, notes, and snippets.

@trxcllnt
Created May 19, 2011 00:05
Show Gist options
  • Save trxcllnt/979883 to your computer and use it in GitHub Desktop.
Save trxcllnt/979883 to your computer and use it in GitHub Desktop.
package org.robotlegs.variance
{
public interface IPackageFilters
{
/**
* Registers a package name to check against in <code>applyFilters()</code>.
*
* @see IPackageFilters#applyFilters
*/
function registerPackageFilter(packageFilter:String):void;
/**
* Removes a package name from the list of packages checked in
* <code>applyFilters()</code>.
*
* @see IPackageFilters#applyFilters
*/
function removePackageFilter(packageFilter:String):void;
/**
* Filters the given <code>instance</code> against every registered
* package name.
*
* @returns false if the instance exists in one of the registered
* package names, true if it does not.
*/
function applyFilters(instance:Object):Boolean;
}
}
package org.robotlegs.variance
{
import org.robotlegs.core.IMediator;
/**
* A map of view types to mediator types. Mapping can be covariant or
* invariant, with the default being covariant.
*/
public interface IVariantMediatorMap
{
/**
* Maps a view to be automatically mediated by an IMediator type.
*
* In invariant mediation, the type of component must match exactly to
* the type being mediated. This is invariant because only instances
* of the exact <code>viewType</code> will cause the
* <code>IVariantMediatorMap</code> to create the mapped mediator.
*
* In covariant mediation, if the component extends or implements any of
* the mapped <code>viewType</code>s, an instance of the mapped mediator
* will be instantiated and associated with the component. This is
* covariant, because a higher level generic type (beit interface or
* superclass) maps to the lower-level concrete implementation.
*
* The default is covariance.
*/
function mapMediator(viewType:Class, mediatorType:Class, covariant:Boolean = true):void;
/**
* Checks if the IVariantMediatorMap has a covariant or invariant typing
* for a given <code>viewType</code>.
*
* @returns true if there is a mapping, false if there isn't.
*/
function hasMediatorMapping(viewType:Class, covariant:Boolean = true):Boolean;
/**
* Unmaps the given viewType with the associated covariance flag from
* the <code>IVariantMediatorMap</code>
*/
function unmapMediator(viewType:Class, covariant:Boolean = true):void;
/**
* Ensures registration of all the mediators for the given
* <code>viewComponent</code> instance.
*
* @returns A Vector of all the IMediator instances associated with the
* <code>viewComponent</code> instance.
*/
function registerMediators(viewComponent:Object):Vector.<IMediator>;
/**
* Gets all the currently registered mediators for the given
* <code>viewComponent</code> instance.
*
* @returns A Vector of all the IMediator instances associated with the
* <code>viewComponent</code>.
*/
function getMediators(viewComponent:Object):Vector.<IMediator>;
/**
* Ensures removal of all the mediators for the given
* <code>viewComponent</code> instance.
*
* @returns A Vector of all the IMediator instances that were
* de-associated with the <code>viewComponent</code> instance.
*/
function removeMediators(viewComponent:Object):Vector.<IMediator>;
}
}
package org.robotlegs.variance
{
import flash.display.*;
import flash.events.Event;
import flash.utils.Dictionary;
import org.robotlegs.base.*;
import org.robotlegs.core.*;
public class VariantMediatorMap extends ViewMapBase implements IVariantMediatorMap, IPackageFilters
{
public function VariantMediatorMap(contextView:DisplayObjectContainer,
injector:IInjector,
reflector:IReflector,
filter:IPackageFilters = null)
{
viewListenerCount = 1;
super(contextView, injector);
this.reflector = reflector;
this.filter = filter || new PackageFilters(reflector);
registerPackageFilter('flash.*');
registerPackageFilter('mx.*');
}
protected var reflector:IReflector;
protected const mediatorClassMap:Dictionary = new Dictionary(false);
protected const mediatorMap:Object = {};
/**
* @inheritDoc
*/
public function mapMediator(viewType:Class, mediatorType:Class, covariant:Boolean = true):void
{
const variance:int = int(covariant);
if(!(viewType in mediatorClassMap))
{
mediatorClassMap[viewType] = [];
}
if(!mediatorClassMap[viewType][variance])
{
mediatorClassMap[viewType][variance] = mediatorType;
}
}
/**
* @inheritDoc
*/
public function hasMediatorMapping(viewType:Class, covariant:Boolean = true):Boolean
{
return (viewType in mediatorClassMap) && Boolean(mediatorClassMap[viewType][int(covariant)]);
}
/**
* @inheritDoc
*/
public function unmapMediator(viewType:Class, covariant:Boolean = true):void
{
if(!hasMediatorMapping(viewType, covariant))
{
return;
}
const mediatorType:Class = mediatorClassMap[viewType][int(covariant)];
if(mediatorClassMap[viewType][int(!covariant)])
{
mediatorClassMap[viewType][int(covariant)] = null;
}
else
{
delete mediatorClassMap[viewType];
}
}
/**
* @inheritDoc
*/
public function registerMediators(viewComponent:Object):Vector.<IMediator>
{
if(!applyFilters(viewComponent))
{
return new <IMediator>[];
}
var viewTypes:Vector.<Class> = retrieveViewTypes(viewComponent);
var mediatorTypes:Vector.<Class> = retrieveMediatorClasses(viewComponent);
var mediatorNames:Vector.<String> = retrieveMediatorNames(viewComponent, mediatorTypes);
var mediators:Vector.<IMediator> = new <IMediator>[];
mediatorNames.forEach(function(name:String, i:int, v:Vector.<String>):void{
if(name in mediatorMap)
{
mediators.push(mediatorMap[name]);
return;
}
var mediatorType:Class = mediatorTypes[i];
viewTypes.forEach(function(viewType:Class, ... args):void{
injector.mapValue(viewType, viewComponent);
});
var mediator:IMediator = injector.instantiate(mediatorType) as IMediator;
viewTypes.forEach(function(viewType:Class, ... args):void{
injector.unmap(viewType);
});
mediator.setViewComponent(viewComponent);
mediator.preRegister();
mediators.push(mediatorMap[name] = mediator);
});
return mediators;
}
/**
* @inheritDoc
*/
public function getMediators(viewComponent:Object):Vector.<IMediator>
{
if(!applyFilters(viewComponent))
{
return new <IMediator>[];
}
var mediatorNames:Vector.<String> =
retrieveMediatorNames(viewComponent, retrieveMediatorClasses(viewComponent));
var mediators:Vector.<IMediator> = new <IMediator>[];
mediatorNames.forEach(function(name:String, ... args):void{
if(!(name in mediatorMap))
return;
mediators.push(mediatorMap[name]);
});
return mediators;
}
/**
* @inheritDoc
*/
public function removeMediators(viewComponent:Object):Vector.<IMediator>
{
if(!applyFilters(viewComponent))
{
return new <IMediator>[];
}
var mediatorNames:Vector.<String> =
retrieveMediatorNames(viewComponent, retrieveMediatorClasses(viewComponent));
var mediators:Vector.<IMediator> = new <IMediator>[];
mediatorNames.forEach(function(name:String, ... args):void{
if(!(name in mediatorMap))
return;
var mediator:IMediator = mediatorMap[name];
mediator.preRemove();
mediator.setViewComponent(null);
delete mediatorMap[name];
mediators.push(mediator);
});
return mediators;
}
/**
* Returns a Vector of Classes that represent all the mapped
* <code>viewType</code>s that this <code>viewComponent</code> instance
* matches.
*
* @private
*/
protected function retrieveViewTypes(viewComponent:Object):Vector.<Class>
{
const classes:Vector.<Class> = new <Class>[];
var viewType:* = reflector.getClass(viewComponent);
if(hasMediatorMapping(viewType, false))
{
classes.push(viewType);
}
for(viewType in mediatorClassMap)
{
if(viewComponent is viewType && hasMediatorMapping(viewType, true))
{
classes.push(viewType);
}
}
return classes;
}
/**
* Returns a Vector of Classes that represent all the mapped
* <code>mediatorType</code>s that this <code>viewComponent</code>
* instance matches.
*
* @private
*/
protected function retrieveMediatorClasses(viewComponent:Object):Vector.<Class>
{
const classes:Vector.<Class> = new <Class>[];
var viewType:* = reflector.getClass(viewComponent);
//Catch invariant type.
if(hasMediatorMapping(viewType, false))
{
classes.push(mediatorClassMap[viewType][int(false)]);
}
//Check for covariance.
for(viewType in mediatorClassMap)
{
if(viewComponent is viewType && hasMediatorMapping(viewType, true))
{
classes.push(mediatorClassMap[viewType][int(true)]);
}
}
return classes;
}
/**
* Returns a Vector of unique String IDs from a combination of the given
* <code>viewComponent</code> instance and each mediator type.
*
* @private
*/
protected function retrieveMediatorNames(viewComponent:Object,
mediatorTypes:Vector.<Class>):Vector.<String>
{
var mediatorNames:Vector.<String> = new <String>[];
mediatorTypes.forEach(function(type:Class, ... args):void{
mediatorNames.push(createMediatorName(viewComponent, type))
});
return mediatorNames;
}
/**
* Creates a semi-unique key from the combination of the given
* <code>viewComponent</code> instance and the mediatorType.
*
* Note: It is feasible that two <code>viewComponent</code>s of the same
* Class type can exist at the same nestLevel and with the same name
* as each other. It might be worth revisiting this later to harden the
* uniqueness guaranteed by this function.
*
* @private
*/
protected function createMediatorName(viewComponent:Object, mediatorType:Class):String
{
var viewName:String = reflector.getFQCN(viewComponent, true);
if('name' in viewComponent)
viewName += '#' + viewComponent['name'];
if('nestLevel' in viewComponent)
viewName += ' : ' + viewComponent['nestLevel'];
return reflector.getFQCN(mediatorType, true) + " : " + viewName;
}
////
// Stubbed IPackageFilters impl. via the Visitor pattern.
////
protected var filter:IPackageFilters;
/**
* @inheritDoc
*/
public function registerPackageFilter(packageFilter:String):void
{
filter.registerPackageFilter(packageFilter);
}
/**
* @inheritDoc
*/
public function removePackageFilter(packageFilter:String):void
{
filter.removePackageFilter(packageFilter);
}
/**
* @inheritDoc
*/
public function applyFilters(viewComponent:Object):Boolean
{
return filter.applyFilters(viewComponent);
}
override public function set enabled(value:Boolean):void
{
viewListenerCount = value ? 1 : 0;
super.enabled = value;
}
////
// Display-list queueing and invalidation logic.
////
override protected function addListeners():void
{
if(!contextView)
return;
contextView.addEventListener(Event.ADDED_TO_STAGE, onViewAdded, useCapture, 0, true);
contextView.addEventListener(Event.REMOVED_FROM_STAGE, onViewRemoved, useCapture, 0, true);
}
override protected function removeListeners():void
{
if(!contextView)
return;
contextView.removeEventListener(Event.ADDED_TO_STAGE, onViewAdded, useCapture);
contextView.removeEventListener(Event.REMOVED_FROM_STAGE, onViewRemoved, useCapture);
}
protected const addedViews:Dictionary = new Dictionary(false);
protected const removedViews:Dictionary = new Dictionary(false);
override protected function onViewAdded(e:Event):void
{
var target:Object = e.target;
addedViews[target] = true;
if(target in removedViews)
delete removedViews[target];
invalidateFrame();
}
protected function onViewRemoved(e:Event):void
{
var target:Object = e.target;
removedViews[target] = true;
if(target in addedViews)
delete addedViews[target];
invalidateFrame();
}
protected const frameProvider:Shape = new Shape();
protected function invalidateFrame():void
{
frameProvider.addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
protected function onEnterFrame(event:Event):void
{
frameProvider.removeEventListener(Event.ENTER_FRAME, onEnterFrame);
var view:Object;
for(view in addedViews)
registerMediators(view);
for(view in removedViews)
removeMediators(view);
}
}
}
import flash.utils.Dictionary;
import org.robotlegs.core.IReflector;
import org.robotlegs.variance.IPackageFilters;
internal class PackageFilters implements IPackageFilters
{
public function PackageFilters(reflector:IReflector)
{
this.reflector = reflector;
}
private var reflector:IReflector;
private const packageFiltersMap:Dictionary = new Dictionary(false);
public function registerPackageFilter(packageFilter:String):void
{
packageFiltersMap[packageFilter] =
new RegExp('^' + packageFilter.replace(/\./g, '\\$&').replace(/\*/g, '.+') + '::');
}
public function removePackageFilter(packageFilter:String):void
{
delete packageFiltersMap[packageFilter];
}
public function applyFilters(viewComponent:Object):Boolean
{
for each(var filter:RegExp in packageFiltersMap)
{
if(filter.test(reflector.getFQCN(viewComponent.constructor)))
{
return false;
}
}
return true;
}
}
/*
* Copyright (c) 2009 the original author or authors
*
* Permission is hereby granted to use, modify, and distribute this file
* in accordance with the terms of the license agreement accompanying it.
*/
package org.robotlegs.variance
{
import flash.display.DisplayObjectContainer;
import flash.events.*;
import mx.core.UIComponent;
import org.flexunit.Assert;
import org.flexunit.async.Async;
import org.fluint.uiImpersonation.UIImpersonator;
import org.robotlegs.adapters.*;
import org.robotlegs.base.*;
import org.robotlegs.core.*;
import org.robotlegs.mvcs.support.*;
import org.robotlegs.variance.support.*;
public class VariantMediatorMapTests
{
public static const TEST_EVENT:String = 'testEvent';
protected var contextView:DisplayObjectContainer;
protected var eventDispatcher:IEventDispatcher;
protected var mediatorMap:VariantMediatorMap;
protected var injector:IInjector;
protected var reflector:IReflector;
protected var eventMap:IEventMap;
[BeforeClass]
public static function runBeforeEntireSuite():void
{
}
[AfterClass]
public static function runAfterEntireSuite():void
{
}
[Before(ui)]
public function runBeforeEachTest():void
{
contextView = new TestContextView();
eventDispatcher = new EventDispatcher();
injector = new SwiftSuspendersInjector();
reflector = new SwiftSuspendersReflector();
mediatorMap = new VariantMediatorMap(contextView, injector, reflector);
injector.mapValue(DisplayObjectContainer, contextView);
injector.mapValue(IInjector, injector);
injector.mapValue(IEventDispatcher, eventDispatcher);
injector.mapValue(IVariantMediatorMap, mediatorMap);
injector.mapValue(IMediatorMap, new MediatorMap(contextView, injector, reflector));
UIImpersonator.addChild(contextView);
}
[After(ui)]
public function runAfterEachTest():void
{
UIImpersonator.removeAllChildren();
injector.unmap(IMediatorMap);
injector.unmap(IVariantMediatorMap);
}
[Test]
public function testInvariantMediatorMapping():void
{
mediatorMap.mapMediator(ViewComponent, ViewMediator, false);
var hasMapping:Boolean = mediatorMap.hasMediatorMapping(ViewComponent, false);
Assert.assertTrue('VariantMediatorMap has invariantly mapped ' +
'ViewMediator for ViewComponent instances', hasMapping);
}
[Test]
public function testCovariantMediatorMapping():void
{
mediatorMap.mapMediator(ViewComponent, ViewMediator, true);
var hasMapping:Boolean = mediatorMap.hasMediatorMapping(ViewComponent, true);
Assert.assertTrue('VariantMediatorMap has covariantly mapped ' +
'ViewMediator for ViewComponent instances', hasMapping);
}
[Test]
public function testCovariantInterfaceMediatorMapping():void
{
mediatorMap.mapMediator(IViewComponent, ViewMediator, true);
var hasMapping:Boolean = mediatorMap.hasMediatorMapping(IViewComponent, true);
Assert.assertTrue('VariantMediatorMap has covariantly mapped ' +
'ViewMediator for IViewComponent instances', hasMapping);
}
[Test]
public function testInvariantMediatorCreation():void
{
mediatorMap.mapMediator(ViewComponent, ViewMediator, false);
var mediators:Vector.<IMediator> = mediatorMap.registerMediators(new ViewComponent());
Assert.assertTrue('VariantMediatorMap should successfully create a ' +
'mediator for the invariant type ViewComponent',
mediators.length == 1, mediators[0] is ViewMediator);
}
[Test]
public function testCovariantInterfaceMediatorCreation():void
{
mediatorMap.mapMediator(IViewComponent, CovariantViewMediator, true);
var mediators:Vector.<IMediator> = mediatorMap.registerMediators(new ViewComponentImpl());
Assert.assertTrue('VariantMediatorMap should successfully create a ' +
'mediator for the covariant type IViewComponent',
mediators.length == 1, mediators[0] is CovariantViewMediator);
}
[Test]
public function testCovariantSuperclassMediatorCreation():void
{
mediatorMap.mapMediator(ViewComponent, ViewMediator, true);
var mediators:Vector.<IMediator> = mediatorMap.registerMediators(new ViewComponentAdvanced());
Assert.assertTrue('VariantMediatorMap should successfully create a ' +
'mediator for the covariant type ViewComponent and instance of ' +
'ViewComponentAdvanced, a lower type', mediators.length == 1, mediators[0] is ViewMediator);
mediatorMap.mapMediator(UIComponent, UIComponentMediator, true);
mediators = mediatorMap.registerMediators(new ViewComponent());
Assert.assertTrue('VariantMediatorMap should successfully create two' +
'mediators for an instance of the type ViewComponent', mediators.length == 2);
Assert.assertTrue('VariantMediatorMap should create an instance of' +
'ViewMediator as the first mediator for an instance of ' +
'ViewComponent', mediators[0] is ViewMediator);
Assert.assertTrue('VariantMediatorMap should create an instance of' +
'UIComponentMediator as the second mediator for an instance of' +
'ViewComponent, since ViewComponent is a lower type than ' +
'UIComponent', mediators[1] is UIComponentMediator);
}
[Test]
public function testMXPackagesAreFilteredOutOfAutoMediation():void
{
mediatorMap.mapMediator(UIComponent, UIComponentMediator, true);
var mediators:Vector.<IMediator> = mediatorMap.registerMediators(new UIComponent());
Assert.assertTrue('VariantMediatorMap should ignore instances of' +
'UIComponent because it is in the mx.* package, which is' +
'filtered out by default for performance reasons', mediators.length == 0);
}
[Test]
public function testMappingAndUnMappingInvariantly():void
{
mediatorMap.mapMediator(ViewComponent, ViewMediator, false);
var hasMapping:Boolean = mediatorMap.hasMediatorMapping(ViewComponent, false);
Assert.assertTrue('VariantMediatorMap has an invariant mapping for ViewComponent', hasMapping);
mediatorMap.unmapMediator(ViewComponent, false);
hasMapping = mediatorMap.hasMediatorMapping(ViewComponent, false);
Assert.assertTrue('VariantMediatorMap has successfully unmapped its' +
'invariant mapping for ViewComponent', hasMapping == false);
}
[Test]
public function testMappingAndUnMappingCovariantly():void
{
mediatorMap.mapMediator(ViewComponent, ViewMediator, true);
var hasMapping:Boolean = mediatorMap.hasMediatorMapping(ViewComponent, true);
Assert.assertTrue('VariantMediatorMap has a covariant mapping for ViewComponent', hasMapping);
mediatorMap.unmapMediator(ViewComponent, true);
hasMapping = mediatorMap.hasMediatorMapping(ViewComponent, true);
Assert.assertTrue('VariantMediatorMap has successfully unmapped its' +
'covariant mapping for ViewComponent', hasMapping == false);
}
[Test(async, timeout='500')]
public function testInvariantMediatorIsCreatedWhenViewIsAddedToTheDisplayList():void
{
mediatorMap.mapMediator(ViewComponent, ViewMediator, false);
var vc:ViewComponent = new ViewComponent();
contextView.addChild(vc);
Async.handleEvent(this, contextView, Event.ENTER_FRAME, delayFurther, 500,
{
dispatcher: contextView,
method: checkMediatorCreated,
view: vc,
mediatorType: ViewMediator
});
}
[Test(async, timeout='500')]
public function testCovariantMediatorIsCreatedWhenViewIsAddedToTheDisplayList():void
{
mediatorMap.mapMediator(ViewComponent, ViewMediator, true);
var vc:ViewComponent = new ViewComponent();
contextView.addChild(vc);
Async.handleEvent(this, contextView, Event.ENTER_FRAME, delayFurther, 500,
{
dispatcher: contextView,
method: checkMediatorCreated,
view: vc,
mediatorType: ViewMediator
});
}
[Test(async, timeout='500')]
public function testCovariantMediatorIsCreatedWhenSubclassOfViewIsAddedToTheDisplayList():void
{
mediatorMap.mapMediator(ViewComponent, ViewMediator, true);
var vc:ViewComponent = new ViewComponentAdvanced();
contextView.addChild(vc);
Async.handleEvent(this, contextView, Event.ENTER_FRAME, delayFurther, 500,
{
dispatcher: contextView,
method: checkMediatorCreated,
view: vc,
mediatorType: ViewMediator
});
}
[Test(async, timeout='500')]
public function testCovariantMediatorIsCreatedWhenViewInterfaceIsAddedToTheDisplayList():void
{
mediatorMap.mapMediator(IViewComponent, CovariantViewMediator, true);
var vc:ViewComponentImpl = new ViewComponentImpl();
contextView.addChild(vc);
Async.handleEvent(this, contextView, Event.ENTER_FRAME, delayFurther, 500,
{
dispatcher: contextView,
method: checkMediatorCreated,
view: vc,
mediatorType: CovariantViewMediator
});
}
[Test(async, timeout='500')]
public function testInvariantMediatorIsDestroyedWhenViewIsRemovedFromTheDisplayList():void
{
mediatorMap.enabled = false;
mediatorMap.mapMediator(ViewComponent, ViewMediator, true);
var vc:ViewComponent = new ViewComponent();
contextView.addChild(vc);
mediatorMap.registerMediators(vc);
mediatorMap.enabled = true;
contextView.removeChild(vc);
Async.handleEvent(this, contextView, Event.ENTER_FRAME, delayFurther, 500,
{
dispatcher: contextView,
method: checkMediatorDestroyed,
view: vc
});
}
[Test(async, timeout='500')]
public function testCovariantMediatorIsDestroyedWhenViewIsRemovedFromTheDisplayList():void
{
mediatorMap.enabled = false;
mediatorMap.mapMediator(ViewComponent, ViewMediator, false);
var vc:ViewComponent = new ViewComponent();
contextView.addChild(vc);
mediatorMap.registerMediators(vc);
mediatorMap.enabled = true;
contextView.removeChild(vc);
Async.handleEvent(this, contextView, Event.ENTER_FRAME, delayFurther, 500,
{
dispatcher: contextView,
method: checkMediatorDestroyed,
view: vc
});
}
[Test(async, timeout='500')]
public function testCovariantMediatorIsDestroyedWhenSubclassOfViewIsRemovedFromTheDisplayList():void
{
mediatorMap.enabled = false;
mediatorMap.mapMediator(ViewComponent, ViewMediator, false);
var vc:ViewComponent = new ViewComponentAdvanced();
contextView.addChild(vc);
mediatorMap.registerMediators(vc);
mediatorMap.enabled = true;
contextView.removeChild(vc);
Async.handleEvent(this, contextView, Event.ENTER_FRAME, delayFurther, 500,
{
dispatcher: contextView,
method: checkMediatorDestroyed,
view: vc
});
}
[Test(async, timeout='500')]
public function testCovariantMediatorIsDestroyedWhenViewInterfaceIsRemovedFromTheDisplayList():void
{
mediatorMap.enabled = false;
mediatorMap.mapMediator(IViewComponent, CovariantViewMediator, false);
var vc:ViewComponentImpl = new ViewComponentImpl();
contextView.addChild(vc);
mediatorMap.registerMediators(vc);
mediatorMap.enabled = true;
contextView.removeChild(vc);
Async.handleEvent(this, contextView, Event.ENTER_FRAME, delayFurther, 500,
{
dispatcher: contextView,
method: checkMediatorDestroyed,
view: vc
});
}
private function checkMediatorCreated(event:Event, data:Object):void
{
var mediators:Vector.<IMediator> = mediatorMap.getMediators(data.view);
Assert.assertTrue('VariantMediatorMap should successfully create a ' +
'mediator for the type "ViewComponent"',
mediators.length == 1, mediators[0] is data.mediatorType);
}
private function checkMediatorDestroyed(event:Event, data:Object):void
{
var mediators:Vector.<IMediator> = mediatorMap.getMediators(data.view);
Assert.assertTrue('VariantMediatorMap should successfully create a ' +
'mediator for the type "ViewComponent"',
mediators.length == 0);
}
private function delayFurther(event:Event, data:Object):void
{
Async.handleEvent(this, data.dispatcher, Event.ENTER_FRAME, data.method, 500, data);
delete data['dispatcher'];
delete data['method'];
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment