Skip to content

Commit

Permalink
Use a template method to reduce duplicated code.
Browse files Browse the repository at this point in the history
Make the various registries use a template method pattern. This reduces
the duplicated code between the various subclasses by keeping the
common parts in the abstract class, and the specializations in the
subclasses.
  • Loading branch information
markstory committed Jul 27, 2013
1 parent 12b7fc5 commit 3bae222
Show file tree
Hide file tree
Showing 4 changed files with 175 additions and 120 deletions.
72 changes: 36 additions & 36 deletions lib/Cake/Console/TaskRegistry.php
Expand Up @@ -41,48 +41,48 @@ public function __construct(Shell $Shell) {
}

/**
* Loads/constructs a task. Will return the instance in the registry if it already exists.
* Resolve a task classname.
*
* You can alias your task as an existing task by setting the 'className' key, i.e.,
* {{{
* public $tasks = array(
* 'DbConfig' => array(
* 'className' => 'Bakeplus.DbConfigure'
* );
* );
* }}}
* All calls to the `DbConfig` task would use `DbConfigure` found in the `Bakeplus` plugin instead.
* Part of the template method for Cake\Utility\ObjectRegistry::load()
*
* @param string $task Task name to load
* @param array $settings Settings for the task.
* @return Task A task object, Either the existing loaded task or a new one.
* @throws Cake\Error\MissingTaskException when the task could not be found
* @param string $class Partial classname to resolve.
* @return string|false Either the correct classname or false.
*/
public function load($task, $settings = array()) {
if (is_array($settings) && isset($settings['className'])) {
$alias = $task;
$task = $settings['className'];
}
list($plugin, $name) = pluginSplit($task, true);
if (!isset($alias)) {
$alias = $name;
}

if (isset($this->_loaded[$alias])) {
return $this->_loaded[$alias];
}
protected function _resolveClassName($class) {
return App::classname($class, 'Console/Command/Task', 'Task');
}

$taskClass = App::classname($task, 'Console/Command/Task', 'Task');
if (!$taskClass) {
throw new Error\MissingTaskException(array(
'class' => $name
));
}
/**
* Throws an exception when a task is missing.
*
* Part of the template method for Cake\Utility\ObjectRegistry::load()
*
* @param string $class The classname that is missing.
* @param string $plugin The plugin the task is missing in.
* @throws Cake\Error\MissingTaskException
*/
protected function _throwMissingClassError($class, $plugin) {
throw new Error\MissingTaskException([
'class' => $class,
'plugin' => $plugin
]);
}

$this->_loaded[$alias] = new $taskClass(
$this->_Shell->stdout, $this->_Shell->stderr, $this->_Shell->stdin
/**
* Create the task instance.
*
* Part of the template method for Cake\Utility\ObjectRegistry::load()
*
* @param string $class The classname that is missing.
* @param array $settings An array of settings to use for the task.
* @return Component The constructed task class.
*/
protected function _create($class, $settings) {
return new $class(
$this->_Shell->stdout,
$this->_Shell->stderr,
$this->_Shell->stdin
);
return $this->_loaded[$alias];
}

}
77 changes: 38 additions & 39 deletions lib/Cake/Controller/ComponentRegistry.php
Expand Up @@ -65,51 +65,50 @@ public function getController() {
}

/**
* Loads/constructs a component. Will return the instance in the registry if it already exists.
* You can use `$settings['enabled'] = false` to disable callbacks on a component when loading it.
* Callbacks default to on. Disabled component methods work as normal, only callbacks are disabled.
* Resolve a component classname.
*
* You can alias your component as an existing component by setting the 'className' key, i.e.,
* {{{
* public $components = array(
* 'Email' => array(
* 'className' => '\App\Controller\Component\AliasedEmailComponent'
* );
* );
* }}}
* All calls to the `Email` component would use `AliasedEmail` instead.
* Part of the template method for Cake\Utility\ObjectRegistry::load()
*
* @param string $component Component name to load
* @param array $settings Settings for the component.
* @return Component A component object, Either the existing loaded component or a new one.
* @throws Cake\Error\MissingComponentException when the component could not be found
* @param string $class Partial classname to resolve.
* @return string|false Either the correct classname or false.
*/
public function load($component, $settings = array()) {
if (is_array($settings) && isset($settings['className'])) {
$alias = $component;
$component = $settings['className'];
}
list($plugin, $name) = pluginSplit($component, true);
if (!isset($alias)) {
$alias = $name;
}
if (isset($this->_loaded[$alias])) {
return $this->_loaded[$alias];
}
$componentClass = App::classname($plugin . $name, 'Controller/Component', 'Component');
if (!$componentClass) {
throw new Error\MissingComponentException(array(
'class' => $component,
'plugin' => substr($plugin, 0, -1)
));
}
$component = new $componentClass($this, $settings);
protected function _resolveClassName($class) {
return App::classname($class, 'Controller/Component', 'Component');
}

/**
* Throws an exception when a component is missing.
*
* Part of the template method for Cake\Utility\ObjectRegistry::load()
*
* @param string $class The classname that is missing.
* @param string $plugin The plugin the component is missing in.
* @throws Cake\Error\MissingComponentException
*/
protected function _throwMissingClassError($class, $plugin) {
throw new Error\MissingComponentException([
'class' => $class,
'plugin' => $plugin
]);
}

/**
* Create the component instance.
*
* Part of the template method for Cake\Utility\ObjectRegistry::load()
* Enabled components will be registered with the event manager.
*
* @param string $class The classname that is missing.
* @param array $settings An array of settings to use for the component.
* @return Component The constructed component class.
*/
protected function _create($class, $settings) {
$instance = new $class($this, $settings);
$enable = isset($settings['enabled']) ? $settings['enabled'] : true;
if ($enable) {
$this->_eventManager->attach($component);
$this->_eventManager->attach($instance);
}
$this->_loaded[$alias] = $component;
return $this->_loaded[$alias];
return $instance;
}

}
67 changes: 63 additions & 4 deletions lib/Cake/Utility/ObjectRegistry.php
Expand Up @@ -37,15 +37,74 @@ abstract class ObjectRegistry {
protected $_loaded = [];

/**
* Load instances for this registry.
* Loads/constructs a object instance.
*
* Overridden in subclasses.
* Will return the instance in the registry if it already exists.
* You can use `$settings['enabled'] = false` to disable events on an object when loading it.
* Not all registry subclasses support events.
*
* You can alias an object by setting the 'className' key, i.e.,
* {{{
* public $components = [
* 'Email' => [
* 'className' => '\App\Controller\Component\AliasedEmailComponent'
* ];
* ];
* }}}
*
* All calls to the `Email` component would use `AliasedEmail` instead.
*
* @param string $name The name/class of the object to load.
* @param array $settings Additional settings to use when loading the object.
* @return mixed.
* @return mixed
*/
public function load($objectName, $settings = []) {
list($plugin, $name) = pluginSplit($objectName);
if (isset($this->_loaded[$name])) {
return $this->_loaded[$name];
}
if (is_array($settings) && isset($settings['className'])) {
$className = $this->_resolveClassName($settings['className']);
}
if (!isset($className)) {
$className = $this->_resolveClassName($objectName);
}
if (!$className) {
$this->_throwMissingClassError($objectName, substr($plugin, 0, -1));
}
$instance = $this->_create($className, $settings);
$this->_loaded[$name] = $instance;
return $instance;
}

/**
* Should resolve the classname for a given object type.
*
* @param string $class The class to resolve.
* @return string|false The resolved name or false for failure.
*/
abstract protected function _resolveClassName($class);

/**
* Throw an exception when the requested object name is missing.
*
* @param string $class The class that is missing.
* @param string $plugin The plugin $class is missing from.
* @throw Cake\Exception
*/
abstract protected function _throwMissingClassError($class, $plugin);

/**
* Create an instance of a given classname.
*
* This method should construct and do any other initialization logic
* required.
*
* @param string $class The class to build.
* @param array $settings The settings for construction
* @return mixed
*/
abstract public function load($name, $settings = []);
abstract protected function _create($class, $settings);

/**
* Get the loaded object list, or get the object instance at a given name.
Expand Down
79 changes: 38 additions & 41 deletions lib/Cake/View/HelperRegistry.php
Expand Up @@ -100,57 +100,54 @@ public function __get($name) {
}

/**
* Loads/constructs a helper. Will return the instance in the registry if it already exists.
* By setting `$enable` to false you can disable callbacks for a helper. Alternatively you
* can set `$settings['enabled'] = false` to disable callbacks. This alias is provided so that when
* declaring $helpers arrays you can disable callbacks on helpers.
* Resolve a helper classname.
*
* You can alias your helper as an existing helper by setting the 'className' key, i.e.,
* {{{
* public $helpers = array(
* 'Html' => array(
* 'className' => '\App\View\Helper\AliasedHtmlHelper'
* );
* );
* }}}
* All calls to the `Html` helper would use `AliasedHtml` instead.
* Part of the template method for Cake\Utility\ObjectRegistry::load()
*
* @param string $helper Helper name to load
* @param array $settings Settings for the helper.
* @return Helper A helper object, Either the existing loaded helper or a new one.
* @throws Cake\Error\MissingHelperException when the helper could not be found
* @param string $class Partial classname to resolve.
* @return string|false Either the correct classname or false.
*/
public function load($helper, $settings = array()) {
list($plugin, $name) = pluginSplit($helper);
if (isset($this->_loaded[$name])) {
return $this->_loaded[$name];
}
if (is_array($settings) && isset($settings['className'])) {
$helperClass = App::classname($settings['className'], 'View/Helper', 'Helper');
}
if (!isset($helperClass)) {
$helperClass = App::classname($helper, 'View/Helper', 'Helper');
}
if (!$helperClass) {
throw new Error\MissingHelperException(array(
'class' => $helper,
'plugin' => substr($plugin, 0, -1)
));
}
$helperObject = new $helperClass($this->_View, $settings);
protected function _resolveClassName($class) {
return App::classname($class, 'View/Helper', 'Helper');
}

/**
* Throws an exception when a helper is missing.
*
* Part of the template method for Cake\Utility\ObjectRegistry::load()
*
* @param string $class The classname that is missing.
* @param string $plugin The plugin the helper is missing in.
* @throws Cake\Error\MissingHelperException
*/
protected function _throwMissingClassError($class, $plugin) {
throw new Error\MissingHelperException([
'class' => $class,
'plugin' => $plugin
]);
}

/**
* Create the helper instance.
*
* Part of the template method for Cake\Utility\ObjectRegistry::load()
* Enabled helpers will be registered with the event manager.
*
* @param string $class The classname that is missing.
* @param array $settings An array of settings to use for the helper.
* @return Component The constructed helper class.
*/
protected function _create($class, $settings) {
$instance = new $class($this->_View, $settings);
$vars = array('request', 'theme', 'plugin');
foreach ($vars as $var) {
$helperObject->{$var} = $this->_View->{$var};
$instance->{$var} = $this->_View->{$var};
}

$this->_loaded[$name] = $helperObject;

$enable = isset($settings['enabled']) ? $settings['enabled'] : true;
if ($enable) {
$this->_eventManager->attach($helperObject);
$this->_eventManager->attach($instance);
}
return $helperObject;
return $instance;
}

}

0 comments on commit 3bae222

Please sign in to comment.