Skip to content

Commit

Permalink
Merge pull request #58 from CouscousPHP/remote-templates
Browse files Browse the repository at this point in the history
Fixes #55 and code refactoring
  • Loading branch information
mnapoli committed Dec 11, 2014
2 parents 25c3d62 + 2775489 commit ec574b5
Show file tree
Hide file tree
Showing 9 changed files with 338 additions and 163 deletions.
1 change: 1 addition & 0 deletions src/Model/Repository.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
* Repository containing files.
*
* Extends stdClass so that properties can be added by processors at will.
* TODO should not extend stdClass anymore! It has been replaced by the metadata property...
*
* @author Matthieu Napoli <matthieu@mnapoli.fr>
*/
Expand Down
86 changes: 86 additions & 0 deletions src/Step/Template/FetchRemoteTemplate.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?php

namespace Couscous\Step\Template;

use Couscous\CommandRunner;
use Couscous\Model\Repository;
use Couscous\Step\StepInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Filesystem\Filesystem;

/**
* Fetch a remote template.
*
* @author Matthieu Napoli <matthieu@mnapoli.fr>
*/
class FetchRemoteTemplate implements StepInterface
{
/**
* @var Filesystem
*/
private $filesystem;

/**
* @var CommandRunner
*/
private $commandRunner;

/**
* Temporarily save the template directory if we are in preview
* to avoid cloning the repository every time.
*
* In theory we shouldn't store state in this object because it's a service
* but we need extensive change to avoid that.
*
* @var string
*/
private $templateDirectory;

public function __construct(Filesystem $filesystem, CommandRunner $commandRunner)
{
$this->filesystem = $filesystem;
$this->commandRunner = $commandRunner;
}

public function __invoke(Repository $repository, OutputInterface $output)
{
// In preview we avoid cloning the repository every time
if ($repository->regenerate && $this->templateDirectory) {
$repository->metadata['template.directory'] = $this->templateDirectory;

return;
}

$templateUrl = $repository->metadata['template.url'];

if ($templateUrl === null) {
return;
}

$directory = $this->fetchGitTemplate($templateUrl, $output);

$this->templateDirectory = $directory;
$repository->metadata['template.directory'] = $directory;
}

private function fetchGitTemplate($gitUrl, OutputInterface $output)
{
$output->writeln("Fetching template from <info>$gitUrl</info>");

$directory = $this->createTempDirectory('couscous_template_');

$this->commandRunner->run("git clone $gitUrl $directory");

return $directory;
}

private function createTempDirectory($prefix)
{
$tempFile = tempnam(sys_get_temp_dir(), $prefix);
// Turn the temp file into a temp directory
$this->filesystem->remove($tempFile);
$this->filesystem->mkdir($tempFile);

return $tempFile;
}
}
95 changes: 0 additions & 95 deletions src/Step/Template/InitTemplate.php

This file was deleted.

2 changes: 1 addition & 1 deletion src/Step/Template/UseDefaultTemplate.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ private function hasCustomTemplateDirectory(Repository $repository)

private function hasTemplateDirectory(Repository $repository)
{
$templateDirectory = $repository->sourceDirectory . '/' . InitTemplate::DEFAULT_TEMPLATE_DIRECTORY;
$templateDirectory = $repository->sourceDirectory . '/' . ValidateTemplateDirectory::DEFAULT_TEMPLATE_DIRECTORY;

return $this->filesystem->exists($templateDirectory);
}
Expand Down
57 changes: 57 additions & 0 deletions src/Step/Template/ValidateTemplateDirectory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

namespace Couscous\Step\Template;

use Couscous\Model\Repository;
use Couscous\Step\StepInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Filesystem\Filesystem;

