Skip to content

Commit

Permalink
Implement hasMethod() and call()
Browse files Browse the repository at this point in the history
Expand the ObjectRegistry::_create interface to include the alias of
the object being built. This allows BehaviorRegistry to use this
information to build method caches. Update all the various
implementations around the framework as well.
  • Loading branch information
markstory committed Oct 23, 2013
1 parent 8b9f916 commit 6fa2d9d
Show file tree
Hide file tree
Showing 10 changed files with 74 additions and 19 deletions.
3 changes: 2 additions & 1 deletion Cake/Cache/CacheRegistry.php
Expand Up @@ -59,12 +59,13 @@ protected function _throwMissingClassError($class, $plugin) {
*
* Part of the template method for Cake\Utility\ObjectRegistry::load()
* @param string|CacheEngine $class The classname or object to make.
* @param string $alias The alias of the object.
* @param array $settings An array of settings to use for the cache engine.
* @return CacheEngine The constructed CacheEngine class.
* @throws Cake\Error\Exception when an object doesn't implement
* the correct interface.
*/
protected function _create($class, $settings) {
protected function _create($class, $alias, $settings) {
if (is_object($class)) {
$instance = $class;
}
Expand Down
5 changes: 3 additions & 2 deletions Cake/Console/TaskRegistry.php
Expand Up @@ -73,11 +73,12 @@ protected function _throwMissingClassError($class, $plugin) {
*
* Part of the template method for Cake\Utility\ObjectRegistry::load()
*
* @param string $class The classname that is missing.
* @param string $class The classname to create.
* @param string $alias The alias of the task.
* @param array $settings An array of settings to use for the task.
* @return Component The constructed task class.
*/
protected function _create($class, $settings) {
protected function _create($class, $alias, $settings) {
return new $class(
$this->_Shell->stdout,
$this->_Shell->stderr,
Expand Down
5 changes: 3 additions & 2 deletions Cake/Controller/ComponentRegistry.php
Expand Up @@ -98,11 +98,12 @@ protected function _throwMissingClassError($class, $plugin) {
* 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 string $class The classname to create.
* @param string $alias The alias of the component.
* @param array $settings An array of settings to use for the component.
* @return Component The constructed component class.
*/
protected function _create($class, $settings) {
protected function _create($class, $alias, $settings) {
$instance = new $class($this, $settings);
$enable = isset($settings['enabled']) ? $settings['enabled'] : true;
if ($enable) {
Expand Down
3 changes: 2 additions & 1 deletion Cake/Database/ConnectionRegistry.php
Expand Up @@ -66,10 +66,11 @@ protected function _throwMissingClassError($class, $plugin) {
* Part of the template method for Cake\Utility\ObjectRegistry::load()
*
* @param string|Driver $class The classname or object to make.
* @param string $alias The alias of the object.
* @param array $settings An array of settings to use for the driver.
* @return Connection A connection with the correct driver.
*/
protected function _create($class, $settings) {
protected function _create($class, $alias, $settings) {
if (is_object($class)) {
$instance = $class;
}
Expand Down
3 changes: 2 additions & 1 deletion Cake/Log/LogEngineRegistry.php
Expand Up @@ -58,12 +58,13 @@ protected function _throwMissingClassError($class, $plugin) {
*
* Part of the template method for Cake\Utility\ObjectRegistry::load()
* @param string|LogInterface $class The classname or object to make.
* @param string $alias The alias of the object.
* @param array $settings An array of settings to use for the logger.
* @return LogEngine The constructed logger class.
* @throws Cake\Error\Exception when an object doesn't implement
* the correct interface.
*/
protected function _create($class, $settings) {
protected function _create($class, $alias, $settings) {
if (is_object($class)) {
$instance = $class;
}
Expand Down
53 changes: 49 additions & 4 deletions Cake/ORM/BehaviorRegistry.php
Expand Up @@ -16,6 +16,7 @@

use Cake\Core\App;
use Cake\Error;
use Cake\ORM\Behavior;
use Cake\ORM\Table;
use Cake\Utility\ObjectRegistry;

Expand Down Expand Up @@ -43,6 +44,13 @@ class BehaviorRegistry extends ObjectRegistry {
*/
protected $_eventManager;

/**
* Method mappings.
*
* @var array
*/
protected $_methodMap = [];

/**
* Constructor
*
Expand Down Expand Up @@ -88,15 +96,47 @@ protected function _throwMissingClassError($class, $plugin) {
* Enabled behaviors will be registered with the event manager.
*
* @param string $class The classname that is missing.
* @param string $alias The alias of the object.
* @param array $settings An array of settings to use for the behavior.
* @return Component The constructed behavior class.
*/
protected function _create($class, $settings) {
protected function _create($class, $alias, $settings) {
$instance = new $class($this->_table, $settings);
$enable = isset($settings['enabled']) ? $settings['enabled'] : true;
if ($enable) {
$this->_eventManager->attach($instance);
}
$this->_mapMethods($instance, $class, $alias);
return $instance;
}

/**
* Store the map of behavior methods and ensure there are no duplicates.
*
* Use the implementedEvents() method to exclude callback methods.
*
* @param Cake\ORM\Behavior $instance
* @return void
*/
protected function _mapMethods(Behavior $instance, $class, $alias) {
$events = $instance->implementedEvents();
$methods = get_class_methods($instance);
foreach ($events as $e => $binding) {
if (is_array($binding) && isset($binding['callable']) && isset($binding['callable'])) {
$binding = $binding['callable'];
}
$index = array_search($binding, $methods);
unset($methods[$index]);
}

foreach ($methods as $method) {
if (isset($this->_methodMap[$method])) {
$message = '%s contains duplicate method "%s" which is already provided by %s';
$error = __d('cake_dev', $message, $class, $method, $this->_methodMap[$method]);
throw new Error\Exception($error);
}
$this->_methodMap[$method] = $alias;
}
return $instance;
}

Expand All @@ -110,17 +150,22 @@ protected function _create($class, $settings) {
* @return boolean
*/
public function hasMethod($method) {
return isset($this->_methodMap[$method]);
}

/**
* Invoke a method on a behavior.
*
* @param string $method The method to invoke.
* @param mixed $args The arguments you want to invoke the method with should
* be provided as the remaining arguments to call()
* @param array $args The arguments you want to invoke the method with.
* @return mixed The return value depends on the underlying behavior method.
*/
public function call($method) {
public function call($method, array $args = []) {
if (!$this->hasMethod($method)) {
return false;
}
$alias = $this->_methodMap[$method];
return call_user_func_array([$this->_loaded[$alias], $method], $args);
}

}
2 changes: 1 addition & 1 deletion Cake/Test/TestApp/Model/Behavior/SluggableBehavior.php
Expand Up @@ -35,7 +35,7 @@ public function findNoSlug(Query $query, $options = []) {
return $query;
}

public function slugify(Table $table, $value) {
public function slugify($value) {
return Inflector::slug($value);
}

Expand Down
9 changes: 6 additions & 3 deletions Cake/Test/TestCase/ORM/BehaviorRegistryTest.php
Expand Up @@ -55,7 +55,7 @@ public function tearDown() {
* @return void
*/
public function testLoad() {
$settings = ['replacement' => '-'];
$settings = ['alias' => 'Sluggable', 'replacement' => '-'];
$result = $this->Behaviors->load('Sluggable', $settings);
$this->assertInstanceOf('TestApp\Model\Behavior\SluggableBehavior', $result);
$this->assertEquals($settings, $result->settings);
Expand Down Expand Up @@ -114,7 +114,7 @@ public function testLoadMissingClass() {
* Test load() duplicate method error
*
* @expectedException Cake\Error\Exception
* @expectedExceptionMessage TestApp\Model\Behavior\DuplicateBehavior contains duplicate method "dupe"
* @expectedExceptionMessage TestApp\Model\Behavior\DuplicateBehavior contains duplicate method "slugify"
* @return void
*/
public function testLoadDuplicateMethodError() {
Expand All @@ -140,7 +140,10 @@ public function testHasMethod() {
*/
public function testCall() {
$this->Behaviors->load('Sluggable');
$result = $this->Behaviors->call('slugify', 'some value');
$result = $this->Behaviors->call('nope');
$this->assertFalse($result);

$result = $this->Behaviors->call('slugify', ['some value']);
$this->assertEquals('some_value', $result);
}

Expand Down
5 changes: 3 additions & 2 deletions Cake/Utility/ObjectRegistry.php
Expand Up @@ -75,7 +75,7 @@ public function load($objectName, $settings = []) {
if (!$className) {
$this->_throwMissingClassError($objectName, substr($plugin, 0, -1));
}
$instance = $this->_create($className, $settings);
$instance = $this->_create($className, $name, $settings);
$this->_loaded[$name] = $instance;
return $instance;
}
Expand Down Expand Up @@ -104,10 +104,11 @@ abstract protected function _throwMissingClassError($class, $plugin);
* required.
*
* @param string $class The class to build.
* @param string $alias The alias of the object.
* @param array $settings The settings for construction
* @return mixed
*/
abstract protected function _create($class, $settings);
abstract protected function _create($class, $alias, $settings);

/**
* Get the loaded object list, or get the object instance at a given name.
Expand Down
5 changes: 3 additions & 2 deletions Cake/View/HelperRegistry.php
Expand Up @@ -133,11 +133,12 @@ protected function _throwMissingClassError($class, $plugin) {
* 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 string $class The class to create.
* @param string $alias The alias of the loaded helper.
* @param array $settings An array of settings to use for the helper.
* @return Component The constructed helper class.
*/
protected function _create($class, $settings) {
protected function _create($class, $alias, $settings) {
$instance = new $class($this->_View, $settings);
$vars = array('request', 'theme', 'plugin');
foreach ($vars as $var) {
Expand Down

0 comments on commit 6fa2d9d

Please sign in to comment.