Skip to content
Browse files

Merge pull request #807 from BlaineSch/feature/respondsTo

Implement `respondsTo` in `Object`, `StaticObject` and child classes.
  • Loading branch information...
2 parents 5bf799c + d17172b commit 05abf7b3e8715f37f4b0fefe5c346c59a9715f89 @nateabele nateabele committed Jan 31, 2013
Showing with 529 additions and 5 deletions.
  1. +16 −0 analysis/Inspector.php
  2. +11 −0 analysis/Logger.php
  3. +12 −0 core/Object.php
  4. +12 −0 core/StaticObject.php
  5. +22 −0 data/Entity.php
  6. +17 −0 data/Model.php
  7. +11 −0 data/model/Query.php
  8. +11 −0 data/model/Relationship.php
  9. +11 −0 data/source/Http.php
  10. +12 −0 data/source/MongoDb.php
  11. +12 −0 data/source/http/adapter/CouchDb.php
  12. +11 −0 g11n/Locale.php
  13. +11 −0 net/http/Service.php
  14. +12 −0 storage/cache/adapter/Redis.php
  15. +11 −0 template/helper/Form.php
  16. +11 −0 template/view/Renderer.php
  17. +12 −0 test/MockerChain.php
  18. +38 −2 tests/cases/analysis/InspectorTest.php
  19. +12 −0 tests/cases/analysis/LoggerTest.php
  20. +12 −0 tests/cases/core/ObjectTest.php
  21. +10 −0 tests/cases/core/StaticObjectTest.php
  22. +22 −0 tests/cases/data/EntityTest.php
  23. +20 −0 tests/cases/data/ModelTest.php
  24. +7 −0 tests/cases/data/model/QueryTest.php
  25. +23 −0 tests/cases/data/model/RelationshipTest.php
  26. +14 −0 tests/cases/data/source/HttpTest.php
  27. +20 −0 tests/cases/data/source/MongoDbTest.php
  28. +7 −0 tests/cases/data/source/http/adapter/CouchDbTest.php
  29. +12 −0 tests/cases/g11n/LocaleTest.php
  30. +7 −0 tests/cases/net/http/ServiceTest.php
  31. +12 −0 tests/cases/storage/cache/adapter/RedisTest.php
  32. +6 −0 tests/cases/template/helper/FormTest.php
  33. +6 −0 tests/cases/template/view/RendererTest.php
  34. +13 −0 tests/cases/test/MockerChainTest.php
  35. +5 −3 tests/cases/test/filter/ComplexityTest.php
  36. +22 −0 tests/cases/util/CollectionTest.php
  37. +12 −0 tests/cases/util/ValidatorTest.php
  38. +4 −0 tests/mocks/core/MockStaticInstantiator.php
  39. +5 −0 tests/mocks/data/MockPost.php
  40. +12 −0 util/Collection.php
  41. +13 −0 util/Validator.php