/**
* Initializes the template directory.
*
* @author Matthieu Napoli <matthieu@mnapoli.fr>
*/
class ValidateTemplateDirectory implements StepInterface
{
const DEFAULT_TEMPLATE_DIRECTORY = 'website';

/**
* @var Filesystem
*/
private $filesystem;

public function __construct(Filesystem $filesystem)
{
$this->filesystem = $filesystem;
}

public function __invoke(Repository $repository, OutputInterface $output)
{
$directory = $repository->metadata['template.directory'];

if ($directory === null) {
$directory = $repository->sourceDirectory . '/' . self::DEFAULT_TEMPLATE_DIRECTORY;
}

if (! $this->filesystem->isAbsolutePath($directory)) {
$directory = $repository->sourceDirectory . '/' . $directory;
}

$this->assertDirectoryExist($directory);

$repository->watchlist->watchDirectory($directory);

$repository->metadata['template.directory'] = $directory;
}

private function assertDirectoryExist($directory)
{
if (! $this->filesystem->exists($directory)) {
throw new \RuntimeException(sprintf(
"The template directory '%s' doesn't exist",
$directory
));
}
}
}
3 changes: 2 additions & 1 deletion src/config.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
'Couscous\Step\Config\OverrideBaseUrlForPreview',
'Couscous\Step\Scripts\ExecuteBeforeScripts',
'Couscous\Step\Template\UseDefaultTemplate',
'Couscous\Step\Template\InitTemplate',
'Couscous\Step\Template\FetchRemoteTemplate',
'Couscous\Step\Template\ValidateTemplateDirectory',
'Couscous\Step\Assets\RunBowerInstall',
'Couscous\Step\Template\LoadAssets',
'Couscous\Step\Markdown\LoadMarkdownFiles',
Expand Down
83 changes: 83 additions & 0 deletions tests/UnitTest/Step/Template/FetchRemoteTemplateTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php

namespace Couscous\Tests\UnitTest\Step\Template;

use Couscous\Step\Template\FetchRemoteTemplate;
use Couscous\Tests\UnitTest\Mock\MockRepository;
use Symfony\Component\Console\Output\NullOutput;

/**
* @covers \Couscous\Step\Template\FetchRemoteTemplate
*/
class FetchRemoteTemplateTest extends \PHPUnit_Framework_TestCase
{
/**
* @test
*/
public function it_should_skip_if_no_template_url()
{
$filesystem = $this->getMock('Symfony\Component\Filesystem\Filesystem');
$commandRunner = $this->getMock('Couscous\CommandRunner');

$step = new FetchRemoteTemplate($filesystem, $commandRunner);

$repository = new MockRepository();

$commandRunner->expects($this->never())
->method('run');

$step->__invoke($repository, new NullOutput());

$this->assertNull($repository->metadata['template.directory']);
}

/**
* @test
*/
public function it_should_clone_and_set_the_template_directory()
{
$filesystem = $this->getMock('Symfony\Component\Filesystem\Filesystem');
$commandRunner = $this->getMock('Couscous\CommandRunner');

$step = new FetchRemoteTemplate($filesystem, $commandRunner);

$repository = new MockRepository();
$repository->metadata['template.url'] = 'git://foo';

$commandRunner->expects($this->once())
->method('run')
->with($this->matches('git clone git://foo %s'));

$step->__invoke($repository, new NullOutput());

$this->assertNotNull($repository->metadata['template.directory']);
}

/**
* @test
*/
public function it_should_not_clone_twice_if_regenerating()
{
$filesystem = $this->getMock('Symfony\Component\Filesystem\Filesystem');
$commandRunner = $this->getMock('Couscous\CommandRunner');

$step = new FetchRemoteTemplate($filesystem, $commandRunner);

$commandRunner->expects($this->once())
->method('run')
->with($this->matches('git clone git://foo %s'));

// Calling once
$repository = new MockRepository();
$repository->metadata['template.url'] = 'git://foo';
$step->__invoke($repository, new NullOutput());
$this->assertNotNull($repository->metadata['template.directory']);

// Calling twice
$repository = new MockRepository();
$repository->regenerate = true;
$repository->metadata['template.url'] = 'git://foo';
$step->__invoke($repository, new NullOutput());
$this->assertNotNull($repository->metadata['template.directory']);
}
}
Loading

0 comments on commit ec574b5

Please sign in to comment.