Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create static file collections #110

Merged
merged 35 commits into from
Jun 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
af3edb7
Create DataCollection.php
caendesilva Jun 24, 2022
e41a10d
Create DataCollectionServiceProvider.php
caendesilva Jun 24, 2022
3ac510f
Register the class alias
caendesilva Jun 24, 2022
7f826f5
Create the _data directory if it doesn't exist
caendesilva Jun 24, 2022
14650e4
Register module service providers
caendesilva Jun 24, 2022
c2fdf4b
Add todo: Make modules configurable
caendesilva Jun 24, 2022
b79c96c
Import the Hyde class
caendesilva Jun 24, 2022
f56bb0a
Add base Markdown collection facade
caendesilva Jun 24, 2022
bb79192
Track execution time
caendesilva Jun 24, 2022
361ad36
Add collection key and inferred name to class
caendesilva Jun 24, 2022
713b411
Inline unnecessary local variable
caendesilva Jun 24, 2022
e19f823
Bind the data path to class property to make it configurable
caendesilva Jun 24, 2022
4769cfb
Get the preferred Markdown file extension from base model class
caendesilva Jun 24, 2022
21acf6f
Move the facade to dedicated namespaced class
caendesilva Jun 24, 2022
c644fd7
Create base collection class to hold data
caendesilva Jun 24, 2022
389f610
Change static property dataPath to sourceDirectory to match convention
caendesilva Jun 24, 2022
fc471b6
Replace dynamic execution time tracking method
caendesilva Jun 24, 2022
147e0bc
Extract long method call to helper method
caendesilva Jun 24, 2022
220896f
Move getMarkdownFiles helper to collection class
caendesilva Jun 24, 2022
20cf7b3
Remove unused use statements
caendesilva Jun 24, 2022
339bd62
Add type annotations
caendesilva Jun 24, 2022
93af26b
Update README.md
caendesilva Jun 25, 2022
1ffdf46
Move facade method to base class
caendesilva Jun 25, 2022
4e11f02
Remove name property
caendesilva Jun 25, 2022
1abcb7b
Rename Collection facade to MarkdownCollection
caendesilva Jun 25, 2022
cc8b02f
Create the MarkdownCollection facade
caendesilva Jun 25, 2022
5cc3a36
Apply fixes from StyleCI
StyleCIBot Jun 25, 2022
2320232
Use the internal helper to get the actual Markdown collection
caendesilva Jun 25, 2022
60ed906
Exclude files starting with underscores from discovery
caendesilva Jun 25, 2022
d420630
Create tests for the DataCollection class
caendesilva Jun 25, 2022
e8a2072
Add test for the MarkdownCollection facade
caendesilva Jun 25, 2022
08b6da9
Add test for the DataCollectionServiceProvider
caendesilva Jun 25, 2022
3122d24
Test source directory can be changed
caendesilva Jun 25, 2022
4e26085
Remove Copilot comments and clean up test names
caendesilva Jun 25, 2022
a84edbe
Apply fixes from StyleCI
StyleCIBot Jun 25, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions packages/framework/src/HydeServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ function () {

Commands\HydePackageDiscoverCommand::class,
]);

$this->registerModuleServiceProviders();
}

/**
Expand Down Expand Up @@ -136,4 +138,14 @@ protected function storeCompiledSiteIn(string $directory): void
{
StaticPageBuilder::$outputPath = $directory;
}

/**
* Register module service providers.
*
* @todo Make modules configurable.
*/
protected function registerModuleServiceProviders(): void
{
$this->app->register(Modules\DataCollections\DataCollectionServiceProvider::class);
}
}
68 changes: 68 additions & 0 deletions packages/framework/src/Modules/DataCollections/DataCollection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

namespace Hyde\Framework\Modules\DataCollections;

use Hyde\Framework\Hyde;
use Hyde\Framework\Models\MarkdownDocument;
use Hyde\Framework\Services\MarkdownFileService;
use Illuminate\Support\Collection;

