Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added option to dump a RST reference for the application #87

Open
wants to merge 2 commits into
base: 1.x
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ alpha-5

### 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 @@ -187,7 +187,6 @@ protected function registerShellCommands()
{
// add shell-specific commands
$this->add(new CommandShell\AliasListCommand());
$this->add(new CommandShell\ClearCommand());
$this->add(new CommandShell\ConfigInitCommand());
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't have been removed ..

$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
17 changes: 15 additions & 2 deletions src/PHPCR/Shell/Console/Command/ShellCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use PHPCR\Shell\Console\Application\Shell;
use PHPCR\Shell\Console\Input\StringInput;
use Symfony\Component\Console\Input\InputArgument;
use PHPCR\Shell\Console\Descriptor\RstDescriptor;

/**
* The shell command is the command used to configure the shell session
Expand Down Expand Up @@ -66,8 +67,10 @@ public function configure()
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 InputArgument('workspace', InputArgument::OPTIONAL, 'Workspace to start with', 'default'),
));
new InputOption('--reference', null, InputOption::VALUE_NONE, 'Dump a complete command reference in RST format'),

new InputArgument('workspace', InputArgument::OPTIONAL, 'Workspace to start with', 'default')
));
}

/**
Expand All @@ -76,8 +79,11 @@ 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);

Expand All @@ -101,6 +107,13 @@ public function execute(InputInterface $input, OutputInterface $output)
$application = new Shell($this->application);
}

if ($dumpReference) {
$this->application->init();
$descriptor = new RstDescriptor();
$descriptor->describe($output, $this->application);
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);
}
}