diff --git a/README.md b/README.md index 51eccdb..8c9afae 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,8 @@ Bookdown has 100% test coverage. To run the tests, install [PHPUnit](http://phpu - `"afterToc"`: indicates a Markdown file to place on the index page after the TOC + - `"subtitle"`: indicates a subtitle on an index page + - navigational elements - sidebar of siblings at the current level diff --git a/src/Config/IndexConfig.php b/src/Config/IndexConfig.php index 1a46728..2c9bf84 100644 --- a/src/Config/IndexConfig.php +++ b/src/Config/IndexConfig.php @@ -56,23 +56,62 @@ protected function initTitle() protected function initContent() { - $this->content = empty($this->json->content) + $content = empty($this->json->content) ? array() - : (array) $this->json->content; + : $this->json->content; - if (! $this->content) { + if (! $content) { throw new Exception("No content listed in '{$this->file}'."); } - if (isset($this->content['index'])) { - throw new Exception("Disallowed 'index' content in {$this->file}."); + if (! is_array($content)) { + throw new Exception("Content must be an array in '{$this->file}'."); } - foreach ($this->content as $name => $origin) { - $this->content[$name] = $this->fixPath($origin); + foreach ($content as $key => $val) { + $this->initContentItem($val); } } + protected function initContentItem($origin) + { + if (is_object($origin)) { + $spec = (array) $origin; + $name = key($spec); + $origin = current($spec); + return $this->addContent($name, $origin); + } + + if (! is_string($origin)) { + throw new Exception("Content origin must be object or string in '{$this->file}'."); + } + + if (substr($origin, -13) == 'bookdown.json') { + $name = basename(dirname($origin)); + return $this->addContent($name, $origin); + } + + $name = basename($origin); + $pos = strrpos($name, '.'); + if ($pos !== false) { + $name = substr($name, 0, $pos); + } + return $this->addContent($name, $origin); + } + + protected function addContent($name, $origin) + { + if ($name == 'index') { + throw new Exception("Disallowed 'index' content name in '{$this->file}'."); + } + + if (isset($this->content[$name])) { + throw new Exception("Content name '{$name}' already set in '{$this->file}'."); + } + + $this->content[$name] = $this->fixPath($origin); + } + protected function fixPath($path) { if (strpos($path, '://') !== false) { diff --git a/tests/BookFixture.php b/tests/BookFixture.php index baeca93..0685314 100644 --- a/tests/BookFixture.php +++ b/tests/BookFixture.php @@ -10,9 +10,9 @@ class BookFixture public $rootConfigFile = '/path/to/bookdown.json'; public $rootConfigData = '{ "title": "Example Book", - "content": { - "chapter": "chapter/bookdown.json" - }, + "content": [ + {"chapter": "chapter/bookdown.json"} + ], "target": "/_site", "templates": { "foo": "/foo.php" @@ -24,9 +24,9 @@ class BookFixture public $indexConfigFile = '/path/to/chapter/bookdown.json'; public $indexConfigData = '{ "title": "Chapter", - "content": { - "section": "section.md" - } + "content": [ + {"section": "section.md"} + ] }'; public $indexConfig; public $indexPage; diff --git a/tests/Config/ConfigFactoryTest.php b/tests/Config/ConfigFactoryTest.php index 5467378..596503a 100644 --- a/tests/Config/ConfigFactoryTest.php +++ b/tests/Config/ConfigFactoryTest.php @@ -11,11 +11,11 @@ class ConfigFactoryTest extends \PHPUnit_Framework_TestCase protected $data = '{ "title": "Example Title", - "content": { - "foo": "foo.md", - "bar": "/bar.md", - "baz": "http://example.com/baz.md" - }, + "content": [ + {"foo": "foo.md"}, + {"bar": "/bar.md"}, + {"baz": "http://example.com/baz.md"} + ], "target": "/_site" }'; diff --git a/tests/Config/IndexConfigTest.php b/tests/Config/IndexConfigTest.php index f5d5f5b..3abd222 100644 --- a/tests/Config/IndexConfigTest.php +++ b/tests/Config/IndexConfigTest.php @@ -1,48 +1,78 @@ newIndexConfig('/path/to/bookdown.json', $this->jsonMissingContent); } + public function testContentNotArray() + { + $this->setExpectedException( + 'Bookdown\Bookdown\Exception', + "Content must be an array in '/path/to/bookdown.json'." + ); + $config = $this->newIndexConfig('/path/to/bookdown.json', $this->jsonContentNotArray); + } + + public function testContentItemNotStringOrObject() + { + $this->setExpectedException( + 'Bookdown\Bookdown\Exception', + "Content origin must be object or string in '/path/to/bookdown.json'." + ); + $config = $this->newIndexConfig('/path/to/bookdown.json', $this->jsonContentItemNotStringOrObject); + } + public function testContentIndex() { $this->setExpectedException( 'Bookdown\Bookdown\Exception', - "Disallowed 'index' content in /path/to/bookdown.json." + "Disallowed 'index' content name in '/path/to/bookdown.json'." ); $config = $this->newIndexConfig('/path/to/bookdown.json', $this->jsonContentIndex); } - public function testValidLocalJson() + public function testValidLocal() { - $config = $this->newIndexConfig('/path/to/bookdown.json', $this->validLocalJson); + $config = $this->newIndexConfig('/path/to/bookdown.json', $this->jsonValidLocal); $this->assertSame('/path/to/bookdown.json', $config->getFile()); $this->assertSame('/path/to/', $config->getDir()); @@ -101,11 +149,11 @@ public function testValidLocalJson() $this->assertSame($expect, $config->getContent()); } - public function testValidRemoteJson() + public function testValidRemote() { $config = $this->newIndexConfig( 'http://example.net/path/to/bookdown.json', - $this->validRemoteJson + $this->jsonValidRemote ); $this->assertSame('http://example.net/path/to/bookdown.json', $config->getFile()); @@ -118,7 +166,38 @@ public function testValidRemoteJson() $this->assertSame($expect, $config->getContent()); } - public function testInvalidRemoteJson() + public function testContentConvenience() + { + $config = $this->newIndexConfig( + '/path/to/bookdown.json', + $this->jsonContentConvenience + ); + + $this->assertSame('/path/to/bookdown.json', $config->getFile()); + + $expect = array( + 'foo' => '/path/to/foo.md', + 'bar' => '/path/to/bar/bookdown.json', + 'baz' => 'http://example.com/baz.md', + 'dib' => 'http://example.dom/dib/bookdown.json', + ); + + $this->assertSame($expect, $config->getContent()); + } + + public function testReusedContentName() + { + $this->setExpectedException( + 'Bookdown\Bookdown\Exception', + "Content name 'foo' already set in '/path/to/bookdown.json'." + ); + $config = $this->newIndexConfig( + '/path/to/bookdown.json', + $this->jsonReusedContentName + ); + } + + public function testInvalidRemote() { $this->setExpectedException( 'Bookdown\Bookdown\Exception', @@ -126,7 +205,7 @@ public function testInvalidRemoteJson() ); $config = $this->newIndexConfig( 'http://example.net/path/to/bookdown.json', - $this->validLocalJson + $this->jsonValidLocal ); } } diff --git a/tests/Config/RootConfigTest.php b/tests/Config/RootConfigTest.php index 2746ea8..48b9580 100644 --- a/tests/Config/RootConfigTest.php +++ b/tests/Config/RootConfigTest.php @@ -7,11 +7,11 @@ class RootConfigTest extends \PHPUnit_Framework_TestCase protected $maxRootJson = '{ "title": "Example Title", - "content": { - "foo": "foo.md", - "bar": "/bar.md", - "baz": "http://example.com/baz.md" - }, + "content": [ + {"foo": "foo.md"}, + {"bar": "/bar.md"}, + {"baz": "http://example.com/baz.md"} + ], "target": "my/target", "template": "../templates/master.php", "conversionProcess": "My\\\\Conversion\\\\Builder", @@ -23,21 +23,21 @@ class RootConfigTest extends \PHPUnit_Framework_TestCase protected $minRootJson = '{ "title": "Example Title", - "content": { - "foo": "foo.md", - "bar": "/bar.md", - "baz": "http://example.com/baz.md" - }, + "content": [ + {"foo": "foo.md"}, + {"bar": "/bar.md"}, + {"baz": "http://example.com/baz.md"} + ], "target": "_site" }'; protected $missingTargetJson = '{ "title": "Example Title", - "content": { - "foo": "foo.md", - "bar": "/bar.md", - "baz": "http://example.com/baz.md" - } + "content": [ + {"foo": "foo.md"}, + {"bar": "/bar.md"}, + {"baz": "http://example.com/baz.md"} + ] }'; protected function newRootConfig($file, $data) diff --git a/tests/Service/ProcessorBuilderTest.php b/tests/Service/ProcessorBuilderTest.php index 64afdd4..7605022 100644 --- a/tests/Service/ProcessorBuilderTest.php +++ b/tests/Service/ProcessorBuilderTest.php @@ -1,5 +1,5 @@ fsio->put('/path/to/bookdown.json', '{ "title": "Example Book", - "content": { - "chapter-1": "chapter-1/bookdown.json" - }, + "content": [ + {"chapter-1": "chapter-1/bookdown.json"} + ], "target": "/_site" }'); $this->fsio->put('/path/to/chapter-1/bookdown.json', '{ "title": "Chapter 1", - "content": { - "section-1": "section-1.md" - } + "content": [ + {"section-1": "section-1.md"} + ] }'); }