Rest icon indicating copy to clipboard operation
Rest copied to clipboard

Routines as events

Open nickl- opened this issue 13 years ago • 3 comments

What if routines were events? Since they

At first purely cosmetic refactor imploring the use of a Dispatcher pattern instead of Abstract Routine doing the work.

<?php

namespace Respect\Rest\Routines;

class RoutineDispatcher
{
    private static $handlers = array();

    public static function addHandler(string $type, Routinable $routine)
    {
        if (!is_array(static::$handlers[$type]))
            static::$handlers[$type] = array();
        static::$handlers[$type][] = $routine;
    }

    public static function dispatch(string $type, string $method, array $args) {
        foreach (static::$handler[$type] as $routine)
            call_user_func_array(array($routine, $method), $args);
    }

}

class Request
{
    /** ...  */
    public function routineCall($type, $method, AbstractRoutinable $routine, &$routeParamsValues)
    {
        /** ... */
        $callbackParameters = $routeParamsValues;

        return RoutineDispatcher::dispatch($type, $method, array($this, $callbackParameters));
    }
}

class AbstractRoute
{
    public function appendRoutine(Routinable $routine)
    {
        RoutineDispatcher::addHandler(get_class($routine), $routine);
        return $this;
    }

}

Making things a tad more structured, understandable logical and already adding the benefit of multiple handlers per type which will allow for this:

$r3->get('/about', function() {
    return array('v' => 2.0);
})->accept(array(
    'text/html' => function($data) {
        list($k,$v)=each($data);
        return "<strong>$k</strong>: $v";
    }
))->accept(array(
    'application/json' => 'json_encode'
));

nickl- avatar Nov 26 '12 18:11 nickl-

We then have a logical point to turn these events asynchronous.

Something like:

class RoutineDispatcher
{
    /** ... */
    public static function dispatch(string $type, string $method, array $args, $request) {
        static::routineBroadcast($type, $request)->send([$method => $args]);
    }

    private static function routineBroadcast($type, $target) {
        while (true) {
            ($method = yield);
            foreach (static::$handler[$type] as $routine)
                $target->send(call_user_func_array([$routine, key($method)], value($method)));
        }
    }

}

class Request
{
    /** ...  */
    public function __toString() {
        $this->response();
        while (true)
            return yield;
    }

    protected function processPreRoutines($target)
    {
        /** do everything as per usual but instead of returning */
        $target->send($result);
    }

    protected function processPostRoutines($target)
    {
        /** do everything as per usual but instead of returning */
        $target->send($result);
    }

    public function response()
    {
        foreach ($this->processFlow() as $i);
    }

    private function processFlow() {
        $process = ['ProxyableBy', 'RunTargets', 'ProxyableThrough'];
        foreach ($process as $proc)
            return yield $this->proceed($proc)->send();
    }

    public function proceed($type) {
        while (true) {
            ($result = yield);
            switch ($type) {
                case 'ProxyableBy':
                    $this->processPreRoutines($this->proceed('RunTargets'));
                    break;
                case 'RunTargets':
                    $this->route->runTarget($this->proceed('ProxyableThrough'), $this->method, $this->params);
                    break;
                case 'ProxyableThrough':
                    $this->processPostRoutines($this->proceed('When'));
                    break;
                case 'When' :
                    if (!$result) {
                        $this->__toString->send("Aborted by failed wwhen routine");
                    }
            }
        }
    }
}

Disclaimer: This has not been tested and I really am only guessing here I have no clue how this will work but the theory is sound, I think and it should serve as a practical example of a possible way forward in future.

@alganet @henriquemoody @wesleyvicthor @augustohp @iannsp What are your thoughts? Does it make any sense?

nickl- avatar Nov 26 '12 18:11 nickl-

I like this! With few modifications we could push this up. Really useful for accept() and rel().

alganet avatar Nov 27 '12 01:11 alganet

What warning signs do you see already?

I've been playing with coroutines on aero, will commit soon and it is totally magic. Passing a target to a generator that waits on a yield to receive a value, which you send to it. On receipt it does its task and then sends to target. Mimicking unix pipes creating components that can be chained loosely together. I think Respect/Rest would benefit from that immensely but lets focus on discussing the first post for now instead of the make belief and discussions on generators and coroutines that is still a while to come.

Events have the ability to decouple efficiently which I think should be the main focus of the exercise, agreed?

nickl- avatar Nov 27 '12 23:11 nickl-