/**
* Generates Laravel Collections from static data files,
* such as Markdown components and YAML files.
*
* @see \Hyde\Framework\Testing\Modules\DataCollections\DataCollectionTest\DataCollectionTest
*/
class DataCollection extends Collection
{
public string $key;

protected float $timeStart;
public float $parseTimeInMs;

public static string $sourceDirectory = '_data';

public function __construct(string $key)
{
$this->timeStart = microtime(true);
$this->key = $key;

parent::__construct();
}

public function getCollection(): DataCollection
{
$this->parseTimeInMs = round((microtime(true) - $this->timeStart) * 1000, 2);
unset($this->timeStart);

return $this;
}

public function getMarkdownFiles(): array
{
return glob(Hyde::path(
static::$sourceDirectory.'/'.$this->key.'/*'.MarkdownDocument::$fileExtension
));
}

/**
* Get a collection of Markdown documents in the _data/<$key> directory.
* Each Markdown file will be parsed into a MarkdownDocument with front matter.
*
* @param string $key for a subdirectory of the _data directory
* @return DataCollection<\Hyde\Framework\Models\MarkdownDocument>
*/
public static function markdown(string $key): DataCollection
{
$collection = new DataCollection($key);
foreach ($collection->getMarkdownFiles() as $file) {
if (! str_starts_with(basename($file), '_')) {
$collection->push(
(new MarkdownFileService($file))->get()
);
}
}

return $collection->getCollection();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace Hyde\Framework\Modules\DataCollections;

use Hyde\Framework\Hyde;
use Illuminate\Foundation\AliasLoader;
use Illuminate\Support\ServiceProvider;

/**
* @see \Hyde\Framework\Testing\Modules\DataCollections\DataCollectionTest\DataCollectionTest
*/
class DataCollectionServiceProvider extends ServiceProvider
{
public function register()
{
// Register the class alias
AliasLoader::getInstance()->alias(
'MarkdownCollection', Facades\MarkdownCollection::class
);
}

public function boot()
{
// Create the _data directory if it doesn't exist
if (! is_dir(Hyde::path('_data'))) {
mkdir(Hyde::path('_data'));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace Hyde\Framework\Modules\DataCollections\Facades;

use Hyde\Framework\Modules\DataCollections\DataCollection;

/**
* @see \Hyde\Framework\Testing\Modules\DataCollections\DataCollectionTest\DataCollectionTest
*/
class MarkdownCollection
{
public static function get(string $collectionKey): DataCollection
{
return DataCollection::markdown($collectionKey);
}
}
8 changes: 7 additions & 1 deletion packages/framework/src/Modules/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
# Hyde Modules

This directory contains self-contained code modules,
that may eventually be extracted into packages.
that may eventually be extracted into packages.

They may also be merged into the Hyde core.

As these modules may be experimental,
the namespaces used may be changed without notice
as per the current 0.x semantic versioning range.
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
<?php

namespace Hyde\Framework\Testing\Modules\DataCollections\DataCollectionTest;

use Hyde\Framework\Hyde;
use Hyde\Framework\Models\MarkdownDocument;
use Hyde\Framework\Modules\DataCollections\DataCollection;
use Hyde\Framework\Modules\DataCollections\DataCollectionServiceProvider;
use Hyde\Framework\Modules\DataCollections\Facades\MarkdownCollection;
use Hyde\Testing\TestCase;
use Illuminate\Foundation\AliasLoader;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\File;

/**
* @covers \Hyde\Framework\Modules\DataCollections\DataCollection
* @covers \Hyde\Framework\Modules\DataCollections\DataCollectionServiceProvider
* @covers \Hyde\Framework\Modules\DataCollections\Facades\MarkdownCollection
*/
class DataCollectionTest extends TestCase
{
// constructor creates new data collection instance
public function test_constructor_creates_new_data_collection_instance()
{
$class = new DataCollection('foo');
$this->assertInstanceOf(DataCollection::class, $class);
$this->assertInstanceOf(Collection::class, $class);
}

public function test_constructor_sets_key()
{
$class = new DataCollection('foo');
$this->assertEquals('foo', $class->key);
}

public function test_key_is_required()
{
$this->expectException(\ArgumentCountError::class);
new DataCollection();
}

public function test_get_collection_method_returns_the_collection_instance()
{
$class = new DataCollection('foo');
$this->assertSame($class, $class->getCollection());
}

public function test_get_collection_method_sets_parse_time_in_ms()
{
$class = new DataCollection('foo');
$class->getCollection();
$this->assertIsFloat($class->parseTimeInMs);
}

public function test_get_markdown_files_method_returns_empty_array_if_the_specified_directory_does_not_exist()
{
$class = new DataCollection('foo');
$this->assertIsArray($class->getMarkdownFiles());
$this->assertEmpty($class->getMarkdownFiles());
}

public function test_get_markdown_files_method_returns_empty_array_if_no_files_are_found_in_specified_directory()
{
mkdir(Hyde::path('_data/foo'));
$class = new DataCollection('foo');
$this->assertIsArray($class->getMarkdownFiles());
$this->assertEmpty($class->getMarkdownFiles());
rmdir(Hyde::path('_data/foo'));
}

public function test_get_markdown_files_method_returns_an_array_of_markdown_files_in_the_specified_directory()
{
mkdir(Hyde::path('_data/foo'));
touch(Hyde::path('_data/foo/foo.md'));
touch(Hyde::path('_data/foo/bar.md'));

$this->assertEquals([
Hyde::path('_data/foo/bar.md'),
Hyde::path('_data/foo/foo.md'),
], (new DataCollection('foo'))->getMarkdownFiles());

File::deleteDirectory(Hyde::path('_data/foo'));
}

public function test_get_markdown_files_method_does_not_include_files_in_subdirectories()
{
mkdir(Hyde::path('_data/foo'));
mkdir(Hyde::path('_data/foo/bar'));
touch(Hyde::path('_data/foo/foo.md'));
touch(Hyde::path('_data/foo/bar/bar.md'));
$this->assertEquals([
Hyde::path('_data/foo/foo.md'),
], (new DataCollection('foo'))->getMarkdownFiles());
File::deleteDirectory(Hyde::path('_data/foo'));
}

public function test_get_markdown_files_method_does_not_include_files_with_extensions_other_than_md()
{
mkdir(Hyde::path('_data/foo'));
touch(Hyde::path('_data/foo/foo.md'));
touch(Hyde::path('_data/foo/bar.txt'));
$this->assertEquals([
Hyde::path('_data/foo/foo.md'),
], (new DataCollection('foo'))->getMarkdownFiles());
File::deleteDirectory(Hyde::path('_data/foo'));
}

public function test_static_markdown_helper_returns_new_data_collection_instance()
{
$this->assertInstanceOf(DataCollection::class, DataCollection::markdown('foo'));
}

public function test_static_markdown_helper_discovers_and_parses_markdown_files_in_the_specified_directory()
{
mkdir(Hyde::path('_data/foo'));
touch(Hyde::path('_data/foo/foo.md'));
touch(Hyde::path('_data/foo/bar.md'));

$collection = DataCollection::markdown('foo');

$this->assertContainsOnlyInstancesOf(MarkdownDocument::class, $collection);

File::deleteDirectory(Hyde::path('_data/foo'));
}

public function test_static_markdown_helper_ignores_files_starting_with_an_underscore()
{
mkdir(Hyde::path('_data/foo'));
touch(Hyde::path('_data/foo/foo.md'));
touch(Hyde::path('_data/foo/_bar.md'));
$this->assertCount(1, DataCollection::markdown('foo'));
File::deleteDirectory(Hyde::path('_data/foo'));
}

public function test_markdown_facade_returns_same_result_as_static_markdown_helper()
{
$expected = DataCollection::markdown('foo');
$actual = MarkdownCollection::get('foo');
unset($expected->parseTimeInMs);
unset($actual->parseTimeInMs);
$this->assertEquals($expected, $actual);
}

// Test DataCollectionServiceProvider registers the facade as an alias
public function test_data_collection_service_provider_registers_the_facade_as_an_alias()
{
$this->assertArrayHasKey('MarkdownCollection', AliasLoader::getInstance()->getAliases());
$this->assertContains(MarkdownCollection::class, AliasLoader::getInstance()->getAliases());
}

public function test_data_collection_service_provider_creates_the__data_directory_if_it_does_not_exist()
{
File::deleteDirectory(Hyde::path('_data'));
$this->assertFileDoesNotExist(Hyde::path('_data'));

(new DataCollectionServiceProvider($this->app))->boot();

$this->assertFileExists(Hyde::path('_data'));
}

public function test_class_has_static_source_directory_property()
{
$this->assertEquals('_data', DataCollection::$sourceDirectory);
}

public function test_source_directory_can_be_changed()
{
DataCollection::$sourceDirectory = 'foo';
mkdir(Hyde::path('foo/bar'), recursive: true);
touch(Hyde::path('foo/bar/foo.md'));
$this->assertEquals([
Hyde::path('foo/bar/foo.md'),
], (new DataCollection('bar'))->getMarkdownFiles());
File::deleteDirectory(Hyde::path('foo'));
}
}