Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
40 changes: 37 additions & 3 deletions src/Operations/DevToolsOperations.php
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,9 @@ public function operations(): array
namedTarget: 'class',
createsNamedTarget: true,
),
new Operation(
// AN EDIT AMENDS THE CLASS IT NAMES (greenhouse decisions/0596): declared around the contract, not
// inside it, because the oldest milpa/command this package runs with cannot carry the declaration.
self::amendingItsNamedTarget(new Operation(
name: 'edit',
effects: new EffectProfile(
Mutation::Persistent,
Expand Down Expand Up @@ -396,9 +398,13 @@ public function operations(): array
'required' => ['plugin', 'class', 'edits'],
],
mutating: true,
// Same contract as implement: the target is THE CLASS, named by the human.
// Same contract as implement: the target is THE CLASS, named by the human — or, since greenhouse
// decisions/0596, by the session's own record of where that class came from. The record of the
// lab's houses: of 34 times a house asked a person to confirm an `edit`, 28 were about a class the
// same session had brought into the house. `edit` declares what it does — it amends a class that
// exists — and the session's floor reads the rest from facts. It still never creates one.
namedTarget: 'class',
),
)),
new Operation(
name: 'test',
effects: new EffectProfile(
Expand Down Expand Up @@ -720,4 +726,32 @@ public function operations(): array
),
];
}
/**
* The same operation, declaring that it amends the target it names — where the installed contract can carry it.
*
* `amendsNamedTarget` was born in a later milpa/command than the oldest this package runs with, and this
* `src/` travels with whatever vendor its owner has: a named argument the installed `Operation` does not know
* is a fatal error at the moment the catalogue is built. Where the contract cannot carry it, the operation is
* returned as it was declared and the session's floor keeps asking — the fail-closed answer an older house
* wants anyway.
*/
private static function amendingItsNamedTarget(Operation $operation): Operation
{
if (!self::contractCarries('amendsNamedTarget')) {
return $operation;
}

return new Operation(...array_replace(get_object_vars($operation), ['amendsNamedTarget' => true]));
}

/**
* Whether the installed `Operation` has that public property.
*
* The name arrives as a plain string on purpose: static analysis sees the milpa/command installed HERE and
* would rule the question settled, while this code runs against the one its owner installed.
*/
private static function contractCarries(string $property): bool
{
return property_exists(Operation::class, $property);
}
}
82 changes: 82 additions & 0 deletions tests/Operations/AnEditAmendsTheClassItNamesTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php

declare(strict_types=1);

namespace Milpa\DevTools\Tests\Operations;

use Milpa\Command\Operation;
use Milpa\DevTools\Operations\DevToolsOperations;
use PHPUnit\Framework\TestCase;

/**
* `edit` says it AMENDS the class it names (greenhouse decisions/0596).
*
* `implement` declares that it creates its named target, so a request that does not name the class is not asked
* about it; `edit` selects a class that exists, and is. The record of the lab's houses says what that question fell
* on: 28 of 34 times, a class the same session had brought into the house a moment before. So `edit` declares what it
* does — it amends, in place, a class that exists — and the session's floor decides, from that session's own record,
* whether the class is one the session itself brought. Declaring it changes nothing here: this package runs no gate.
*
* The declaration was born in a later milpa/command than the oldest this package runs with. Where the installed
* contract cannot carry it, `edit` is declared exactly as before and the floor keeps asking — which is the answer an
* older house wants anyway.
*
* @guards `edit` carrying the declaration wherever the installed contract can; nothing else in this package carrying
* it; the package still declaring its operations on a contract that cannot
*
* @refuses `implement` saying it amends (it creates); `edit` saying it creates; an operation with no named target
* saying anything about one
*/
final class AnEditAmendsTheClassItNamesTest extends TestCase
{
public function testEditSaysItAmendsTheClassItNames(): void
{
$this->needsAContractThatCanSayIt();

$edit = $this->operations()['edit'];

self::assertTrue($edit->amendsNamedTarget);
self::assertSame('class', $edit->namedTarget, 'and the target it speaks of is the class');
self::assertFalse($edit->createsNamedTarget, 'amending is not creating: only `make` creates a class');
}

public function testImplementKeepsSayingItCreates(): void
{
$this->needsAContractThatCanSayIt();

$implement = $this->operations()['implement'];

self::assertTrue($implement->createsNamedTarget);
self::assertFalse($implement->amendsNamedTarget, 'the two declarations are not the same statement');
}

public function testNothingElseInThisPackageSaysIt(): void
{
$this->needsAContractThatCanSayIt();

$amending = array_keys(array_filter($this->operations(), static fn (Operation $operation): bool => $operation->amendsNamedTarget));

self::assertSame(['edit'], $amending, 'an exception to the intent contract is declared one operation at a time');
}

public function testTheOperationsAreDeclaredWhateverTheInstalledContractCanSay(): void
{
$operations = $this->operations();

self::assertArrayHasKey('edit', $operations);
self::assertSame('class', $operations['edit']->namedTarget, 'the intent contract `edit` always had');
}

/** @return array<string, Operation> */
private function operations(): array
{
return array_column((new DevToolsOperations())->operations(), null, 'name');
}

private function needsAContractThatCanSayIt(): void
{
if (!property_exists(Operation::class, 'amendsNamedTarget')) {
self::markTestSkipped('The installed milpa/command cannot carry amendsNamedTarget (greenhouse decisions/0596): `edit` is declared as before, and the floor keeps asking.');
}
}
}
Loading