Skip to content

Instantly share code, notes, and snippets.

@Ekstazi
Created May 24, 2013 12:31
Show Gist options
  • Save Ekstazi/5643180 to your computer and use it in GitHub Desktop.
Save Ekstazi/5643180 to your computer and use it in GitHub Desktop.
Haxe callbacks
package mysql.common;
/**
* ...
* @author Ekstazi
*/
/**
* @todo
*/
class Callbacks<T>
{
/**
* указывает, что список колбэков может быть выполнен только единожды,
* второй и последующие вызовы метода fire() будут безрезультатны
* (как это сделано в объекте deferred), если этот флаг не указан,
* то можно несколько раз вызывать метод fire().
*/
public static var ONCE = 1;
/**
* указывает, что необходимо запоминать параметры последнего вызова метода fire()
* (и выполнения колбэков из списка) и немедленно выполнять добавляемые колбэки
* с соответствующими параметрами, если они добавляются уже после вызова метода fire()
* (как это сделано в объекте deferred).
*/
public static var MEMORY = 2;
/**
* указывает, что каждый колбэк может быть добавлен в список только один раз,
* повторная попытка добавить колбэк в список ни к чему ни приведёт.
*/
public static var UNIQUE = 4;
/**
* указывает, что нужно прекратить выполнение колбэков из списка,
* если какой-то из них вернул false, в пределах текущей сессии вызова fire().
* Следующий вызов метода fire() начинает новую сессию выполнения списка колбэков,
* и они будут выполняться опять до тех пор, пока один из списка не вернёт false
* либо пока не закончатся.
*/
public static var STOP_ON_FALSE = 8;
private var _callbacks:Array<T->Dynamic>;
private var _fired:Bool;
private var _iterrupted:Bool;
private var _flags:UInt;
private var _params:Array<T>;
public var isActive:Bool;
public var isLocked:Bool;
public function new(flags:UInt=0)
{
isActive = true;
empty();
_flags = flags;
}
/**
* Добавляет в список колбэки,
* можно одновременно передавать в аргументах этого метода несколько функций с помощью массива
* или всего одну функцию
* @param ?closure
* @param ?closures
*/
public function add(?closure:T->Dynamic,?closures:Array<T->Dynamic>)
{
if (!isActive)
return this;
if(closure!=null){
if (_flags & UNIQUE == 0 || !has(closure)) {
_callbacks.push(closure);
if (_fired && _flags & MEMORY > 0) {
for(i in _params)
execute(i,closure);
}
}
}
if (closures != null) {
Lambda.iter(closures, function(f:T->Dynamic) { add(f); } );
}
return this;
}
/**
* Удаляет колбэки из списка, причем даже если колбэк был добавлен дважды,
* удаление его произойдёт с обеих позиций. Т.о. если вызвать метод удаления
* некоторого колбэка из списка, то можно быть уверенным, что его в списке больше нет,
* сколько бы раз его не добавляли. Можно передавать несколько функций одновременно
* как массив.
* @param ?closure
* @param ?closures
*/
public function remove(?closure:T->Dynamic,?closures:Array<T->Dynamic>)
{
if (!isActive)
return this;
if(closure!=null){
while (_callbacks.remove(closure)) { };
}
if (closures != null) {
Lambda.iter(closures, function(f:T->Dynamic) { remove(f); } );
}
return this;
}
/**
* Проверяет, есть ли указанная функция в списке колбэков
* @param closure
* @return Bool
*/
public function has(closure:T->Dynamic):Bool
{
if (!isActive)
return false;
return Lambda.has(_callbacks, closure);
}
/**
* очищает список колбэков и сбрасывает состояние
*/
public function empty()
{
if (!isActive)
return this;
_callbacks = [];
_fired = isLocked = _iterrupted = false;
return this;
}
/**
* Фиксирует текущее состояние объекта callbacks относительно параметров
* и состояния выполнения списка колбэков. Этот метод актулен при использовании
* флага memory и предназначен для блокирования только последующих вызовов fire(),
* в остальных случаях он равносилен вызову disable().
*/
public function lock()
{
isLocked = true;
if (!_fired)
isActive = false;
return this;
}
public function unlock()
{
isLocked = false;
if (_flags & MEMORY == 0 || !_fired)
isActive = true;
return this;
}
/**
* «отключает» объект callbacks, все действия с ним будут безрезультатны.
* При этом перестают работать вообще все методы: add — ни к чему не приводит,
* has — всегда возвращает false и пр.
*/
public function disable()
{
isActive = false;
return this;
}
/**
* Включает все методы объекта
*/
public function enable()
{
isActive = true;
return this;
}
private function execute(arg:T,closure:T->Dynamic)
{
if (_iterrupted)
return false;
var r:Dynamic = closure(arg);
if (_flags & STOP_ON_FALSE > 0 && Std.is(r, Bool) && false == cast r) {
_iterrupted = true;
return false;
}
return true;
}
/**
* Запускает выполнение всех колбэков в списке с аргументом этого метода.
* @param arg
*/
public function fire(arg:T)
{
if (isLocked || !isActive || _fired && _flags & ONCE > 0 )
return this;
_iterrupted = false;
Lambda.foreach(_callbacks, callback(execute,arg));
_fired = true;
if(_flags & MEMORY>0)
_params.push(arg);
return this;
}
public function fired()
{
return _fired;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment