From e3e0040e4019c70c2792978741bb26fc0e4102e9 Mon Sep 17 00:00:00 2001 From: "Maury M. Marques" Date: Mon, 19 Dec 2011 00:40:39 -0200 Subject: [PATCH 1/8] Fitness for CakePHP 2.0 Changed from cakeError() to MissingConnectionException() Added method to search for the latest revision updates without the review Change the query() method to let you use the methods of socket freely --- Model/Datasource/CouchdbSource.php | 177 ++++-- .../Model/Datasource/CouchdbSourceTest.php | 592 ++++++++++++++++++ 2 files changed, 723 insertions(+), 46 deletions(-) create mode 100644 Test/Case/Model/Datasource/CouchdbSourceTest.php diff --git a/Model/Datasource/CouchdbSource.php b/Model/Datasource/CouchdbSource.php index 73f1301..e731db8 100644 --- a/Model/Datasource/CouchdbSource.php +++ b/Model/Datasource/CouchdbSource.php @@ -17,7 +17,8 @@ * @since CakePHP Datasources v 0.3 * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ -App::import('Core', 'HttpSocket'); +App::uses('HttpSocket', 'Network/Http'); +App::uses('DataSource', 'Model/Datasource'); /** * CouchDB Datasource @@ -28,10 +29,24 @@ class CouchdbSource extends DataSource { /** - * Constructor + * Start quote + * + * @var string + */ + public $startQuote = null; + +/** + * End quote + * + * @var string + */ + public $endQuote = null; + +/** + * Constructor. * * @param array $config Connection setup for CouchDB. - * @param integer $autoConnect Autoconnect + * @param integer $autoConnect Autoconnect. * @return boolean */ public function __construct($config = null, $autoConnect = true) { @@ -51,10 +66,10 @@ public function __construct($config = null, $autoConnect = true) { } /** - * Reconnects to the database with optional new settings + * Reconnects to the database with optional new settings. * - * @param array $config New settings - * @return boolean Success + * @param array $config New settings. + * @return boolean Success. */ public function reconnect($config = null) { $this->disconnect(); @@ -64,9 +79,9 @@ public function reconnect($config = null) { } /** - * Connects to the database. Options are specified in the $config instance variable + * Connects to the database. Options are specified in the $config instance variable. * - * @return boolean Connected + * @return boolean Connected. */ public function connect() { if ($this->connected !== true) { @@ -76,12 +91,12 @@ public function connect() { if (isset($this->config['password'])) $this->config['request']['uri']['pass'] = $this->config['password']; - $this->Socket = new HttpSocket($this->config); - if (strpos($this->Socket->get(), 'couchdb') !== false) { + try { + $this->Socket = new HttpSocket($this->config); + $this->Socket->get(); $this->connected = true; - } else { - trigger_error(__('CouchDB Error: connection failed ', true), E_USER_WARNING); - return $this->cakeError('missingConnection', array(array('code' => 500, 'className' => 'CouchdbSource'))); + } catch (SocketException $e) { + throw new MissingConnectionException(array('class' => $e->getMessage())); } } return $this->connected; @@ -92,7 +107,7 @@ public function connect() { * connection is closed, and if DEBUG is turned on (equal to 2) displays the * log of stored data. * - * @return boolean Disconnected + * @return boolean Disconnected. */ public function close() { if (Configure::read('debug') > 1) { @@ -102,9 +117,9 @@ public function close() { } /** - * Disconnect from the database + * Disconnect from the database. * - * @return boolean Disconnected + * @return boolean Disconnected. */ public function disconnect() { if (isset($this->results) && is_resource($this->results)) { @@ -115,19 +130,20 @@ public function disconnect() { } /** - * List of databases + * List of databases. * - * @return array Databases + * @return array Databases. */ public function listSources() { - return $this->__decode($this->Socket->get($this->__uri('_all_dbs')), true); + $databases = $this->__decode($this->Socket->get($this->__uri('_all_dbs')), true); + return $databases; } /** * Convenience method for DboSource::listSources(). * Returns the names of databases in lowercase. * - * @return array Lowercase databases + * @return array Lowercase databases. */ public function sources($reset = false) { if ($reset === true) { @@ -137,10 +153,10 @@ public function sources($reset = false) { } /** - * Returns a description of the model (metadata) + * Returns a description of the model (metadata). * * @param Model $model - * @return array + * @return array Schema. */ public function describe($model) { return $model->schema; @@ -149,12 +165,12 @@ public function describe($model) { /** * Creates a new document in the database. * If the primaryKey is declared, creates the document with the specified ID. - * To create a new database: $this->__decode($this->Socket->put($this->__uri('databaseName'))); + * To create a new database: $this->Model->curlPut('databaseName'); * * @param Model $model * @param array $fields An array of field names to insert. If null, $model->data will be used to generate the field names. * @param array $values An array with key values of the fields. If null, $model->data will be used to generate the field names. - * @return boolean Success + * @return boolean Success. */ public function create($model, $fields = null, $values = null) { $data = $model->data; @@ -183,7 +199,7 @@ public function create($model, $fields = null, $values = null) { * Reads data from a document. * * @param Model $model - * @param array $queryData An array of information containing $queryData keys, similar to Model::find() + * @param array $queryData An array of information containing $queryData keys, similar to Model::find(). * @param integer $recursive Level number of associations. * @return mixed False if an error occurred, otherwise an array of results. */ @@ -224,7 +240,7 @@ public function read($model, $queryData = array(), $recursive = null) { * Applies the rules to the document read. * * @param Model $model - * @param array $queryData An array of information containing $queryData keys, similar to Model::find() + * @param array $queryData An array of information containing $queryData keys, similar to Model::find(). * @param array $result Data read from the document. * @return mixed False if an error occurred, otherwise an array of results. */ @@ -269,7 +285,7 @@ private function __readResult($model, $queryData, $result) { * @param array $fields * @param array $values * @param mixed $conditions - * @return boolean Success + * @return boolean Success. */ public function update($model, $fields = null, $values = null, $conditions = null) { $data = $model->data[$model->alias]; @@ -307,15 +323,29 @@ private function __idRevData(&$model, &$data) { unset($data['rev']); } else if ($model->rev) { $data['_rev'] = $model->rev; + } else { + $data['_rev'] = $this->__lastRevision($model, $model->id); } } /** - * Generates and executes a DELETE statement + * The method searches for the latest revision of a document + * + * @param object $model + * @param int $id + * @return string Last revision of the document + */ + private function __lastRevision(&$model, $id) { + $result = $this->__decode($this->Socket->get($this->__uri($model, $id))); + return $result->_rev; + } + +/** + * Generates and executes a DELETE statement. * * @param Model $model * @param mixed $conditions - * @return boolean Success + * @return boolean Success. */ public function delete($model, $conditions = null) { $id = $model->id; @@ -330,23 +360,23 @@ public function delete($model, $conditions = null) { } /** - * Returns an instruction to count data. (SQL, i.e. COUNT() or MAX()) + * Returns an instruction to count data. (SQL, i.e. COUNT() or MAX()). * * @param model $model - * @param string $func Lowercase name of SQL function, i.e. 'count' or 'max' - * @param array $params Function parameters (any values must be quoted manually) - * @return string An SQL calculation function + * @param string $func Lowercase name of SQL function, i.e. 'count' or 'max'. + * @param array $params Function parameters (any values must be quoted manually). + * @return string An SQL calculation function. */ public function calculate($model, $func, $params = array()) { return true; } /** - * Gets full table name including prefix + * Gets full table name including prefix. * * @param mixed $model * @param boolean $quote - * @return string Full name of table + * @return string Full name of table. */ public function fullTableName($model = null, $quote = true) { $table = null; @@ -361,22 +391,75 @@ public function fullTableName($model = null, $quote = true) { } /** - * Perform any function in CouchDB + * Perform any function in CouchDB. + * The first position of the $params array is used to mount the uri. + * The second place in the $params array is used to assemble data from a POST or PUT. + * The third parameter is used to decode the return json. + * The fourth parameter is used to build an associative array. + * + * The method can be performed by a Model of the following ways: + * + * $this->Model->curlGet('_all_dbs'); + * $this->Model->curlPut('document_name'); + * $this->Model->curlPost('document_name', array('field' => 'value')); + * $this->Model->curlDelete('document_name'); + * $this->Model->curlPost('document_name', array('field' => 'value'), false); + * $this->Model->curlPost('document_name', array('field' => 'value'), true , false); * - * @param string $uri - * @param array $post + * @param string $method + * @param array $params Parâmetros aceitos na ordem: uri, data, decode, assoc * @return object */ - public function query($uri, $post) { - return $this->__decode($this->Socket->post($uri, $this->__encode($post))); + public function query($method, $params) { + list($uri, $data, $decode, $assoc) = $this->__queryParams($params); + + $request = array( + 'method' => strtoupper(str_replace('curl', '', $method)) + ); + + if (!empty($uri)) + $request['uri'] = '/' . $uri; + + if (!empty($data)) + $request['body'] = $this->__encode($data); + + $result = $this->Socket->request($request); + + if ($decode === true) { + $result = $this->__decode($result, $assoc); + } + + return $result; + } + +/** + * Construct the parameter of the query method. + * + * @param array $params + * @return array + */ + private function __queryParams($params) { + if (isset($params[0])) $uri = $params[0]; + else $uri = ''; + + if (isset($params[1])) $data = $params[1]; + else $data = array(); + + if (isset($params[2])) $decode = $params[2]; + else $decode = true; + + if (isset($params[3])) $assoc = $params[3]; + else $assoc = true; + + return array($uri, $data, $decode, $assoc); } /** - * Get a URI + * Get a URI. * * @param mixed $model * @param string $params - * @return string URI + * @return string URI. */ private function __uri($model = null, $params = null) { if (!is_null($params)) { @@ -386,17 +469,18 @@ private function __uri($model = null, $params = null) { } /** - * JSON encode + * JSON encode. * * @param string json $data - * @return string JSON + * @return string JSON. */ private function __encode($data) { return json_encode($data); } /** - * JSON decode + * JSON decode. + * * @param string json $data * @param boolean $assoc If true, returns array. If false, returns object. * @return mixed Object or Array. @@ -406,7 +490,7 @@ private function __decode($data, $assoc = false) { } /** - * Checks if the result returned ok = true + * Checks if the result returned ok = true. * * @param object $object * @return boolean @@ -415,3 +499,4 @@ private function __checkOk($object = null) { return isset($object->ok) && $object->ok === true; } } +?> \ No newline at end of file diff --git a/Test/Case/Model/Datasource/CouchdbSourceTest.php b/Test/Case/Model/Datasource/CouchdbSourceTest.php new file mode 100644 index 0000000..ed53abe --- /dev/null +++ b/Test/Case/Model/Datasource/CouchdbSourceTest.php @@ -0,0 +1,592 @@ + array( + 'notempty' => array( + 'rule' => array('notempty'), + //'message' => 'Your custom message here', + //'allowEmpty' => false, + //'required' => false, + //'last' => false, // Stop validation after this rule + //'on' => 'create', // Limit validation to 'create' or 'update' operations + ), + ), + ); + + public $schema = array( + 'id' => array( + 'type' => 'string', + 'null' => true, + 'key' => 'primary', + 'length' => 32, + ), + 'rev' => array( + 'type' => 'string', + 'null' => true, + 'key' => 'primary', + 'length' => 34, + ), + 'title' => array( + 'type' => 'string', + 'null' => true, + 'length' => 255, + ), + 'description' => array( + 'type' => 'text', + 'null' => true, + ) + ); +} + +/** + * CouchdbTestCase + * + * @package datasources + * @subpackage datasources.tests.cases.models.datasources + */ +class CouchdbTestCase extends CakeTestCase { + +/** + * CouchDB Datasource object + * + * @var object + */ + public $Couchdb = null; + +/** + * Configuration + * + * @var array + */ + protected $config = array( + 'datasource' => 'CouchdbSource', + 'persistent' => false, + 'host' => 'localhost', + 'port' => '5984', + 'login' => 'root', + 'password' => 'root', + 'database' => null + ); + +/** + * Start Test + * + * @return void + */ + public function startTest() { + config('database'); + $config = new DATABASE_CONFIG(); + + if (isset($config->couchdb_test)) { + $this->config = $config->couchdb_test; + } + + ConnectionManager::create('couchdb_test', $this->config); + + $this->Post = ClassRegistry::init('Post'); + $this->removeAllDocuments(); + } + +/** + * testConnection + * + * @return void + */ + public function testConnection() { + $this->Couchdb = new CouchdbSource($this->config); + $this->Couchdb =& ConnectionManager::getDataSource($this->Post->useDbConfig); + + $reconnect = $this->Couchdb->reconnect($this->config); + $this->assertIdentical($reconnect, true, __('Not reconnected')); + + $disconnect = $this->Couchdb->disconnect(); + $this->assertIdentical($disconnect, true, __('Not disconnect')); + } + +/** + * testFind + * + * @return void + */ + public function testFind() { + $data = array( + 'title' => 'My first post', + 'description' => 'My first post' + ); + $this->Post->save($data); + + $result = $this->Post->find('all'); + $this->assertEqual(1, count($result)); + + $resultData = $result[0]['Post']; + $this->assertEqual(4, count($resultData)); + $this->assertTrue(!empty($resultData['id'])); + $this->assertEqual($this->Post->id, $resultData['id']); + $this->assertEqual($this->Post->rev, $resultData['rev']); + $this->assertEqual($data['title'], $resultData['title']); + $this->assertEqual($data['description'], $resultData['description']); + } + + +/** + * testFindConditions + * + * @return void + */ + public function testFindConditions() { + $data = array( + 'title' => 'My first post', + 'description' => 'My first post' + ); + $this->Post->save($data); + + $this->Post->create(); + $this->Post->save($data); + + $result = $this->Post->find('all'); + $this->assertEqual(2, count($result)); + + $result = $this->Post->find('all', array('conditions' => array('Post.id' => $this->Post->id))); + $this->assertEqual(1, count($result)); + + $result = $this->Post->find('all', array('conditions' => array('id' => $this->Post->id))); + $this->assertEqual(1, count($result)); + } + +/** + * testFindRevs + * + * @return void + */ + public function testFindRevs() { + $data = array( + 'title' => 'My first post', + 'description' => 'My first post' + ); + $this->Post->save($data); + $this->Post->save($data); + + $this->Post->recursive = 0; + $result = $this->Post->find('all', array('conditions' => array('id' => $this->Post->id))); + $this->assertEqual(2, count($result[0]['Post']['_revs_info'])); + } + +/** + * Tests save method. + * + * @return void + */ + public function testSave() { + $data = array( + 'title' => 'My first post', + 'description' => 'My first post' + ); + + $this->Post->create(); + $saveResult = $this->Post->save($data); + $this->assertIdentical(is_array($saveResult), true); + + $result = $this->Post->find('all'); + $this->assertEqual(1, count($result)); + + $resultData = $result[0]['Post']; + $this->assertEqual(4, count($resultData)); + $this->assertTrue(!empty($resultData['id'])); + $this->assertEqual($this->Post->id, $resultData['id']); + $this->assertEqual($this->Post->rev, $resultData['rev']); + $this->assertEqual($data['title'], $resultData['title']); + $this->assertEqual($data['description'], $resultData['description']); + } + +/** + * Tests save method. + * + * @return void + */ + public function testSaveWithId() { + $data = array( + 'id' => String::uuid(), + 'title' => 'My first post', + 'description' => 'My first post' + ); + + $this->Post->create(); + $saveResult = $this->Post->save($data); + $this->assertIdentical(is_array($saveResult), true); + + $result = $this->Post->find('all'); + $this->assertEqual(1, count($result)); + + $resultData = $result[0]['Post']; + $this->assertEqual(4, count($resultData)); + $this->assertTrue(!empty($resultData['id'])); + $this->assertEqual($resultData['id'], $data['id']); + $this->assertEqual($this->Post->id, $resultData['id']); + $this->assertEqual($this->Post->rev, $resultData['rev']); + $this->assertEqual($data['title'], $resultData['title']); + $this->assertEqual($data['description'], $resultData['description']); + } + +/** + * Tests saveAll method. + * + * @return void + */ + public function testSaveAll() { + $data[0]['Post'] = array( + 'title' => 'My first post', + 'description' => 'My first post' + ); + + $data[1]['Post'] = array( + 'title' => 'My second post', + 'description' => 'My second post' + ); + + $this->Post->create(); + $saveResult = $this->Post->saveAll($data); + + $result = $this->Post->find('all'); + $this->assertEqual(2, count($result)); + + $resultData = $result[0]['Post']; + $this->assertEqual(4, count($resultData)); + $this->assertTrue(!empty($resultData['id'])); + $this->assertEqual($data[0]['Post']['title'], $resultData['title']); + $this->assertEqual($data[0]['Post']['description'], $resultData['description']); + + $resultData = $result[1]['Post']; + $this->assertEqual(4, count($resultData)); + $this->assertTrue(!empty($resultData['id'])); + $this->assertEqual($data[1]['Post']['title'], $resultData['title']); + $this->assertEqual($data[1]['Post']['description'], $resultData['description']); + } + +/** + * Tests update method. + * + * @return void + */ + public function updateTest() { + // Count posts + $uri = '/posts/_temp_view?group=true'; + $post = array( + 'map' => 'function(doc) { emit(doc._id,1); }', + 'reduce' => 'function(keys, values) { return sum(values); }' + ); + + $mapReduce = $this->Post->query($uri, $post); + if(isset($mapReduce->rows[0]->value)) $count0 = $mapReduce->rows[0]->value; + else $count0 = 0; + + $count1 = $this->updateTest1($uri, $post, $count0); + $count2 = $this->updateTest2($uri, $post, $count1); + $count3 = $this->updateTest3($uri, $post, $count2); + $count4 = $this->updateTest4($uri, $post, $count2); + $updateData = $this->updateTest5($uri, $post, $count4); + + // Final test + $result = $this->Post->find('all'); + $this->assertEqual(1, count($result)); + + $resultData = $result[0]['Post']; + $this->assertEqual(4, count($resultData)); + $this->assertTrue(!empty($resultData['id'])); + $this->assertEqual($this->Post->id, $resultData['id']); + $this->assertEqual($this->Post->rev, $resultData['rev']); + $this->assertNotEqual($updateData['title'], $resultData['title']); + $this->assertNotEqual($updateData['description'], $resultData['description']); + + } + +/** + * Tests update1 method. + * + * @param string $uri + * @param array $post + * @param interger $previousCount + * @return void + */ + private function updateTest1($uri, $post, $previousCount) { + $data = array( + 'title' => 'My first post', + 'description' => 'My first post' + ); + + $this->Post->create(); + $saveResult = $this->Post->save($data); + $this->assertIdentical(is_array($saveResult), true); + $this->assertIdentical(!empty($this->Post->id), true); + + $mapReduce = $this->Post->curlPost($uri, $post); + $count1 = $mapReduce['rows'][0]['value']; + + $this->assertIdentical($count1 - $previousCount, 1); + + return $count1; + } + +/** + * Tests update2 method. + * + * @param string $uri + * @param array $post + * @param interger $previousCount + * @return void + */ + private function updateTest2($uri, $post, $previousCount) { + $findResult = $this->Post->find('first'); + $this->assertEqual(4, count($findResult['Post'])); + + $updateData = array( + 'title' => 'My post update', + 'description' => 'My post update' + ); + + $this->Post->id = $findResult['Post']['id']; + $this->Post->rev = $findResult['Post']['rev']; + $saveResult = $this->Post->save($updateData); + $this->assertIdentical(is_array($saveResult), true); + + $mapReduce = $this->Post->curlPost($uri, $post); + $count2 = $mapReduce['rows'][0]['value']; + + $this->assertIdentical($count2 - $previousCount, 0); + + return $count2; + } + +/** + * Tests update3 method. + * + * @param string $uri + * @param array $post + * @param interger $previousCount + * @return void + */ + private function updateTest3($uri, $post, $previousCount) { + $findResult = $this->Post->find('first'); + $this->assertEqual(4, count($findResult['Post'])); + + $updateData = array( + 'id' => $findResult['Post']['id'], + 'title' => 'My post update', + 'description' => 'My post update' + ); + + $this->Post->rev = $findResult['Post']['rev']; + $saveResult = $this->Post->save($updateData); + $this->assertIdentical(is_array($saveResult), true); + $this->assertIdentical($this->Post->id, $findResult['Post']['id']); + + $mapReduce = $this->Post->curlPost($uri, $post); + $count3 = $mapReduce['rows'][0]['value']; + + $this->assertIdentical($count3 - $previousCount, 0); + + return $count3; + } + +/** + * Tests update4 method. + * + * @param string $uri + * @param array $post + * @param interger $previousCount + * @return void + */ + private function updateTest4($uri, $post, $previousCount) { + $findResult = $this->Post->find('first'); + $this->assertEqual(4, count($findResult['Post'])); + + $updateData = array( + 'id' => $findResult['Post']['id'], + 'rev' => $findResult['Post']['rev'], + 'title' => 'My post update', + 'description' => 'My post update' + ); + + $saveResult = $this->Post->save($updateData); + $this->assertIdentical(is_array($saveResult), true); + $this->assertIdentical($this->Post->id, $findResult['Post']['id']); + + $mapReduce = $this->Post->curlPost($uri, $post); + $count4 = $mapReduce['rows'][0]['value']; + + $this->assertIdentical($count4 - $previousCount, 0); + + return $count4; + } + +/** + * Tests update5 method. + * + * @param string $uri + * @param array $post + * @param interger $previousCount + * @return void + */ + private function updateTest5($uri, $post, $previousCount) { + $findResult = $this->Post->find('first'); + $this->assertEqual(4, count($findResult['Post'])); + + $updateData = array( + 'id' => $findResult['Post']['id'], + 'rev' => 'whatever', + 'title' => 'My post fail', + 'description' => 'My post fail' + ); + + $saveResult = $this->Post->save($updateData); + $this->assertFalse($saveResult); + $this->assertIdentical($this->Post->id, $findResult['Post']['id']); + + $mapReduce = $this->Post->curlPost($uri, $post); + $count5 = $mapReduce['rows'][0]['value']; + + $this->assertIdentical($count5 - $previousCount, 0); + + return $updateData; + } + +/** + * Test update without revision + * + * @return void + */ + public function testUpdateWithoutRevision() { + $data = array( + 'title' => 'My first post', + 'description' => 'My first post' + ); + + $this->Post->create(); + $saveResult = $this->Post->save($data); + + $result = $this->Post->find('first'); + + unset($result['Post']['rev']); + unset($this->Post->rev); + + $updateResult = $this->Post->save($result); + + $this->assertIdentical(is_array($updateResult), true); + $this->assertIdentical($this->Post->id, $saveResult['Post']['id']); + } + +/** + * Tests delete method. + * + * @return void + */ + public function testDelete() { + $data = array( + 'title' => 'My first post', + 'description' => 'My first post' + ); + + $this->Post->create(); + $saveResult = $this->Post->save($data); + + $result = $this->Post->find('all'); + $this->assertEqual(1, count($result)); + + $this->Post->id = $result[0]['Post']['id']; + $this->Post->rev = $result[0]['Post']['rev']; + $this->Post->delete(); + + $result = $this->Post->find('all'); + $this->assertEqual(0, count($result)); + } + +/** + * Tests query method. + * + * @return void + */ + public function testQuery() { + // GET + $result = $this->Post->curlGet('_all_dbs'); + $this->assertIdentical(is_array($result), true); + + // POST + $data = array( + 'title' => 'My first post', + 'description' => 'My first post' + ); + + $result = $this->Post->curlPost('/posts', $data); + $this->assertIdentical($result['ok'], true); + + // PUT + $data = array( + '_rev' => $result['rev'], + 'title' => 'My first update', + 'description' => 'My first update' + ); + + $result = $this->Post->curlPut('/posts/' . $result['id'], $data); + $this->assertIdentical($result['ok'], true); + + // DELETE + $result = $this->Post->curlDelete('/posts/' . $result['id'] . '/?rev=' . $result['rev']); + $this->assertIdentical($result['ok'], true); + } + +/** + * Remove all documents from database + * + * @return void + */ + private function removeAllDocuments() { + $posts = $this->Post->find('list', array('fields' => array('Post.rev'))); + foreach($posts as $id => $post) { + $this->Post->rev = $post; + $this->Post->delete($id); + } + } + +/** + * End Test + * + * @return void + */ + public function endTest() { + $this->removeAllDocuments(); + unset($this->Post); + unset($this->Couchdb); + ClassRegistry::flush(); + } +} From be17699bf83413d445122cfbaf1dbbfb021b9861 Mon Sep 17 00:00:00 2001 From: "Maury M. Marques" Date: Thu, 22 Dec 2011 01:04:23 -0200 Subject: [PATCH 2/8] Delete without revision --- Model/Datasource/CouchdbSource.php | 3 ++- .../Model/Datasource/CouchdbSourceTest.php | 26 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/Model/Datasource/CouchdbSource.php b/Model/Datasource/CouchdbSource.php index e731db8..66f9ade 100644 --- a/Model/Datasource/CouchdbSource.php +++ b/Model/Datasource/CouchdbSource.php @@ -351,7 +351,8 @@ public function delete($model, $conditions = null) { $id = $model->id; $rev = $model->rev; - if (!empty($id) && !empty($rev)) { + if (!empty($id)) { + if (empty($rev)) $rev = $this->__lastRevision($model, $id); $id_rev = $id . '/?rev=' . $rev; $result = $this->__decode($this->Socket->delete($this->__uri($model, $id_rev))); return $this->__checkOk($result); diff --git a/Test/Case/Model/Datasource/CouchdbSourceTest.php b/Test/Case/Model/Datasource/CouchdbSourceTest.php index ed53abe..1aca9d3 100644 --- a/Test/Case/Model/Datasource/CouchdbSourceTest.php +++ b/Test/Case/Model/Datasource/CouchdbSourceTest.php @@ -531,6 +531,32 @@ public function testDelete() { $this->assertEqual(0, count($result)); } +/** + * Test delete without revision + * + * @return void + */ + public function testDeleteWithoutRevision() { + $data = array( + 'title' => 'My first post', + 'description' => 'My first post' + ); + + $this->Post->create(); + $saveResult = $this->Post->save($data); + + $result = $this->Post->find('all'); + $this->assertEqual(1, count($result)); + + unset($result['Post']['rev']); + unset($this->Post->rev); + + $this->Post->delete(); + + $result = $this->Post->find('all'); + $this->assertEqual(0, count($result)); + } + /** * Tests query method. * From 82faa7216b5eb6446566188071f68e8891f9aa5b Mon Sep 17 00:00:00 2001 From: "Maury M. Marques" Date: Mon, 30 Jan 2012 16:15:05 -0200 Subject: [PATCH 3/8] Changing Couchdb for CouchDB --- Model/Datasource/CouchdbSource.php | 2 +- .../Model/Datasource/CouchdbSourceTest.php | 20 +++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Model/Datasource/CouchdbSource.php b/Model/Datasource/CouchdbSource.php index 66f9ade..d30e5c2 100644 --- a/Model/Datasource/CouchdbSource.php +++ b/Model/Datasource/CouchdbSource.php @@ -26,7 +26,7 @@ * @package datasources * @subpackage datasources.models.datasources */ -class CouchdbSource extends DataSource { +class CouchDBSource extends DataSource { /** * Start quote diff --git a/Test/Case/Model/Datasource/CouchdbSourceTest.php b/Test/Case/Model/Datasource/CouchdbSourceTest.php index 1aca9d3..1f7f9a0 100644 --- a/Test/Case/Model/Datasource/CouchdbSourceTest.php +++ b/Test/Case/Model/Datasource/CouchdbSourceTest.php @@ -1,6 +1,6 @@ 'CouchdbSource', + 'datasource' => 'CouchDBSource', 'persistent' => false, 'host' => 'localhost', 'port' => '5984', @@ -125,13 +125,13 @@ public function startTest() { * @return void */ public function testConnection() { - $this->Couchdb = new CouchdbSource($this->config); - $this->Couchdb =& ConnectionManager::getDataSource($this->Post->useDbConfig); + $this->CouchDB = new CouchDBSource($this->config); + $this->CouchDB =& ConnectionManager::getDataSource($this->Post->useDbConfig); - $reconnect = $this->Couchdb->reconnect($this->config); + $reconnect = $this->CouchDB->reconnect($this->config); $this->assertIdentical($reconnect, true, __('Not reconnected')); - $disconnect = $this->Couchdb->disconnect(); + $disconnect = $this->CouchDB->disconnect(); $this->assertIdentical($disconnect, true, __('Not disconnect')); } @@ -612,7 +612,7 @@ private function removeAllDocuments() { public function endTest() { $this->removeAllDocuments(); unset($this->Post); - unset($this->Couchdb); + unset($this->CouchDB); ClassRegistry::flush(); } } From 4b564dd2c83c82e419fd3ade7f8d6364b1d4dd8c Mon Sep 17 00:00:00 2001 From: "Maury M. Marques" Date: Mon, 30 Jan 2012 16:33:29 -0200 Subject: [PATCH 4/8] CouchDBSource as a plugin --- Test/Case/Model/Datasource/CouchdbSourceTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Test/Case/Model/Datasource/CouchdbSourceTest.php b/Test/Case/Model/Datasource/CouchdbSourceTest.php index 1f7f9a0..52dea3d 100644 --- a/Test/Case/Model/Datasource/CouchdbSourceTest.php +++ b/Test/Case/Model/Datasource/CouchdbSourceTest.php @@ -91,7 +91,7 @@ class CouchDBTestCase extends CakeTestCase { * @var array */ protected $config = array( - 'datasource' => 'CouchDBSource', + 'datasource' => 'CouchDB.CouchDBSource', 'persistent' => false, 'host' => 'localhost', 'port' => '5984', From 8b190b2cbccc38bdcb99e493f63d46a2db0d5894 Mon Sep 17 00:00:00 2001 From: "Maury M. Marques" Date: Mon, 30 Jan 2012 17:21:00 -0200 Subject: [PATCH 5/8] Test name --- Model/Datasource/{CouchdbSource.php => CouchDB.php} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Model/Datasource/{CouchdbSource.php => CouchDB.php} (100%) diff --git a/Model/Datasource/CouchdbSource.php b/Model/Datasource/CouchDB.php similarity index 100% rename from Model/Datasource/CouchdbSource.php rename to Model/Datasource/CouchDB.php From 6cf3f9bb30eb3d2c9807c39c2b5ed52b0e8fbd84 Mon Sep 17 00:00:00 2001 From: "Maury M. Marques" Date: Mon, 30 Jan 2012 17:22:31 -0200 Subject: [PATCH 6/8] Test name --- Model/Datasource/CouchDB.php | 503 -------------- .../Model/Datasource/CouchdbSourceTest.php | 618 ------------------ 2 files changed, 1121 deletions(-) delete mode 100644 Model/Datasource/CouchDB.php delete mode 100644 Test/Case/Model/Datasource/CouchdbSourceTest.php diff --git a/Model/Datasource/CouchDB.php b/Model/Datasource/CouchDB.php deleted file mode 100644 index d30e5c2..0000000 --- a/Model/Datasource/CouchDB.php +++ /dev/null @@ -1,503 +0,0 @@ -fullDebug = Configure::read('debug') > 1; - - if ($autoConnect) { - return $this->connect(); - } else { - return true; - } - } - -/** - * Reconnects to the database with optional new settings. - * - * @param array $config New settings. - * @return boolean Success. - */ - public function reconnect($config = null) { - $this->disconnect(); - $this->setConfig($config); - $this->_sources = null; - return $this->connect(); - } - -/** - * Connects to the database. Options are specified in the $config instance variable. - * - * @return boolean Connected. - */ - public function connect() { - if ($this->connected !== true) { - if (isset($this->config['login'])) - $this->config['request']['uri']['user'] = $this->config['login']; - - if (isset($this->config['password'])) - $this->config['request']['uri']['pass'] = $this->config['password']; - - try { - $this->Socket = new HttpSocket($this->config); - $this->Socket->get(); - $this->connected = true; - } catch (SocketException $e) { - throw new MissingConnectionException(array('class' => $e->getMessage())); - } - } - return $this->connected; - } - -/** - * Disconnects from the database, kills the connection and advises that the - * connection is closed, and if DEBUG is turned on (equal to 2) displays the - * log of stored data. - * - * @return boolean Disconnected. - */ - public function close() { - if (Configure::read('debug') > 1) { - //$this->showLog(); - } - $this->disconnect(); - } - -/** - * Disconnect from the database. - * - * @return boolean Disconnected. - */ - public function disconnect() { - if (isset($this->results) && is_resource($this->results)) { - $this->results = null; - } - $this->connected = false; - return !$this->connected; - } - -/** - * List of databases. - * - * @return array Databases. - */ - public function listSources() { - $databases = $this->__decode($this->Socket->get($this->__uri('_all_dbs')), true); - return $databases; - } - -/** - * Convenience method for DboSource::listSources(). - * Returns the names of databases in lowercase. - * - * @return array Lowercase databases. - */ - public function sources($reset = false) { - if ($reset === true) { - $this->_sources = null; - } - return array_map('strtolower', $this->listSources()); - } - -/** - * Returns a description of the model (metadata). - * - * @param Model $model - * @return array Schema. - */ - public function describe($model) { - return $model->schema; - } - -/** - * Creates a new document in the database. - * If the primaryKey is declared, creates the document with the specified ID. - * To create a new database: $this->Model->curlPut('databaseName'); - * - * @param Model $model - * @param array $fields An array of field names to insert. If null, $model->data will be used to generate the field names. - * @param array $values An array with key values of the fields. If null, $model->data will be used to generate the field names. - * @return boolean Success. - */ - public function create($model, $fields = null, $values = null) { - $data = $model->data; - if ($fields !== null && $values !== null) { - $data = array_combine($fields, $values); - } - - if (isset($data[$model->primaryKey]) && !empty($data[$model->primaryKey])) { - $params = $data[$model->primaryKey]; - } else { - $uuids = $this->__decode($this->Socket->get('/_uuids')); - $params = $uuids->uuids[0]; - } - - $result = $this->__decode($this->Socket->put($this->__uri($model, $params), $this->__encode($data))); - - if ($this->__checkOk($result)) { - $model->id = $result->id; - $model->rev = $result->rev; - return true; - } - return false; - } - -/** - * Reads data from a document. - * - * @param Model $model - * @param array $queryData An array of information containing $queryData keys, similar to Model::find(). - * @param integer $recursive Level number of associations. - * @return mixed False if an error occurred, otherwise an array of results. - */ - public function read($model, $queryData = array(), $recursive = null) { - if ($recursive === null && isset($queryData['recursive'])) { - $recursive = $queryData['recursive']; - } - - if (!is_null($recursive)) { - $model->recursive = $recursive; - } - - $params = null; - - if (empty($queryData['conditions'])) { - $params = $params . '_all_docs?include_docs=true'; - if (!empty($queryData['limit'])) { - $params = $params . '&limit=' . $queryData['limit']; - } - } else { - if (isset($queryData['conditions'][$model->alias . '.' . $model->primaryKey])) { - $params = $queryData['conditions'][$model->alias . '.' . $model->primaryKey]; - } else { - $params = $queryData['conditions'][$model->primaryKey]; - } - - if ($model->recursive > -1) { - $params = $params . '?revs_info=true'; - } - } - - $result = array(); - $result[0][$model->alias] = $this->__decode($this->Socket->get($this->__uri($model, $params)), true); - return $this->__readResult($model, $queryData, $result); - } - -/** - * Applies the rules to the document read. - * - * @param Model $model - * @param array $queryData An array of information containing $queryData keys, similar to Model::find(). - * @param array $result Data read from the document. - * @return mixed False if an error occurred, otherwise an array of results. - */ - private function __readResult($model, $queryData, $result) { - if (isset($result[0][$model->alias]['_id'])) { - if (isset($queryData['fields']) && $queryData['fields'] === true) { - $result[0][0]['count'] = 1; - } - - $result[0][$model->alias]['id'] = $result[0][$model->alias]['_id']; - $result[0][$model->alias]['rev'] = $result[0][$model->alias]['_rev']; - - unset($result[0][$model->alias]['_id']); - unset($result[0][$model->alias]['_rev']); - - return $result; - } else if (isset($result[0][$model->alias]['rows'])) { - $docs = array(); - foreach ($result[0][$model->alias]['rows'] as $k => $doc) { - - $docs[$k][$model->alias]['id'] = $doc['doc']['_id']; - $docs[$k][$model->alias]['rev'] = $doc['doc']['_rev']; - - unset($doc['doc']['_id']); - unset($doc['doc']['_rev']); - unset($doc['doc']['id']); - unset($doc['doc']['rev']); - - foreach ($doc['doc'] as $field => $value) { - $docs[$k][$model->alias][$field] = $value; - } - } - return $docs; - } - return false; - } - -/** - * Generates and executes an UPDATE statement for a given model, fields and values. - * - * @param Model $model - * @param array $fields - * @param array $values - * @param mixed $conditions - * @return boolean Success. - */ - public function update($model, $fields = null, $values = null, $conditions = null) { - $data = $model->data[$model->alias]; - if ($fields !== null && $values !== null) { - $data = array_combine($fields, $values); - } - - $this->__idRevData($model, $data); - - if (!empty($model->id)) { - $result = $this->__decode($this->Socket->put($this->__uri($model, $model->id), $this->__encode($data))); - if ($this->__checkOk($result)) { - $model->rev = $result->rev; - return true; - } - } - return false; - } - -/** - * The method sets the "id" and "rev"to avoid problems in update of a document written shortly after a create a other document. - * - * @param object $model - * @param array $data - * @return void - */ - private function __idRevData(&$model, &$data) { - if (isset($data[$model->primaryKey]) && !empty($data[$model->primaryKey])) { - $model->id = $data[$model->primaryKey]; - unset($data[$model->primaryKey]); - } - - if (isset($data['rev']) && !empty($data['rev'])) { - $data['_rev'] = $data['rev']; - unset($data['rev']); - } else if ($model->rev) { - $data['_rev'] = $model->rev; - } else { - $data['_rev'] = $this->__lastRevision($model, $model->id); - } - } - -/** - * The method searches for the latest revision of a document - * - * @param object $model - * @param int $id - * @return string Last revision of the document - */ - private function __lastRevision(&$model, $id) { - $result = $this->__decode($this->Socket->get($this->__uri($model, $id))); - return $result->_rev; - } - -/** - * Generates and executes a DELETE statement. - * - * @param Model $model - * @param mixed $conditions - * @return boolean Success. - */ - public function delete($model, $conditions = null) { - $id = $model->id; - $rev = $model->rev; - - if (!empty($id)) { - if (empty($rev)) $rev = $this->__lastRevision($model, $id); - $id_rev = $id . '/?rev=' . $rev; - $result = $this->__decode($this->Socket->delete($this->__uri($model, $id_rev))); - return $this->__checkOk($result); - } - return false; - } - -/** - * Returns an instruction to count data. (SQL, i.e. COUNT() or MAX()). - * - * @param model $model - * @param string $func Lowercase name of SQL function, i.e. 'count' or 'max'. - * @param array $params Function parameters (any values must be quoted manually). - * @return string An SQL calculation function. - */ - public function calculate($model, $func, $params = array()) { - return true; - } - -/** - * Gets full table name including prefix. - * - * @param mixed $model - * @param boolean $quote - * @return string Full name of table. - */ - public function fullTableName($model = null, $quote = true) { - $table = null; - if (is_object($model)) { - $table = $model->tablePrefix . $model->table; - } elseif (isset($this->config['prefix'])) { - $table = $this->config['prefix'] . strval($model); - } else { - $table = strval($model); - } - return $table; - } - -/** - * Perform any function in CouchDB. - * The first position of the $params array is used to mount the uri. - * The second place in the $params array is used to assemble data from a POST or PUT. - * The third parameter is used to decode the return json. - * The fourth parameter is used to build an associative array. - * - * The method can be performed by a Model of the following ways: - * - * $this->Model->curlGet('_all_dbs'); - * $this->Model->curlPut('document_name'); - * $this->Model->curlPost('document_name', array('field' => 'value')); - * $this->Model->curlDelete('document_name'); - * $this->Model->curlPost('document_name', array('field' => 'value'), false); - * $this->Model->curlPost('document_name', array('field' => 'value'), true , false); - * - * @param string $method - * @param array $params Parâmetros aceitos na ordem: uri, data, decode, assoc - * @return object - */ - public function query($method, $params) { - list($uri, $data, $decode, $assoc) = $this->__queryParams($params); - - $request = array( - 'method' => strtoupper(str_replace('curl', '', $method)) - ); - - if (!empty($uri)) - $request['uri'] = '/' . $uri; - - if (!empty($data)) - $request['body'] = $this->__encode($data); - - $result = $this->Socket->request($request); - - if ($decode === true) { - $result = $this->__decode($result, $assoc); - } - - return $result; - } - -/** - * Construct the parameter of the query method. - * - * @param array $params - * @return array - */ - private function __queryParams($params) { - if (isset($params[0])) $uri = $params[0]; - else $uri = ''; - - if (isset($params[1])) $data = $params[1]; - else $data = array(); - - if (isset($params[2])) $decode = $params[2]; - else $decode = true; - - if (isset($params[3])) $assoc = $params[3]; - else $assoc = true; - - return array($uri, $data, $decode, $assoc); - } - -/** - * Get a URI. - * - * @param mixed $model - * @param string $params - * @return string URI. - */ - private function __uri($model = null, $params = null) { - if (!is_null($params)) { - $params = '/' . $params; - } - return '/' . $this->fullTableName($model) . $params; - } - -/** - * JSON encode. - * - * @param string json $data - * @return string JSON. - */ - private function __encode($data) { - return json_encode($data); - } - -/** - * JSON decode. - * - * @param string json $data - * @param boolean $assoc If true, returns array. If false, returns object. - * @return mixed Object or Array. - */ - private function __decode($data, $assoc = false) { - return json_decode($data, $assoc); - } - -/** - * Checks if the result returned ok = true. - * - * @param object $object - * @return boolean - */ - private function __checkOk($object = null) { - return isset($object->ok) && $object->ok === true; - } -} -?> \ No newline at end of file diff --git a/Test/Case/Model/Datasource/CouchdbSourceTest.php b/Test/Case/Model/Datasource/CouchdbSourceTest.php deleted file mode 100644 index 52dea3d..0000000 --- a/Test/Case/Model/Datasource/CouchdbSourceTest.php +++ /dev/null @@ -1,618 +0,0 @@ - array( - 'notempty' => array( - 'rule' => array('notempty'), - //'message' => 'Your custom message here', - //'allowEmpty' => false, - //'required' => false, - //'last' => false, // Stop validation after this rule - //'on' => 'create', // Limit validation to 'create' or 'update' operations - ), - ), - ); - - public $schema = array( - 'id' => array( - 'type' => 'string', - 'null' => true, - 'key' => 'primary', - 'length' => 32, - ), - 'rev' => array( - 'type' => 'string', - 'null' => true, - 'key' => 'primary', - 'length' => 34, - ), - 'title' => array( - 'type' => 'string', - 'null' => true, - 'length' => 255, - ), - 'description' => array( - 'type' => 'text', - 'null' => true, - ) - ); -} - -/** - * CouchDBTestCase - * - * @package datasources - * @subpackage datasources.tests.cases.models.datasources - */ -class CouchDBTestCase extends CakeTestCase { - -/** - * CouchDB Datasource object - * - * @var object - */ - public $CouchDB = null; - -/** - * Configuration - * - * @var array - */ - protected $config = array( - 'datasource' => 'CouchDB.CouchDBSource', - 'persistent' => false, - 'host' => 'localhost', - 'port' => '5984', - 'login' => 'root', - 'password' => 'root', - 'database' => null - ); - -/** - * Start Test - * - * @return void - */ - public function startTest() { - config('database'); - $config = new DATABASE_CONFIG(); - - if (isset($config->couchdb_test)) { - $this->config = $config->couchdb_test; - } - - ConnectionManager::create('couchdb_test', $this->config); - - $this->Post = ClassRegistry::init('Post'); - $this->removeAllDocuments(); - } - -/** - * testConnection - * - * @return void - */ - public function testConnection() { - $this->CouchDB = new CouchDBSource($this->config); - $this->CouchDB =& ConnectionManager::getDataSource($this->Post->useDbConfig); - - $reconnect = $this->CouchDB->reconnect($this->config); - $this->assertIdentical($reconnect, true, __('Not reconnected')); - - $disconnect = $this->CouchDB->disconnect(); - $this->assertIdentical($disconnect, true, __('Not disconnect')); - } - -/** - * testFind - * - * @return void - */ - public function testFind() { - $data = array( - 'title' => 'My first post', - 'description' => 'My first post' - ); - $this->Post->save($data); - - $result = $this->Post->find('all'); - $this->assertEqual(1, count($result)); - - $resultData = $result[0]['Post']; - $this->assertEqual(4, count($resultData)); - $this->assertTrue(!empty($resultData['id'])); - $this->assertEqual($this->Post->id, $resultData['id']); - $this->assertEqual($this->Post->rev, $resultData['rev']); - $this->assertEqual($data['title'], $resultData['title']); - $this->assertEqual($data['description'], $resultData['description']); - } - - -/** - * testFindConditions - * - * @return void - */ - public function testFindConditions() { - $data = array( - 'title' => 'My first post', - 'description' => 'My first post' - ); - $this->Post->save($data); - - $this->Post->create(); - $this->Post->save($data); - - $result = $this->Post->find('all'); - $this->assertEqual(2, count($result)); - - $result = $this->Post->find('all', array('conditions' => array('Post.id' => $this->Post->id))); - $this->assertEqual(1, count($result)); - - $result = $this->Post->find('all', array('conditions' => array('id' => $this->Post->id))); - $this->assertEqual(1, count($result)); - } - -/** - * testFindRevs - * - * @return void - */ - public function testFindRevs() { - $data = array( - 'title' => 'My first post', - 'description' => 'My first post' - ); - $this->Post->save($data); - $this->Post->save($data); - - $this->Post->recursive = 0; - $result = $this->Post->find('all', array('conditions' => array('id' => $this->Post->id))); - $this->assertEqual(2, count($result[0]['Post']['_revs_info'])); - } - -/** - * Tests save method. - * - * @return void - */ - public function testSave() { - $data = array( - 'title' => 'My first post', - 'description' => 'My first post' - ); - - $this->Post->create(); - $saveResult = $this->Post->save($data); - $this->assertIdentical(is_array($saveResult), true); - - $result = $this->Post->find('all'); - $this->assertEqual(1, count($result)); - - $resultData = $result[0]['Post']; - $this->assertEqual(4, count($resultData)); - $this->assertTrue(!empty($resultData['id'])); - $this->assertEqual($this->Post->id, $resultData['id']); - $this->assertEqual($this->Post->rev, $resultData['rev']); - $this->assertEqual($data['title'], $resultData['title']); - $this->assertEqual($data['description'], $resultData['description']); - } - -/** - * Tests save method. - * - * @return void - */ - public function testSaveWithId() { - $data = array( - 'id' => String::uuid(), - 'title' => 'My first post', - 'description' => 'My first post' - ); - - $this->Post->create(); - $saveResult = $this->Post->save($data); - $this->assertIdentical(is_array($saveResult), true); - - $result = $this->Post->find('all'); - $this->assertEqual(1, count($result)); - - $resultData = $result[0]['Post']; - $this->assertEqual(4, count($resultData)); - $this->assertTrue(!empty($resultData['id'])); - $this->assertEqual($resultData['id'], $data['id']); - $this->assertEqual($this->Post->id, $resultData['id']); - $this->assertEqual($this->Post->rev, $resultData['rev']); - $this->assertEqual($data['title'], $resultData['title']); - $this->assertEqual($data['description'], $resultData['description']); - } - -/** - * Tests saveAll method. - * - * @return void - */ - public function testSaveAll() { - $data[0]['Post'] = array( - 'title' => 'My first post', - 'description' => 'My first post' - ); - - $data[1]['Post'] = array( - 'title' => 'My second post', - 'description' => 'My second post' - ); - - $this->Post->create(); - $saveResult = $this->Post->saveAll($data); - - $result = $this->Post->find('all'); - $this->assertEqual(2, count($result)); - - $resultData = $result[0]['Post']; - $this->assertEqual(4, count($resultData)); - $this->assertTrue(!empty($resultData['id'])); - $this->assertEqual($data[0]['Post']['title'], $resultData['title']); - $this->assertEqual($data[0]['Post']['description'], $resultData['description']); - - $resultData = $result[1]['Post']; - $this->assertEqual(4, count($resultData)); - $this->assertTrue(!empty($resultData['id'])); - $this->assertEqual($data[1]['Post']['title'], $resultData['title']); - $this->assertEqual($data[1]['Post']['description'], $resultData['description']); - } - -/** - * Tests update method. - * - * @return void - */ - public function updateTest() { - // Count posts - $uri = '/posts/_temp_view?group=true'; - $post = array( - 'map' => 'function(doc) { emit(doc._id,1); }', - 'reduce' => 'function(keys, values) { return sum(values); }' - ); - - $mapReduce = $this->Post->query($uri, $post); - if(isset($mapReduce->rows[0]->value)) $count0 = $mapReduce->rows[0]->value; - else $count0 = 0; - - $count1 = $this->updateTest1($uri, $post, $count0); - $count2 = $this->updateTest2($uri, $post, $count1); - $count3 = $this->updateTest3($uri, $post, $count2); - $count4 = $this->updateTest4($uri, $post, $count2); - $updateData = $this->updateTest5($uri, $post, $count4); - - // Final test - $result = $this->Post->find('all'); - $this->assertEqual(1, count($result)); - - $resultData = $result[0]['Post']; - $this->assertEqual(4, count($resultData)); - $this->assertTrue(!empty($resultData['id'])); - $this->assertEqual($this->Post->id, $resultData['id']); - $this->assertEqual($this->Post->rev, $resultData['rev']); - $this->assertNotEqual($updateData['title'], $resultData['title']); - $this->assertNotEqual($updateData['description'], $resultData['description']); - - } - -/** - * Tests update1 method. - * - * @param string $uri - * @param array $post - * @param interger $previousCount - * @return void - */ - private function updateTest1($uri, $post, $previousCount) { - $data = array( - 'title' => 'My first post', - 'description' => 'My first post' - ); - - $this->Post->create(); - $saveResult = $this->Post->save($data); - $this->assertIdentical(is_array($saveResult), true); - $this->assertIdentical(!empty($this->Post->id), true); - - $mapReduce = $this->Post->curlPost($uri, $post); - $count1 = $mapReduce['rows'][0]['value']; - - $this->assertIdentical($count1 - $previousCount, 1); - - return $count1; - } - -/** - * Tests update2 method. - * - * @param string $uri - * @param array $post - * @param interger $previousCount - * @return void - */ - private function updateTest2($uri, $post, $previousCount) { - $findResult = $this->Post->find('first'); - $this->assertEqual(4, count($findResult['Post'])); - - $updateData = array( - 'title' => 'My post update', - 'description' => 'My post update' - ); - - $this->Post->id = $findResult['Post']['id']; - $this->Post->rev = $findResult['Post']['rev']; - $saveResult = $this->Post->save($updateData); - $this->assertIdentical(is_array($saveResult), true); - - $mapReduce = $this->Post->curlPost($uri, $post); - $count2 = $mapReduce['rows'][0]['value']; - - $this->assertIdentical($count2 - $previousCount, 0); - - return $count2; - } - -/** - * Tests update3 method. - * - * @param string $uri - * @param array $post - * @param interger $previousCount - * @return void - */ - private function updateTest3($uri, $post, $previousCount) { - $findResult = $this->Post->find('first'); - $this->assertEqual(4, count($findResult['Post'])); - - $updateData = array( - 'id' => $findResult['Post']['id'], - 'title' => 'My post update', - 'description' => 'My post update' - ); - - $this->Post->rev = $findResult['Post']['rev']; - $saveResult = $this->Post->save($updateData); - $this->assertIdentical(is_array($saveResult), true); - $this->assertIdentical($this->Post->id, $findResult['Post']['id']); - - $mapReduce = $this->Post->curlPost($uri, $post); - $count3 = $mapReduce['rows'][0]['value']; - - $this->assertIdentical($count3 - $previousCount, 0); - - return $count3; - } - -/** - * Tests update4 method. - * - * @param string $uri - * @param array $post - * @param interger $previousCount - * @return void - */ - private function updateTest4($uri, $post, $previousCount) { - $findResult = $this->Post->find('first'); - $this->assertEqual(4, count($findResult['Post'])); - - $updateData = array( - 'id' => $findResult['Post']['id'], - 'rev' => $findResult['Post']['rev'], - 'title' => 'My post update', - 'description' => 'My post update' - ); - - $saveResult = $this->Post->save($updateData); - $this->assertIdentical(is_array($saveResult), true); - $this->assertIdentical($this->Post->id, $findResult['Post']['id']); - - $mapReduce = $this->Post->curlPost($uri, $post); - $count4 = $mapReduce['rows'][0]['value']; - - $this->assertIdentical($count4 - $previousCount, 0); - - return $count4; - } - -/** - * Tests update5 method. - * - * @param string $uri - * @param array $post - * @param interger $previousCount - * @return void - */ - private function updateTest5($uri, $post, $previousCount) { - $findResult = $this->Post->find('first'); - $this->assertEqual(4, count($findResult['Post'])); - - $updateData = array( - 'id' => $findResult['Post']['id'], - 'rev' => 'whatever', - 'title' => 'My post fail', - 'description' => 'My post fail' - ); - - $saveResult = $this->Post->save($updateData); - $this->assertFalse($saveResult); - $this->assertIdentical($this->Post->id, $findResult['Post']['id']); - - $mapReduce = $this->Post->curlPost($uri, $post); - $count5 = $mapReduce['rows'][0]['value']; - - $this->assertIdentical($count5 - $previousCount, 0); - - return $updateData; - } - -/** - * Test update without revision - * - * @return void - */ - public function testUpdateWithoutRevision() { - $data = array( - 'title' => 'My first post', - 'description' => 'My first post' - ); - - $this->Post->create(); - $saveResult = $this->Post->save($data); - - $result = $this->Post->find('first'); - - unset($result['Post']['rev']); - unset($this->Post->rev); - - $updateResult = $this->Post->save($result); - - $this->assertIdentical(is_array($updateResult), true); - $this->assertIdentical($this->Post->id, $saveResult['Post']['id']); - } - -/** - * Tests delete method. - * - * @return void - */ - public function testDelete() { - $data = array( - 'title' => 'My first post', - 'description' => 'My first post' - ); - - $this->Post->create(); - $saveResult = $this->Post->save($data); - - $result = $this->Post->find('all'); - $this->assertEqual(1, count($result)); - - $this->Post->id = $result[0]['Post']['id']; - $this->Post->rev = $result[0]['Post']['rev']; - $this->Post->delete(); - - $result = $this->Post->find('all'); - $this->assertEqual(0, count($result)); - } - -/** - * Test delete without revision - * - * @return void - */ - public function testDeleteWithoutRevision() { - $data = array( - 'title' => 'My first post', - 'description' => 'My first post' - ); - - $this->Post->create(); - $saveResult = $this->Post->save($data); - - $result = $this->Post->find('all'); - $this->assertEqual(1, count($result)); - - unset($result['Post']['rev']); - unset($this->Post->rev); - - $this->Post->delete(); - - $result = $this->Post->find('all'); - $this->assertEqual(0, count($result)); - } - -/** - * Tests query method. - * - * @return void - */ - public function testQuery() { - // GET - $result = $this->Post->curlGet('_all_dbs'); - $this->assertIdentical(is_array($result), true); - - // POST - $data = array( - 'title' => 'My first post', - 'description' => 'My first post' - ); - - $result = $this->Post->curlPost('/posts', $data); - $this->assertIdentical($result['ok'], true); - - // PUT - $data = array( - '_rev' => $result['rev'], - 'title' => 'My first update', - 'description' => 'My first update' - ); - - $result = $this->Post->curlPut('/posts/' . $result['id'], $data); - $this->assertIdentical($result['ok'], true); - - // DELETE - $result = $this->Post->curlDelete('/posts/' . $result['id'] . '/?rev=' . $result['rev']); - $this->assertIdentical($result['ok'], true); - } - -/** - * Remove all documents from database - * - * @return void - */ - private function removeAllDocuments() { - $posts = $this->Post->find('list', array('fields' => array('Post.rev'))); - foreach($posts as $id => $post) { - $this->Post->rev = $post; - $this->Post->delete($id); - } - } - -/** - * End Test - * - * @return void - */ - public function endTest() { - $this->removeAllDocuments(); - unset($this->Post); - unset($this->CouchDB); - ClassRegistry::flush(); - } -} From ee24bb2597e4ba42db37bf1fba79d2bebb03b848 Mon Sep 17 00:00:00 2001 From: "Maury M. Marques" Date: Mon, 30 Jan 2012 17:23:21 -0200 Subject: [PATCH 7/8] Test name --- Model/Datasource/CouchDBSource.php | 503 ++++++++++++++ .../Model/Datasource/CouchDBSourceTest.php | 618 ++++++++++++++++++ 2 files changed, 1121 insertions(+) create mode 100644 Model/Datasource/CouchDBSource.php create mode 100644 Test/Case/Model/Datasource/CouchDBSourceTest.php diff --git a/Model/Datasource/CouchDBSource.php b/Model/Datasource/CouchDBSource.php new file mode 100644 index 0000000..d30e5c2 --- /dev/null +++ b/Model/Datasource/CouchDBSource.php @@ -0,0 +1,503 @@ +fullDebug = Configure::read('debug') > 1; + + if ($autoConnect) { + return $this->connect(); + } else { + return true; + } + } + +/** + * Reconnects to the database with optional new settings. + * + * @param array $config New settings. + * @return boolean Success. + */ + public function reconnect($config = null) { + $this->disconnect(); + $this->setConfig($config); + $this->_sources = null; + return $this->connect(); + } + +/** + * Connects to the database. Options are specified in the $config instance variable. + * + * @return boolean Connected. + */ + public function connect() { + if ($this->connected !== true) { + if (isset($this->config['login'])) + $this->config['request']['uri']['user'] = $this->config['login']; + + if (isset($this->config['password'])) + $this->config['request']['uri']['pass'] = $this->config['password']; + + try { + $this->Socket = new HttpSocket($this->config); + $this->Socket->get(); + $this->connected = true; + } catch (SocketException $e) { + throw new MissingConnectionException(array('class' => $e->getMessage())); + } + } + return $this->connected; + } + +/** + * Disconnects from the database, kills the connection and advises that the + * connection is closed, and if DEBUG is turned on (equal to 2) displays the + * log of stored data. + * + * @return boolean Disconnected. + */ + public function close() { + if (Configure::read('debug') > 1) { + //$this->showLog(); + } + $this->disconnect(); + } + +/** + * Disconnect from the database. + * + * @return boolean Disconnected. + */ + public function disconnect() { + if (isset($this->results) && is_resource($this->results)) { + $this->results = null; + } + $this->connected = false; + return !$this->connected; + } + +/** + * List of databases. + * + * @return array Databases. + */ + public function listSources() { + $databases = $this->__decode($this->Socket->get($this->__uri('_all_dbs')), true); + return $databases; + } + +/** + * Convenience method for DboSource::listSources(). + * Returns the names of databases in lowercase. + * + * @return array Lowercase databases. + */ + public function sources($reset = false) { + if ($reset === true) { + $this->_sources = null; + } + return array_map('strtolower', $this->listSources()); + } + +/** + * Returns a description of the model (metadata). + * + * @param Model $model + * @return array Schema. + */ + public function describe($model) { + return $model->schema; + } + +/** + * Creates a new document in the database. + * If the primaryKey is declared, creates the document with the specified ID. + * To create a new database: $this->Model->curlPut('databaseName'); + * + * @param Model $model + * @param array $fields An array of field names to insert. If null, $model->data will be used to generate the field names. + * @param array $values An array with key values of the fields. If null, $model->data will be used to generate the field names. + * @return boolean Success. + */ + public function create($model, $fields = null, $values = null) { + $data = $model->data; + if ($fields !== null && $values !== null) { + $data = array_combine($fields, $values); + } + + if (isset($data[$model->primaryKey]) && !empty($data[$model->primaryKey])) { + $params = $data[$model->primaryKey]; + } else { + $uuids = $this->__decode($this->Socket->get('/_uuids')); + $params = $uuids->uuids[0]; + } + + $result = $this->__decode($this->Socket->put($this->__uri($model, $params), $this->__encode($data))); + + if ($this->__checkOk($result)) { + $model->id = $result->id; + $model->rev = $result->rev; + return true; + } + return false; + } + +/** + * Reads data from a document. + * + * @param Model $model + * @param array $queryData An array of information containing $queryData keys, similar to Model::find(). + * @param integer $recursive Level number of associations. + * @return mixed False if an error occurred, otherwise an array of results. + */ + public function read($model, $queryData = array(), $recursive = null) { + if ($recursive === null && isset($queryData['recursive'])) { + $recursive = $queryData['recursive']; + } + + if (!is_null($recursive)) { + $model->recursive = $recursive; + } + + $params = null; + + if (empty($queryData['conditions'])) { + $params = $params . '_all_docs?include_docs=true'; + if (!empty($queryData['limit'])) { + $params = $params . '&limit=' . $queryData['limit']; + } + } else { + if (isset($queryData['conditions'][$model->alias . '.' . $model->primaryKey])) { + $params = $queryData['conditions'][$model->alias . '.' . $model->primaryKey]; + } else { + $params = $queryData['conditions'][$model->primaryKey]; + } + + if ($model->recursive > -1) { + $params = $params . '?revs_info=true'; + } + } + + $result = array(); + $result[0][$model->alias] = $this->__decode($this->Socket->get($this->__uri($model, $params)), true); + return $this->__readResult($model, $queryData, $result); + } + +/** + * Applies the rules to the document read. + * + * @param Model $model + * @param array $queryData An array of information containing $queryData keys, similar to Model::find(). + * @param array $result Data read from the document. + * @return mixed False if an error occurred, otherwise an array of results. + */ + private function __readResult($model, $queryData, $result) { + if (isset($result[0][$model->alias]['_id'])) { + if (isset($queryData['fields']) && $queryData['fields'] === true) { + $result[0][0]['count'] = 1; + } + + $result[0][$model->alias]['id'] = $result[0][$model->alias]['_id']; + $result[0][$model->alias]['rev'] = $result[0][$model->alias]['_rev']; + + unset($result[0][$model->alias]['_id']); + unset($result[0][$model->alias]['_rev']); + + return $result; + } else if (isset($result[0][$model->alias]['rows'])) { + $docs = array(); + foreach ($result[0][$model->alias]['rows'] as $k => $doc) { + + $docs[$k][$model->alias]['id'] = $doc['doc']['_id']; + $docs[$k][$model->alias]['rev'] = $doc['doc']['_rev']; + + unset($doc['doc']['_id']); + unset($doc['doc']['_rev']); + unset($doc['doc']['id']); + unset($doc['doc']['rev']); + + foreach ($doc['doc'] as $field => $value) { + $docs[$k][$model->alias][$field] = $value; + } + } + return $docs; + } + return false; + } + +/** + * Generates and executes an UPDATE statement for a given model, fields and values. + * + * @param Model $model + * @param array $fields + * @param array $values + * @param mixed $conditions + * @return boolean Success. + */ + public function update($model, $fields = null, $values = null, $conditions = null) { + $data = $model->data[$model->alias]; + if ($fields !== null && $values !== null) { + $data = array_combine($fields, $values); + } + + $this->__idRevData($model, $data); + + if (!empty($model->id)) { + $result = $this->__decode($this->Socket->put($this->__uri($model, $model->id), $this->__encode($data))); + if ($this->__checkOk($result)) { + $model->rev = $result->rev; + return true; + } + } + return false; + } + +/** + * The method sets the "id" and "rev"to avoid problems in update of a document written shortly after a create a other document. + * + * @param object $model + * @param array $data + * @return void + */ + private function __idRevData(&$model, &$data) { + if (isset($data[$model->primaryKey]) && !empty($data[$model->primaryKey])) { + $model->id = $data[$model->primaryKey]; + unset($data[$model->primaryKey]); + } + + if (isset($data['rev']) && !empty($data['rev'])) { + $data['_rev'] = $data['rev']; + unset($data['rev']); + } else if ($model->rev) { + $data['_rev'] = $model->rev; + } else { + $data['_rev'] = $this->__lastRevision($model, $model->id); + } + } + +/** + * The method searches for the latest revision of a document + * + * @param object $model + * @param int $id + * @return string Last revision of the document + */ + private function __lastRevision(&$model, $id) { + $result = $this->__decode($this->Socket->get($this->__uri($model, $id))); + return $result->_rev; + } + +/** + * Generates and executes a DELETE statement. + * + * @param Model $model + * @param mixed $conditions + * @return boolean Success. + */ + public function delete($model, $conditions = null) { + $id = $model->id; + $rev = $model->rev; + + if (!empty($id)) { + if (empty($rev)) $rev = $this->__lastRevision($model, $id); + $id_rev = $id . '/?rev=' . $rev; + $result = $this->__decode($this->Socket->delete($this->__uri($model, $id_rev))); + return $this->__checkOk($result); + } + return false; + } + +/** + * Returns an instruction to count data. (SQL, i.e. COUNT() or MAX()). + * + * @param model $model + * @param string $func Lowercase name of SQL function, i.e. 'count' or 'max'. + * @param array $params Function parameters (any values must be quoted manually). + * @return string An SQL calculation function. + */ + public function calculate($model, $func, $params = array()) { + return true; + } + +/** + * Gets full table name including prefix. + * + * @param mixed $model + * @param boolean $quote + * @return string Full name of table. + */ + public function fullTableName($model = null, $quote = true) { + $table = null; + if (is_object($model)) { + $table = $model->tablePrefix . $model->table; + } elseif (isset($this->config['prefix'])) { + $table = $this->config['prefix'] . strval($model); + } else { + $table = strval($model); + } + return $table; + } + +/** + * Perform any function in CouchDB. + * The first position of the $params array is used to mount the uri. + * The second place in the $params array is used to assemble data from a POST or PUT. + * The third parameter is used to decode the return json. + * The fourth parameter is used to build an associative array. + * + * The method can be performed by a Model of the following ways: + * + * $this->Model->curlGet('_all_dbs'); + * $this->Model->curlPut('document_name'); + * $this->Model->curlPost('document_name', array('field' => 'value')); + * $this->Model->curlDelete('document_name'); + * $this->Model->curlPost('document_name', array('field' => 'value'), false); + * $this->Model->curlPost('document_name', array('field' => 'value'), true , false); + * + * @param string $method + * @param array $params Parâmetros aceitos na ordem: uri, data, decode, assoc + * @return object + */ + public function query($method, $params) { + list($uri, $data, $decode, $assoc) = $this->__queryParams($params); + + $request = array( + 'method' => strtoupper(str_replace('curl', '', $method)) + ); + + if (!empty($uri)) + $request['uri'] = '/' . $uri; + + if (!empty($data)) + $request['body'] = $this->__encode($data); + + $result = $this->Socket->request($request); + + if ($decode === true) { + $result = $this->__decode($result, $assoc); + } + + return $result; + } + +/** + * Construct the parameter of the query method. + * + * @param array $params + * @return array + */ + private function __queryParams($params) { + if (isset($params[0])) $uri = $params[0]; + else $uri = ''; + + if (isset($params[1])) $data = $params[1]; + else $data = array(); + + if (isset($params[2])) $decode = $params[2]; + else $decode = true; + + if (isset($params[3])) $assoc = $params[3]; + else $assoc = true; + + return array($uri, $data, $decode, $assoc); + } + +/** + * Get a URI. + * + * @param mixed $model + * @param string $params + * @return string URI. + */ + private function __uri($model = null, $params = null) { + if (!is_null($params)) { + $params = '/' . $params; + } + return '/' . $this->fullTableName($model) . $params; + } + +/** + * JSON encode. + * + * @param string json $data + * @return string JSON. + */ + private function __encode($data) { + return json_encode($data); + } + +/** + * JSON decode. + * + * @param string json $data + * @param boolean $assoc If true, returns array. If false, returns object. + * @return mixed Object or Array. + */ + private function __decode($data, $assoc = false) { + return json_decode($data, $assoc); + } + +/** + * Checks if the result returned ok = true. + * + * @param object $object + * @return boolean + */ + private function __checkOk($object = null) { + return isset($object->ok) && $object->ok === true; + } +} +?> \ No newline at end of file diff --git a/Test/Case/Model/Datasource/CouchDBSourceTest.php b/Test/Case/Model/Datasource/CouchDBSourceTest.php new file mode 100644 index 0000000..52dea3d --- /dev/null +++ b/Test/Case/Model/Datasource/CouchDBSourceTest.php @@ -0,0 +1,618 @@ + array( + 'notempty' => array( + 'rule' => array('notempty'), + //'message' => 'Your custom message here', + //'allowEmpty' => false, + //'required' => false, + //'last' => false, // Stop validation after this rule + //'on' => 'create', // Limit validation to 'create' or 'update' operations + ), + ), + ); + + public $schema = array( + 'id' => array( + 'type' => 'string', + 'null' => true, + 'key' => 'primary', + 'length' => 32, + ), + 'rev' => array( + 'type' => 'string', + 'null' => true, + 'key' => 'primary', + 'length' => 34, + ), + 'title' => array( + 'type' => 'string', + 'null' => true, + 'length' => 255, + ), + 'description' => array( + 'type' => 'text', + 'null' => true, + ) + ); +} + +/** + * CouchDBTestCase + * + * @package datasources + * @subpackage datasources.tests.cases.models.datasources + */ +class CouchDBTestCase extends CakeTestCase { + +/** + * CouchDB Datasource object + * + * @var object + */ + public $CouchDB = null; + +/** + * Configuration + * + * @var array + */ + protected $config = array( + 'datasource' => 'CouchDB.CouchDBSource', + 'persistent' => false, + 'host' => 'localhost', + 'port' => '5984', + 'login' => 'root', + 'password' => 'root', + 'database' => null + ); + +/** + * Start Test + * + * @return void + */ + public function startTest() { + config('database'); + $config = new DATABASE_CONFIG(); + + if (isset($config->couchdb_test)) { + $this->config = $config->couchdb_test; + } + + ConnectionManager::create('couchdb_test', $this->config); + + $this->Post = ClassRegistry::init('Post'); + $this->removeAllDocuments(); + } + +/** + * testConnection + * + * @return void + */ + public function testConnection() { + $this->CouchDB = new CouchDBSource($this->config); + $this->CouchDB =& ConnectionManager::getDataSource($this->Post->useDbConfig); + + $reconnect = $this->CouchDB->reconnect($this->config); + $this->assertIdentical($reconnect, true, __('Not reconnected')); + + $disconnect = $this->CouchDB->disconnect(); + $this->assertIdentical($disconnect, true, __('Not disconnect')); + } + +/** + * testFind + * + * @return void + */ + public function testFind() { + $data = array( + 'title' => 'My first post', + 'description' => 'My first post' + ); + $this->Post->save($data); + + $result = $this->Post->find('all'); + $this->assertEqual(1, count($result)); + + $resultData = $result[0]['Post']; + $this->assertEqual(4, count($resultData)); + $this->assertTrue(!empty($resultData['id'])); + $this->assertEqual($this->Post->id, $resultData['id']); + $this->assertEqual($this->Post->rev, $resultData['rev']); + $this->assertEqual($data['title'], $resultData['title']); + $this->assertEqual($data['description'], $resultData['description']); + } + + +/** + * testFindConditions + * + * @return void + */ + public function testFindConditions() { + $data = array( + 'title' => 'My first post', + 'description' => 'My first post' + ); + $this->Post->save($data); + + $this->Post->create(); + $this->Post->save($data); + + $result = $this->Post->find('all'); + $this->assertEqual(2, count($result)); + + $result = $this->Post->find('all', array('conditions' => array('Post.id' => $this->Post->id))); + $this->assertEqual(1, count($result)); + + $result = $this->Post->find('all', array('conditions' => array('id' => $this->Post->id))); + $this->assertEqual(1, count($result)); + } + +/** + * testFindRevs + * + * @return void + */ + public function testFindRevs() { + $data = array( + 'title' => 'My first post', + 'description' => 'My first post' + ); + $this->Post->save($data); + $this->Post->save($data); + + $this->Post->recursive = 0; + $result = $this->Post->find('all', array('conditions' => array('id' => $this->Post->id))); + $this->assertEqual(2, count($result[0]['Post']['_revs_info'])); + } + +/** + * Tests save method. + * + * @return void + */ + public function testSave() { + $data = array( + 'title' => 'My first post', + 'description' => 'My first post' + ); + + $this->Post->create(); + $saveResult = $this->Post->save($data); + $this->assertIdentical(is_array($saveResult), true); + + $result = $this->Post->find('all'); + $this->assertEqual(1, count($result)); + + $resultData = $result[0]['Post']; + $this->assertEqual(4, count($resultData)); + $this->assertTrue(!empty($resultData['id'])); + $this->assertEqual($this->Post->id, $resultData['id']); + $this->assertEqual($this->Post->rev, $resultData['rev']); + $this->assertEqual($data['title'], $resultData['title']); + $this->assertEqual($data['description'], $resultData['description']); + } + +/** + * Tests save method. + * + * @return void + */ + public function testSaveWithId() { + $data = array( + 'id' => String::uuid(), + 'title' => 'My first post', + 'description' => 'My first post' + ); + + $this->Post->create(); + $saveResult = $this->Post->save($data); + $this->assertIdentical(is_array($saveResult), true); + + $result = $this->Post->find('all'); + $this->assertEqual(1, count($result)); + + $resultData = $result[0]['Post']; + $this->assertEqual(4, count($resultData)); + $this->assertTrue(!empty($resultData['id'])); + $this->assertEqual($resultData['id'], $data['id']); + $this->assertEqual($this->Post->id, $resultData['id']); + $this->assertEqual($this->Post->rev, $resultData['rev']); + $this->assertEqual($data['title'], $resultData['title']); + $this->assertEqual($data['description'], $resultData['description']); + } + +/** + * Tests saveAll method. + * + * @return void + */ + public function testSaveAll() { + $data[0]['Post'] = array( + 'title' => 'My first post', + 'description' => 'My first post' + ); + + $data[1]['Post'] = array( + 'title' => 'My second post', + 'description' => 'My second post' + ); + + $this->Post->create(); + $saveResult = $this->Post->saveAll($data); + + $result = $this->Post->find('all'); + $this->assertEqual(2, count($result)); + + $resultData = $result[0]['Post']; + $this->assertEqual(4, count($resultData)); + $this->assertTrue(!empty($resultData['id'])); + $this->assertEqual($data[0]['Post']['title'], $resultData['title']); + $this->assertEqual($data[0]['Post']['description'], $resultData['description']); + + $resultData = $result[1]['Post']; + $this->assertEqual(4, count($resultData)); + $this->assertTrue(!empty($resultData['id'])); + $this->assertEqual($data[1]['Post']['title'], $resultData['title']); + $this->assertEqual($data[1]['Post']['description'], $resultData['description']); + } + +/** + * Tests update method. + * + * @return void + */ + public function updateTest() { + // Count posts + $uri = '/posts/_temp_view?group=true'; + $post = array( + 'map' => 'function(doc) { emit(doc._id,1); }', + 'reduce' => 'function(keys, values) { return sum(values); }' + ); + + $mapReduce = $this->Post->query($uri, $post); + if(isset($mapReduce->rows[0]->value)) $count0 = $mapReduce->rows[0]->value; + else $count0 = 0; + + $count1 = $this->updateTest1($uri, $post, $count0); + $count2 = $this->updateTest2($uri, $post, $count1); + $count3 = $this->updateTest3($uri, $post, $count2); + $count4 = $this->updateTest4($uri, $post, $count2); + $updateData = $this->updateTest5($uri, $post, $count4); + + // Final test + $result = $this->Post->find('all'); + $this->assertEqual(1, count($result)); + + $resultData = $result[0]['Post']; + $this->assertEqual(4, count($resultData)); + $this->assertTrue(!empty($resultData['id'])); + $this->assertEqual($this->Post->id, $resultData['id']); + $this->assertEqual($this->Post->rev, $resultData['rev']); + $this->assertNotEqual($updateData['title'], $resultData['title']); + $this->assertNotEqual($updateData['description'], $resultData['description']); + + } + +/** + * Tests update1 method. + * + * @param string $uri + * @param array $post + * @param interger $previousCount + * @return void + */ + private function updateTest1($uri, $post, $previousCount) { + $data = array( + 'title' => 'My first post', + 'description' => 'My first post' + ); + + $this->Post->create(); + $saveResult = $this->Post->save($data); + $this->assertIdentical(is_array($saveResult), true); + $this->assertIdentical(!empty($this->Post->id), true); + + $mapReduce = $this->Post->curlPost($uri, $post); + $count1 = $mapReduce['rows'][0]['value']; + + $this->assertIdentical($count1 - $previousCount, 1); + + return $count1; + } + +/** + * Tests update2 method. + * + * @param string $uri + * @param array $post + * @param interger $previousCount + * @return void + */ + private function updateTest2($uri, $post, $previousCount) { + $findResult = $this->Post->find('first'); + $this->assertEqual(4, count($findResult['Post'])); + + $updateData = array( + 'title' => 'My post update', + 'description' => 'My post update' + ); + + $this->Post->id = $findResult['Post']['id']; + $this->Post->rev = $findResult['Post']['rev']; + $saveResult = $this->Post->save($updateData); + $this->assertIdentical(is_array($saveResult), true); + + $mapReduce = $this->Post->curlPost($uri, $post); + $count2 = $mapReduce['rows'][0]['value']; + + $this->assertIdentical($count2 - $previousCount, 0); + + return $count2; + } + +/** + * Tests update3 method. + * + * @param string $uri + * @param array $post + * @param interger $previousCount + * @return void + */ + private function updateTest3($uri, $post, $previousCount) { + $findResult = $this->Post->find('first'); + $this->assertEqual(4, count($findResult['Post'])); + + $updateData = array( + 'id' => $findResult['Post']['id'], + 'title' => 'My post update', + 'description' => 'My post update' + ); + + $this->Post->rev = $findResult['Post']['rev']; + $saveResult = $this->Post->save($updateData); + $this->assertIdentical(is_array($saveResult), true); + $this->assertIdentical($this->Post->id, $findResult['Post']['id']); + + $mapReduce = $this->Post->curlPost($uri, $post); + $count3 = $mapReduce['rows'][0]['value']; + + $this->assertIdentical($count3 - $previousCount, 0); + + return $count3; + } + +/** + * Tests update4 method. + * + * @param string $uri + * @param array $post + * @param interger $previousCount + * @return void + */ + private function updateTest4($uri, $post, $previousCount) { + $findResult = $this->Post->find('first'); + $this->assertEqual(4, count($findResult['Post'])); + + $updateData = array( + 'id' => $findResult['Post']['id'], + 'rev' => $findResult['Post']['rev'], + 'title' => 'My post update', + 'description' => 'My post update' + ); + + $saveResult = $this->Post->save($updateData); + $this->assertIdentical(is_array($saveResult), true); + $this->assertIdentical($this->Post->id, $findResult['Post']['id']); + + $mapReduce = $this->Post->curlPost($uri, $post); + $count4 = $mapReduce['rows'][0]['value']; + + $this->assertIdentical($count4 - $previousCount, 0); + + return $count4; + } + +/** + * Tests update5 method. + * + * @param string $uri + * @param array $post + * @param interger $previousCount + * @return void + */ + private function updateTest5($uri, $post, $previousCount) { + $findResult = $this->Post->find('first'); + $this->assertEqual(4, count($findResult['Post'])); + + $updateData = array( + 'id' => $findResult['Post']['id'], + 'rev' => 'whatever', + 'title' => 'My post fail', + 'description' => 'My post fail' + ); + + $saveResult = $this->Post->save($updateData); + $this->assertFalse($saveResult); + $this->assertIdentical($this->Post->id, $findResult['Post']['id']); + + $mapReduce = $this->Post->curlPost($uri, $post); + $count5 = $mapReduce['rows'][0]['value']; + + $this->assertIdentical($count5 - $previousCount, 0); + + return $updateData; + } + +/** + * Test update without revision + * + * @return void + */ + public function testUpdateWithoutRevision() { + $data = array( + 'title' => 'My first post', + 'description' => 'My first post' + ); + + $this->Post->create(); + $saveResult = $this->Post->save($data); + + $result = $this->Post->find('first'); + + unset($result['Post']['rev']); + unset($this->Post->rev); + + $updateResult = $this->Post->save($result); + + $this->assertIdentical(is_array($updateResult), true); + $this->assertIdentical($this->Post->id, $saveResult['Post']['id']); + } + +/** + * Tests delete method. + * + * @return void + */ + public function testDelete() { + $data = array( + 'title' => 'My first post', + 'description' => 'My first post' + ); + + $this->Post->create(); + $saveResult = $this->Post->save($data); + + $result = $this->Post->find('all'); + $this->assertEqual(1, count($result)); + + $this->Post->id = $result[0]['Post']['id']; + $this->Post->rev = $result[0]['Post']['rev']; + $this->Post->delete(); + + $result = $this->Post->find('all'); + $this->assertEqual(0, count($result)); + } + +/** + * Test delete without revision + * + * @return void + */ + public function testDeleteWithoutRevision() { + $data = array( + 'title' => 'My first post', + 'description' => 'My first post' + ); + + $this->Post->create(); + $saveResult = $this->Post->save($data); + + $result = $this->Post->find('all'); + $this->assertEqual(1, count($result)); + + unset($result['Post']['rev']); + unset($this->Post->rev); + + $this->Post->delete(); + + $result = $this->Post->find('all'); + $this->assertEqual(0, count($result)); + } + +/** + * Tests query method. + * + * @return void + */ + public function testQuery() { + // GET + $result = $this->Post->curlGet('_all_dbs'); + $this->assertIdentical(is_array($result), true); + + // POST + $data = array( + 'title' => 'My first post', + 'description' => 'My first post' + ); + + $result = $this->Post->curlPost('/posts', $data); + $this->assertIdentical($result['ok'], true); + + // PUT + $data = array( + '_rev' => $result['rev'], + 'title' => 'My first update', + 'description' => 'My first update' + ); + + $result = $this->Post->curlPut('/posts/' . $result['id'], $data); + $this->assertIdentical($result['ok'], true); + + // DELETE + $result = $this->Post->curlDelete('/posts/' . $result['id'] . '/?rev=' . $result['rev']); + $this->assertIdentical($result['ok'], true); + } + +/** + * Remove all documents from database + * + * @return void + */ + private function removeAllDocuments() { + $posts = $this->Post->find('list', array('fields' => array('Post.rev'))); + foreach($posts as $id => $post) { + $this->Post->rev = $post; + $this->Post->delete($id); + } + } + +/** + * End Test + * + * @return void + */ + public function endTest() { + $this->removeAllDocuments(); + unset($this->Post); + unset($this->CouchDB); + ClassRegistry::flush(); + } +} From a9283c069a48f17dbc4285a2c9e6cd6b5d9153f9 Mon Sep 17 00:00:00 2001 From: "Maury M. Marques" Date: Sat, 11 Feb 2012 02:04:04 -0200 Subject: [PATCH 8/8] Add function expression --- Model/Datasource/CouchDBSource.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Model/Datasource/CouchDBSource.php b/Model/Datasource/CouchDBSource.php index d30e5c2..04a735d 100644 --- a/Model/Datasource/CouchDBSource.php +++ b/Model/Datasource/CouchDBSource.php @@ -372,6 +372,20 @@ public function calculate($model, $func, $params = array()) { return true; } +/** + * Returns an object to represent a database expression in a query. Expression objects + * are not sanitized or escaped. + * + * @param string $expression An arbitrary expression to be inserted into a query. + * @return stdClass An object representing a database expression to be used in a query + */ + public function expression($expression) { + $obj = new stdClass(); + $obj->type = 'expression'; + $obj->value = $expression; + return $obj; + } + /** * Gets full table name including prefix. *