View
16 analysis/Inspector.php
@@ -49,6 +49,22 @@ class Inspector extends \lithium\core\StaticObject {
);
/**
+ * Will determine if a method can be called.
+ *
+ * @param string|object $class Class to inspect.
+ * @param string $method Method name.
+ * @param bool $internal Interal call or not.
+ * @return bool
+ */
+ public static function isCallable($object, $method, $internal = false) {
+ $methodExists = method_exists($object, $method);
+ $callable = function($object, $method) {
+ return is_callable(array($object, $method));
+ };
+ return $internal ? $methodExists : $methodExists && $callable($object, $method);
+ }
+
+ /**
* Determines if a given $identifier is a class property, a class method, a class itself,
* or a namespace identifier.
*
View
11 analysis/Logger.php
@@ -140,6 +140,17 @@ public static function __callStatic($priority, $params) {
}
/**
+ * Custom check to determine if our given magic methods can be responded to.
+ *
+ * @param string $method Method name.
+ * @param bool $internal Interal call or not.
+ * @return bool
+ */
+ public static function respondsTo($method, $internal = false) {
+ return isset(static::$_priorities[$method]) || parent::respondsTo($method, $internal);
+ }
+
+ /**
* This method is called automatically to initialize the default configuration of a log adapter,
* such that the adapter defaults to accepting log messages of any priority (i.e. the
* `'priority'` key is set to `true`).
View
12 core/Object.php
@@ -10,6 +10,7 @@
use lithium\core\Libraries;
use lithium\util\collection\Filters;
+use lithium\analysis\Inspector;
/**
* Base class in Lithium's hierarchy, from which all concrete classes inherit. This class defines
@@ -206,6 +207,17 @@ public static function __set_state($data) {
}
/**
+ * Will determine if a method can be called.
+ *
+ * @param string $method Method name.
+ * @param bool $internal Interal call or not.
+ * @return bool
+ */
+ public function respondsTo($method, $internal = false) {
+ return Inspector::isCallable($this, $method, $internal);
+ }
+
+ /**
* Returns an instance of a class with given `config`. The `name` could be a key from the
* `classes` array, a fully-namespaced class name, or an object. Typically this method is used
* in `_init` to create the dependencies used in the current class.
View
12 core/StaticObject.php
@@ -10,6 +10,7 @@
use lithium\core\Libraries;
use lithium\util\collection\Filters;
+use lithium\analysis\Inspector;
/**
* Provides a base class for all static classes in the Lithium framework. Similar to its
@@ -91,6 +92,17 @@ public static function invokeMethod($method, $params = array()) {
}
/**
+ * Will determine if a method can be called.
+ *
+ * @param string $method Method name.
+ * @param bool $internal Interal call or not.
+ * @return bool
+ */
+ public static function respondsTo($method, $internal = false) {
+ return Inspector::isCallable(get_called_class(), $method, $internal);
+ }
+
+ /**
* Returns an instance of a class with given `config`. The `name` could be a key from the
* `classes` array, a fully namespaced class name, or an object. Typically this method is used
* in `_init` to create the dependencies used in the current class.
View
22 data/Entity.php
@@ -11,6 +11,7 @@
use BadMethodCallException;
use UnexpectedValueException;
use lithium\data\Collection;
+use lithium\analysis\Inspector;
/**
* `Entity` is a smart data object which represents data such as a row or document in a
@@ -197,6 +198,27 @@ public function __call($method, $params) {
}
/**
+ * Custom check to determine if our given magic methods can be responded to.
+ *
+ * @param string $method Method name.
+ * @param bool $internal Interal call or not.
+ * @return bool
+ */
+ public function respondsTo($method, $internal = false) {
+ $class = $this->_model;
+ $modelRespondsTo = false;
+ $parentRespondsTo = parent::respondsTo($method, $internal);
+ $staticRespondsTo = $class::respondsTo($method, $internal);
+ if (method_exists($class, '_object')) {
+ $model = $class::invokeMethod('_object');
+ $modelRespondsTo = $model->respondsTo($method);
+ } else {
+ $modelRespondsTo = Inspector::isCallable($class, $method, $internal);
+ }
+ return $parentRespondsTo || $staticRespondsTo || $modelRespondsTo;
+ }
+
+ /**
* Allows several properties to be assigned at once, i.e.:
* {{{
* $record->set(array('title' => 'Lorem Ipsum', 'value' => 42));
View
17 data/Model.php
@@ -524,6 +524,23 @@ public function __call($method, $params) {
}
/**
+ * Custom check to determine if our given magic methods can be responded to.
+ *
+ * @param string $method Method name.
+ * @param bool $internal Interal call or not.
+ * @return bool
+ */
+ public static function respondsTo($method, $internal = false) {
+ $self = static::_object();
+ $methods = static::instanceMethods();
+ $isFinder = isset($self->_finders[$method]);
+ preg_match('/^findBy(?P<field>\w+)$|^find(?P<type>\w+)By(?P<fields>\w+)$/', $method, $args);
+ $staticRepondsTo = $isFinder || $method === 'all' || !!$args;
+ $instanceRespondsTo = isset($methods[$method]);
+ return $instanceRespondsTo || $staticRepondsTo || parent::respondsTo($method, $internal);
+ }
+
+ /**
* The `find` method allows you to retrieve data from the connected data source.
*
* Examples:
View
11 data/model/Query.php
@@ -766,6 +766,17 @@ public function __call($method, array $params = array()) {
}
/**
+ * Custom check to determine if our given magic methods can be responded to.
+ *
+ * @param string $method Method name.
+ * @param bool $internal Interal call or not.
+ * @return bool
+ */
+ public function respondsTo($method, $internal = false) {
+ return isset($this->_config[$method]) || parent::respondsTo($method, $internal);
+ }
+
+ /**
* Will return a find first condition on the associated model if a record is connected.
* Called by conditions when it is called as a get and no condition is set.
*
View
11 data/model/Relationship.php
@@ -135,6 +135,17 @@ public function __call($name, $args = array()) {
return $this->data($name);
}
+ /**
+ * Custom check to determine if our given magic methods can be responded to.
+ *
+ * @param string $method Method name.
+ * @param bool $internal Interal call or not.
+ * @return bool
+ */
+ public function respondsTo($method, $internal = false) {
+ return is_callable(array($this, $method), true);
+ }
+
protected function _keys($keys) {
$config = $this->_config;
$hasRel = ($related = ($config['type'] === 'belongsTo') ? $config['to'] : $config['from']);
View
11 data/source/Http.php
@@ -139,6 +139,17 @@ public function __call($method, $params) {
}
/**
+ * Custom check to determine if our given magic methods can be responded to.
+ *
+ * @param string $method Method name.
+ * @param bool $internal Interal call or not.
+ * @return bool
+ */
+ public function respondsTo($method, $internal = false) {
+ return isset($this->_methods[$method]) || parent::respondsTo($method, $internal);
+ }
+
+ /**
* Method to send to a specific resource.
*
* @param array $query a query object
View
12 data/source/MongoDb.php
@@ -352,6 +352,18 @@ public function __call($method, $params) {
}
/**
+ * Custom check to determine if our given magic methods can be responded to.
+ *
+ * @param string $method Method name.
+ * @param bool $internal Interal call or not.
+ * @return bool
+ */
+ public function respondsTo($method, $internal = false) {
+ $childRespondsTo = is_object($this->server) && is_callable(array($this->server, $method));
+ return parent::respondsTo($method, $internal) || $childRespondsTo;
+ }
+
+ /**
* Normally used in cases where the query is a raw string (as opposed to a `Query` object),
* to database must determine the correct column names from the result resource. Not
* applicable to this data source.
View
12 data/source/http/adapter/CouchDb.php
@@ -115,6 +115,18 @@ public function __call($method, $params = array()) {
}
/**
+ * Custom check to determine if our given magic methods can be responded to.
+ *
+ * @param string $method Method name.
+ * @param bool $internal Interal call or not.
+ * @return bool
+ */
+ public function respondsTo($method, $internal = false) {
+ $parentRespondsTo = parent::respondsTo($method, $internal);
+ return $parentRespondsTo || is_callable(array($this->connection, $method));
+ }
+
+ /**
* Returns an array of object types accessible through this database.
*
* @param object $class
View
11 g11n/Locale.php
@@ -82,6 +82,17 @@ public static function __callStatic($method, $params = array()) {
}
/**
+ * Custom check to determine if our given magic methods can be responded to.
+ *
+ * @param string $method Method name.
+ * @param bool $internal Interal call or not.
+ * @return bool
+ */
+ public static function respondsTo($method, $internal = false) {
+ return isset(static::$_tags[$method]) || parent::respondsTo($method, $internal);
+ }
+
+ /**
* Composes a locale from locale tags. This is the pendant to `Locale::decompose()`.
*
* @param array $tags An array as obtained from `Locale::decompose()`.
View
11 net/http/Service.php
@@ -116,6 +116,17 @@ public function __call($method, $params = array()) {
}
/**
+ * Custom check to determine if our given magic methods can be responded to.
+ *
+ * @param string $method Method name.
+ * @param bool $internal Interal call or not.
+ * @return bool
+ */
+ public function respondsTo($method, $internal = false) {
+ return is_callable(array($this, $method), true);
+ }
+
+ /**
* Send HEAD request.
*
* @param string $path
View
12 storage/cache/adapter/Redis.php
@@ -121,6 +121,18 @@ public function __call($method, $params = array()) {
}
/**
+ * Custom check to determine if our given magic methods can be responded to.
+ *
+ * @param string $method Method name.
+ * @param bool $internal Interal call or not.
+ * @return bool
+ */
+ public function respondsTo($method, $internal = 0) {
+ $parentRespondsTo = parent::respondsTo($method, $internal);
+ return $parentRespondsTo || is_callable(array($this->connection, $method));
+ }
+
+ /**
* Sets expiration time for cache keys
*
* @param string $key The key to uniquely identify the cached item
View
11 template/helper/Form.php
@@ -398,6 +398,17 @@ public function __call($type, array $params = array()) {
}
/**
+ * Custom check to determine if our given magic methods can be responded to.
+ *
+ * @param string $method Method name.
+ * @param bool $internal Interal call or not.
+ * @return bool
+ */
+ public function respondsTo($method, $internal = false) {
+ return is_callable(array($this, $method), true);
+ }
+
+ /**
* Generates a form field with a label, input, and error message (if applicable), all contained
* within a wrapping element.
*
View
11 template/view/Renderer.php
@@ -298,6 +298,17 @@ public function __call($method, $params) {
}
/**
+ * Custom check to determine if our given magic methods can be responded to.
+ *
+ * @param string $method Method name.
+ * @param bool $internal Interal call or not.
+ * @return bool
+ */
+ public function respondsTo($method, $internal = false) {
+ return is_callable(array($this, $method), true);
+ }
+
+ /**
* Brokers access to helpers attached to this rendering context, and loads helpers on-demand if
* they are not available.
*
View
12 test/MockerChain.php
@@ -117,6 +117,18 @@ public function __call($comparison, $args) {
}
/**
+ * Custom check to determine if our given magic methods can be responded to.
+ *
+ * @param string $method Method name.
+ * @param bool $internal Interal call or not.
+ * @return bool
+ */
+ public function respondsTo($method, $internal = false) {
+ $methodExists = in_array($method, array('gt', 'gte', 'lt', 'lte', 'eq'), true);
+ return $methodExists || parent::respondsTo($method, $internal);
+ }
+
+ /**
* Valides the method was called after the last call.
*
* @param string $method Method to assert
View
40 tests/cases/analysis/InspectorTest.php
@@ -12,6 +12,7 @@
use lithium\analysis\Inspector;
use lithium\core\Libraries;
use lithium\tests\mocks\analysis\MockEmptyClass;
+use lithium\tests\mocks\core\MockMethodFiltering;
class InspectorTest extends \lithium\test\Unit {
@@ -109,8 +110,8 @@ public function testLineIntrospection() {
$expected = array(__LINE__ - 2 => "\tpublic function testLineIntrospection() {");
$this->assertEqual($expected, $result);
- $result = Inspector::lines(__CLASS__, array(16));
- $expected = array(16 => 'class InspectorTest extends \lithium\test\Unit {');
+ $result = Inspector::lines(__CLASS__, array(17));
+ $expected = array(17 => 'class InspectorTest extends \lithium\test\Unit {');
$this->assertEqual($expected, $result);
$lines = 'This is the first line.' . PHP_EOL . 'And this the second.';
@@ -314,6 +315,41 @@ function($property) { return $property['name']; },
$this->assertNull(Inspector::properties('\lithium\core\Foo'));
}
+
+ public function testCallableObjectWithBadMethods() {
+ $stdObj = new MockEmptyClass;
+ $this->assertFalse(Inspector::isCallable($stdObj, 'foo', 0));
+ $this->assertFalse(Inspector::isCallable($stdObj, 'bar', 0));
+ $this->assertFalse(Inspector::isCallable($stdObj, 'baz', 0));
+ }
+
+ public function testCallableClassWithBadMethods() {
+ $this->assertFalse(Inspector::isCallable('lithium\action\Dispatcher', 'foo', 0));
+ $this->assertFalse(Inspector::isCallable('lithium\action\Dispatcher', 'bar', 0));
+ $this->assertFalse(Inspector::isCallable('lithium\action\Dispatcher', 'baz', 0));
+ }
+
+ public function testCallableObjectWithRealMethods() {
+ $obj = new MockMethodFiltering();
+ $this->assertTrue(Inspector::isCallable($obj, 'method', 0));
+ $this->assertTrue(Inspector::isCallable($obj, 'method2', 0));
+ $this->assertTrue(Inspector::isCallable($obj, 'manual', 0));
+ }
+
+ public function testCallableClassWithRealMethods() {
+ $this->assertTrue(Inspector::isCallable('lithium\action\Dispatcher', 'config', 0));
+ $this->assertTrue(Inspector::isCallable('lithium\action\Dispatcher', 'run', 0));
+ $this->assertTrue(Inspector::isCallable('lithium\action\Dispatcher', 'applyRules', 0));
+ }
+
+ public function testCallableVisibility() {
+ $obj = new MockMethodFiltering();
+ $this->assertTrue(Inspector::isCallable($obj, 'method', 0));
+ $this->assertTrue(Inspector::isCallable($obj, 'method', 1));
+ $this->assertFalse(Inspector::isCallable('lithium\action\Dispatcher', '_callable', 0));
+ $this->assertTrue(Inspector::isCallable('lithium\action\Dispatcher', '_callable', 1));
+ }
+
}
?>
View
12 tests/cases/analysis/LoggerTest.php
@@ -197,6 +197,18 @@ public function testMultipleAdaptersWriteByNameSecondary() {
unlink($base . '/info_secondary.log');
}
+
+ public function testRespondsToParentCall() {
+ $this->assertTrue(Logger::respondsTo('applyFilter'));
+ $this->assertFalse(Logger::respondsTo('fooBarBaz'));
+ }
+
+ public function testRespondsToMagic() {
+ $this->assertTrue(Logger::respondsTo('emergency'));
+ $this->assertTrue(Logger::respondsTo('debug'));
+ $this->assertFalse(Logger::respondsTo('foobar'));
+ }
+
}
?>
View
12 tests/cases/core/ObjectTest.php
@@ -260,6 +260,18 @@ public function testResetClass() {
$this->assertTrue($obj->manual(array()) !== false);
}
+ public function testRespondsTo() {
+ $obj = new MockMethodFiltering();
+ $this->assertTrue($this->respondsTo('applyFilter'));
+ $this->assertFalse($this->respondsTo('fooBarBaz'));
+ }
+
+ public function testRespondsToProtectedMethod() {
+ $obj = new MockMethodFiltering();
+ $this->assertFalse($this->respondsTo('_parents'));
+ $this->assertTrue($this->respondsTo('_parents', 1));
+ }
+
}
?>
View
10 tests/cases/core/StaticObjectTest.php
@@ -209,6 +209,16 @@ public function testResetClass() {
$this->assertTrue($class::manual(array()) !== false);
}
+ public function testRespondsTo() {
+ $this->assertTrue(MockStaticInstantiator::respondsTo('applyFilter'));
+ $this->assertFalse(MockStaticInstantiator::respondsTo('fooBarBaz'));
+ }
+
+ public function testRespondsToProtectedMethod() {
+ $this->assertFalse(MockStaticInstantiator::respondsTo('_foo'));
+ $this->assertTrue(MockStaticInstantiator::respondsTo('_foo', 1));
+ }
+
}
?>
View
22 tests/cases/data/EntityTest.php
@@ -193,6 +193,28 @@ public function testStringCasting() {
$model::meta('title', $old);
}
+
+ public function testRespondsTo() {
+ $model = $this->_model;
+ $data = array('foo' => true);
+ $entity = new Entity(compact('model', 'data'));
+
+ $this->assertTrue($entity->respondsTo('foobar'));
+ $this->assertTrue($entity->respondsTo('findByFoo'));
+ $this->assertFalse($entity->respondsTo('barbaz'));
+ $this->assertTrue($entity->respondsTo('model'));
+ $this->assertTrue($entity->respondsTo('instances'));
+ }
+
+ public function testRespondsToParentCall() {
+ $model = $this->_model;
+ $data = array('foo' => true);
+ $entity = new Entity(compact('model', 'data'));
+
+ $this->assertTrue($entity->respondsTo('applyFilter'));
+ $this->assertFalse($entity->respondsTo('fooBarBaz'));
+ }
+
}
?>
View
20 tests/cases/data/ModelTest.php
@@ -890,6 +890,26 @@ public function testLazyMetadataInit() {
);
$this->assertEqual($expected, MockPost::meta());
}
+
+ public function testRespondsTo() {
+ $this->assertTrue(MockPost::respondsTo('findByFoo'));
+ $this->assertTrue(MockPost::respondsTo('findFooByBar'));
+ $this->assertFalse(MockPost::respondsTo('fooBarBaz'));
+ }
+
+ public function testRespondsToParentCall() {
+ $this->assertTrue(MockPost::respondsTo('applyFilter'));
+ $this->assertFalse(MockPost::respondsTo('fooBarBaz'));
+ }
+
+ public function testRespondsToInstanceMethod() {
+ $this->assertFalse(MockPost::respondsTo('foo_Bar_Baz'));
+ MockPost::instanceMethods(array(
+ 'foo_Bar_Baz' => function($entity) {}
+ ));
+ $this->assertTrue(MockPost::respondsTo('foo_Bar_Baz'));
+ }
+
}
?>
View
7 tests/cases/data/model/QueryTest.php
@@ -730,6 +730,13 @@ public function testExportWithUndefinedStrategy() {
$this->expectException('Undefined query strategy `custom`.');
$export = $query->export($this->db);
}
+
+ public function testRespondsTo() {
+ $query = new Query();
+ $this->assertTrue($query->respondsTo('calculate'));
+ $this->assertFalse($query->respondsTo('foobarbaz'));
+ }
+
}
?>
View
23 tests/cases/data/model/RelationshipTest.php
@@ -0,0 +1,23 @@
+<?php
+/**
+ * Lithium: the most rad php framework
+ *
+ * @copyright Copyright 2013, Union of RAD (http://union-of-rad.org)
+ * @license http://opensource.org/licenses/bsd-license.php The BSD License
+ */
+
+namespace lithium\tests\cases\data\model;
+
+use lithium\data\model\Relationship;
+
+class RelationshipTest extends \lithium\test\Unit {
+
+ public function testRespondsTo() {
+ $query = new Relationship();
+ $this->assertTrue($query->respondsTo('foobarbaz'));
+ $this->assertFalse($query->respondsTo(0));
+ }
+
+}
+
+?>
View
14 tests/cases/data/source/HttpTest.php
@@ -337,6 +337,20 @@ public function testSendWithQueryObject() {
$result = (string) $http->last->request;
$this->assertEqual($expected, $result);
}
+
+ public function testRespondsTo() {
+ $http = new Http();
+ $this->assertFalse($http->respondsTo('refactor'));
+ $this->assertTrue($http->respondsTo('create'));
+ $this->assertTrue($http->respondsTo('read'));
+ }
+
+ public function testRespondsToParentCall() {
+ $http = new Http();
+ $this->assertTrue($http->respondsTo('applyFilter'));
+ $this->assertFalse($http->respondsTo('fooBarBaz'));
+ }
+
}
?>
View
20 tests/cases/data/source/MongoDbTest.php
@@ -932,6 +932,26 @@ public function testGridFsDeleteWithCustomPrefix() {
$model::create($data, array('exists' => true))->delete();
$this->assertIdentical('custom', $db->connection->gridFsPrefix);
}
+
+ public function testRespondsToParentCall() {
+ $db = new MongoDb($this->_testConfig);
+ $this->assertTrue($db->respondsTo('applyFilter'));
+ $this->assertFalse($db->respondsTo('fooBarBaz'));
+ }
+
+ public function testRespondsToWithNoServer() {
+ $db = new MongoDb($this->_testConfig);
+ $this->assertFalse($db->respondsTo('listDBs'));
+ $this->assertFalse($db->respondsTo('foobarbaz'));
+ }
+
+ public function testRespondsToWithServer() {
+ $db = new MongoDb($this->_testConfig);
+ $db->server = new MockMongoConnection();
+ $this->assertTrue($db->respondsTo('listDBs'));
+ $this->assertFalse($db->respondsTo('foobarbaz'));
+ }
+
}
?>
View
7 tests/cases/data/source/http/adapter/CouchDbTest.php
@@ -218,6 +218,13 @@ public function testEnabled() {
$this->assertEqual(CouchDb::enabled('booleans'), true);
$this->assertEqual(CouchDb::enabled('relationships'), false);
}
+
+ public function testRespondsTo() {
+ $couchdb = new CouchDb($this->_testConfig);
+ $this->assertTrue($couchdb->respondsTo('foobarbaz'));
+ $this->assertFalse($couchdb->respondsTo(0));
+ }
+
}
?>
View
12 tests/cases/g11n/LocaleTest.php
@@ -478,6 +478,18 @@ public function testPreferredMalformedSpanish() {
$result = Locale::preferred($request, $available);
$this->assertNull($result);
}
+
+ public function testRespondsToParentCall() {
+ $this->assertTrue(Locale::respondsTo('applyFilter'));
+ $this->assertFalse(Locale::respondsTo('fooBarBaz'));
+ }
+
+ public function testRespondsToMagic() {
+ $this->assertTrue(Locale::respondsTo('language'));
+ $this->assertTrue(Locale::respondsTo('territory'));
+ $this->assertFalse(Locale::respondsTo('foobar'));
+ }
+
}
?>
View
7 tests/cases/net/http/ServiceTest.php
@@ -289,6 +289,13 @@ public function testDigestAuth() {
$response = $http->get('/http_auth/', array(), array('return' => 'response'));
$this->assertEqual('success', $response->body());
}
+
+ public function testRespondsTo() {
+ $query = new Service();
+ $this->assertTrue($query->respondsTo('foobarbaz'));
+ $this->assertFalse($query->respondsTo(0));
+ }
+
}
?>
View
12 tests/cases/storage/cache/adapter/RedisTest.php
@@ -402,6 +402,18 @@ public function testMethodDispatch() {
$result = $this->redis->info();
$this->assertTrue(is_array($result), 'redis method dispatch failed');
}
+
+ public function testRespondsTo() {
+ $this->assertTrue($this->redis->respondsTo('bgsave'));
+ $this->assertTrue($this->redis->respondsTo('dbSize'));
+ $this->assertFalse($this->redis->respondsTo('foobarbaz'));
+ }
+
+ public function testRespondsToParentCall() {
+ $this->assertTrue($this->redis->respondsTo('applyFilter'));
+ $this->assertFalse($this->redis->respondsTo('fooBarBaz'));
+ }
+
}
?>
View
6 tests/cases/template/helper/FormTest.php
@@ -1461,6 +1461,12 @@ public function testBindingByName() {
$this->assertEqual($post, $this->form->binding('post'));
$this->assertEqual($info, $this->form->binding('info'));
}
+
+ public function testRespondsTo() {
+ $this->assertTrue($this->form->respondsTo('foobarbaz'));
+ $this->assertFalse($this->form->respondsTo(0));
+ }
+
}
?>
View
6 tests/cases/template/view/RendererTest.php
@@ -267,6 +267,12 @@ public function testHandlers() {
$this->assertEqual("foo\n\tbar", trim($this->subject->head('bar')));
$this->assertEqual("foo\n\tbar", trim($this->subject->head()));
}
+
+ public function testRespondsTo() {
+ $this->assertTrue($this->subject->respondsTo('foobarbaz'));
+ $this->assertFalse($this->subject->respondsTo(0));
+ }
+
}
?>
View
13 tests/cases/test/MockerChainTest.php
@@ -122,6 +122,19 @@ public function testMultipleCallsWithArgsAndSpecificCalled() {
->called('method1')->with()->eq(1)->success());
}
+ public function testRespondsToParentCall() {
+ $chain = Mocker::chain(array());
+ $this->assertTrue($chain->respondsTo('applyFilter'));
+ $this->assertFalse($chain->respondsTo('fooBarBaz'));
+ }
+
+ public function testRespondsToMagic() {
+ $chain = Mocker::chain(array());
+ $this->assertTrue($chain->respondsTo('gt'));
+ $this->assertTrue($chain->respondsTo('lt'));
+ $this->assertFalse($chain->respondsTo('et'));
+ }
+
}
?>
View
8 tests/cases/test/filter/ComplexityTest.php
@@ -39,7 +39,8 @@ class ComplexityTest extends \lithium\test\Unit {
'applyFilter' => 5,
'_parents' => 2,
'_instance' => 2,
- '_stop' => 1
+ '_stop' => 1,
+ 'respondsTo' => 1,
);
/**
@@ -86,12 +87,13 @@ public function testAnalyze() {
Complexity::apply($this->report, $group->tests());
$results = Complexity::analyze($this->report);
- $expected = array('class' => array($testClass => 3.5));
+ $expected = array('class' => array($testClass => 3.1));
foreach ($this->_metrics as $method => $metric) {
$expected['max'][$testClass . '::' . $method . '()'] = $metric;
}
$this->assertEqual($expected['max'], $results['max']);
- $this->assertIdentical($expected['class'][$testClass], $results['class'][$testClass]);
+ $result = round($results['class'][$testClass], 1);
+ $this->assertIdentical($expected['class'][$testClass], $result);
}
/**
View
22 tests/cases/util/CollectionTest.php
@@ -9,6 +9,7 @@
namespace lithium\tests\cases\util;
use stdClass;
+use lithium\data\Entity;
use lithium\util\Collection;
use lithium\tests\mocks\util\MockCollectionMarker;
use lithium\tests\mocks\util\MockCollectionObject;
@@ -459,6 +460,27 @@ public function testValid() {
$collection = new Collection(array('data' => array(1, 5)));
$this->assertTrue($collection->valid());
}
+
+ public function testRespondsToParent() {
+ $collection = new Collection();
+ $this->assertTrue($collection->respondsTo('applyFilter'));
+ $this->assertFalse($collection->respondsTo('fooBarBaz'));
+ }
+
+ public function testRespondsToMagic() {
+ $collection = new Collection(array(
+ 'data' => array(
+ new Entity(array(
+ 'model' => 'lithium\tests\mocks\data\MockPost',
+ 'data' => array('stats' => array('foo' => 'bar')),
+ ))
+ )
+ ));
+ $this->assertTrue($collection->respondsTo('instances'));
+ $this->assertTrue($collection->respondsTo('foobar'));
+ $this->assertFalse($collection->respondsTo('foobarbaz'));
+ }
+
}
?>
View
12 tests/cases/util/ValidatorTest.php
@@ -1209,6 +1209,18 @@ public function testNestedFields() {
$result = Validator::check($data, $rules);
$this->assertEqual(array('id' => array('Bad ID')), $result);
}
+
+ public function testRespondsToParentCall() {
+ $this->assertTrue(Validator::respondsTo('applyFilter'));
+ $this->assertFalse(Validator::respondsTo('fooBarBaz'));
+ }
+
+ public function testRespondsToMagic() {
+ $this->assertTrue(Validator::respondsTo('isAlphaNumeric'));
+ $this->assertTrue(Validator::respondsTo('isCreditCard'));
+ $this->assertFalse(Validator::respondsTo('isFoobar'));
+ }
+
}
?>
View
4 tests/mocks/core/MockStaticInstantiator.php
@@ -15,6 +15,10 @@ class MockStaticInstantiator extends \lithium\core\StaticObject {
public static function instance($name, array $config = array()) {
return static::_instance($name, $config);
}
+
+ protected static function _foo() {
+ return false;
+ }
}
?>
View
5 tests/mocks/data/MockPost.php
@@ -19,6 +19,11 @@ class MockPost extends \lithium\tests\mocks\data\MockBase {
public static function instances() {
return array_keys(static::$_instances);
}
+
+ public function foobar() {
+ return;
+ }
+
}
?>
View
12 util/Collection.php
@@ -210,6 +210,18 @@ public function __call($method, $parameters = array()) {
}
/**
+ * Custom check to determine if our given magic methods can be responded to.
+ *
+ * @param string $method Method name.
+ * @param bool $internal Interal call or not.
+ * @return bool
+ */
+ public function respondsTo($method, $internal = false) {
+ $magicMethod = count($this->_data) > 0 && $this->_data[0]->respondsTo($method, $internal);
+ return $magicMethod || parent::respondsTo($method, $internal);
+ }
+
+ /**
* Converts a `Collection` object to another type of object, or a simple type such as an array.
* The supported values of `$format` depend on the format handlers registered in the static
* property `Collection::$_formats`. The `Collection` class comes with built-in support for
View
13 util/Validator.php
@@ -388,6 +388,19 @@ public static function __callStatic($method, $args = array()) {
}
/**
+ * Custom check to determine if our given magic methods can be responded to.
+ *
+ * @param string $method Method name.
+ * @param bool $internal Interal call or not.
+ * @return bool
+ */
+ public static function respondsTo($method, $internal = false) {
+ $rule = preg_replace("/^is([A-Z][A-Za-z0-9]+)$/", '$1', $method);
+ $rule[0] = strtolower($rule[0]);
+ return isset(static::$_rules[$rule]) || parent::respondsTo($method, $internal);
+ }
+
+ /**
* Checks a set of values against a specified rules list. This method may be used to validate
* any arbitrary array of data against a set of validation rules.
*

0 comments on commit 05abf7b

Please sign in to comment.
Something went wrong with that request. Please try again.