Initial checkin
This commit is contained in:
		
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
/vendor
 | 
			
		||||
/composer.lock
 | 
			
		||||
							
								
								
									
										10
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
# NoccyLabs Juicer
 | 
			
		||||
 | 
			
		||||
This is a library to help write juice-mixing stuff in PHP.
 | 
			
		||||
 | 
			
		||||
## Features
 | 
			
		||||
 | 
			
		||||
* Mix by weight or by volume.
 | 
			
		||||
* Calculate weights for mixing based on the VG/PG ratio or a specific values.
 | 
			
		||||
* Calculate weights for nicotine based on the nicotine strength and base.
 | 
			
		||||
* Interfaces allow for custom entity-backed implementations.
 | 
			
		||||
							
								
								
									
										17
									
								
								composer.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								composer.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
{
 | 
			
		||||
    "name": "noccylabs/juicer",
 | 
			
		||||
    "description": "E-liquid mixing library",
 | 
			
		||||
    "type": "library",
 | 
			
		||||
    "license": "GPL-3.0",
 | 
			
		||||
    "authors": [
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Christopher Vagnetoft",
 | 
			
		||||
            "email": "cvagnetoft@gmail.com"
 | 
			
		||||
        }
 | 
			
		||||
    ],
 | 
			
		||||
    "autoload": {
 | 
			
		||||
        "psr-4": {
 | 
			
		||||
            "NoccyLabs\\Juicer\\": "src/"
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										21
									
								
								phpunit.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								phpunit.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 | 
			
		||||
         xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/7.5/phpunit.xsd"
 | 
			
		||||
         bootstrap="vendor/autoload.php"
 | 
			
		||||
         forceCoversAnnotation="true"
 | 
			
		||||
         beStrictAboutCoversAnnotation="true"
 | 
			
		||||
         beStrictAboutOutputDuringTests="true"
 | 
			
		||||
         beStrictAboutTodoAnnotatedTests="true"
 | 
			
		||||
         verbose="true">
 | 
			
		||||
    <testsuites>
 | 
			
		||||
        <testsuite name="default">
 | 
			
		||||
            <directory suffix="Test.php">tests</directory>
 | 
			
		||||
        </testsuite>
 | 
			
		||||
    </testsuites>
 | 
			
		||||
 | 
			
		||||
    <filter>
 | 
			
		||||
        <whitelist processUncoveredFilesFromWhitelist="true">
 | 
			
		||||
            <directory suffix=".php">src</directory>
 | 
			
		||||
        </whitelist>
 | 
			
		||||
    </filter>
 | 
			
		||||
</phpunit>
 | 
			
		||||
							
								
								
									
										73
									
								
								src/Ingredient/Base.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								src/Ingredient/Base.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,73 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace NoccyLabs\Juicer\Ingredient;
 | 
			
		||||
 | 
			
		||||
class Base
 | 
			
		||||
{
 | 
			
		||||
    const MASS_PG = 1.038;
 | 
			
		||||
    const MASS_VG = 1.26;
 | 
			
		||||
 | 
			
		||||
    protected static $MASS = [
 | 
			
		||||
        'PG' => self::MASS_PG,
 | 
			
		||||
        'VG' => self::MASS_VG,
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    protected $base;
 | 
			
		||||
 | 
			
		||||
    protected $components = [];
 | 
			
		||||
 | 
			
		||||
    protected $specificGravity;
 | 
			
		||||
 | 
			
		||||
    public function __construct(string $base)
 | 
			
		||||
    {
 | 
			
		||||
        $this->components = self::parseComponents($base);
 | 
			
		||||
        $this->base = $base;
 | 
			
		||||
        $this->specificGravity = self::calculateSpecificGravity($this->components);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getSpecificGravity(): float
 | 
			
		||||
    {
 | 
			
		||||
        return $this->specificGravity;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getComponents(): array
 | 
			
		||||
    {
 | 
			
		||||
        return $this->components;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function parseComponents(string $base)
 | 
			
		||||
    {
 | 
			
		||||
        $found = [];
 | 
			
		||||
        if (!preg_match('/^([A-Z]+)([0-9]{1,3}+)$/i', $base, $match)) {
 | 
			
		||||
            throw new \Exception();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ($match[1] == "PG") {
 | 
			
		||||
            return [
 | 
			
		||||
                'PG' => $match[2],
 | 
			
		||||
                'VG' => 100 - $match[2]
 | 
			
		||||
            ];
 | 
			
		||||
        } elseif ($match[1] == "VG") {
 | 
			
		||||
            return [
 | 
			
		||||
                'PG' => 100 - $match[2],
 | 
			
		||||
                'VG' => $match[2]
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static function calculateSpecificGravity(array $components): float
 | 
			
		||||
    {
 | 
			
		||||
        $specificGravity = 0.0;
 | 
			
		||||
        foreach ($components as $component=>$percent) {
 | 
			
		||||
            $percentFloat = $percent / 100;
 | 
			
		||||
            $specificGravity += (self::$MASS[$component] * $percentFloat);
 | 
			
		||||
        }
 | 
			
		||||
        return $specificGravity;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function __toString()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->base;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										55
									
								
								src/Ingredient/Ingredient.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/Ingredient/Ingredient.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace NoccyLabs\Juicer\Ingredient;
 | 
			
		||||
 | 
			
		||||
class Ingredient implements IngredientInterface
 | 
			
		||||
{
 | 
			
		||||
    protected $name;
 | 
			
		||||
 | 
			
		||||
    protected $brand;
 | 
			
		||||
 | 
			
		||||
    protected $percent;
 | 
			
		||||
 | 
			
		||||
    protected $base;
 | 
			
		||||
 | 
			
		||||
    public function __construct(string $name, ?string $brand=null, float $percent=0.0, string $base="PG100")
 | 
			
		||||
    {
 | 
			
		||||
        $this->name = $name;
 | 
			
		||||
        $this->brand = $brand;
 | 
			
		||||
        $this->percent = $percent;
 | 
			
		||||
        $this->base = $base;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     */
 | 
			
		||||
    public function getFlavorName(): string
 | 
			
		||||
    {
 | 
			
		||||
        return $this->name;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     */
 | 
			
		||||
    public function getFlavorBrand(): ?string
 | 
			
		||||
    {
 | 
			
		||||
        return $this->brand;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     */
 | 
			
		||||
    public function getPercent(): float
 | 
			
		||||
    {
 | 
			
		||||
        return $this->percent;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     */
 | 
			
		||||
    public function getBase(): ?string
 | 
			
		||||
    {
 | 
			
		||||
        return $this->base;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										37
									
								
								src/Ingredient/IngredientInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/Ingredient/IngredientInterface.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace NoccyLabs\Juicer\Ingredient;
 | 
			
		||||
 | 
			
		||||
interface IngredientInterface
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Return the name of the flavor
 | 
			
		||||
     * 
 | 
			
		||||
     * @return string The name of the flavor
 | 
			
		||||
     */
 | 
			
		||||
    public function getFlavorName(): string;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Return the short name of the flavor brand. This should be the upper-case
 | 
			
		||||
     * abbreviation, and not the complete name.
 | 
			
		||||
     * 
 | 
			
		||||
     * @return string|null The upper-case abbreviation of the flavor brand
 | 
			
		||||
     */
 | 
			
		||||
    public function getFlavorBrand(): ?string;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Return the percent (0-100)
 | 
			
		||||
     * 
 | 
			
		||||
     * @return float The percent of the flavor to use
 | 
			
		||||
     */
 | 
			
		||||
    public function getPercent(): float;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Return the base mix used for this flavoring (usually PG100)
 | 
			
		||||
     * 
 | 
			
		||||
     * @return string|null The base mix used
 | 
			
		||||
     */
 | 
			
		||||
    public function getBase(): ?string;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										24
									
								
								src/Ingredient/NicotineBase.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/Ingredient/NicotineBase.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace NoccyLabs\Juicer\Ingredient;
 | 
			
		||||
 | 
			
		||||
class NicotineBase
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    protected $base;
 | 
			
		||||
 | 
			
		||||
    protected $strength;
 | 
			
		||||
 | 
			
		||||
    public function __construct(Base $base, int $strength)
 | 
			
		||||
    {
 | 
			
		||||
        $this->base = $base;
 | 
			
		||||
        $this->nicotineStrength = $strength;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getSpecificGravity(): float
 | 
			
		||||
    {
 | 
			
		||||
        return 0.0; 
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										49
									
								
								src/Recipe/Exporter/JsonExporter.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/Recipe/Exporter/JsonExporter.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,49 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace NoccyLabs\Juicer\Recipe\Exporter;
 | 
			
		||||
 | 
			
		||||
use NoccyLabs\Juicer\Recipe\RecipeInterface;
 | 
			
		||||
use NoccyLabs\Juicer\Ingredient\IngredientInterface;
 | 
			
		||||
 | 
			
		||||
class JsonExporter
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public function export(RecipeInterface $recipe)
 | 
			
		||||
    {
 | 
			
		||||
        $ingredients = [];
 | 
			
		||||
        /** @var IngredientInterface $ingredient */
 | 
			
		||||
        foreach ($recipe->getIngredients() as $ingredient) {
 | 
			
		||||
            $ingredients[] = [
 | 
			
		||||
                'brand' => $ingredient->getFlavorBrand(),
 | 
			
		||||
                'flavor' => $ingredient->getFlavorName(),
 | 
			
		||||
                'percent' => $ingredient->getPercent()
 | 
			
		||||
            ];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $document = [
 | 
			
		||||
            'recipe' => $recipe->getRecipeName(),
 | 
			
		||||
            'author' => $recipe->getRecipeAuthor(),
 | 
			
		||||
            'tags' => $recipe->getTags(),
 | 
			
		||||
            'description' => $recipe->getDescription(),
 | 
			
		||||
            'extra' => $recipe->getExtra(),
 | 
			
		||||
            'ingredients' => $ingredients
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        return json_encode($document,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function writeToFile(RecipeInterface $recipe, string $filename)
 | 
			
		||||
    {
 | 
			
		||||
        $json = $this->export($recipe);
 | 
			
		||||
        
 | 
			
		||||
        $fd = fopen($filename, "w");
 | 
			
		||||
        if (!$fd) {
 | 
			
		||||
            throw new \InvalidArgumentException();
 | 
			
		||||
        }
 | 
			
		||||
        fwrite($fd, $json, strlen($json));
 | 
			
		||||
        fclose($fd);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										44
									
								
								src/Recipe/Importer/JsonImporter.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/Recipe/Importer/JsonImporter.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace NoccyLabs\Juicer\Recipe\Importer;
 | 
			
		||||
 | 
			
		||||
use NoccyLabs\Juicer\Recipe\RecipeInterface;
 | 
			
		||||
use NoccyLabs\Juicer\Ingredient\IngredientInterface;
 | 
			
		||||
use NoccyLabs\Juicer\Recipe\Recipe;
 | 
			
		||||
use NoccyLabs\Juicer\Ingredient\Ingredient;
 | 
			
		||||
 | 
			
		||||
class JsonImporter
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    public function import(string $json): RecipeInterface
 | 
			
		||||
    {
 | 
			
		||||
        $data = json_decode($json);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        $recipe = new Recipe();
 | 
			
		||||
        $recipe->setRecipeName(@$data->recipe);
 | 
			
		||||
        $recipe->setRecipeAuthor(@$data->author);
 | 
			
		||||
        $recipe->setDescription(@$data->description);
 | 
			
		||||
        $recipe->setExtra((array)@$data->extra);
 | 
			
		||||
 | 
			
		||||
        foreach ((array)@$data->ingredients as $ingredientData) {
 | 
			
		||||
            $ingredient = new Ingredient($ingredientData->flavor, $ingredientData->brand, $ingredientData->percent);
 | 
			
		||||
            $recipe->addIngredient($ingredient);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $recipe;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function readFromFile(string $filename): RecipeInterface
 | 
			
		||||
    {
 | 
			
		||||
        $fd = fopen($filename, "r");
 | 
			
		||||
        if (!$fd) {
 | 
			
		||||
            throw new \InvalidArgumentException();
 | 
			
		||||
        }
 | 
			
		||||
        $json = fread($fd, filesize($filename));
 | 
			
		||||
        fclose($fd);
 | 
			
		||||
 | 
			
		||||
        return $this->import($json);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										70
									
								
								src/Recipe/Mixer/MeasuredIngredient.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								src/Recipe/Mixer/MeasuredIngredient.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,70 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace NoccyLabs\Juicer\Recipe\Mixer;
 | 
			
		||||
 | 
			
		||||
use NoccyLabs\Juicer\Ingredient\IngredientInterface;
 | 
			
		||||
use NoccyLabs\Juicer\Ingredient\Base;
 | 
			
		||||
 | 
			
		||||
class MeasuredIngredient implements IngredientInterface
 | 
			
		||||
{
 | 
			
		||||
    /** @var string */
 | 
			
		||||
    protected $name;
 | 
			
		||||
    /** @var string|null */
 | 
			
		||||
    protected $brand;
 | 
			
		||||
    /** @var string */
 | 
			
		||||
    protected $base;
 | 
			
		||||
    /** @var float The apparent specific gravity (ASG) of this ingredient in g/mL */
 | 
			
		||||
    protected $asg;
 | 
			
		||||
    /** @var float The percent of this ingredient in the final mix */
 | 
			
		||||
    protected $pecent;
 | 
			
		||||
    /** @var float Volume in milliliters (mL) */
 | 
			
		||||
    protected $volume;
 | 
			
		||||
    /** @var float Weight in grams (g) */
 | 
			
		||||
    protected $weight;
 | 
			
		||||
 | 
			
		||||
    public function __construct(string $name, ?string $brand, string $base, float $asg, float $percent, float $volume)
 | 
			
		||||
    {
 | 
			
		||||
        $this->name = $name;
 | 
			
		||||
        $this->brand = $brand;
 | 
			
		||||
        $this->base = $base;
 | 
			
		||||
        $this->asg = $asg;
 | 
			
		||||
        $this->percent = $percent;
 | 
			
		||||
        $this->volume = $volume;
 | 
			
		||||
        $this->weight = $volume * $asg;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getFlavorName(): string
 | 
			
		||||
    {
 | 
			
		||||
        return $this->name;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getFlavorBrand(): ?string
 | 
			
		||||
    {
 | 
			
		||||
        return $this->brand;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getBase(): ?string
 | 
			
		||||
    {
 | 
			
		||||
        return $this->base;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getPercent(): float
 | 
			
		||||
    {
 | 
			
		||||
        return $this->percent;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getSpecificGravity(): float
 | 
			
		||||
    {
 | 
			
		||||
        return $this->asg;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getVolume(): float
 | 
			
		||||
    {
 | 
			
		||||
        return $this->volume;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function getWeight(): float
 | 
			
		||||
    {
 | 
			
		||||
        return $this->weight;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										31
									
								
								src/Recipe/Mixer/Mixer.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/Recipe/Mixer/Mixer.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace NoccyLabs\Juicer\Recipe\Mixer;
 | 
			
		||||
 | 
			
		||||
use NoccyLabs\Juicer\Ingredient\IngredientInterface;
 | 
			
		||||
use NoccyLabs\Juicer\Recipe\RecipeInterface;
 | 
			
		||||
use NoccyLabs\Juicer\Ingredient\Base;
 | 
			
		||||
use NoccyLabs\Juicer\Ingredient\NicotineBase;
 | 
			
		||||
 | 
			
		||||
class Mixer
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    public function mixRecipe(RecipeInterface $recipe, int $volume, Base $base, int $nicotineStrength, ?NicotineBase $nicotineBase=null)
 | 
			
		||||
    {
 | 
			
		||||
        $mixed = [];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        $components = $base->getComponents();
 | 
			
		||||
        
 | 
			
		||||
        if (array_key_exists('VG', $components) && $components['VG'] > 0) {
 | 
			
		||||
            $mixed[] = new MeasuredIngredient("VG", null, "VG100", Base::MASS_VG, 100, $volume);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (array_key_exists('PG', $components) && $components['PG'] > 0) {
 | 
			
		||||
            $mixed[] = new MeasuredIngredient("PG", null, "PG100", Base::MASS_PG, 100, $volume);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return $mixed;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										110
									
								
								src/Recipe/Recipe.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								src/Recipe/Recipe.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,110 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace NoccyLabs\Juicer\Recipe;
 | 
			
		||||
 | 
			
		||||
use NoccyLabs\Juicer\Ingredient\IngredientInterface;
 | 
			
		||||
 | 
			
		||||
class Recipe implements RecipeInterface
 | 
			
		||||
{
 | 
			
		||||
    /** @var string */
 | 
			
		||||
    protected $name;
 | 
			
		||||
 | 
			
		||||
    /** @var string */
 | 
			
		||||
    protected $author;
 | 
			
		||||
 | 
			
		||||
    /** @var string[] */
 | 
			
		||||
    protected $tags = [];
 | 
			
		||||
 | 
			
		||||
    /** @var array */
 | 
			
		||||
    protected $extra = [];
 | 
			
		||||
 | 
			
		||||
    /** @var string */
 | 
			
		||||
    protected $description;
 | 
			
		||||
 | 
			
		||||
    /** @var IngredientInterface[] */
 | 
			
		||||
    protected $ingredients = [];
 | 
			
		||||
 | 
			
		||||
    public function setRecipeName(?string $name)
 | 
			
		||||
    {
 | 
			
		||||
        $this->name = $name;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setRecipeAuthor(?string $author)
 | 
			
		||||
    {
 | 
			
		||||
        $this->author = $author;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setTags(array $tags)
 | 
			
		||||
    {
 | 
			
		||||
        $this->tags = $tags;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function addTag(string $tag)
 | 
			
		||||
    {
 | 
			
		||||
        $this->tags[] = $tag;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setDescription(?string $description)
 | 
			
		||||
    {
 | 
			
		||||
        $this->description = $description;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function addIngredient(IngredientInterface $ingredient)
 | 
			
		||||
    {
 | 
			
		||||
        $this->ingredients[] = $ingredient;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function setExtra(array $extra)
 | 
			
		||||
    {
 | 
			
		||||
        $this->extra = $extra;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     */
 | 
			
		||||
    public function getRecipeName(): ?string
 | 
			
		||||
    {
 | 
			
		||||
        return $this->name;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     */
 | 
			
		||||
    public function getRecipeAuthor(): ?string
 | 
			
		||||
    {
 | 
			
		||||
        return $this->author;    
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     */
 | 
			
		||||
    public function getTags(): array
 | 
			
		||||
    {
 | 
			
		||||
        return $this->tags;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     */
 | 
			
		||||
    public function getExtra(): array
 | 
			
		||||
    {
 | 
			
		||||
        return $this->extra;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     */
 | 
			
		||||
    public function getDescription(): ?string
 | 
			
		||||
    {
 | 
			
		||||
        return $this->description;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * {@inheritDoc}
 | 
			
		||||
     */
 | 
			
		||||
    public function getIngredients(): array
 | 
			
		||||
    {
 | 
			
		||||
        return $this->ingredients;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										27
									
								
								src/Recipe/RecipeInterface.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/Recipe/RecipeInterface.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace NoccyLabs\Juicer\Recipe;
 | 
			
		||||
 | 
			
		||||
use NoccyLabs\Juicer\Ingredient\IngredientInterface;
 | 
			
		||||
 | 
			
		||||
interface RecipeInterface
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    public function getRecipeName(): ?string;
 | 
			
		||||
 | 
			
		||||
    public function getRecipeAuthor(): ?string;
 | 
			
		||||
 | 
			
		||||
    public function getTags(): array;
 | 
			
		||||
 | 
			
		||||
    public function getDescription(): ?string;
 | 
			
		||||
 | 
			
		||||
    public function getExtra(): array;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get an array containing the ingredients of the recipe.
 | 
			
		||||
     * 
 | 
			
		||||
     * @return IngredientInterface[] The ingredients
 | 
			
		||||
     */
 | 
			
		||||
    public function getIngredients(): array;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										28
									
								
								tests/Ingredient/BaseTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								tests/Ingredient/BaseTest.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace NoccyLabs\Juicer\Ingredient;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BaseTest extends \PhpUnit\Framework\TestCase
 | 
			
		||||
{
 | 
			
		||||
    public function getBaseAsgData()
 | 
			
		||||
    {
 | 
			
		||||
        return [
 | 
			
		||||
            [ 'PG100', Base::MASS_PG ],
 | 
			
		||||
            [ 'VG100', Base::MASS_VG ],
 | 
			
		||||
            // NOTE: The internet claims we should get (for 50/50;) 1.1425g/mL
 | 
			
		||||
            // but calculating a 50/50 by volume base gives 1.149g/mL.
 | 
			
		||||
            [ 'VG50', 1.149 ]
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @dataProvider getBaseAsgData
 | 
			
		||||
     */
 | 
			
		||||
    public function testThatTheBaseHasTheProperApparentSpecificGravity($base, $expected)
 | 
			
		||||
    {
 | 
			
		||||
        $base = new Base($base);
 | 
			
		||||
        $this->assertEquals($expected, $base->getSpecificGravity());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										65
									
								
								tests/Recipe/Exporter/JsonExporterTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								tests/Recipe/Exporter/JsonExporterTest.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,65 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace NoccyLabs\Juicer\Recipe\Exporter;
 | 
			
		||||
 | 
			
		||||
use NoccyLabs\Juicer\Recipe\Recipe;
 | 
			
		||||
use NoccyLabs\Juicer\Ingredient\Ingredient;
 | 
			
		||||
 | 
			
		||||
class JsonExporterTest extends \PhpUnit\Framework\TestCase
 | 
			
		||||
{
 | 
			
		||||
    public function testThatRecipesCanBeExportedToJson()
 | 
			
		||||
    {
 | 
			
		||||
        $recipe = new Recipe();
 | 
			
		||||
        $recipe->setRecipeName("foo");
 | 
			
		||||
        $recipe->setRecipeAuthor("bar");
 | 
			
		||||
 | 
			
		||||
        $this->assertInstanceOf(Recipe::class, $recipe);
 | 
			
		||||
        $this->assertEquals("foo", $recipe->getRecipeName());
 | 
			
		||||
        $this->assertEquals("bar", $recipe->getRecipeAuthor());
 | 
			
		||||
 | 
			
		||||
        $exporter = new JsonExporter();
 | 
			
		||||
 | 
			
		||||
        $exported = $exporter->export($recipe);
 | 
			
		||||
        $expected = json_encode([
 | 
			
		||||
            'recipe' => 'foo',
 | 
			
		||||
            'author' => 'bar',
 | 
			
		||||
            'tags' => [],
 | 
			
		||||
            'description' => null,
 | 
			
		||||
            'extra' => [],
 | 
			
		||||
            'ingredients' => []
 | 
			
		||||
        ], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
 | 
			
		||||
 | 
			
		||||
        $this->assertEquals($expected, $exported);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testThatRecipesWithIngredientsCanBeExportedToJson()
 | 
			
		||||
    {
 | 
			
		||||
        $recipe = new Recipe();
 | 
			
		||||
        $recipe->setRecipeName("foo");
 | 
			
		||||
        $recipe->setRecipeAuthor("bar");
 | 
			
		||||
        $recipe->addIngredient(new Ingredient("Cherry", "FA", 1));
 | 
			
		||||
        $recipe->addIngredient(new Ingredient("Vanilla Swirl", "TFA", 2));
 | 
			
		||||
 | 
			
		||||
        $this->assertInstanceOf(Recipe::class, $recipe);
 | 
			
		||||
        $this->assertEquals("foo", $recipe->getRecipeName());
 | 
			
		||||
        $this->assertEquals("bar", $recipe->getRecipeAuthor());
 | 
			
		||||
 | 
			
		||||
        $exporter = new JsonExporter();
 | 
			
		||||
 | 
			
		||||
        $exported = $exporter->export($recipe);
 | 
			
		||||
        $expected = json_encode([
 | 
			
		||||
            'recipe' => 'foo',
 | 
			
		||||
            'author' => 'bar',
 | 
			
		||||
            'tags' => [],
 | 
			
		||||
            'description' => null,
 | 
			
		||||
            'extra' => [],
 | 
			
		||||
            'ingredients' => [
 | 
			
		||||
                [ 'brand' => 'FA', 'flavor' => 'Cherry', 'percent' => 1 ],
 | 
			
		||||
                [ 'brand' => 'TFA', 'flavor' => 'Vanilla Swirl', 'percent' => 2 ]
 | 
			
		||||
            ]
 | 
			
		||||
        ], JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
 | 
			
		||||
 | 
			
		||||
        $this->assertEquals($expected, $exported);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										45
									
								
								tests/Recipe/Importer/JsonImporterTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								tests/Recipe/Importer/JsonImporterTest.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace NoccyLabs\Juicer\Recipe\Importer;
 | 
			
		||||
 | 
			
		||||
use NoccyLabs\Juicer\Recipe\Recipe;
 | 
			
		||||
use NoccyLabs\Juicer\Ingredient\Ingredient;
 | 
			
		||||
use NoccyLabs\Juicer\Recipe\Exporter\JsonExporter;
 | 
			
		||||
 | 
			
		||||
class JsonImporterTest extends \PhpUnit\Framework\TestCase
 | 
			
		||||
{
 | 
			
		||||
    public function testThatRecipesCanBeImportedFromJson()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        $json = '{ "recipe":"foo", "author":"bar", "ingredients": [{"flavor":"Cherry", "brand":"FA", "percent":1}] }';
 | 
			
		||||
 | 
			
		||||
        $importer = new JsonImporter();
 | 
			
		||||
        $recipe = $importer->import($json);
 | 
			
		||||
 | 
			
		||||
        $this->assertEquals("foo", $recipe->getRecipeName());
 | 
			
		||||
        $this->assertEquals("bar", $recipe->getRecipeAuthor());
 | 
			
		||||
 | 
			
		||||
        $this->assertCount(1, $recipe->getIngredients());
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testThatRecipesCanBeImportedFromExportedJson()
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        $recipe = new Recipe();
 | 
			
		||||
        $recipe->setRecipeName("foo");
 | 
			
		||||
        $recipe->setRecipeAuthor("bar");
 | 
			
		||||
        $recipe->addIngredient(new Ingredient("Cherry", "FA", 1));
 | 
			
		||||
        $recipe->addIngredient(new Ingredient("Vanilla Swirl", "TFA", 2));
 | 
			
		||||
 | 
			
		||||
        $exporter = new JsonExporter();
 | 
			
		||||
        $exported = $exporter->export($recipe);
 | 
			
		||||
 | 
			
		||||
        $importer = new JsonImporter();
 | 
			
		||||
        $importedRecipe = $importer->import($exported);
 | 
			
		||||
 | 
			
		||||
        $this->assertEquals($recipe, $importedRecipe);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										59
									
								
								tests/Recipe/Mixer/MixerTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								tests/Recipe/Mixer/MixerTest.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,59 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace NoccyLabs\Juicer\Recipe\Mixer;
 | 
			
		||||
 | 
			
		||||
use NoccyLabs\Juicer\Recipe\Recipe;
 | 
			
		||||
use NoccyLabs\Juicer\Ingredient\Ingredient;
 | 
			
		||||
use NoccyLabs\Juicer\Ingredient\Base;
 | 
			
		||||
 | 
			
		||||
class MixerTest extends \PhpUnit\Framework\TestCase
 | 
			
		||||
{
 | 
			
		||||
    public function testMixingEmptyRecipesWithVg()
 | 
			
		||||
    {
 | 
			
		||||
        $recipe = new Recipe();
 | 
			
		||||
        $mixer = new Mixer();
 | 
			
		||||
 | 
			
		||||
        $base = new Base("VG100");
 | 
			
		||||
        $mixed = $mixer->mixRecipe($recipe, 10, $base, 0);
 | 
			
		||||
 | 
			
		||||
        $this->assertCount(1, $mixed);
 | 
			
		||||
        $mixedVg = reset($mixed);
 | 
			
		||||
 | 
			
		||||
        $this->assertEquals(10, $mixedVg->getVolume());
 | 
			
		||||
        $this->assertEquals("VG", $mixedVg->getFlavorName());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testMixingEmptyRecipesWithPg()
 | 
			
		||||
    {
 | 
			
		||||
        $recipe = new Recipe();
 | 
			
		||||
        $mixer = new Mixer();
 | 
			
		||||
 | 
			
		||||
        $base = new Base("PG100");
 | 
			
		||||
        $mixed = $mixer->mixRecipe($recipe, 10, $base, 0);
 | 
			
		||||
 | 
			
		||||
        $this->assertCount(1, $mixed);
 | 
			
		||||
        $mixedPg = reset($mixed);
 | 
			
		||||
 | 
			
		||||
        $this->assertEquals(10, $mixedPg->getVolume());
 | 
			
		||||
        $this->assertEquals("PG", $mixedPg->getFlavorName());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testMixingEmptyRecipesWith70Vg30Pg()
 | 
			
		||||
    {
 | 
			
		||||
        $recipe = new Recipe();
 | 
			
		||||
        $mixer = new Mixer();
 | 
			
		||||
 | 
			
		||||
        $base = new Base("VG70");
 | 
			
		||||
        $mixed = $mixer->mixRecipe($recipe, 10, $base, 0);
 | 
			
		||||
 | 
			
		||||
        $this->assertCount(2, $mixed);
 | 
			
		||||
 | 
			
		||||
        $mixedVg = array_shift($mixed);
 | 
			
		||||
        $this->assertEquals(10, $mixedVg->getVolume());
 | 
			
		||||
        $this->assertEquals("VG", $mixedVg->getFlavorName());
 | 
			
		||||
        $mixedPg = array_shift($mixed);
 | 
			
		||||
        $this->assertEquals(10, $mixedPg->getVolume());
 | 
			
		||||
        $this->assertEquals("PG", $mixedPg->getFlavorName());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										28
									
								
								tests/Recipe/RecipeTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								tests/Recipe/RecipeTest.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace NoccyLabs\Juicer\Recipe;
 | 
			
		||||
 | 
			
		||||
use NoccyLabs\Juicer\Ingredient\Ingredient;
 | 
			
		||||
 | 
			
		||||
class RecipeTest extends \PhpUnit\Framework\TestCase
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    public function testCreatingAndModifyingRecipes()
 | 
			
		||||
    {
 | 
			
		||||
        $recipe = new Recipe();
 | 
			
		||||
        $recipe->setRecipeName("foo");
 | 
			
		||||
        $recipe->setRecipeAuthor("bar");
 | 
			
		||||
        
 | 
			
		||||
        $ingredient1 = new Ingredient("Ingredient 1");
 | 
			
		||||
        $ingredient2 = new Ingredient("Ingredient 2");
 | 
			
		||||
 | 
			
		||||
        $recipe->addIngredient($ingredient1);
 | 
			
		||||
        $recipe->addIngredient($ingredient2);
 | 
			
		||||
 | 
			
		||||
        $this->assertInstanceOf(Recipe::class, $recipe);
 | 
			
		||||
        $this->assertEquals("foo", $recipe->getRecipeName());
 | 
			
		||||
        $this->assertEquals("bar", $recipe->getRecipeAuthor());
 | 
			
		||||
        $this->assertEquals([$ingredient1, $ingredient2], $recipe->getIngredients());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user