"seebz/uri-template": "dev-master",
"psr/log": "^1.0",
"psr/cache": "^1.0",
- "psr/simple-cache": "^1.0",
- "async-interop/promise": "^0.4.0"
+ "psr/simple-cache": "^1.0"
},
"require-dev": {
- "react/promise": "dev-async-interop",
- "amphp/amp": "dev-master",
- "amphp/loop": "dev-master",
- "monolog/monolog": "^1.19",
+ "amphp/amp": "^2",
+ "react/promise": "^2",
+ "monolog/monolog": "^1",
"phpunit/phpunit": "^5"
}
}
]);
array_shift($argv);
-($self = function($error, $api) use(&$self) {
+($self = function($api) use(&$self) {
global $argv;
while (null !== ($arg = array_shift($argv))) {
if ("." === $arg) {
- $api->when($self);
+ $api->then($self);
return;
}
$api = $api->$arg;
}
echo $api, "\n";
-})(null, $api);
+})($api);
$api->send();
$cli = new http\Client("curl", "seekat");
-$api = new API(API\Future\react(), [
+$api = new API(API\Future\amp(), [
"Authorization" => "token ".getenv("GITHUB_TOKEN")
], null, $cli, $log);
$events = yield $next;
}
return $count;
-})->when(function($error, $count) {
+})->onResolve(function($error, $count) {
printf("Listed %d events\n", $count);
});
foreach ($repos as $repo) {
$batch[] = $repo->hooks();
}
- foreach (yield $batch as $key => $hooks) {
+ $result = yield $batch;
+ foreach ($result as $key => $hooks) {
if (!count($hooks)) {
continue;
}
$api = new API(API\Future\amp(), API\auth("token", getenv("GITHUB_TOKEN")));
-$api->users->m6w6->gists()->when(function($error, $gists) {
+$api->users->m6w6->gists()->onResolve(function($error, $gists) {
$error and die($error);
foreach ($gists as $gist) {
- $gist->commits()->when(function($error, $commits) use($gist) {
+ $gist->commits()->onResolve(function($error, $commits) use($gist) {
$error and die($error);
foreach ($commits as $i => $commit) {
if (!$i) {
namespace seekat;
-use AsyncInterop\Promise;
use Countable;
use Generator;
use http\{
*
* @param string $method The API's "path" element to ascend into
* @param array $args Array of arguments forwarded to \seekat\API::get()
- * @return Promise
+ * @return mixed promise
*/
- function __call(string $method, array $args) : Promise {
+ function __call(string $method, array $args) {
/* We cannot implement an explicit then() method,
* because the Promise implementation might think
* we're actually implementing Thenable,
* which might cause an infinite loop.
- * FIXXME: then/when
*/
- if ($method === "when") {
- $promise = $this->get();
- $promise->when(...$args);
- return $promise;
- }
-
+ if ($method === "then"
/*
* very short-hand version:
- * ->users->m6w6->gists->get()->when(...)
+ * ->users->m6w6->gists->get()->then(...)
* vs:
* ->users->m6w6->gists(...)
*/
- if (is_callable(current($args))) {
- $promise = $this->get();
- $promise->when(current($args));
- return $promise;
+ || is_callable(current($args))) {
+ return $this->future->handlePromise($this->get(), ...$args);
}
return (new Call($this, $method))($args);
* Run the send loop through a generator
*
* @param callable|Generator $cbg A \Generator or a factory of a \Generator yielding promises
- * @return Promise The promise of the generator's return value
+ * @return mixed The promise of the generator's return value
* @throws InvalidArgumentException
*/
- function __invoke($cbg) : Promise {
+ function __invoke($cbg) {
$this->logger->debug(__FUNCTION__);
$consumer = new Consumer($this->getFuture(), function() {
*
* @param mixed $args The HTTP query string parameters
* @param array $headers The request's additional HTTP headers
- * @return Promise
+ * @return mixed promise
*/
- function head($args = null, array $headers = null, $cache = null) : Promise {
+ function head($args = null, array $headers = null, $cache = null) {
return $this->request("HEAD", $args, null, $headers, $cache);
}
*
* @param mixed $args The HTTP query string parameters
* @param array $headers The request's additional HTTP headers
- * @return Promise
+ * @return mixed promise
*/
- function get($args = null, array $headers = null, $cache = null) : Promise {
+ function get($args = null, array $headers = null, $cache = null) {
return $this->request("GET", $args, null, $headers, $cache);
}
*
* @param mixed $args The HTTP query string parameters
* @param array $headers The request's additional HTTP headers
- * @return Promise
+ * @return mixed promise
*/
- function delete($args = null, array $headers = null) : Promise {
+ function delete($args = null, array $headers = null) {
return $this->request("DELETE", $args, null, $headers);
}
* @param mixed $body The HTTP message's body
* @param mixed $args The HTTP query string parameters
* @param array $headers The request's additional HTTP headers
- * @return Promise
+ * @return mixed promise
*/
- function post($body = null, $args = null, array $headers = null) : Promise {
+ function post($body = null, $args = null, array $headers = null) {
return $this->request("POST", $args, $body, $headers);
}
* @param mixed $body The HTTP message's body
* @param mixed $args The HTTP query string parameters
* @param array $headers The request's additional HTTP headers
- * @return Promise
+ * @return mixed promise
*/
- function put($body = null, $args = null, array $headers = null) : Promise {
+ function put($body = null, $args = null, array $headers = null) {
return $this->request("PUT", $args, $body, $headers);
}
* @param mixed $body The HTTP message's body
* @param mixed $args The HTTP query string parameters
* @param array $headers The request's additional HTTP headers
- * @return Promise
+ * @return mixed promise
*/
- function patch($body = null, $args = null, array $headers = null) : Promise {
+ function patch($body = null, $args = null, array $headers = null) {
return $this->request("PATCH", $args, $body, $headers);
}
* @param mixed $body Thee HTTP message's body
* @param array $headers The request's additional HTTP headers
* @param Call\Cache\Service $cache
- * @return Promise
+ * @return mixed promise
*/
- private function request(string $method, $args = null, $body = null, array $headers = null, Call\Cache\Service $cache = null) : Promise {
+ private function request(string $method, $args = null, $body = null, array $headers = null, Call\Cache\Service $cache = null) {
if (isset($this->data)) {
$this->logger->debug("request -> resolve", [
"method" => $method,
namespace seekat\API;
-use AsyncInterop\Promise;
use http\Url;
use seekat\API;
-use seekat\Exception;
final class Call
{
$this->call = $call;
}
- function __invoke(array $args) : Promise {
+ function __invoke(array $args) {
if ($this->api->exists($this->call."_url", $url)) {
$url = new Url(uri_template($url, (array)current($args)));
$promise = $this->api->withUrl($url)->get(...$args);
namespace seekat\API\Call;
-use AsyncInterop\Promise;
use http\{
Client, Client\Request, Client\Response
};
private $response;
/**
- * @var Promise
+ * @var mixed
*/
private $promise;
$this->reject = API\Future\rejecter($future, $context);
}
- function __invoke() : Promise {
+ function __invoke() {
if ($this->cache->load($this->request, $cached)) {
$this->logger->info("deferred -> cached", [
"method" => $this->request->getRequestMethod(),
namespace seekat\API;
-use AsyncInterop\Promise;
use Generator;
use http\Client;
use seekat\API;
private $cancelled = false;
/**
- * @var Promise
+ * @var Future
*/
- private $promise;
+ private $future;
+
+ /**
+ * @var mixed
+ */
+ private $context;
/**
* @var \Closure
function __construct(Future $future, callable $loop) {
$this->loop = $loop;
- $context = $future->createContext(function() {
+ $this->future = $future;
+ $this->context = $future->createContext(function() {
$this->cancelled = true;
});
- $this->promise = $future->getPromise($context);
- $this->resolve = API\Future\resolver($future, $context);
- $this->reject = API\Future\rejecter($future, $context);
- $this->reduce = API\Future\reducer($future, $context);
+ $this->resolve = API\Future\resolver($future, $this->context);
+ $this->reject = API\Future\rejecter($future, $this->context);
+ $this->reduce = API\Future\reducer($future, $this->context);
}
/**
* Iterate over $gen, a \Generator yielding promises
*
* @param Generator $gen
- * @return Promise
+ * @return mixed promise
*/
- function __invoke(Generator $gen) : Promise {
+ function __invoke(Generator $gen) {
$this->cancelled = false;
foreach ($gen as $promise) {
($this->reject)("Cancelled");
}
- return $this->promise;
+ return $this->context->promise();
}
/**
* Promise handler
*
- * @param array|Promise $promise
+ * @param mixed $promise
* @param Generator $gen
*/
private function give($promise, Generator $gen) {
if (is_array($promise)) {
$promise = ($this->reduce)($promise);
}
- if ($promise instanceof Promise) {
- $promise->when(function($error, $result) use($gen) {
- if ($error) {
- $gen->throw(exception($error));
- }
- if (($promise = $gen->send($result))) {
- $this->give($promise, $gen);
- }
- });
- } else {
- $gen->throw(new UnexpectedValueException(
- "Expected Promise or array of Promises; got ".\seekat\typeof($promise)));
- }
+
+ $this->future->handlePromise($promise, function($result) use($gen) {
+ if (($promise = $gen->send($result))) {
+ $this->give($promise, $gen);
+ }
+ }, function($error) use($gen) {
+ $gen->throw(exception($error));
+ });
+
/* FIXME: external loop */
($this->loop)();
}
namespace seekat\API;
-use AsyncInterop\Promise;
-
interface Future
{
/**
/**
* @param object $context Promisor
- * @return Promise
+ * @return mixed promise
+ */
+ function getPromise($context);
+
+ /**
+ * @param mixed $promise
+ * @return bool
*/
- function getPromise($context) : Promise;
+ function isPromise($promise) : bool;
/**
- * @param Promise $promise
+ * @param mixed $promise
* @return bool
*/
- function cancelPromise(Promise $promise) : bool;
+ function cancelPromise($promise) : bool;
+
+ /**
+ * @param mixed $promise
+ * @param callable|null $onResult
+ * @param callable|null $onError
+ * @return mixed promise
+ */
+ function handlePromise($promise, callable $onResult = null, callable $onError = null);
/**
* @param object $context Promisor returned by createContext
* @param mixed $value
* @return void
*/
- function onSuccess($context, $value);
+ function resolve($context, $value);
/**
- * @param object $context Proisor returned by createContext
+ * @param object $context Promisor returned by createContext
* @param mixed $reason
* @return void
*/
- function onFailure($context, $reason);
+ function reject($context, $reason);
/**
* @param object $context Promisor returned by createContext
* @param array $promises
- * @return Promise
+ * @return mixed promise
*/
- function onMultiple($context, array $promises) : Promise;
+ function all($context, array $promises);
}
namespace seekat\API\Future;
use Amp\Deferred as AmpDeferred;
-use AsyncInterop\Promise;
+use Amp\Promise as AmpPromise;
use React\Promise\Deferred as ReactDeferred;
+use React\Promise\PromiseInterface as ReactPromise;
use seekat\API\Future;
/**
* @param Future $future
* @param mixed $value
- * @return Promise
+ * @return mixed promise
*/
function resolve(Future $future, $value) {
$promisor = $future->createContext();
- $future->onSuccess($promisor, $value);
+ $future->resolve($promisor, $value);
return $future->getPromise($promisor);
}
/**
* @param Future $future
* @param mixed $reason
- * @return Promise
+ * @return mixed promise
*/
function reject(Future $future, $reason) {
$promisor = $future->createContext();
- $future->onFailure($promisor, $reason);
+ $future->reject($promisor, $reason);
return $future->getPromise($promisor);
}
*/
function resolver(Future $future, $context) {
return function($value) use($future, $context) {
- return $future->onSuccess($context, $value);
+ return $future->resolve($context, $value);
};
}
*/
function rejecter(Future $future, $context) {
return function($reason) use($future, $context) {
- return $future->onFailure($context, $reason);
+ return $future->reject($context, $reason);
};
}
* @return \Closure
*/
function reducer(Future $future, $context) {
- return function(array $promises) use($future, $context) : Promise {
- return $future->onMultiple($context, $promises);
+ return function(array $promises) use($future, $context) {
+ return $future->all($context, $promises);
};
}
return new ReactDeferred($onCancel);
}
- function getPromise($context) : Promise {
+ function getPromise($context) {
/* @var $context ReactDeferred */
return $context->promise();
}
- function cancelPromise(Promise $promise) : bool {
+ function isPromise($promise) : bool {
+ return $promise instanceof ReactPromise;
+ }
+
+ function handlePromise($promise, callable $onResult = null, callable $onError = null) {
+ return $promise->then($onResult, $onError);
+ }
+
+ function cancelPromise($promise) : bool {
/* @var $promise \React\Promise\Promise */
$promise->cancel();
return true;
}
- function onSuccess($context, $value) {
+ function resolve($context, $value) {
/* @var $context ReactDeferred */
$context->resolve($value);
}
- function onFailure($context, $reason) {
+ function reject($context, $reason) {
/* @var $context ReactDeferred */
$context->reject($reason);
}
- function onMultiple($context, array $promises) : Promise {
+ function all($context, array $promises) {
return \React\Promise\all($promises);
}
};
return new AmpDeferred();
}
- function getPromise($context) : Promise {
+ function getPromise($context) {
/* @var $context AmpDeferred */
return $context->promise();
}
- function cancelPromise(Promise $promise) : bool {
+ function isPromise($promise) : bool {
+ return $promise instanceof AmpPromise;
+ }
+
+ function handlePromise($promise, callable $onResult = null, callable $onError = null) {
+ $promise->onResolve(function($error = null, $result = null) use($onResult, $onError) {
+ if ($error) {
+ if ($onError) {
+ $onError($error);
+ }
+ } else {
+ if ($onResult) {
+ $onResult($result);
+ }
+ }
+ });
+ return $promise;
+ }
+
+ function cancelPromise($promise) : bool {
return false;
}
- function onSuccess($context, $value) {
+ function resolve($context, $value) {
/* @var $context AmpDeferred */
$context->resolve($value);
}
- function onFailure($context, $reason) {
+ function reject($context, $reason) {
/* @var $context AmpDeferred */
$context->fail(\seekat\Exception\exception($reason));
}
- function onMultiple($context, array $promises) : Promise {
+ function all($context, array $promises) {
return \Amp\all($promises);
}
};
/**
* Perform a GET request against the link's "first" relation
*
- * @return Promise
+ * @return mixed promise
*/
-function first(API $api, Cache\Service $cache = null) : Promise {
+function first(API $api, Cache\Service $cache = null) {
$links = $api->getLinks();
if ($links && ($first = $links->getFirst())) {
return $api->withUrl($first)->get(null, null, $cache);
/**
* Perform a GET request against the link's "prev" relation
*
- * @return Promise
+ * @return mixed promise
*/
-function prev(API $api, Cache\Service $cache = null) : Promise {
+function prev(API $api, Cache\Service $cache = null) {
$links = $api->getLinks();
if ($links && ($prev = $links->getPrev())) {
return $api->withUrl($prev)->get(null, null, $cache);
/**
* Perform a GET request against the link's "next" relation
*
- * @return Promise
+ * @return mixed promise
*/
-function next(API $api, Cache\Service $cache = null) : Promise {
+function next(API $api, Cache\Service $cache = null) {
$links = $api->getLinks();
if ($links && ($next = $links->getNext())) {
return $api->withUrl($next)->get(null, null, $cache);
/**
* Perform a GET request against the link's "last" relation
*
- * @return Promise
+ * @return mixed promise
*/
-function last(API $api, Cache\Service $cache = null) : Promise {
+function last(API $api, Cache\Service $cache = null) {
$links = $api->getLinks();
if ($links && ($last = $links->getLast())) {
return $api->withUrl($last)->get(null, null, $cache);
<?php
-use AsyncInterop\Promise;
use seekat\API;
class APITest extends BaseTest
* @dataProvider provideAPI
*/
function testReturnsPromiseOnMethodCall($api) {
- $this->assertInstanceOf(Promise::class, $api->baz());
+ $this->assertTrue($api->getFuture()->isPromise($api->baz()));
}
/**
* @dataProvider provideHttpMethodAndAPI
*/
function testReturnsPromiseOnMethodCallForStandardHttpMethod($api, $method) {
- $this->assertInstanceOf(Promise::class, $api->$method());
+ $this->assertTrue($api->getFuture()->isPromise($api->$method()));
}
/**
trait ConsumePromise
{
- function consumePromise(\AsyncInterop\Promise $p, &$errors, &$results) {
- $p->when(function($error, $result) use(&$errors, &$results) {
- if ($error) $errors[] = $error;
- if ($result) $results[] = $result;
- });
+ function consumePromise($p, &$errors, &$results) {
+ if (method_exists($p, "done")) {
+ $p->then(function($result) use(&$results) {
+ if (isset($result)) {
+ $results[] = $result;
+ }
+ }, function($error) use (&$errors) {
+ if (isset($error)) {
+ $errors[] = $error;
+ }
+ });
+ } else {
+ $p->onResolve(function($error, $result) use(&$errors, &$results) {
+ if (isset($error)) {
+ $errors[] = $error;
+ }
+ if (isset($result)) {
+ $results[] = $result;
+ }
+ });
+ }
}
}
trait AssertCancelled
{
- function assertCancelled(\AsyncInterop\Promise $promise) {
+ function assertCancelled($promise) {
$this->consumePromise($promise, $errors, $results);
$this->assertEmpty($results);
- $this->assertStringMatchesFormat("%SCancelled%S", $errors[0]->getMessage());
+ $this->assertStringMatchesFormat("%SCancelled%S", \seekat\Exception\message($errors[0]));
}
}