diff --git a/src/Actions/CreatesNewMarkdownPostFile.php b/src/Actions/CreatesNewMarkdownPostFile.php index d038a5d5..4909fda0 100644 --- a/src/Actions/CreatesNewMarkdownPostFile.php +++ b/src/Actions/CreatesNewMarkdownPostFile.php @@ -2,7 +2,7 @@ namespace Hyde\Framework\Actions; -use Exception; +use Hyde\Framework\Exceptions\FileConflictException; use Hyde\Framework\Hyde; use Illuminate\Support\Str; @@ -95,14 +95,14 @@ public function __construct( * @param bool $force Should the file be created even if a file with the same path already exists? * @return string|false Returns the path to the file if successful, or false if the file could not be saved. * - * @throws Exception if a file with the same slug already exists and the force flag is not set. + * @throws FileConflictException if a file with the same slug already exists and the force flag is not set. */ public function save(bool $force = false): string|false { $path = Hyde::path("_posts/$this->slug.md"); if ($force !== true && file_exists($path)) { - throw new Exception("File at $path already exists! ", 409); + throw new FileConflictException($path); } $arrayWithoutSlug = ((array) $this); diff --git a/src/Actions/CreatesNewPageSourceFile.php b/src/Actions/CreatesNewPageSourceFile.php index 5d7c3db9..143bd2cb 100644 --- a/src/Actions/CreatesNewPageSourceFile.php +++ b/src/Actions/CreatesNewPageSourceFile.php @@ -2,7 +2,8 @@ namespace Hyde\Framework\Actions; -use Exception; +use Hyde\Framework\Exceptions\FileConflictException; +use Hyde\Framework\Exceptions\UnsupportedPageTypeException; use Hyde\Framework\Hyde; use Hyde\Framework\Models\BladePage; use Hyde\Framework\Models\DocumentationPage; @@ -16,32 +17,10 @@ */ class CreatesNewPageSourceFile { - /** - * The Page title. - * - * @var string - */ public string $title; - - /** - * The Page slug. - */ public string $slug; + public string $outputPath; - /** - * The file path. - */ - public string $path; - - /** - * Construct the class. - * - * @param string $title - The page title, will be used to generate the slug - * @param string $type - The page type, FQDN of the page class - * @param bool $force - Overwrite any existing files? - * - * @throws Exception if the page type is not supported or the file already exists - */ public function __construct(string $title, string $type = MarkdownPage::class, public bool $force = false) { $this->title = $title; @@ -50,25 +29,13 @@ public function __construct(string $title, string $type = MarkdownPage::class, p $this->createPage($type); } - /** - * Check if the file can be saved. - * - * @throws Exception if the file already exists and cannot be overwritten - */ public function canSaveFile(string $path): void { if (file_exists($path) && ! $this->force) { - throw new Exception("File $path already exists!", 409); + throw new FileConflictException($path); } } - /** - * Create the page. - * - * @param string $type FQDN of the page class - * - * @throws Exception if the page type is not supported - */ public function createPage(string $type): int|false { if ($type === MarkdownPage::class) { @@ -82,39 +49,29 @@ public function createPage(string $type): int|false return $this->createDocumentationFile(); } - throw new Exception('The page type must be either "markdown", "blade", or "documentation"'); + throw new UnsupportedPageTypeException('The page type must be either "markdown", "blade", or "documentation"'); } - /** - * Create the Markdown file. - * - * @throws Exception if the file cannot be saved. - */ public function createMarkdownFile(): int|false { - $this->path = Hyde::path("_pages/$this->slug.md"); + $this->outputPath = Hyde::path("_pages/$this->slug.md"); - $this->canSaveFile($this->path); + $this->canSaveFile($this->outputPath); return file_put_contents( - $this->path, + $this->outputPath, "---\ntitle: $this->title\n---\n\n# $this->title\n" ); } - /** - * Create the Blade file. - * - * @throws Exception if the file cannot be saved. - */ public function createBladeFile(): int|false { - $this->path = Hyde::path("_pages/$this->slug.blade.php"); + $this->outputPath = Hyde::path("_pages/$this->slug.blade.php"); - $this->canSaveFile($this->path); + $this->canSaveFile($this->outputPath); return file_put_contents( - $this->path, + $this->outputPath, <<path = Hyde::path("_docs/$this->slug.md"); + $this->outputPath = Hyde::path("_docs/$this->slug.md"); - $this->canSaveFile($this->path); + $this->canSaveFile($this->outputPath); return file_put_contents( - $this->path, + $this->outputPath, "# $this->title\n" ); } diff --git a/src/Concerns/HasDateString.php b/src/Concerns/HasDateString.php index 0a3adb91..2b1ec719 100644 --- a/src/Concerns/HasDateString.php +++ b/src/Concerns/HasDateString.php @@ -14,7 +14,7 @@ trait HasDateString public ?DateString $date = null; /** - * @throws \Exception + * @throws \Hyde\Framework\Exceptions\CouldNotParseDateStringException */ public function constructDateString(): void { diff --git a/src/Concerns/ValidatesExistence.php b/src/Concerns/ValidatesExistence.php index e160770b..148392ee 100644 --- a/src/Concerns/ValidatesExistence.php +++ b/src/Concerns/ValidatesExistence.php @@ -2,18 +2,20 @@ namespace Hyde\Framework\Concerns; -use Exception; +use Hyde\Framework\Exceptions\FileNotFoundException; use Hyde\Framework\Hyde; /** * Validate the existence of a Page model's source file. + * + * @see \Tests\Unit\ValidatesExistenceTest */ trait ValidatesExistence { /** * Check if a supplied source file exists or throw an exception. * - * @throws Exception If the file does not exist. + * @throws FileNotFoundException If the file does not exist. */ public function validateExistence(string $model, string $slug): void { @@ -22,7 +24,7 @@ public function validateExistence(string $model, string $slug): void $slug.$model::$fileExtension; if (! file_exists(Hyde::path($filepath))) { - throw new Exception("File $filepath not found.", 404); + throw new FileNotFoundException($filepath); } } } diff --git a/src/Exceptions/CouldNotParseDateStringException.php b/src/Exceptions/CouldNotParseDateStringException.php new file mode 100644 index 00000000..a1da344d --- /dev/null +++ b/src/Exceptions/CouldNotParseDateStringException.php @@ -0,0 +1,10 @@ +message = $path ? "File already exists: {$path}" : $this->message; + } +} diff --git a/src/Exceptions/FileNotFoundException.php b/src/Exceptions/FileNotFoundException.php new file mode 100644 index 00000000..1304ce47 --- /dev/null +++ b/src/Exceptions/FileNotFoundException.php @@ -0,0 +1,16 @@ +message = $path ? "File not found: {$path}" : $this->message; + } +} diff --git a/src/Exceptions/UnsupportedPageTypeException.php b/src/Exceptions/UnsupportedPageTypeException.php new file mode 100644 index 00000000..a2ecb496 --- /dev/null +++ b/src/Exceptions/UnsupportedPageTypeException.php @@ -0,0 +1,16 @@ +message = $page ? "The page type is not supported: {$page}" : $this->message; + } +} diff --git a/src/Hyde.php b/src/Hyde.php index 821f68b2..f54c16fd 100644 --- a/src/Hyde.php +++ b/src/Hyde.php @@ -53,9 +53,6 @@ public static function titleFromSlug(string $slug): string return Str::title(str_replace('-', ' ', ($slug))); } - /** - * @throws \Exception - */ public static function getLatestPosts(): Collection { $collection = new Collection(); diff --git a/src/Models/DateString.php b/src/Models/DateString.php index d5e0f1fe..c4bc0d2e 100644 --- a/src/Models/DateString.php +++ b/src/Models/DateString.php @@ -3,9 +3,12 @@ namespace Hyde\Framework\Models; use DateTime; +use Hyde\Framework\Exceptions\CouldNotParseDateStringException; /** * Parse a date string and create normalized formats. + * + * @see \Tests\Unit\DateStringTest */ class DateString { @@ -27,13 +30,17 @@ class DateString /** * @param string $string * - * @throws \Exception + * @throws \Hyde\Framework\Exceptions\CouldNotParseDateStringException */ public function __construct(string $string) { $this->string = $string; - $this->dateTimeObject = new DateTime($this->string); + try { + $this->dateTimeObject = new DateTime($this->string); + } catch (\Exception $e) { + throw new CouldNotParseDateStringException($e->getMessage()); + } $this->datetime = $this->dateTimeObject->format('c'); $this->sentence = $this->dateTimeObject->format('l M jS, Y, \a\t g:ia'); diff --git a/src/Models/MarkdownPost.php b/src/Models/MarkdownPost.php index 0f2a699f..776e7f47 100644 --- a/src/Models/MarkdownPost.php +++ b/src/Models/MarkdownPost.php @@ -22,7 +22,7 @@ class MarkdownPost extends MarkdownDocument public static string $parserClass = MarkdownPostParser::class; /** - * @throws \Exception + * @throws \Hyde\Framework\Exceptions\CouldNotParseDateStringException */ public function __construct(array $matter, string $body, string $title = '', string $slug = '') { diff --git a/tests/Feature/Actions/CreatesNewPageSourceFileTest.php b/tests/Feature/Actions/CreatesNewPageSourceFileTest.php index 04fac809..fb694bd7 100644 --- a/tests/Feature/Actions/CreatesNewPageSourceFileTest.php +++ b/tests/Feature/Actions/CreatesNewPageSourceFileTest.php @@ -1,9 +1,10 @@ expectException(Exception::class); + $this->expectException(UnsupportedPageTypeException::class); $this->expectExceptionMessage('The page type must be either "markdown", "blade", or "documentation"'); (new CreatesNewPageSourceFile('682072b Test Page', 'invalid')); @@ -56,8 +57,8 @@ public function test_that_an_exception_is_thrown_if_file_already_exists_and_over $path = Hyde::path('_pages/foo.md'); file_put_contents($path, 'foo'); - $this->expectException(Exception::class); - $this->expectExceptionMessage("File $path already exists!"); + $this->expectException(FileConflictException::class); + $this->expectExceptionMessage("File already exists: $path"); $this->expectExceptionCode(409); new CreatesNewPageSourceFile('foo'); @@ -131,12 +132,12 @@ public function test_that_the_file_path_can_be_returned() { $this->assertEquals( Hyde::path('_pages/682072b-test-page.md'), - (new CreatesNewPageSourceFile('682072b Test Page'))->path + (new CreatesNewPageSourceFile('682072b Test Page'))->outputPath ); $this->assertEquals( Hyde::path('_pages/682072b-test-page.blade.php'), - (new CreatesNewPageSourceFile('682072b Test Page', BladePage::class))->path + (new CreatesNewPageSourceFile('682072b Test Page', BladePage::class))->outputPath ); } } diff --git a/tests/Feature/Commands/HydeMakePageCommandTest.php b/tests/Feature/Commands/HydeMakePageCommandTest.php index 7e8840b2..3ce17bac 100644 --- a/tests/Feature/Commands/HydeMakePageCommandTest.php +++ b/tests/Feature/Commands/HydeMakePageCommandTest.php @@ -3,6 +3,7 @@ namespace Tests\Feature\Commands; use Exception; +use Hyde\Framework\Exceptions\FileConflictException; use Hyde\Framework\Hyde; use Tests\TestCase; @@ -102,8 +103,8 @@ public function test_command_fails_if_file_already_exists() { file_put_contents($this->markdownPath, 'This should not be overwritten'); - $this->expectException(Exception::class); - $this->expectExceptionMessage("File $this->markdownPath already exists!"); + $this->expectException(FileConflictException::class); + $this->expectExceptionMessage("File already exists: $this->markdownPath"); $this->expectExceptionCode(409); $this->artisan('make:page "8450de2 test page"')->assertExitCode(409); diff --git a/tests/Unit/DateStringTest.php b/tests/Unit/DateStringTest.php index cd1ffc22..891b6819 100644 --- a/tests/Unit/DateStringTest.php +++ b/tests/Unit/DateStringTest.php @@ -3,9 +3,13 @@ namespace Tests\Unit; use DateTime; +use Hyde\Framework\Exceptions\CouldNotParseDateStringException; use Hyde\Framework\Models\DateString; use PHPUnit\Framework\TestCase; +/** + * @covers \Hyde\Framework\Models\DateString + */ class DateStringTest extends TestCase { // Test it can parse a date string @@ -42,4 +46,11 @@ public function test_it_can_format_date_string_into_short_human_readable_string( $dateString = new DateString('2020-01-01 UTC'); $this->assertEquals('Jan 1st, 2020', $dateString->short); } + + // Test it handles invalid date strings + public function test_it_handles_invalid_date_strings() + { + $this->expectException(CouldNotParseDateStringException::class); + new DateString('foo bar'); + } } diff --git a/tests/Unit/ValidatesExistenceTest.php b/tests/Unit/ValidatesExistenceTest.php new file mode 100644 index 00000000..563f0822 --- /dev/null +++ b/tests/Unit/ValidatesExistenceTest.php @@ -0,0 +1,38 @@ +validateExistence(BladePage::class, 'index'); + + $this->assertTrue(true); + } + + public function test_validate_existence_throws_file_not_found_exception_if_file_does_not_exist() + { + $this->expectException(FileNotFoundException::class); + + $class = new class + { + use ValidatesExistence; + }; + + $class->validateExistence(BladePage::class, 'not-found'); + } +}