Skip to content

Commit

Permalink
Continued refactoring Create command. Implementing Adapter genera…
Browse files Browse the repository at this point in the history
…tor to generate class adapter stubs.
  • Loading branch information
nateabele committed May 29, 2012
1 parent 9ede286 commit d88b211
Show file tree
Hide file tree
Showing 3 changed files with 219 additions and 28 deletions.
86 changes: 66 additions & 20 deletions console/command/Create.php
Expand Up @@ -11,6 +11,7 @@
use lithium\util\String; use lithium\util\String;
use lithium\core\Libraries; use lithium\core\Libraries;
use lithium\util\Inflector; use lithium\util\Inflector;
use lithium\analysis\Inspector;
use lithium\core\ClassNotFoundException; use lithium\core\ClassNotFoundException;


/** /**
Expand Down Expand Up @@ -39,6 +40,14 @@ class Create extends \lithium\console\Command {
*/ */
public $template = null; public $template = null;


/**
* Specifies the service location path of the sub-commands used to generate classes or other
* files. Defaults to `'command.create'`.
*
* @var string
*/
public $commandPath = 'command.create';

/** /**
* Holds library data from `lithium\core\Libraries::get()`. * Holds library data from `lithium\core\Libraries::get()`.
* *
Expand All @@ -53,6 +62,18 @@ class Create extends \lithium\console\Command {
*/ */
protected $_commands = array(); protected $_commands = array();


/**
* List of commands to run when executing against a name rather than a command.
*
* @var array
*/
protected $_defaultCommands = array(
array('model'),
array('controller'),
array('test', 'model'),
array('test', 'controller')
);

