Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Routines as events #82

Open
nickl- opened this issue Nov 26, 2012 · 3 comments
Open

Routines as events #82

nickl- opened this issue Nov 26, 2012 · 3 comments
Milestone

Comments

@nickl-
Copy link
Member

nickl- commented Nov 26, 2012

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-
Copy link
Member Author

nickl- commented Nov 26, 2012

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?

@alganet
Copy link
Member

alganet commented Nov 27, 2012

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

@nickl-
Copy link
Member Author

nickl- commented Nov 27, 2012

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?

@augustohp augustohp modified the milestone: Ideas May 13, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants