tarsana /
functional
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php namespace Tarsana\Functional; |
||
| 2 | /** |
||
| 3 | * This script parses the source files using [dox](https://github.com/tj/dox) |
||
| 4 | * and generates the unit tests and documentation files. |
||
| 5 | */ |
||
| 6 | require __DIR__ . '/vendor/autoload.php'; |
||
| 7 | |||
| 8 | /** |
||
| 9 | * Custom Types: |
||
| 10 | * DoxBlock :: { |
||
| 11 | * tags: [{ |
||
| 12 | * type: String, |
||
| 13 | * string: String, |
||
| 14 | * types: [String], |
||
| 15 | * name: String, |
||
| 16 | * description: String |
||
| 17 | * ... |
||
| 18 | * }], |
||
| 19 | * description: { |
||
| 20 | * full: String, |
||
| 21 | * summary: String, |
||
| 22 | * body: String |
||
| 23 | * }, |
||
| 24 | * code: String, |
||
| 25 | * ctx: { |
||
| 26 | * type: String, |
||
| 27 | * name: String, |
||
| 28 | * ... |
||
| 29 | * } |
||
| 30 | * isPrivate: |
||
| 31 | * isEvent: |
||
| 32 | * isConstructor: |
||
| 33 | * line: |
||
| 34 | * ignore: |
||
| 35 | * } |
||
| 36 | * |
||
| 37 | * Block :: { |
||
| 38 | * type: file|function|class|method |
||
| 39 | * name: String // DoxBlock.ctx.name |
||
| 40 | * params: [{type: String, name: String}] |
||
| 41 | * return: String |
||
| 42 | * signatures: [String] |
||
| 43 | * description: String |
||
| 44 | * summary: String |
||
| 45 | * internal: Boolean |
||
| 46 | * ignore: Boolean |
||
| 47 | * code: String |
||
| 48 | * } |
||
| 49 | * |
||
| 50 | * Operation :: { |
||
| 51 | * name: String, |
||
| 52 | * signature: String |
||
| 53 | * } |
||
| 54 | * |
||
| 55 | * Module :: { |
||
| 56 | * path: String |
||
| 57 | * name: String |
||
| 58 | * docsPath: String |
||
| 59 | * testsPath: String |
||
| 60 | * blocks: [Block] |
||
| 61 | * docs: String |
||
| 62 | * tests: String |
||
| 63 | * testsFooter: String |
||
| 64 | * streamOperations: String |
||
| 65 | * streamMethods: String |
||
| 66 | * } |
||
| 67 | */ |
||
| 68 | |||
| 69 | /** |
||
| 70 | * The entry point. |
||
| 71 | * |
||
| 72 | * @signature [String] -> IO |
||
| 73 | * @param array $modules |
||
| 74 | * @return void |
||
| 75 | */ |
||
| 76 | function build_main($modules) { |
||
| 77 | build_init_stream_operations(); |
||
| 78 | each(_f('build_module'), $modules); |
||
| 79 | build_close_stream_operations(); |
||
| 80 | } |
||
| 81 | |||
| 82 | /** |
||
| 83 | * Writes the header of the stream operations file. |
||
| 84 | * |
||
| 85 | * @signature IO |
||
| 86 | * @return void |
||
| 87 | */ |
||
| 88 | function build_init_stream_operations() { |
||
| 89 | file_put_contents( |
||
| 90 | 'src/Internal/_stream_operations.php', |
||
| 91 | "<?php\n\nuse Tarsana\Functional as F;\n\nreturn F\map(F\apply(F\_f('_stream_operation')), [\n\t['then', 'Function -> Any -> Any', F\_f('_stream_then')],\n" |
||
| 92 | ); |
||
| 93 | file_put_contents( |
||
| 94 | 'docs/stream-methods.md', |
||
| 95 | "# Stream Methods" |
||
| 96 | ); |
||
| 97 | } |
||
| 98 | |||
| 99 | /** |
||
| 100 | * Writes the footer of the stream operations file. |
||
| 101 | * |
||
| 102 | * @signature IO |
||
| 103 | * @return void |
||
| 104 | */ |
||
| 105 | function build_close_stream_operations() { |
||
| 106 | file_put_contents( |
||
| 107 | 'src/Internal/_stream_operations.php', |
||
| 108 | "\n]);\n", FILE_APPEND |
||
| 109 | ); |
||
| 110 | } |
||
| 111 | |||
| 112 | /** |
||
| 113 | * Extracts the modules files from composer.json. |
||
| 114 | * |
||
| 115 | * @signature [String] |
||
| 116 | * @return array |
||
| 117 | */ |
||
| 118 | function get_modules() { |
||
| 119 | $composer = json_decode(file_get_contents(__DIR__.'/composer.json')); |
||
| 120 | return $composer->autoload->files; |
||
| 121 | } |
||
| 122 | |||
| 123 | /** |
||
| 124 | * Generates unit tests and documentation for a module. |
||
| 125 | * |
||
| 126 | * @signature String -> IO |
||
| 127 | * @param string $path |
||
| 128 | * @return void |
||
| 129 | */ |
||
| 130 | function build_module($path) { |
||
| 131 | apply(process_of([ |
||
| 132 | 'module_of', |
||
| 133 | 'generate_docs', |
||
| 134 | 'generate_tests', |
||
| 135 | 'generate_stream_operations', |
||
| 136 | 'generate_stream_methods', |
||
| 137 | 'write_module' |
||
| 138 | ]), [$path]); |
||
| 139 | } |
||
| 140 | |||
| 141 | /** |
||
| 142 | * Writes the module's docs and tests. |
||
| 143 | * |
||
| 144 | * @signature Module -> IO |
||
| 145 | * @param object $module |
||
| 146 | * @return void |
||
| 147 | */ |
||
| 148 | function write_module($module) { |
||
| 149 | View Code Duplication | if ($module->docs) { |
|
| 150 | $docsDir = dirname($module->docsPath); |
||
| 151 | if (!is_dir($docsDir)) |
||
| 152 | mkdir($docsDir, 0777, true); |
||
| 153 | file_put_contents($module->docsPath, $module->docs); |
||
| 154 | } |
||
| 155 | View Code Duplication | if ($module->tests) { |
|
| 156 | $testsDir = dirname($module->testsPath); |
||
| 157 | if (!is_dir($testsDir)) |
||
| 158 | mkdir($testsDir, 0777, true); |
||
| 159 | file_put_contents($module->testsPath, $module->tests); |
||
| 160 | } |
||
| 161 | if ($module->streamOperations) { |
||
| 162 | file_put_contents('src/Internal/_stream_operations.php', $module->streamOperations, FILE_APPEND); |
||
| 163 | } |
||
| 164 | if ($module->streamMethods) { |
||
| 165 | file_put_contents('docs/stream-methods.md', $module->streamMethods, FILE_APPEND); |
||
| 166 | } |
||
| 167 | } |
||
| 168 | |||
| 169 | /** |
||
| 170 | * Creates a module from a path. |
||
| 171 | * |
||
| 172 | * @signature String -> Module |
||
| 173 | * @param string $path |
||
| 174 | * @return object |
||
| 175 | */ |
||
| 176 | function module_of($path) { |
||
| 177 | return apply(process_of([ |
||
| 178 | 'fill_name', |
||
| 179 | 'fill_docs_path', |
||
| 180 | 'fill_tests_path', |
||
| 181 | 'fill_blocks' |
||
| 182 | ]), [(object)['path' => $path]]); |
||
| 183 | } |
||
| 184 | |||
| 185 | /** |
||
| 186 | * Fills documentation file path based on source file path. |
||
| 187 | * 'src/xxx.php' -> 'docs/xxx.md' |
||
| 188 | * |
||
| 189 | * @signature Module -> Module |
||
| 190 | * @param object $module |
||
| 191 | * @return object |
||
| 192 | */ |
||
| 193 | function fill_docs_path($module) { |
||
| 194 | $module->docsPath = replace(['src', '.php'], ['docs', '.md'], $module->path); |
||
| 195 | return $module; |
||
| 196 | } |
||
| 197 | |||
| 198 | /** |
||
| 199 | * Fills tests file path based on source file path. |
||
| 200 | * 'src/xxx.php' -> 'tests/xxxTest.php' |
||
| 201 | * |
||
| 202 | * @signature Module -> Module |
||
| 203 | * @param object $module |
||
| 204 | * @return object |
||
| 205 | */ |
||
| 206 | function fill_tests_path($module) { |
||
| 207 | $name = ucfirst(camelCase($module->name)); |
||
| 208 | $dir = 'tests' . remove(3, dirname($module->path)); |
||
| 209 | $module->testsPath = "{$dir}/{$name}Test.php"; |
||
| 210 | return $module; |
||
| 211 | } |
||
| 212 | |||
| 213 | /** |
||
| 214 | * Fills the name of the Module based on the path. |
||
| 215 | * 'src/xxx/aaa.php' -> 'aaa' |
||
| 216 | * |
||
| 217 | * @signature Module -> Module |
||
| 218 | * @param object $module |
||
| 219 | * @return object |
||
| 220 | */ |
||
| 221 | function fill_name($module) { |
||
| 222 | $module->name = apply(pipe(split('/'), last(), split('.'), head()), [$module->path]); |
||
| 223 | return $module; |
||
| 224 | } |
||
| 225 | |||
| 226 | /** |
||
| 227 | * Fills the blocks of the Module based on the path. |
||
| 228 | * |
||
| 229 | * @signature Module -> Module |
||
| 230 | * @param array $module |
||
| 231 | * @return array |
||
| 232 | */ |
||
| 233 | function fill_blocks($module) { |
||
| 234 | $module->blocks = apply(pipe( |
||
| 235 | prepend('dox -r < '), // "dox -r < src/...php" |
||
| 236 | 'shell_exec', // "[{...}, ...]" |
||
| 237 | 'json_decode', // [DoxBlock] |
||
| 238 | map(_f('make_block')) |
||
| 239 | // sort() |
||
| 240 | ), [$module->path]); |
||
| 241 | return $module; |
||
| 242 | } |
||
| 243 | |||
| 244 | /** |
||
| 245 | * Converts a DoxBlock to a Block. |
||
| 246 | * |
||
| 247 | * @signature DoxBlock -> Block |
||
| 248 | * @param object $doxBlock |
||
| 249 | * @return object |
||
| 250 | */ |
||
| 251 | function make_block($doxBlock) { |
||
| 252 | $tags = groupBy(get('name'), tags_of($doxBlock)); |
||
| 253 | |||
| 254 | $type = 'function'; |
||
| 255 | if (has('file', $tags)) $type = 'file'; |
||
| 256 | if (has('class', $tags)) $type = 'class'; |
||
| 257 | if (has('method', $tags)) $type = 'method'; |
||
| 258 | |||
| 259 | $params = map(function($tag){ |
||
| 260 | $parts = split(' ', get('value', $tag)); |
||
| 261 | return [ |
||
| 262 | 'type' => $parts[0], |
||
| 263 | 'name' => $parts[1] |
||
| 264 | ]; |
||
| 265 | }, get('param', $tags) ?: []); |
||
| 266 | |||
| 267 | $return = getPath(['return', 0, 'value'], $tags); |
||
| 268 | $signatures = get('signature', $tags); |
||
| 269 | if ($signatures) |
||
| 270 | $signatures = map(get('value'), $signatures); |
||
| 271 | return (object) [ |
||
| 272 | 'type' => $type, |
||
| 273 | 'name' => getPath(['ctx', 'name'], $doxBlock), |
||
| 274 | 'params' => $params, |
||
| 275 | 'return' => $return, |
||
| 276 | 'signatures' => $signatures, |
||
| 277 | 'description' => getPath(['description', 'full'], $doxBlock), |
||
| 278 | 'summary' => getPath(['description', 'summary'], $doxBlock), |
||
| 279 | 'internal' => has('internal', $tags), |
||
| 280 | 'ignore' => has('ignore', $tags), |
||
| 281 | 'stream' => has('stream', $tags) |
||
| 282 | // 'code' => get('code', $doxBlock) |
||
| 283 | ]; |
||
| 284 | } |
||
| 285 | |||
| 286 | /** |
||
| 287 | * Returns an array of tags, each having a name and a value. |
||
| 288 | * |
||
| 289 | * @signature DoxBlock -> [{name: String, value: String}] |
||
| 290 | * @param object $doxBlock |
||
| 291 | * @return array |
||
| 292 | */ |
||
| 293 | function tags_of($doxBlock) { |
||
| 294 | if ($doxBlock->tags) |
||
| 295 | return map(function($tag){ |
||
| 296 | return (object) [ |
||
| 297 | 'name' => $tag->type, |
||
| 298 | 'value' => $tag->string |
||
| 299 | ]; |
||
| 300 | }, $doxBlock->tags); |
||
| 301 | return []; |
||
| 302 | } |
||
| 303 | |||
| 304 | /** |
||
| 305 | * Generates documentation contents for a module. |
||
| 306 | * |
||
| 307 | * @signature Module -> Module |
||
| 308 | * @param object $module |
||
| 309 | * @return object |
||
| 310 | */ |
||
| 311 | function generate_docs($module) { |
||
| 312 | $module->docs = ''; |
||
| 313 | if (startsWith('_', $module->name)) |
||
| 314 | return $module; |
||
| 315 | return apply(process_of([ |
||
| 316 | 'generate_docs_header', |
||
| 317 | 'generate_docs_sommaire', |
||
| 318 | 'generate_docs_contents' |
||
| 319 | ]), [$module]); |
||
| 320 | } |
||
| 321 | |||
| 322 | /** |
||
| 323 | * Generates documentation header. |
||
| 324 | * |
||
| 325 | * @signature Module -> Module |
||
| 326 | * @param object $module |
||
| 327 | * @return object |
||
| 328 | */ |
||
| 329 | function generate_docs_header($module) { |
||
| 330 | $name = $module->name; |
||
| 331 | $description = get('description', head($module->blocks)); |
||
| 332 | $module->docs .= "#{$name}\n\n{$description}\n\n"; |
||
| 333 | return $module; |
||
| 334 | } |
||
| 335 | |||
| 336 | /** |
||
| 337 | * Generates documentation table of contents. |
||
| 338 | * |
||
| 339 | * @signature Module -> Module |
||
| 340 | * @param object $module |
||
| 341 | * @return object |
||
| 342 | */ |
||
| 343 | View Code Duplication | function generate_docs_sommaire($module) { |
|
| 344 | $blocks = filter ( |
||
| 345 | satisfiesAll(['ignore' => not(), 'internal' => not(), 'type' => equals('function')]), |
||
| 346 | $module->blocks |
||
| 347 | ); |
||
| 348 | $items = map(_f('generate_docs_sommaire_item'), $blocks); |
||
| 349 | $module->docs .= join('', $items); |
||
| 350 | return $module; |
||
| 351 | } |
||
| 352 | |||
| 353 | /** |
||
| 354 | * Generates an item of the documentation's table of contents. |
||
| 355 | * |
||
| 356 | * @signature Block -> String |
||
| 357 | * @param object $block |
||
| 358 | * @return string |
||
| 359 | */ |
||
| 360 | function generate_docs_sommaire_item($block) { |
||
| 361 | $title = get('name', $block); |
||
| 362 | $link = lowerCase($title); |
||
| 363 | return "- [{$title}](#{$link}) - {$block->summary}\n\n"; |
||
| 364 | } |
||
| 365 | |||
| 366 | /** |
||
| 367 | * Generates documentation contents. |
||
| 368 | * |
||
| 369 | * @signature Module -> Module |
||
| 370 | * @param object $module |
||
| 371 | * @return object |
||
| 372 | */ |
||
| 373 | View Code Duplication | function generate_docs_contents($module) { |
|
| 374 | $blocks = filter ( |
||
| 375 | satisfiesAll(['ignore' => not(), 'internal' => not()]), |
||
| 376 | $module->blocks |
||
| 377 | ); |
||
| 378 | $contents = map(_f('generate_docs_contents_item'), $blocks); |
||
| 379 | $module->docs .= join('', $contents); |
||
| 380 | return $module; |
||
| 381 | } |
||
| 382 | |||
| 383 | /** |
||
| 384 | * Generates an item of the documentation's contents. |
||
| 385 | * |
||
| 386 | * @signature Block -> String |
||
| 387 | * @param object $block |
||
| 388 | * @return string |
||
| 389 | */ |
||
| 390 | function generate_docs_contents_item($block) { |
||
| 391 | if ($block->type != 'function') |
||
| 392 | return ''; |
||
| 393 | $params = join(', ', map(pipe(values(), join(' ')), get('params', $block))); |
||
| 394 | $return = get('return', $block); |
||
| 395 | $prototype = "```php\n{$block->name}({$params}) : {$return}\n```\n\n"; |
||
| 396 | $signature = ''; |
||
| 397 | $blockSignature = join("\n", $block->signatures); |
||
| 398 | if ($blockSignature) |
||
| 399 | $signature = "```\n{$blockSignature}\n```\n\n"; |
||
| 400 | return "# {$block->name}\n\n{$prototype}{$signature}{$block->description}\n\n"; |
||
| 401 | } |
||
| 402 | |||
| 403 | /** |
||
| 404 | * Generates tests contents for a module. |
||
| 405 | * |
||
| 406 | * @signature Module -> Module |
||
| 407 | * @param object $module |
||
| 408 | * @return object |
||
| 409 | */ |
||
| 410 | function generate_tests($module) { |
||
| 411 | $module->tests = ''; |
||
| 412 | $module->testsFooter = ''; |
||
| 413 | return apply(process_of([ |
||
| 414 | 'generate_tests_header', |
||
| 415 | 'generate_tests_contents', |
||
| 416 | 'generate_tests_footer' |
||
| 417 | ]), [$module]); |
||
| 418 | } |
||
| 419 | |||
| 420 | /** |
||
| 421 | * Generates module's tests header. |
||
| 422 | * |
||
| 423 | * @signature Module -> Module |
||
| 424 | * @param object $module |
||
| 425 | * @return object |
||
| 426 | */ |
||
| 427 | function generate_tests_header($module) { |
||
| 428 | $namespace = "Tarsana\UnitTests\Functional"; |
||
| 429 | $additionalNamespace = replace("/", "\\", remove(6, dirname($module->testsPath))); |
||
| 430 | if ($additionalNamespace) |
||
| 431 | $namespace .= "\\" . $additionalNamespace; |
||
| 432 | $name = remove(-4, last(split("/", $module->testsPath))); |
||
| 433 | $module->tests .= "<?php namespace {$namespace};\n\nuse Tarsana\Functional as F;\n\nclass {$name} extends \Tarsana\UnitTests\Functional\UnitTest {\n"; |
||
| 434 | return $module; |
||
| 435 | } |
||
| 436 | |||
| 437 | /** |
||
| 438 | * Generates module's tests contents. |
||
| 439 | * |
||
| 440 | * @signature Module -> Module |
||
| 441 | * @param object $module |
||
| 442 | * @return object |
||
| 443 | */ |
||
| 444 | function generate_tests_contents($module) { |
||
| 445 | $blocks = filter ( |
||
| 446 | satisfiesAll(['ignore' => not()]), |
||
| 447 | $module->blocks |
||
| 448 | ); |
||
| 449 | $contents = join("\n", map(function($block) use($module) { |
||
| 450 | return generate_tests_contents_item($block, $module); |
||
| 451 | }, $blocks)); |
||
| 452 | if (trim($contents) != '') |
||
| 453 | $module->tests .= $contents; |
||
| 454 | else |
||
| 455 | $module->tests = ''; |
||
| 456 | return $module; |
||
| 457 | } |
||
| 458 | |||
| 459 | /** |
||
| 460 | * Generates a test for a module. |
||
| 461 | * |
||
| 462 | * @signature Block -> Module -> String |
||
| 463 | * @param object $block |
||
| 464 | * @param object $module |
||
| 465 | * @return string |
||
| 466 | */ |
||
| 467 | function generate_tests_contents_item($block, $module) { |
||
| 468 | if ($block->type != 'function') |
||
| 469 | return ''; |
||
| 470 | |||
| 471 | $code = apply(pipe( |
||
| 472 | _f('code_from_description'), |
||
| 473 | chunks("\"\"''{}[]()", "\n"), |
||
| 474 | map(function($part) use($module) { |
||
| 475 | return add_assertions($part, $module); |
||
| 476 | }), |
||
| 477 | filter(pipe('trim', notEq(''))), |
||
| 478 | chain(split("\n")), |
||
| 479 | map(prepend("\t\t")), |
||
| 480 | join("\n") |
||
| 481 | ), [$block]); |
||
| 482 | |||
| 483 | if ('' == trim($code)) |
||
| 484 | return ''; |
||
| 485 | return prepend("\tpublic function test_{$block->name}() {\n", |
||
| 486 | append("\n\t}\n", $code) |
||
| 487 | ); |
||
| 488 | } |
||
| 489 | |||
| 490 | /** |
||
| 491 | * Extracts the code snippet from the description of a block. |
||
| 492 | * |
||
| 493 | * @signature Block -> String |
||
| 494 | * @param object $block |
||
| 495 | * @return string |
||
| 496 | */ |
||
| 497 | function code_from_description($block) { |
||
| 498 | $description = get('description', $block); |
||
| 499 | if (!contains('```php', $description)) |
||
| 500 | return ''; |
||
| 501 | $code = remove(7 + indexOf('```php', $description), $description); |
||
| 502 | return remove(-4, trim($code)); |
||
| 503 | } |
||
| 504 | |||
| 505 | /** |
||
| 506 | * Adds assertions to a part of the code. |
||
| 507 | * |
||
| 508 | * @signature String -> String |
||
| 509 | * @param string $part |
||
| 510 | * @return string |
||
| 511 | */ |
||
| 512 | function add_assertions($part, $module) { |
||
| 513 | if (contains('; //=> ', $part)) { |
||
| 514 | $pieces = split('; //=> ', $part); |
||
| 515 | $part = "\$this->assertEquals({$pieces[1]}, {$pieces[0]});"; |
||
| 516 | } |
||
| 517 | elseif (contains('; // throws ', $part)) { |
||
| 518 | $pieces = split('; // throws ', $part); |
||
| 519 | $variables = match('/ \$[0-9a-zA-Z_]+/', $pieces[0]); |
||
| 520 | $use = ''; |
||
| 521 | if (length($variables)) { |
||
| 522 | $variables = join(', ', map('trim', $variables)); |
||
| 523 | $use = "use({$variables}) "; |
||
| 524 | } |
||
| 525 | return "\$this->assertErrorThrown(function() {$use}{\n\t$pieces[0]; \n},\n{$pieces[1]});"; |
||
| 526 | } |
||
| 527 | elseif (startsWith('class ', $part) || startsWith('function ', $part)) { |
||
| 528 | $module->testsFooter .= $part . "\n\n"; |
||
| 529 | $part = ''; |
||
| 530 | } |
||
| 531 | return $part; |
||
| 532 | } |
||
| 533 | |||
| 534 | /** |
||
| 535 | * Generates module's tests footer. |
||
| 536 | * |
||
| 537 | * @signature Module -> Module |
||
| 538 | * @param object $module |
||
| 539 | * @return object |
||
| 540 | */ |
||
| 541 | function generate_tests_footer($module) { |
||
| 542 | if ($module->tests) |
||
| 543 | $module->tests .= "}\n\n{$module->testsFooter}"; |
||
| 544 | return $module; |
||
| 545 | } |
||
| 546 | |||
| 547 | /** |
||
| 548 | * Generates module's stream operations. |
||
| 549 | * |
||
| 550 | * @signature Module -> Module |
||
| 551 | * @param array $module |
||
| 552 | * @return array |
||
| 553 | */ |
||
| 554 | function generate_stream_operations($module) { |
||
| 555 | $blocks = filter ( |
||
| 556 | satisfiesAll(['ignore' => equals(false), 'stream' => equals(true)]), |
||
| 557 | $module->blocks |
||
| 558 | ); |
||
| 559 | $operations = map(_f('stream_operation_declaration'), chain(_f('stream_operations_of_block'), $blocks)); |
||
| 560 | $module->streamOperations = join("", $operations); |
||
| 561 | return $module; |
||
| 562 | } |
||
| 563 | |||
| 564 | /** |
||
| 565 | * Gets stream operations from a block. |
||
| 566 | * |
||
| 567 | * @signature Block -> [Operation] |
||
| 568 | * @param object $block |
||
| 569 | * @return string |
||
| 570 | */ |
||
| 571 | function stream_operations_of_block($block) { |
||
| 572 | return map(function($signature) use($block) { |
||
| 573 | return (object) [ |
||
| 574 | 'name' => $block->name, |
||
| 575 | 'signature' => normalize_signature($signature) |
||
| 576 | ]; |
||
| 577 | }, get('signatures', $block)); |
||
| 578 | } |
||
| 579 | |||
| 580 | /** |
||
| 581 | * Converts a formal signature to a stream signature. |
||
| 582 | * [a] becomes List |
||
| 583 | * {k: v} becomes Array|Object |
||
| 584 | * (a -> b) becomes Function |
||
| 585 | * * becomes Any |
||
| 586 | * |
||
| 587 | * @signature String -> String |
||
| 588 | * @param string $signature |
||
| 589 | * @return string |
||
| 590 | */ |
||
| 591 | function normalize_signature($signature) { |
||
| 592 | // This is not the best way to do it :P |
||
| 593 | return join(' -> ', map(pipe( |
||
| 594 | regReplace('/Maybe\([a-z][^\)]*\)/', 'Any'), |
||
| 595 | regReplace('/Maybe\(([^\)]+)\)/', '$1|Null'), |
||
| 596 | regReplace('/\([^\)]+\)/', 'Function'), |
||
| 597 | regReplace('/\[[^\]]+\]/', 'List'), |
||
| 598 | regReplace('/\{[^\}]+\}/', 'Object|Array'), |
||
| 599 | regReplace('/^.$/', 'Any'), |
||
| 600 | regReplace('/[\(\)\[\]\{\}]/', '') |
||
| 601 | ), chunks('(){}', ' -> ', $signature))); |
||
| 602 | } |
||
| 603 | |||
| 604 | /** |
||
| 605 | * Converts a stream operation to declaration array. |
||
| 606 | * |
||
| 607 | * @signature Operation -> String |
||
| 608 | * @param object $operation |
||
| 609 | * @return string |
||
| 610 | */ |
||
| 611 | function stream_operation_declaration($operation) { |
||
| 612 | $name = rtrim($operation->name, '_'); |
||
| 613 | return "\t['{$name}', '{$operation->signature}', F\\{$operation->name}()],\n"; |
||
| 614 | } |
||
| 615 | |||
| 616 | /** |
||
| 617 | * Generates module's stream methods documentation. |
||
| 618 | * |
||
| 619 | * @signature Module -> Module |
||
| 620 | * @param array $module |
||
| 621 | * @return array |
||
| 622 | */ |
||
| 623 | function generate_stream_methods($module) { |
||
| 624 | $blocks = filter ( |
||
| 625 | satisfiesAll(['ignore' => equals(false), 'stream' => equals(true)]), |
||
| 626 | $module->blocks |
||
| 627 | ); |
||
| 628 | $methods = map(stream_method_link($module->name), $blocks); |
||
| 629 | $module->streamMethods = "\n\n## {$module->name}\n\n" . join("\n", $methods); |
||
| 630 | return $module; |
||
| 631 | } |
||
| 632 | |||
| 633 | /** |
||
| 634 | * Gets an element of the stream methods list. |
||
| 635 | * |
||
| 636 | * @signature String -> Block -> String |
||
| 637 | * @param string $moduleName |
||
| 638 | * @param object $block |
||
| 639 | * @return string |
||
| 640 | */ |
||
| 641 | function stream_method_link() { |
||
| 642 | static $curried = false; |
||
| 643 | $curried = $curried ?: curry(function($moduleName, $block) { |
||
| 644 | return "- [{$block->name}](https://github.com/tarsana/functional/blob/master/docs/{$moduleName}.md#{$block->name}) - {$block->summary}\n"; |
||
| 645 | }); |
||
| 646 | return _apply($curried, func_get_args()); |
||
| 647 | } |
||
| 648 | |||
| 649 | /** |
||
| 650 | * process_of(['f1', 'f2']) == pipe(_f('f1'), _f('f2')); |
||
| 651 | * |
||
| 652 | * @signature [String] -> Function |
||
| 653 | * @param array $fns |
||
| 654 | * @return callable |
||
| 655 | */ |
||
| 656 | function process_of($fns) { |
||
| 657 | return apply(_f('pipe'), map(_f('_f'), $fns)); |
||
| 658 | } |
||
| 659 | |||
| 660 | /** |
||
| 661 | * Dump a variable and returns it. |
||
| 662 | * |
||
| 663 | * @signature a -> a |
||
| 664 | * @param mixed $something |
||
| 665 | * @return mixed |
||
| 666 | */ |
||
| 667 | function log() { |
||
| 668 | $log = function($something) { |
||
| 669 | var_dump($something); |
||
|
0 ignored issues
–
show
Security
Debugging Code
introduced
by
Loading history...
|
|||
| 670 | return $something; |
||
| 671 | }; |
||
| 672 | return apply(curry($log), func_get_args()); |
||
| 673 | } |
||
| 674 | |||
| 675 | // Run the build |
||
| 676 | build_main(get_modules()); |
||
| 677 |