Skip to content

Commit

Permalink
Added option to dump a RST reference for the application
Browse files Browse the repository at this point in the history
- For use by the documentation
  • Loading branch information
dantleech committed Aug 17, 2014
1 parent 9515c6f commit b495bf4
Show file tree
Hide file tree
Showing 6 changed files with 275 additions and 43 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ dev-master

### Features

- [shell] Added "--reference" option to session command to dump a complete shell reference
in restructured text format for the official documentation.
- [shell] Added "shell:clear" command to support clearing the console output
- [general] The shell supports being embedded as a dependency
- [node:edit] New command `node:edit` enables editing of entire node
Expand Down
1 change: 0 additions & 1 deletion src/PHPCR/Shell/Console/Application/ShellApplication.php
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,6 @@ protected function registerShellCommands()
{
// add shell-specific commands
$this->add(new CommandShell\AliasListCommand());
$this->add(new CommandShell\ClearCommand());
$this->add(new CommandShell\ConfigInitCommand());
$this->add(new CommandShell\ConfigReloadCommand());
$this->add(new CommandShell\PathChangeCommand());
Expand Down
1 change: 1 addition & 0 deletions src/PHPCR/Shell/Console/Command/Phpcr/NodeCopyCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ protected function configure()
The subgraph rooted at and including N' (call it S') is created and is
identical to the subgraph rooted at and including N (call it S) with the
following exceptions:
- Every node in S' is given a new and distinct identifier
- or, if <info>srcWorkspace</info> is given -
Every referenceable node in S' is given a new and distinct identifier
Expand Down
81 changes: 41 additions & 40 deletions src/PHPCR/Shell/Console/Command/Phpcr/VersionRestoreCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,50 +20,49 @@ protected function configure()
$this->setHelp(<<<HERE
Attempt to restore an old version of a node.
<em>If <info>path</info> is given and <info>versionName</info> is a version name:</em>
* If <info>path</info> is given and <info>versionName</info> is a version name:
Restores the node at <info>path</info> to the state defined by the version with
the specified version name (<info>versionName</info>).
This method will work regardless of whether the node at path is
checked-in or not.
<em>If <info>path</info> is given and <info>versionName</info> is a VersionInterface instance:
</em>
Restores the specified version to <info>path</info>. There must be no existing
node at <info>path</info>. If one exists, a VersionException is thrown.
There must be a parent node to the location at <info>path</info>, otherwise a
PathNotFoundException is thrown.
If the would-be parent of the location <info>path</info> is actually a property,
or if a node type restriction would be violated, then a
ConstraintViolationException is thrown.
<em>If <info>versionName</info> is VersionInterface instance:</em>
Restores the node in the current workspace that is the versionable node
of the specified version to the state reflected in that version.
This method ignores checked-in status.
<em>If <info>versionName</info> is an array of VersionInterface instances:</em>
Restores a set of versions at once. Used in cases where a "chicken and
egg" problem of mutually referring REFERENCE properties would prevent
the restore in any serial order.
The following restrictions apply to the set of versions specified: If S
is the set of versions being restored simultaneously,
- For every version V in S that corresponds to a missing node, there
must also be a parent of V in S.
- S must contain at least one version that corresponds to an existing
node in the workspace.
- No V in S can be a root version (jcr:rootVersion).
If any of these restrictions does not hold, the restore will fail
because the system will be unable to determine the path locations to
which one or more versions are to be restored. In this case a
VersionException is thrown.
The versionable nodes in the current workspace that correspond to the
versions being restored define a set of (one or more) subgraphs.
<em>If the restore succeeds the changes made are dispatched immediately;
</em>
* If <info>path</info> is given and <info>versionName</info> is a VersionInterface instance:
Restores the specified version to <info>path</info>. There must be no existing
node at <info>path</info>. If one exists, a VersionException is thrown.
There must be a parent node to the location at <info>path</info>, otherwise a
PathNotFoundException is thrown.
If the would-be parent of the location <info>path</info> is actually a property,
or if a node type restriction would be violated, then a
ConstraintViolationException is thrown.
* If <info>versionName</info> is VersionInterface instance:
Restores the node in the current workspace that is the versionable node
of the specified version to the state reflected in that version.
This method ignores checked-in status.
* If <info>versionName</info> is an array of VersionInterface instances:
Restores a set of versions at once. Used in cases where a "chicken and
egg" problem of mutually referring REFERENCE properties would prevent
the restore in any serial order.
The following restrictions apply to the set of versions specified: If S
is the set of versions being restored simultaneously,
- For every version V in S that corresponds to a missing node, there
must also be a parent of V in S.
- S must contain at least one version that corresponds to an existing
node in the workspace.
- No V in S can be a root version (jcr:rootVersion).
If any of these restrictions does not hold, the restore will fail
because the system will be unable to determine the path locations to
which one or more versions are to be restored. In this case a
VersionException is thrown.
The versionable nodes in the current workspace that correspond to the
versions being restored define a set of (one or more) subgraphs.
If the restore succeeds the changes made are dispatched immediately;
there is no need to call save.
If an array of VersionInterface instances is restored, an identifier
Expand All @@ -85,8 +84,10 @@ protected function configure()
OnParentVersion settings of COPY or VERSION are also governed by the
<info>removeExisting</info> flag.
<b>Note:</b> The Java API defines this with multiple differing
signatures, you need to act accordingly in your implementation.
Note:
The Java API defines this with multiple differing
signatures, you need to act accordingly in your implementation.
HERE
);
}
Expand Down
16 changes: 14 additions & 2 deletions src/PHPCR/Shell/Console/Command/ShellCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use PHPCR\Shell\Console\Application\ShellApplication;
use PHPCR\Shell\Console\Application\Shell;
use PHPCR\Shell\Console\Input\StringInput;
use PHPCR\Shell\Console\Descriptor\RstDescriptor;