/** /**
* Class initializer. Parses template and sets up params that need to be filled. * Class initializer. Parses template and sets up params that need to be filled.
* *
Expand All @@ -63,26 +84,26 @@ protected function _init() {
$this->library = $this->library ?: true; $this->library = $this->library ?: true;
$defaults = array('prefix' => null, 'path' => null); $defaults = array('prefix' => null, 'path' => null);
$this->_library = (array) Libraries::get($this->library) + $defaults; $this->_library = (array) Libraries::get($this->library) + $defaults;
$commands = Libraries::locate('command.create'); $commands = Libraries::locate($this->commandPath);


$this->_commands = array_combine($commands, array_map( $map = function($cmd) {
function($cmd) { return str_replace('_', '-', Inflector::underscore(
return str_replace('_', '-', Inflector::underscore( substr($cmd, strrpos($cmd, '\\') + 1)
substr($cmd, strrpos($cmd, '\\') + 1) ));
)); };
}, $this->_commands = array_combine($commands, array_map($map, $commands));
$commands
));
} }


/** /**
* Run the create command. Takes `$command` and delegates to `$command::$method` * Run the create command. Takes `$command` and delegates to `$command::$method`
* *
* @param string $command * @param string $command The name of the command to run, which specifies what type of file to
* generate. Available options can be determined by running the `generators`
* command.
* @return boolean * @return boolean
*/ */
public function run($command = null) { public function run($command = null) {
if ($command && !in_array($command, $this->_commands)) { if ($command && !in_array($command, $this->_commands) && !$this->request->args()) {
return $this->_default($command); return $this->_default($command);
} }
$this->request->shift(); $this->request->shift();
Expand All @@ -98,6 +119,35 @@ public function run($command = null) {
return false; return false;
} }


/**
* Lists the available generator commands that can be used to produce stub classes and files.
*
* @return boolean
*/
public function generators() {
$this->out('');
$this->out('Available console generators:');
$this->out('');

foreach ($this->_commands as $class => $title) {
$this->out("- {$title}");
$info = Inspector::info($class);

if ($info['description']) {
$this->out($info['description']);
}
if ($info['description'] && $info['text']) {
$this->out('');
}
if ($info['text']) {
$this->out($info['text']);
}
$this->out('');
$this->out('');
}
return true;
}

/** /**
* Execute the given sub-command for the current request. * Execute the given sub-command for the current request.
* *
Expand Down Expand Up @@ -133,13 +183,9 @@ protected function _execute($command) {
* @return boolean * @return boolean
*/ */
protected function _default($name) { protected function _default($name) {
$commands = array( foreach ($this->_defaultCommands as $args) {
array('model', Inflector::pluralize($name)), array_push($args, Inflector::pluralize($name));
array('controller', Inflector::pluralize($name)),
array('test', 'model', Inflector::pluralize($name)),
array('test', 'controller', Inflector::pluralize($name))
);
foreach ($commands as $args) {
$command = $this->template = $this->request->params['command'] = array_shift($args); $command = $this->template = $this->request->params['command'] = array_shift($args);
$this->request->params['action'] = array_shift($args); $this->request->params['action'] = array_shift($args);
$this->request->params['args'] = $args; $this->request->params['args'] = $args;
Expand All @@ -158,7 +204,7 @@ protected function _default($name) {
* @param array $options * @param array $options
* @return string * @return string
*/ */
protected function _namespace($request, $options = array()) { protected function _namespace($request, $options = array()) {
$name = $request->command; $name = $request->command;
$defaults = array( $defaults = array(
'prefix' => $this->_library['prefix'], 'prefix' => $this->_library['prefix'],
Expand Down
160 changes: 152 additions & 8 deletions console/command/create/Adapter.php
Expand Up @@ -8,27 +8,171 @@


namespace lithium\console\command\create; namespace lithium\console\command\create;


use RuntimeException;
use lithium\util\String;
use lithium\util\Inflector; use lithium\util\Inflector;
use lithium\core\Libraries;
use lithium\analysis\Inspector;


/** /**
* Generate a Model class in the `--library` namespace * Generate an adapter class in the `--library` namespace, according to the `--type` specified.
*
* `li3 create model Posts`
* `li3 create --library=li3_plugin model Posts`
*
*/ */
class Adapter extends \lithium\console\command\Create { class Adapter extends \lithium\console\command\Create {


public $class; /**
* The class to create an adapter for. Can be a short class name, i.e. `Cache`, or a
* fully-qualified class name, i.e. `lithium\storage\Session`. The class must have an
* `$_adapters` property defined, and may also have a custom class type registered with
* `Libraries::paths()`.
*
* @see lithium\core\Adaptable::$_adapters
* @see lithium\core\Libraries::paths()
* @var string
*/
public $type;

/**
* The base class that the generated adapter should extend.
*
* @var string
*/
public $parent = '\lithium\core\Object';

protected $_methodDef = "\tpublic function {:name}({:params}) {\n\t\t\n\t}";

protected $_methodDefEnabled = "\tpublic static function enabled() {\n\t\t\n\t}";

protected $_typeClass;

protected function _init() {
parent::_init();

$findOptions = array(
'recursive' => true,
'filter' => '/' . preg_quote($this->type, '/') . '/',
'exclude' => '/Mock|Test|\\\\adapter\\\\/i'
);

switch (true) {
case (class_exists($this->type)):
$this->_typeClass = $this->type;
break;
case (($located = Libraries::find(true, $findOptions)) && count($located) == 1):
$this->_typeClass = reset($located);
break;
case ($located && count($located) > 1):
$this->out("Multiple matches found for '{$this->type}'");

foreach ($located as $i => $class) {
$i++;
$this->out("$i) {$class}");
}
$located = $located[intval($this->in('Please select one:')) - 1];
break;
default:
$msg = "Unabled to find class for which to generate adapter. Please use the ";
$msg .= "--type flag to specify a fully-qualified class name.";
throw new RuntimeException($msg);
}
}


/** /**
* Get the class name for the model. * Get the class name for the adapter.
* *
* @param string $request * @param string $request
* @return string * @return string
*/ */
protected function _class($request) { protected function _class($request) {
return Inflector::camelize(Inflector::pluralize($request->action)); return Inflector::camelize($request->action);
}

protected function _namespace($request, $options = array()) {
$path = explode('.', $this->_adapterPath($this->_typeClass));
$type = array_shift($path);
$paths = Libraries::paths($type);

$result = str_replace('\\\\', '\\', String::insert(reset($paths), array(
'library' => $this->_library['prefix'],
'namespace' => join('\\', $path),
'class' => null,
'name' => null
)));
return rtrim($result, '\\');
}

/**
* Gets the dot-separated adapter lookup path for the given adaptable class.
*
* @param string $class The fully-namespaced class name of the class to get the path for.
* @return string Returns the dot-separated service lookup path used to find adapters for the
* given class.
*/
protected function _adapterPath($class) {
if (!$result = Inspector::info($class . '::$_adapters')) {
$msg = "Class `{$class}` is not a valid adapter-supporting class, ";
$msg .= "no `\$_adapters` class property found.";
throw new RuntimeException($msg);
}
return $result['value'];
}

protected function _parent($request) {
if (class_exists($this->parent)) {
return '\\' . ltrim($this->parent, '\\');
}
$result = Libraries::find(true, array(
'recursive' => true,
'filter' => '/' . preg_quote($this->parent, '/') . '/',
'exclude' => '/Mock|Test|\\\\adapter\\\\/i'
));
return reset($result);
}

protected function _methods($request) {
$methods = array();

Inspector::methods($this->_typeClass)->map(function($method) use (&$methods) {
$methods[$method->getName()] = array_map(
function($param) {
$name = '$' . $param->getName();

if (!$param->isOptional()) {
return $name;
}
$name .= ' = ';

switch (true) {
case ($param->getDefaultValue() === null):
$name .= 'null';
break;
case ($param->getDefaultValue() === array()):
$name .= 'array()';
break;
default:
$name .= var_export($param->getDefaultValue(), true);
break;
}

if ($name === '$options = array()' || $name === '$config = array()') {
$name = "array {$name}";
}
return $name;
},
$method->getParameters()
);
});
$result = array();

foreach ($methods as $name => $params) {
if (!$params || array_shift($params) !== '$name') {
continue;
}
$params = join(', ', $params);
$result[] = String::insert($this->_methodDef, compact('name', 'params'));
}
$result[] = $this->_methodDefEnabled;

return join("\n\n", $result);
} }
} }


Expand Down
1 change: 1 addition & 0 deletions tests/cases/core/LibrariesTest.php
Expand Up @@ -534,6 +534,7 @@ public function testLocateCommandInLithiumRecursiveTrue() {
'lithium\console\command\Route', 'lithium\console\command\Route',
'lithium\console\command\Test', 'lithium\console\command\Test',
'lithium\console\command\g11n\Extract', 'lithium\console\command\g11n\Extract',
'lithium\console\command\create\Adapter',
'lithium\console\command\create\Controller', 'lithium\console\command\create\Controller',
'lithium\console\command\create\Mock', 'lithium\console\command\create\Mock',
'lithium\console\command\create\Model', 'lithium\console\command\create\Model',
Expand Down

0 comments on commit d88b211

Please sign in to comment.