Skip to content

Commit

Permalink
Adding Stack toTop and toBottom methods
Browse files Browse the repository at this point in the history
- Fixed several bugs in the Stack
- Added new helper methods
- Added documentation

Signed-off-by: RJ Garcia <rj@bighead.net>
  • Loading branch information
ragboyjr committed Mar 31, 2017
1 parent 84caf1f commit 3a85351
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 14 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

### Added

- `toTop`, `toBottom`, `get`, `has` methods
- Additional documentation

### Fixed

- Bug in Stack where unshifting would mess up the stored index values for named entries.
- Bug where removed named entries wouldn't fully be deleted.

## [0.5.0] - 2017-03-11
### Added

Expand Down
8 changes: 8 additions & 0 deletions doc/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,14 @@ array pop($sort = 0)
Pops the stack at the priority given be taking an element from the back/top of the stack. The popped stack entry is returned as a tuple.
array remove($name)
Removes a named middleware. The removed middleware is returned as a tuple.
bool has($name)
Returns true if a named middleware exists in the stack
array get($name)
Returns a 3-tuple of an entry like so: ``[$entry, $sort, $name]``. This will throw an exception if no entry is found.
Stack toTop($name)
Moves the given entry to the top of its stack to be executed first.
Stack toBottom($name)
Moves the given entry to the bottom of its stack to be executed last.
array toArray()
Normalizes the stack into an array of middleware that can be used with ``mw\compose``
mixed __invoke(...$params)
Expand Down
43 changes: 43 additions & 0 deletions doc/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,46 @@ The library also comes with a Stack that allows you to easily build a set of mid
$handler = mw\compose([$stack]);
$res = $handler('a');
assert($res == 'abxcy');
Priority Stacks
---------------

You can also manage priority by determining the stack index when you push an entry. the default stack index is 0.

.. code-block:: php
<?php
use Krak\Mw;
$stack = mw\stack()
->push(mw2(), 1)
->push(mw1(), 1)
->push(mw3())
->push(mw4, -1);
In the given stack, the flow of execution is ``mw1 -> mw2 -> mw3 -> mw4`` because ``mw1`` and ``mw2`` were pushed at a higher stack index than the other entries.

Moving Entries
--------------

You can change the position of an entry by calling the ``toTop`` or ``toBottom`` methods of the stack. These will move the named entry to either the top or bottom of their stacks respectively.

.. code-block:: php
<?php
use Krak\Mw;
$stack = mw\stack()->push(function($s, $next) {
return $next($s . 'a');
}, 0, 'append-a')
->push(function($s, $next) {
return $next($s . 'b');
});
// the append-a entry is now at the top of the stack
$stack->toTop('append-a');
$handler = mw\compose([$stack]);
assert($handler('') == 'ab');
63 changes: 53 additions & 10 deletions src/Stack.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public function push($mw, $sort = 0, $name = null) {
}
array_push($this->entries[$sort], [$mw, $name]);
if ($name) {
$this->names[$name] = [$sort, count($this->entries[$sort]) - 1];
$this->names[$name] = $sort;
}
return $this;
}
Expand All @@ -50,7 +50,7 @@ public function unshift($mw, $sort = 0, $name = null) {
}
array_unshift($this->entries[$sort], [$mw, $name]);
if ($name) {
$this->names[$name] = [$sort, 0];
$this->names[$name] = $sort;
}
return $this;
}
Expand All @@ -64,32 +64,65 @@ public function shift($sort = 0) {
}

public function after($target, $mw, $name = null) {
if (!isset($this->names[$target])) {
if (!$this->has($target)) {
throw new \LogicException("Cannot insert entry after '$target' because it's not in the stack");
}
list($sort, $i) = $this->names[$target];
$sort = $this->names[$target];
return $this->push($mw, $sort, $name);
}
public function before($target, $mw, $name = null) {
if (!isset($this->names[$target])) {
if (!$this->has($target)) {
throw new \LogicException("Cannot insert entry before '$target' because it's not in the stack");
}
list($sort, $i) = $this->names[$target];
$sort = $this->names[$target];
return $this->unshift($mw, $sort, $name);
}

public function on($name, $mw, $sort = 0) {
return $this->push($mw, $sort, $name);
}
public function remove($name) {
if (!isset($this->names[$name])) {
if (!$this->has($name)) {
return $this;
}
list($sort, $i) = $this->names[$name];
$sort = $this->names[$name];
$i = $this->indexOf($name);
array_splice($this->entries[$sort], $i, 1);
unset($this->names[$name]);
return $this;
}

public function get($name) {
if (!$this->has($name)) {
throw new \LogicException("Cannot get entry '$name' because it doesn't exist.");
}
$sort = $this->names[$name];
$i = $this->indexOf($name);
return [$this->entries[$sort][$i][0], $sort, $name];
}

public function has($name) {
return isset($this->names[$name]);
}

public function toTop($name) {
if (!$this->has($name)) {
throw new \LogicException("Cannot move entry '$name' to top because it doesn't exist.");
}

list($entry, $sort, $name) = $this->get($name);
return $this->remove($name)->push($entry, $sort, $name);
}

public function toBottom($name) {
if (!$this->has($name)) {
throw new \LogicException("Cannot move entry '$name' to top because it doesn't exist.");
}

list($entry, $sort, $name) = $this->get($name);
$this->remove($name);
return $this->remove($name)->unshift($entry, $sort, $name);
}

public function toArray() {
ksort($this->entries);
$iter = function($entries) {
Expand Down Expand Up @@ -118,8 +151,18 @@ private function replaceEntry($mw, $name) {
return false;
}

list($sort, $i) = $this->names[$name];
$sort = $this->names[$name];
$i = $this->indexOf($name);
$this->entries[$sort][$i] = [$mw, $name];
return true;
}

private function indexOf($name) {
$sort = $this->names[$name];
foreach ($this->entries[$sort] as $i => list($entry, $entry_name)) {
if ($entry_name === $name) {
return $i;
}
}
}
}
31 changes: 27 additions & 4 deletions test/mw.spec.php
Original file line number Diff line number Diff line change
Expand Up @@ -164,13 +164,36 @@ function() { return 1; },
});
it('replaces an entry if it is pushed with the same name', function() {
$stack = mw\stack();
$stack->push(id())
->push(append('a'))
$stack->push(append('a'))
->push(append('d'), 0, 'mw')
->push(append('c'))
->unshift(append('c'))
->unshift(id())
->push(append('b'), 0, 'mw');
$handler = mw\compose([$stack]);
assert($handler('') == 'cba');
assert($handler('') == 'bac');
});
it('can retrieve a named entry', function() {
$stack = mw\stack();
$stack->on('a', function() {})
->on('b', function() {});
$entry = $stack->get('b');
assert($entry[0] instanceof Closure && $entry[1] === 0 && $entry[2] === 'b');
});
it('can move an entry to the top of its stack', function() {
$stack = mw\stack();
$stack->push(append('a'), 0, 'append_a')
->push(append('b'))
->toTop('append_a');
$handler = mw\compose([id(), $stack]);
assert($handler('') == 'ab');
});
it('can move an entry to the bottom of its stack', function() {
$stack = mw\stack();
$stack->push(append('a'))
->push(append('b'), 0, 'append_b')
->toBottom('append_b');
$handler = mw\compose([id(), $stack]);
assert($handler('') == 'ab');
});
});
describe('#methodInvoke', function() {
Expand Down

0 comments on commit 3a85351

Please sign in to comment.