diff --git a/README.md b/README.md index 363db4b..8db64cc 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,52 @@ # Mate -Your functional programming helper. +[![Build Status](https://travis-ci.org/webNeat/mate.svg?branch=master)](https://travis-ci.org/webNeat/mate) +[![Coverage Status](https://coveralls.io/repos/github/webNeat/mate/badge.svg?branch=master)](https://coveralls.io/github/webNeat/mate?branch=master) +[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/webneat) +[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat)](https://github.com/webNeat/mate/blob/master/LICENSE) +# What is Mate? + +**Mate** is a tool to generate documentation and tests from PHPDoc comments for functional libraries. + +> What is a PHPDoc comment? + +it's a comment like + +```php +/** + * Description here. + * @tag value + * @other-tag some other value + */ +``` + +> Ok, and what do you mean by "functional library"? + +I mean a group of [pure functoions](https://en.wikipedia.org/wiki/Pure_function) and type definitions. Testing pure functions is easy and can be done inside a comment. Testing a function which has side effects or a class that alters its internal state would be complicated and is not part of Mate features (yet?). + +> Hmm, so you mean that I can't use Mate if my project contains classes or non-pure functions? + +You can use Mate on any project to generate documentation and tests for your pure functions. This will not influence other parts of your project. Mate can also be used to watch changes on your source files and run `phpunit` whenever a file changes. + +# Requirements + +- PHP 7.1+ + +# Installation + +Install it globally + +``` +composer global require wn/mate +``` + +and/or as a dev dependency + +``` +composer require wn/mate --dev +``` + +# Getting Started + +... diff --git a/composer.json b/composer.json index e627ee5..1bd037a 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "wn/mate", - "description": "Your functional programming helper.", + "description": "A tool to generate documentation and tests from PHPDoc comments.", "type": "library", "license": "MIT", "authors": [ diff --git a/docs/_types.md b/docs/_types.md index 632e22c..d47938b 100644 --- a/docs/_types.md +++ b/docs/_types.md @@ -18,15 +18,11 @@ - [UnknownBlock](#unknown_block) # Block -/** One of `FunctionBlock`, `TypeBlock` or `UnknownBlock`. -*/ # Config -/** -*/ ```php { string $srcDir, @@ -37,8 +33,7 @@ One of `FunctionBlock`, `TypeBlock` or `UnknownBlock`. ``` # Doc -/** -*/ + ```php { string $path;, @@ -50,8 +45,7 @@ One of `FunctionBlock`, `TypeBlock` or `UnknownBlock`. ``` # File -/** -*/ + ```php { string $path, @@ -60,8 +54,7 @@ One of `FunctionBlock`, `TypeBlock` or `UnknownBlock`. ``` # FunctionBlock -/** -*/ + ```php { string $type, @@ -74,8 +67,7 @@ One of `FunctionBlock`, `TypeBlock` or `UnknownBlock`. ``` # FunctionDoc -/** -*/ + ```php { string $name, @@ -86,8 +78,7 @@ One of `FunctionBlock`, `TypeBlock` or `UnknownBlock`. ``` # FunctionTest -/** -*/ + ```php { string $name, @@ -96,8 +87,7 @@ One of `FunctionBlock`, `TypeBlock` or `UnknownBlock`. ``` # Module -/** -*/ + ```php { string $path, @@ -110,8 +100,7 @@ One of `FunctionBlock`, `TypeBlock` or `UnknownBlock`. ``` # Parameter -/** -*/ + ```php { string $name, @@ -121,8 +110,7 @@ One of `FunctionBlock`, `TypeBlock` or `UnknownBlock`. ``` # Tag -/** -*/ + ```php { string $name, @@ -131,8 +119,7 @@ One of `FunctionBlock`, `TypeBlock` or `UnknownBlock`. ``` # Test -/** -*/ + ```php { string $path;, @@ -146,8 +133,7 @@ One of `FunctionBlock`, `TypeBlock` or `UnknownBlock`. ``` # TypeBlock -/** -*/ + ```php { string $type, @@ -159,8 +145,7 @@ One of `FunctionBlock`, `TypeBlock` or `UnknownBlock`. ``` # TypeDoc -/** -*/ + ```php { string $name, @@ -170,8 +155,7 @@ One of `FunctionBlock`, `TypeBlock` or `UnknownBlock`. ``` # UnknownBlock -/** -*/ + ```php { string $type, diff --git a/docs/common.md b/docs/common.md index 5d6abad..01b033c 100644 --- a/docs/common.md +++ b/docs/common.md @@ -1,7 +1,6 @@ # common -/** -*/ + - [alias](#alias) - [codes_from_description](#codes_from_description) @@ -12,9 +11,7 @@ ```php function alias(string $name) : string ``` -/** Returns the full name of a function within this project. -*/ ```php alias('parse_file'); //=> 'Wn\Mate\parse_file' ``` @@ -23,9 +20,7 @@ alias('parse_file'); //=> 'Wn\Mate\parse_file' ```php function codes_from_description(string $description) : array ``` -/** Gets an array of code snippets from a text. -*/ ```php $description = "some text\n```php\nfirst code\n```\nother text\n```php\nother\ncode\n```"; codes_from_description($description); //=> [ @@ -38,9 +33,7 @@ codes_from_description($description); //=> [ ```php function split_code(string $code) : array ``` -/** Splits code into multiple statements. -*/ ```php $code = "\$n = 5;\n\$add = function (\$a) {\n return \$a + 1;\n};\n\$y = \$add(\$n);"; diff --git a/docs/doc.md b/docs/doc.md index 104fcff..86bbc3c 100644 --- a/docs/doc.md +++ b/docs/doc.md @@ -1,7 +1,6 @@ # doc -/** -*/ + - [make_doc](#make_doc) @@ -10,10 +9,7 @@ ```php function make_doc(Config $config, Module $module, bool $withTypes) : Doc ``` -/** Make a `Doc` from a module. - -*/ ```php $module = Module::of( '/path/to/src/awesome-name.php', diff --git a/docs/parse.md b/docs/parse.md index 56cefe9..e6a332c 100644 --- a/docs/parse.md +++ b/docs/parse.md @@ -1,8 +1,6 @@ # parse -/** Functions to parse source files. -*/ - [make_module](#make_module) - [blocks_from_content](#blocks_from_content) @@ -17,62 +15,44 @@ Functions to parse source files. ```php function make_module(Module $file) : Module ``` -/** Creates a module from a file. -*/ - # blocks_from_content ```php function blocks_from_content(string $content) : array ``` -/** Returns an array of `Block`s from a content. -*/ - # block_from_comment ```php function block_from_comment(string $comment) : Block ``` -/** Parses a comment and returns a `Block`. -*/ - # make_type_block ```php function make_type_block(UnknownBlock $block) : Block ``` -/** Makes a `TypeBlock` from an `Block` if it has a `@type` tag. The `Block` is returned otherwise. -*/ - # make_function_block ```php function make_function_block(UnknownBlock $block) : Block ``` -/** Makes a `FunctionBlock` from an `UnknownBlock` if it has a `@function` tag. The `UnknownBlock` is returned otherwise. -*/ - # make_parameter ```php function make_parameter(string $text) : Parameter ``` -/** Creates a Parameter from the content of `@param` or `@field` tags. - -*/ ```php make_parameter(' string $name ...'); //=> Parameter::of('$name', 'string', '...') make_parameter('array< T > $fns '); //=> Parameter::of('$fns', 'array< T >') @@ -83,10 +63,7 @@ make_parameter('((int, int): int) $fn '); //=> Parameter::of('$fn', '((int, int) ```php function tag_from_line(string $line) : array ``` -/** Converts a comment line `@xxx yyyy zzz` to the pair `['xxx', 'yyyy zzz']`. - -*/ ```php tag_from_line('@tag value'); //=> ['tag', 'value'] tag_from_line('@tag value is here'); //=> ['tag', 'value is here'] diff --git a/docs/test.md b/docs/test.md index fd93900..2ded075 100644 --- a/docs/test.md +++ b/docs/test.md @@ -1,7 +1,6 @@ # test -/** -*/ + - [make_test](#make_test) @@ -10,10 +9,7 @@ ```php function make_test(Config $config, Module $module) : Test ``` -/** Make a `Test` from a module. - -*/ ```php $module = Module::of( '/path/to/src/awesome-name.php', diff --git a/mate.lock b/mate.lock index c88b18b..74714b2 100644 --- a/mate.lock +++ b/mate.lock @@ -1 +1 @@ -{"files":{"\/home\/webneat\/workspace\/projects\/mate\/src\/common.php":"18832394","\/home\/webneat\/workspace\/projects\/mate\/src\/doc.php":"1db85edc","\/home\/webneat\/workspace\/projects\/mate\/src\/parse.php":"d6edd445","\/home\/webneat\/workspace\/projects\/mate\/src\/render.php":"ca386b19","\/home\/webneat\/workspace\/projects\/mate\/src\/test.php":"015b5be1","\/home\/webneat\/workspace\/projects\/mate\/src\/classes\/MateCommand.php":"9ed3d4b5","\/home\/webneat\/workspace\/projects\/mate\/src\/classes\/TestCase.php":"37226420","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/Block.php":"9cd83fb2","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/Config.php":"8de82492","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/Doc.php":"d863c18f","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/File.php":"ec3668e3","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/FunctionBlock.php":"9258ee3f","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/FunctionDoc.php":"7947baae","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/FunctionTest.php":"94bc6bc7","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/Module.php":"3459f54c","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/Parameter.php":"8bbd8f90","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/Tag.php":"3a306237","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/Test.php":"72eb0187","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/TypeBlock.php":"de56c2f4","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/TypeDoc.php":"f74091f5","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/UnknownBlock.php":"72e597a0","\/home\/webneat\/workspace\/projects\/mate\/src\/sample.php":"01a0b8b2"},"modules":{"\/home\/webneat\/workspace\/projects\/mate\/src\/common.php":"2fb83deb","\/home\/webneat\/workspace\/projects\/mate\/src\/doc.php":"cfc1d651","\/home\/webneat\/workspace\/projects\/mate\/src\/parse.php":"e3d25da4","\/home\/webneat\/workspace\/projects\/mate\/src\/test.php":"226ffed0","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/Block.php":"97d2a56e","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/Config.php":"20460810","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/Doc.php":"4a0a2daa","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/File.php":"653cc010","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/FunctionBlock.php":"1f4b5e77","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/FunctionDoc.php":"09af0d57","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/FunctionTest.php":"a83ac59b","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/Module.php":"5ab158bf","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/Parameter.php":"b593e72c","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/Tag.php":"8eccbf3a","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/Test.php":"dbb6742f","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/TypeBlock.php":"c1e93663","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/TypeDoc.php":"3a81e9ea","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/UnknownBlock.php":"bd173a7d","\/home\/webneat\/workspace\/projects\/mate\/src\/_types.php":"39c2e6ce","\/home\/webneat\/workspace\/projects\/mate\/src\/sample.php":"7efdc011"}} \ No newline at end of file +{"files":[],"modules":{"\/home\/webneat\/workspace\/projects\/mate\/src\/common.php":"df2b3a2c","\/home\/webneat\/workspace\/projects\/mate\/src\/doc.php":"0e95d477","\/home\/webneat\/workspace\/projects\/mate\/src\/parse.php":"e31955fb","\/home\/webneat\/workspace\/projects\/mate\/src\/test.php":"3bcefced","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/Block.php":"0275a392","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/Config.php":"7c450622","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/Doc.php":"938d2bd2","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/File.php":"814fbe38","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/FunctionBlock.php":"acee5c9f","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/FunctionDoc.php":"6d710b7f","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/FunctionTest.php":"8925c3c3","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/Module.php":"24a156e7","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/Parameter.php":"6f51e554","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/Tag.php":"b32dbd62","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/Test.php":"ae137257","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/TypeBlock.php":"b8b2348b","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/TypeDoc.php":"dee4e812","\/home\/webneat\/workspace\/projects\/mate\/src\/types\/UnknownBlock.php":"8cea38a5","\/home\/webneat\/workspace\/projects\/mate\/src\/_types.php":"06d7df6a"}} \ No newline at end of file diff --git a/src/classes/MateCommand.php b/src/classes/MateCommand.php index 3485037..a93e06d 100644 --- a/src/classes/MateCommand.php +++ b/src/classes/MateCommand.php @@ -1,8 +1,9 @@ name('Mate') ->version('1.0.0-alpha') - ->description('Your functional programming helper.') + ->description('a tool to generate documentation and tests from PHPDoc comments.') ->syntax('configPath: (string:mate.json)') - ->options(['--dont-run-tests', '--watch', '--no-cache']) + ->options(['--dont-run-tests', '--watch', '--no-cache', '--no-tests', '--no-docs']) ->describe('configPath', 'Path to the config file.') ->describe('--dont-run-tests', "Don't run phpunit after the build.") ->describe('--watch', "Watch source files for changes.") - ->describe('--no-cache', "Don't use cache. Please don't combine this option with --watch.") + ->describe('--no-cache', "Don't use cache. Should not be combined with --watch.") + ->describe('--no-tests', "Don't generate test files.") + ->describe('--no-docs', "Don't generate documentation files.") ->configPaths([__DIR__ . '/../../mate.json', 'mate.json']); } protected function initConfig() { $data = $this->config(); - $this->fs->dir($data['testsDir'], true); - $this->fs->dir($data['docsDir'], true); + if (! $this->option('--no-tests')) { + $this->fs->dir($data['testsDir'], true); + } + if (! $this->option('--no-docs')) { + $this->fs->dir($data['docsDir'], true); + } $this->config = M\Config::of($data); return $this; } protected function loadCache() { + if ($this->option('--no-cache')) { + $this->cache = ['files' => [], 'modules' => []]; + return $this; + } $cachePath = $this->config->cachePath; $this->cache = $this->fs->isFile($cachePath) ? json_decode($this->fs->file($cachePath)->content(), true) @@ -122,10 +133,12 @@ protected function generate() { } } $this->writeTypes($types); - if (! $this->option('--no-cache')) + if (! $this->option('--no-cache')) { $this->saveCache(); - if (! $this->option('--dont-run-tests') && $runTests) + } + if (! $this->option('--dont-run-tests') && $runTests) { $this->runTests(); + } } protected function runTests() { @@ -139,19 +152,24 @@ protected function hashModule(M\Module $module) { } protected function build(M\Module $module) { - if (!empty($module->functions)) { + if (!$this->option('--no-tests') && !empty($module->functions)) { $test = M\make_test($this->config, $module); $this->fs->file($test->path, true)->content(M\render_test($test)); } - if (!empty($module->functions)) { + if (!$this->option('--no-docs') && !empty($module->functions)) { $doc = M\make_doc($this->config, $module); $this->fs->file($doc->path, true)->content(M\render_doc($doc)); } } protected function writeTypes(array $types) { - if (empty($types)) return; + if ($this->option('--no-docs') || empty($types)) { + return; + } + usort($types, function($a, $b) { + return strcmp($a->name, $b->name); + }); $path = $this->config->srcDir . '/_types.php'; $module = M\Module::of($path, '', '', [], $types, []); $hash = $this->hashModule($module); diff --git a/src/parse.php b/src/parse.php index 36b0ea6..c8584ff 100644 --- a/src/parse.php +++ b/src/parse.php @@ -52,7 +52,7 @@ function make_module(File $file) { */ function blocks_from_content(string $content): array { return F\s($content) - ->match("/\s(?:\/\*(?:[^*]|(?:\*[^\/]))*\*\/)\s/") + ->match("/\/\*(?:[^*]|(?:\*[^\/]))*\*\//") ->map(function($comment) { $lines = explode("\n", $comment); array_pop($lines); diff --git a/tests/AcceptanceTest.php b/tests/AcceptanceTest.php index ca45f10..e7043a0 100644 --- a/tests/AcceptanceTest.php +++ b/tests/AcceptanceTest.php @@ -14,13 +14,13 @@ public function test_it_generates_test_and_doc() { $this->assertTrue($this->fs->isFile('docs/sample.md')); $this->assertEquals( - file_get_contents(__DIR__.'/examples/sample_test.php'), - $this->fs->file('tests/SampleTest.php')->content() + trim(file_get_contents(__DIR__.'/examples/sample_test.php')), + trim($this->fs->file('tests/SampleTest.php')->content()) ); $this->assertEquals( - file_get_contents(__DIR__.'/examples/sample.md'), - $this->fs->file('docs/sample.md')->content() + trim(file_get_contents(__DIR__.'/examples/sample.md')), + trim($this->fs->file('docs/sample.md')->content()) ); } diff --git a/tests/DocTest.php b/tests/DocTest.php index 7870022..2a216f6 100644 --- a/tests/DocTest.php +++ b/tests/DocTest.php @@ -81,7 +81,7 @@ function test_make_doc() { $config->srcDir = '/other/src/path'; $this->assertThrows(function() use($config, $module) { - make_doc($config, $module); + make_doc($config, $module); }, "The module path '/path/to/src/awesome-name.php' does not start with the configured source path '/other/src/path', please use a config file to customize this value."); } diff --git a/tests/examples/_types.md b/tests/examples/_types.md index c4c6f61..2cf0fb5 100644 --- a/tests/examples/_types.md +++ b/tests/examples/_types.md @@ -5,10 +5,8 @@ - [Developer](#developer) # Developer -/** Represents a developer. -*/ ```php { string $name, diff --git a/tests/examples/sample.md b/tests/examples/sample.md index 11ca4c3..2f4273d 100644 --- a/tests/examples/sample.md +++ b/tests/examples/sample.md @@ -1,9 +1,7 @@ # sample -/** This is a source file to test the Mate command. it contains some random functions and types definitions. -*/ - [add](#add) @@ -12,11 +10,9 @@ it contains some random functions and types definitions. ```php function add(int|float $x, int|float $y) : int|float ``` -/** Adds two numbers. -*/ ```php add(5, 2); //=> 7 add(0, 1); //=> 1 add('Hey', 'you'); // throws "'Hey' is not a number!" -``` \ No newline at end of file +```