/**
* The shell command is the command used to configure the shell session
Expand Down Expand Up @@ -64,7 +65,8 @@ public function configure()
new InputOption('--profile', '-p', InputOption::VALUE_OPTIONAL, 'Speicfy a profile name, use wit <info>--transport</info> to update or create'),
new InputOption('--unsupported', null, InputOption::VALUE_NONE, 'Show all commands, including commands not supported by the repository'),
new InputOption('--command', null, InputOption::VALUE_REQUIRED|InputOption::VALUE_IS_ARRAY, 'Run the given command'),
));
new InputOption('--reference', null, InputOption::VALUE_NONE, 'Dump a complete command reference in RST format')
));
}

/**
Expand All @@ -73,12 +75,14 @@ public function configure()
public function execute(InputInterface $input, OutputInterface $output)
{
$showUnspported = $input->getOption('unsupported');
$noInteraction = $input->getOption('no-interaction');
$dumpReference = $input->getOption('reference');

$application = $this->application;

$application->setShowUnsupported($showUnspported);
$application->dispatchProfileInitEvent($input, $output);

$noInteraction = $input->getOption('no-interaction');

if ($commands = $input->getOption('command')) {
$application->setCatchExceptions(false);
Expand All @@ -94,6 +98,14 @@ public function execute(InputInterface $input, OutputInterface $output)
$application = new Shell($this->application);
}

if ($dumpReference) {
$this->application->init();
$descriptor = new RstDescriptor();
$out = $descriptor->describe($this->application);
die($out);
return 0;
}

if ($noInteraction) {
return 0;
}
Expand Down
217 changes: 217 additions & 0 deletions src/PHPCR/Shell/Console/Descriptor/RstDescriptor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
<?php

namespace PHPCR\Shell\Console\Descriptor;

use Symfony\Component\Console\Descriptor\Descriptor;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Descriptor\ApplicationDescription;

/**
* Class which dumps the command reference in RST format
* for use by the official documentation.
*
* @author Daniel Leech <[email protected]>
*/
class RstDescriptor extends Descriptor
{
protected $ignoreOptions = array(
'verbose',
'version',
'quiet',
'ansi',
'no-ansi',
'no-interaction',
);

protected function getCommandRefName($name)
{
return 'phpcr_shell_command_' . str_replace(':', '', $name);
}

protected function underline($string, $char)
{
return str_repeat($char, strlen($string));
}

protected function formatText($text)
{
$lines = explode("\n", $text);
$newLines = array();
$blockLines = array();

foreach ($lines as $line) {
// if line is indented by 2 or 4 spaces, assume
// that it is a code block
if (preg_match('{^[ | ]}', $line)) {
$inBlock = true;
$blockLines = array();
} else {
$inBlock = false;
}

if (true === $inBlock) {
$blockLines[] = $line;
continue;
}

if (false === $inBlock && $blockLines) {
$newLines[] = '';
$newLines[] = '.. code-block:: bash';
$newLines[] = '';
foreach ($blockLines as $blockLine) {
$blockLine = preg_replace('{( +)<(.*?)>(.*)</(.*)>}', '\3', $blockLine);
$newLines[] = ' ' . $blockLine;
}
$newLines[] = '';
$blockLines = array();
} else {
// replace inline tags with literals
$line = preg_replace('{<(.*?)>(.*)</(.*)>}', '``\2``', $line);
$newLines[] = $line;
}
}


return implode("\n", $newLines);
}

/**
* {@inheritdoc}
*/
protected function describeInputArgument(InputArgument $argument, array $options = array())
{
return implode("\n", array(
$argument->getName(),
$this->underline($argument->getName(), '"'),
'',
'* **Name:** ``'. ($argument->getName() ?: '*<none>*').'``',
'* **Is required:** '.($argument->isRequired() ? 'yes' : 'no'),
'* **Is array:** '.($argument->isArray() ? 'yes' : 'no'),
'* **Description:** '.($argument->getDescription() ?: '*<none>*'),
'* **Default:** ``'.str_replace("\n", '', var_export($argument->getDefault(), true)).'``',
'',
));
}

/**
* {@inheritdoc}
*/
protected function describeInputOption(InputOption $option, array $options = array())
{
if (in_array($option->getName(), $this->ignoreOptions)) {
return '';
}

return implode("\n", array(
$option->getName(),
$this->underline($option->getName(), '"'),
'',
'* **Name:** ``--'.$option->getName().'``',
'* **Accept value:** '.($option->acceptValue() ? 'yes' : 'no'),
'* **Is value required:** '.($option->isValueRequired() ? 'yes' : 'no'),
'* **Is multiple:** '.($option->isArray() ? 'yes' : 'no'),
'* **Description:** '.($option->getDescription() ?: '*<none>*'),
'* **Default:** ``'.str_replace("\n", '', var_export($option->getDefault(), true)).'``',
'',
));
}

/**
* {@inheritdoc}
*/
protected function describeInputDefinition(InputDefinition $definition, array $options = array())
{
$blocks = array();

if (count($definition->getArguments()) > 0) {
$blocks[] = 'Arguments:';
$blocks[] = '~~~~~~~~~~';
$blocks[] = '';
foreach ($definition->getArguments() as $argument) {
$blocks[] = $this->describeInputArgument($argument);
}
$blocks[] = '';
}

if (count($definition->getOptions()) > 0) {
$blocks[] = 'Options:';
$blocks[] = '~~~~~~~~';
$blocks[] = '';
foreach ($definition->getOptions() as $option) {
$blocks[] = $this->describeInputOption($option);
}
$blocks[] = '';
}

return implode("\n", $blocks);
}

/**
* {@inheritdoc}
*/
protected function describeCommand(Command $command, array $options = array())
{
$command->getSynopsis();
$command->mergeApplicationDefinition(false);

$rst = array(
'',
'.. _' . $this->getCommandRefName($command->getName()) . ':',
'',
$command->getName(),
$this->underline($command->getName(), '-'),
'',
'* **Description:** '.($command->getDescription() ? $this->formatText($command->getDescription()) : '*<none>*'),
'* **Usage:** ``'.$command->getSynopsis().'``',
);

$rst[] = '';

if ($help = $command->getProcessedHelp()) {
$rst[] = $this->formatText($help);
$rst[] = '';
}

if ($definitionRst = $this->describeInputDefinition($command->getNativeDefinition())) {
$rst[] = $this->formatText($definitionRst);
$rst[] = '';
}

return implode("\n", $rst);
}

/**
* {@inheritdoc}
*/
protected function describeApplication(Application $application, array $options = array())
{
$describedNamespace = isset($options['namespace']) ? $options['namespace'] : null;
$description = new ApplicationDescription($application, $describedNamespace);
$blocks[] = 'Reference';
$blocks[] = '=========';
$blocks[] = '';

foreach ($description->getNamespaces() as $namespace) {
if (ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) {
$blocks[] = $namespace['id'];
$blocks[] = str_repeat('-', strlen($namespace['id']));
$blocks[] = '';
}

$blocks[] = implode("\n", array_map(function ($commandName) {
return '* :ref:`' . $this->getCommandRefName($commandName) . '`';
} , $namespace['commands']));
$blocks[] = '';
}

foreach ($description->getCommands() as $command) {
$blocks[] = $this->describeCommand($command);
}

return implode("\n", $blocks);
}
}

0 comments on commit b495bf4

Please sign in to comment.