diff --git a/README.md b/README.md index a90a09a..bc1f07d 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,7 @@ Lets start with an simple version implementation and grow from it. php src/prompt.php ``` - -___With Docker ___ +***With Docker*** ```bash docker run --rm -ti -v $(pwd):/app -w /app composer:latest /app/scripts/run_in_docker.sh @@ -46,6 +45,13 @@ I think the code and git log will give a lot of insight of why/how i did X in th - Normally in large code bases i work using the git-flow approach but for small repos/projects, working alone and in the begin of the project i dont use it to remove the overhead of it, that is why in most of this project i didn't use it. - I have use PHPUnit for some time, but i dont really like the way the testing API is done, that is why i use atoum, which i found more expresive, and easy to understand at first look +# Info + +The SERVICE option has been implemented as a different shell with its own commands the commands so far are + +- Info +- exit + # Extras @@ -78,7 +84,6 @@ Setup a VendingMachine class with the basic funcionality I/O (Simple version to - [ ] Cleaning tests - ### Interfaces #### Terminal UI diff --git a/src/Checkout.php b/src/Checkout.php index 6427e70..88560c9 100644 --- a/src/Checkout.php +++ b/src/Checkout.php @@ -18,7 +18,7 @@ public function __construct(CoinManager $coinManager, Products $products) public function sell(string $productCode, array &$money) { $product = $this->products->find($productCode); - if (!$product->any()) { + if ($product == null || !$product->any()) { throw new \Exception("Product $productCode not available", 11); } diff --git a/src/CoinManager.php b/src/CoinManager.php index 17222c1..535c5e4 100644 --- a/src/CoinManager.php +++ b/src/CoinManager.php @@ -51,4 +51,9 @@ public function add(...$coins) return true; } + + public function getCoins() + { + return $this->coinDrawers; + } } diff --git a/src/ConsoleUI.php b/src/ConsoleUI.php deleted file mode 100644 index 761dd3b..0000000 --- a/src/ConsoleUI.php +++ /dev/null @@ -1,111 +0,0 @@ -status()); - $commands = $this->parse($line); - echo ' -> ' . $this->execute(...$commands) . PHP_EOL . PHP_EOL; - } - } - - public function status():string - { - $insertedAmount = number_format($this->vendingMachine->getInsertedAmount(), 2, '.', ','); - return " $insertedAmount$ >"; - } - - public function parse($line): array - { - $sanitized = str_replace(' ', '', $line); - return explode(',', $sanitized); - } - - public function execute(...$commands):string - { - $result = []; - $invalidCoins = []; - foreach ($commands as $command) { - $command = strtoupper($command); - $spltCommand = explode('-', $command); - - switch ($spltCommand[0]) { - case 'GET': - try { - list($product, $coins) = $this->vendingMachine->sellProduct($spltCommand[1]); - } catch (\Exception $e) { - return $e->getMessage(); - } - return "SOLD $product RETURN: ".implode(', ', $coins); - break; - case 'RETURN': - if ($command == 'RETURN-COIN') { - return "RETURN: ". implode(', ', $this->vendingMachine->returnCoins()); - } - break; - default: - - } - - - - $fltValue = floatval($command); - - - if ($fltValue != 0) { - if ($fltValue < 0) { - // no negative money in coins - $fltValue *= -1; - } - $invalid = $this->vendingMachine->insertCoin($fltValue); - if ($invalid != 0) { - $invalidCoins[] = $invalid; - } - continue; - } - } - - if (count($invalidCoins) > 0) { - $result[] = "RETURN INVALID COINS: ".implode(', ', $invalidCoins); - } - - return implode(', ', $result); - } - - public function setDefaultValues() - { - $this->coinManager = new CoinManager( - new Coins( - new Coin(0.05, 10), - new Coin(0.1, 10), - new Coin(0.25, 10), - new Coin(1, 10) - ) - ); - - $this->products = new Products( - new Product('WATER', 0.65, 10), - new Product('JUICE', 1.00, 10), - new Product('SODA', 1.50, 10) - ); - - $this->vendingMachine = new VendingMachine($this->coinManager, $this->products); - } -} diff --git a/src/VendingMachine.php b/src/VendingMachine.php index 6ab656e..1c9408f 100644 --- a/src/VendingMachine.php +++ b/src/VendingMachine.php @@ -11,6 +11,7 @@ class VendingMachine private $insertedCoins = []; private $products = null; private $checkout; + private $service = false; public function __construct(CoinManager $coinManager, Products $products) @@ -53,4 +54,24 @@ public function sellProduct(string $productCode) : array return [$product, $this->returnCoins()]; } + + public function setService(bool $status) + { + $this->service = $status; + } + + public function inService(): bool + { + return $this->service; + } + + public function getCoinManager() + { + return $this->coinManager; + } + + public function getProducts() + { + return $this->products; + } } diff --git a/src/models/generic/Collection.php b/src/models/generic/Collection.php index dbe7d8d..6d20d4a 100644 --- a/src/models/generic/Collection.php +++ b/src/models/generic/Collection.php @@ -38,6 +38,11 @@ public function getIterator() return new \ArrayIterator($this->items->toArray()); } + public function toArray() + { + return $this->items->toArray(); + } + private function internalSort() { $this->items->sort(function ($a, $b) { diff --git a/src/models/generic/Item.php b/src/models/generic/Item.php index fa0f9d4..7e59d58 100644 --- a/src/models/generic/Item.php +++ b/src/models/generic/Item.php @@ -53,4 +53,9 @@ public function any(): bool { return $this->count() > 0; } + + public function updateCount(int $count) + { + $this->count = $count; + } } diff --git a/src/prompt.php b/src/prompt.php index 29dbb0b..246b9ba 100644 --- a/src/prompt.php +++ b/src/prompt.php @@ -3,7 +3,29 @@ require_once './vendor/autoload.php'; use vending\ui\ConsoleUI; +use vending\models\Coins; +use vending\models\Coin; +use vending\models\Product; -$console = new ConsoleUI(); -$console->setDefaultValues(); +function defaultValues() +{ + $coinManager = new vending\CoinManager( + new Coins( + new Coin(0.05, 10), + new Coin(0.1, 10), + new Coin(0.25, 10), + new Coin(1, 10) + ) + ); + + $products = new vending\models\Products( + new Product('WATER', 0.65, 10), + new Product('JUICE', 1.00, 10), + new Product('SODA', 1.50, 10) + ); + + return new \vending\VendingMachine($coinManager, $products); +} + +$console = new ConsoleUI(defaultValues()); $console->start(); diff --git a/src/ui/BaseShell.php b/src/ui/BaseShell.php new file mode 100644 index 0000000..d76f301 --- /dev/null +++ b/src/ui/BaseShell.php @@ -0,0 +1,91 @@ +consoleOutput = new ConsoleOutput(); + $this->vendingMachine = $vendingMachine; + } + + public function start() + { + $this->consoleOutput->writeln(''); + $this->consoleOutput->writeln($this->header()); + + while ($this->running) { + // $this->consoleOutput->write(); + $line = readline($this->prompt()); + $commands = $this->parse($line); + $this->processCommands(...$commands); + } + } + + public function prompt():string + { + return ">"; + } + + protected function parse($line): array + { + $sanitized = trim(str_replace(' ', ' ', $line)); + $commands = []; + foreach (explode(',', $sanitized) as $part) { + $commands[] = trim(str_replace('-', ' ', $part)); + } + return $commands; + } + + protected function processCommands(...$commands) + { + foreach ($commands as $fullCommand) { + $args = explode(' ', $fullCommand); + $command = array_shift($args); + + if (strtoupper($command) == 'EXIT') { + $this->running = false; + return; + } + + if ($command) { + $command = ucfirst(strtolower($command)); + if (is_numeric($command)) { + $args = [$command]; + $command = 'AddCoin'; + } + $this->execute($command, ...$args); + } + } + } + + protected function execute($command, ...$args) + { + $commandInstance = $this->initializeCommand($command); + if ($commandInstance) { + $result = $commandInstance->execute(...$args); + if (!empty($result)) { + $this->consoleOutput->writeln(' -> ' . $result . PHP_EOL); + } + } + } + + + protected function initializeCommand(string $command) + { + try { + $class = new \ReflectionClass("vending\\ui\\commands\\{$command}Command"); + return $class->newInstanceArgs([$this->vendingMachine, $this->consoleOutput]); + } catch (\ReflectionException $e) { + return false; + } + } +} diff --git a/src/ui/ConsoleUI.php b/src/ui/ConsoleUI.php new file mode 100644 index 0000000..b8afe85 --- /dev/null +++ b/src/ui/ConsoleUI.php @@ -0,0 +1,28 @@ +vendingMachine->getInsertedAmount(), 2, '.', ','); + return " $insertedAmount$ >"; + } + + public function header() + { + $productHeader = array_reduce( + $this->vendingMachine->getProducts()->toArray(), + function ($initial, $product) { + return $initial .= " ".$product->hash()."(".$product->count()."): ".(string)$product->getValue(); + }, + '' + ); + + return ' PRODUCTS: '.$productHeader; + } +} diff --git a/src/ui/commands/AddCoinCommand.php b/src/ui/commands/AddCoinCommand.php new file mode 100644 index 0000000..8b810c1 --- /dev/null +++ b/src/ui/commands/AddCoinCommand.php @@ -0,0 +1,18 @@ + 0 && is_numeric($args[0])) { + $fvalue = $this->vendingMachine->insertCoin((float)$args[0]); + if ($fvalue != 0) { + return 'Invalid Coin to add! RETURN: '.$fvalue; + } + } + #we should throw error + return ""; + } +} diff --git a/src/ui/commands/BaseCommand.php b/src/ui/commands/BaseCommand.php new file mode 100644 index 0000000..1f999a7 --- /dev/null +++ b/src/ui/commands/BaseCommand.php @@ -0,0 +1,22 @@ +vendingMachine = $vendingMachine; + $this->consoleOutput = $consoleOutput; + } + + public function execute(...$args):string + { + } +} diff --git a/src/ui/commands/CommandInteface.php b/src/ui/commands/CommandInteface.php new file mode 100644 index 0000000..f0844d8 --- /dev/null +++ b/src/ui/commands/CommandInteface.php @@ -0,0 +1,12 @@ + 0) { + try { + list($product, $coins) = $this->vendingMachine->sellProduct($args[0]); + } catch (\Exception $e) { + return $e->getMessage(); + } + return "SOLD $product RETURN: ".implode(', ', $coins); + } + return ''; + } +} diff --git a/src/ui/commands/ReturnCommand.php b/src/ui/commands/ReturnCommand.php new file mode 100644 index 0000000..46e2b5c --- /dev/null +++ b/src/ui/commands/ReturnCommand.php @@ -0,0 +1,14 @@ + 0 &&strtoupper($args[0]) == 'COIN') { + return "RETURN: ". implode(', ', $this->vendingMachine->returnCoins()); + } + return 'Can only return coins'; + } +} diff --git a/src/ui/commands/ServiceCommand.php b/src/ui/commands/ServiceCommand.php new file mode 100644 index 0000000..08c7f23 --- /dev/null +++ b/src/ui/commands/ServiceCommand.php @@ -0,0 +1,16 @@ +vendingMachine); + $console->start(); + + return ''; + } +} diff --git a/src/ui/output/ConsoleOutput.php b/src/ui/output/ConsoleOutput.php new file mode 100644 index 0000000..90d85f0 --- /dev/null +++ b/src/ui/output/ConsoleOutput.php @@ -0,0 +1,16 @@ +write($message . PHP_EOL); + } +} diff --git a/src/ui/output/OutputInterface.php b/src/ui/output/OutputInterface.php new file mode 100644 index 0000000..cd245a0 --- /dev/null +++ b/src/ui/output/OutputInterface.php @@ -0,0 +1,9 @@ +"; + } + + public function header() + { + return ''; + } + + protected function initializeCommand(string $command) + { + try { + $class = new \ReflectionClass("vending\\ui\\service\\commands\\{$command}Command"); + return $class->newInstanceArgs([$this->vendingMachine, $this->consoleOutput]); + } catch (\ReflectionException $e) { + return false; + } + } +} diff --git a/src/ui/service/commands/InfoCommand.php b/src/ui/service/commands/InfoCommand.php new file mode 100644 index 0000000..9f77c25 --- /dev/null +++ b/src/ui/service/commands/InfoCommand.php @@ -0,0 +1,41 @@ +getProductsInfo(); + $this->getCoinsInfo(); + return ""; + } + + public function getProductsInfo() + { + $productHeader = array_reduce( + $this->vendingMachine->getProducts()->toArray(), + function ($initial, $product) { + return $initial .= str_pad(" ".$product->hash().": ".$product->count()."\t", 15, ' ', STR_PAD_LEFT); + }, + '' + ); + + $this->consoleOutput->writeln(" PRODUCTS: $productHeader"); + } + + public function getCoinsInfo() + { + $coinsHeader = array_reduce( + $this->vendingMachine->getCoinManager()->getCoins()->toArray(), + function ($initial, $coin) { + return $initial .= str_pad(" ".(string)$coin->getValue().": ".$coin->count()."\t", 15, ' ', STR_PAD_LEFT); + }, + '' + ); + + $this->consoleOutput->writeln(" COINS: $coinsHeader"); + } +} diff --git a/src/ui/service/commands/UpdateCommand.php b/src/ui/service/commands/UpdateCommand.php new file mode 100644 index 0000000..b2adf76 --- /dev/null +++ b/src/ui/service/commands/UpdateCommand.php @@ -0,0 +1,45 @@ +updateCoin($args[1], (int)$args[2]); + break; + + case 'PRODUCT': + $this->updateProduct($args[1], (int)$args[2]); + break; + } + + return ""; + } + + private function updateCoin($coin, int $amount) + { + $coinManager = $this->vendingMachine->getCoinManager(); + $coin = $coinManager->getCoin((float)$coin); + if ($coin) { + $coin->updateCount($amount); + } + } + + private function updateProduct($product, int $amount) + { + $product = $this->vendingMachine->getProducts()->find($product); + + if ($product) { + $product->updateCount($amount); + } + } +}