Skip to content

Commit

Permalink
introducing watch
Browse files Browse the repository at this point in the history
  • Loading branch information
henzeb committed Feb 25, 2024
1 parent ca6872a commit 069b3a1
Show file tree
Hide file tree
Showing 7 changed files with 403 additions and 0 deletions.
44 changes: 44 additions & 0 deletions playground/watch.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

use function Laravel\Prompts\info;
use function Laravel\Prompts\note;
use function Laravel\Prompts\progress;
use function Laravel\Prompts\table;
use function Laravel\Prompts\text;
use function Laravel\Prompts\watch;

require __DIR__ . '/../vendor/autoload.php';

watch(
function () {
static $iteration = 0;
static $items = [];

if (count($items) === 5) {
array_shift($items);
}

$items[] = [$iteration += 1, (new Datetime())->format(DateTime::RFC850)];

if (count($items) === 5) {
info(sprintf('Now the table just scrolls, %s and counting...', $iteration));
} else {
info('Filling up the table...');
}

progress('a nice progressbar', 5)->advance($iteration % 5);

$ralph = text('Ralph', default: 'Ralph Wiggum: I\'m ignored!');

note($ralph);

table(
[
'Iteration',
'DateTime'
],
$items
);
},
1,
);
9 changes: 9 additions & 0 deletions src/Concerns/FakesInputOutput.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Laravel\Prompts\Output\BufferedConsoleOutput;
use Laravel\Prompts\Terminal;
use Mockery\MockInterface;
use PHPUnit\Framework\Assert;
use RuntimeException;

Expand Down Expand Up @@ -37,6 +38,14 @@ public static function fake(array $keys = []): void
self::setOutput(new BufferedConsoleOutput());
}

/**
* Tells if Prompt is currently being faked.
*/
protected static function isFaked(): bool
{
return static::terminal() instanceof MockInterface;
}

/**
* Assert that the output contains the given string.
*/
Expand Down
3 changes: 3 additions & 0 deletions src/Concerns/Themes.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
use Laravel\Prompts\Themes\Default\SuggestPromptRenderer;
use Laravel\Prompts\Themes\Default\TableRenderer;
use Laravel\Prompts\Themes\Default\TextPromptRenderer;
use Laravel\Prompts\Themes\Default\WatchRenderer;
use Laravel\Prompts\Watch;

trait Themes
{
Expand Down Expand Up @@ -57,6 +59,7 @@ trait Themes
Note::class => NoteRenderer::class,
Table::class => TableRenderer::class,
Progress::class => ProgressRenderer::class,
Watch::class => WatchRenderer::class,
],
];

Expand Down
27 changes: 27 additions & 0 deletions src/Themes/Default/WatchRenderer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace Laravel\Prompts\Themes\Default;

use Laravel\Prompts\Output\BufferedConsoleOutput;
use Laravel\Prompts\Watch;
use Symfony\Component\Console\Output\OutputInterface;

class WatchRenderer extends Renderer
{
/**
* buffers the output from a Buffered Prompt and flushes the output to Prompts
* in order to utilize Prompts neat way of updating lines
*/
public function __invoke(Watch $watch, OutputInterface $originalOutput): string
{
$bufferedOutput = new BufferedConsoleOutput();

$watch::setOutput($bufferedOutput);

($watch->watch)();

$watch::setOutput($originalOutput);

return $bufferedOutput->fetch();
}
}
153 changes: 153 additions & 0 deletions src/Watch.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
<?php

namespace Laravel\Prompts;

use Closure;
use Mockery\MockInterface;
use PHPUnit\Framework\Assert;
use RuntimeException;
use ValueError;

class Watch extends Prompt
{
/**
* How many times to fake an iteration.
*/
protected static int $fakeTimes = 1;

/**
* count of faked iterations.
*/
protected int $fakedTimes = 0;

/**
* Faking sleep or not.
*/
protected static bool $fakeSleep = true;

/**
* The amount of seconds slept during intervals in total.
*/
protected static int $sleptSeconds = 0;

/**
* The closure to execute on interval.
*/
public Closure $watch;

/**
* The interval between updates.
*/
protected int $interval;

/**
* Create a new Watch instance.
*/
public function __construct(callable $watch, ?int $interval = 2)
{
static::$sleptSeconds = 0;
$this->watch = $watch(...);
$this->interval = $interval ?? 2;

if ($this->interval < 0) {
throw new ValueError('watch interval must be greater than or equal to 0');
}
}

public function display(): void
{
$this->prompt();
}

/**
* displays the watched output and updates after the specified interval.
*/
public function render(): void
{
$faked = static::isFaked();

static::interactive(false);

while (!$faked || $this->fakedTimes < static::$fakeTimes) {

parent::render();

if ($faked) {
$this->fakedTimes++;

if ($this->fakedTimes >= static::$fakeTimes) {

if (static::$terminal instanceof MockInterface) {
$this->state = 'submit';
static::$terminal->expects('read')->zeroOrMoreTimes()->andReturn(false);
}
static::$fakeSleep = true;
break;
}

if (static::$fakeSleep) {
static::$sleptSeconds += $this->interval;
continue;
}
}

sleep($this->interval);
}
}

/**
* Render the prompt using the active theme.
* Overrides default behaviour to pass along the current output.
*/
protected function renderTheme(): string
{
$renderer = static::getRenderer();

return $renderer($this, static::output());
}

/**
* Get the value of the prompt.
*/
public function value(): bool
{
return true;
}

/**
* Tell Prompt how many iterations to fake.
*/
public static function fakeTimes(int $times): void
{
if (!static::isFaked()) {
throw new RuntimeException('Prompt must be faked before faking iterations.');
}

static::$fakeTimes = $times;
}

/**
* Asserts the amount of seconds slept during intervals in total.
*/
public static function assertSecondsSleptBetweenIntervals(int $seconds): void
{
if (!static::isFaked()) {
throw new RuntimeException('Prompt must be faked before asserting.');
}

Assert::assertEquals($seconds, static::$sleptSeconds);
}

/**
* By default, when Prompt is faked, the intervals are faked.
* Use this to actually sleep between updates.
*/
public static function shouldNotFakeIntervalSleep(): void
{
if (!self::isFaked()) {
throw new RuntimeException('Not faking sleep makes no sense when not faking Prompt.');
}

static::$fakeSleep = false;
}
}
10 changes: 10 additions & 0 deletions src/helpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -192,3 +192,13 @@ function progress(string $label, iterable|int $steps, ?Closure $callback = null,

return $progress;
}

/**
* Continuously updates output on each interval.
*
* @param callable(): void $watch
*/
function watch(callable $watch, ?int $interval = 2): void
{
(new Watch($watch, $interval))->display();
}
Loading

0 comments on commit 069b3a1

Please sign in to comment.