From 2c1a79a3eae1926e482d1ff81e5db72ebe1da54c Mon Sep 17 00:00:00 2001 From: Markus Bachmann Date: Tue, 19 Jun 2012 21:52:20 +0200 Subject: [PATCH 1/6] Add Riak storage --- README.md | 2 +- composer.json | 3 + composer.lock | 29 ++- .../KeyValueStore/Storage/RiakStorage.php | 175 +++++++++++++ .../KeyValueStore/Storage/RiakStorageTest.php | 233 ++++++++++++++++++ 5 files changed, 437 insertions(+), 5 deletions(-) create mode 100644 lib/Doctrine/KeyValueStore/Storage/RiakStorage.php create mode 100644 tests/Doctrine/Tests/KeyValueStore/Storage/RiakStorageTest.php diff --git a/README.md b/README.md index 1fe9e13..7ceed41 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Following vendors are targeted: * Amazon DynamoDB * CouchDB * MongoDB -* Riak +* Riak (Implemented) We happily accept contributions for any of the drivers. diff --git a/composer.json b/composer.json index 67ad338..5c98bc5 100644 --- a/composer.json +++ b/composer.json @@ -3,6 +3,9 @@ "require": { "doctrine/common": "*" }, + "require-dev": { + "riak/riak-client": "*" + }, "description": "Simple Key-Value Store Abstraction Layer that maps to PHP objects, allowing for many backends.", "license": "MIT", "autoload": { diff --git a/composer.lock b/composer.lock index 226023d..1f4765d 100644 --- a/composer.lock +++ b/composer.lock @@ -1,9 +1,30 @@ { - "hash": "09b80610c08784c764e1df9edca9b53f", + "hash": "23665765c3ec49f9478cd201eaf36803", "packages": [ { - "package": "doctrine\/common", - "version": "master-dev" + "package": "doctrine/common", + "version": "dev-master", + "alias-pretty-version": "2.3.x-dev", + "alias-version": "2.3.9999999.9999999-dev" + }, + { + "package": "doctrine/common", + "version": "dev-master", + "source-reference": "f0b548aa55bb7dac36d79061270a085dd38635b4" + } + ], + "packages-dev": [ + { + "package": "riak/riak-client", + "version": "dev-master", + "source-reference": "cea446f04d759caaef44020a2064a38b41cbdfab" } + ], + "aliases": [ + + ], + "minimum-stability": "dev", + "stability-flags": [ + ] -} \ No newline at end of file +} diff --git a/lib/Doctrine/KeyValueStore/Storage/RiakStorage.php b/lib/Doctrine/KeyValueStore/Storage/RiakStorage.php new file mode 100644 index 0000000..63f8109 --- /dev/null +++ b/lib/Doctrine/KeyValueStore/Storage/RiakStorage.php @@ -0,0 +1,175 @@ +. + */ + + +namespace Doctrine\KeyValueStore\Storage; + +use Doctrine\KeyValueStore\NotFoundException; + +use Riak\Client; + +/** + * @author Markus Bachmann + */ +class RiakStorage implements Storage +{ + /** + * @var \Riak\Client + */ + protected $client; + + /** + * @var string + */ + protected $bucketName; + + /** + * Constructor + * + * @param \Riak\Client $riak + * @param string $bucketName + */ + public function __construct(Client $riak, $bucketName) + { + $this->client = $riak; + $this->bucketName = $bucketName; + } + + /** + * Determine if the storage supports updating only a subset of properties, + * or if all properties have to be set, even if only a subset of properties + * changed. + * + * @return bool + */ + public function supportsPartialUpdates() + { + return false; + } + + /** + * Does this storage support composite primary keys? + * + * @return bool + */ + function supportsCompositePrimaryKeys() + { + return false; + } + + /** + * Does this storage require composite primary keys? + * + * @return bool + */ + function requiresCompositePrimaryKeys() + { + return false; + } + + /** + * Insert data into the storage key specified. + * + * @param string $storageName + * @param array|string $key + * @param array $data + * @return void + */ + public function insert($storageName, $key, array $data) + { + $bucket = $this->client->bucket($this->bucketName); + $object = $bucket->newObject($key, $data); + $object->store(); + } + + /** + * Update data into the given key. + * + * @param string $storageName + * @param array|string $key + * @param array $data + * @return void + */ + public function update($storageName, $key, array $data) + { + $bucket = $this->client->bucket($this->bucketName); + /** @var $object \Riak\Object */ + $object = $bucket->get($key); + + $object->setData($data); + $object->store(); + } + + /** + * Delete data at key + * + * @param string $storageName + * @param array|string $key + * @return void + */ + public function delete($storageName, $key) + { + $bucket = $this->client->bucket($this->bucketName); + + /** @var $object \Riak\Object */ + $object = $bucket->get($key); + + if (!$object->exists()) { + // object does not exist, do nothing + return; + } + + $object->delete(); + } + + /** + * Find data at key + * + * Important note: The returned array does contain the identifier (again)! + * + * @throws Doctrine\KeyValueStore\NotFoundException When data with key is not found. + * + * @param string $storageName + * @param array|string $key + * @return array + */ + public function find($storageName, $key) + { + $bucket = $this->client->bucket($this->bucketName); + + /** @var $object \Riak\Object */ + $object = $bucket->get($key); + + if (!$object->exists()) { + throw new NotFoundException; + } + + return $object->getData(); + } + + /** + * Return a name of the underlying storage. + * + * @return string + */ + public function getName() + { + return 'riak'; + } +} diff --git a/tests/Doctrine/Tests/KeyValueStore/Storage/RiakStorageTest.php b/tests/Doctrine/Tests/KeyValueStore/Storage/RiakStorageTest.php new file mode 100644 index 0000000..8097c8c --- /dev/null +++ b/tests/Doctrine/Tests/KeyValueStore/Storage/RiakStorageTest.php @@ -0,0 +1,233 @@ + + */ +class RiakStorageTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var RiakStorage + */ + private $storage; + + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $riak; + + protected function setup() + { + $this->riak = $this->getMockBuilder('Riak\\Client') + ->disableOriginalConstructor() + ->getMock(); + + + $this->storage = new RiakStorage($this->riak, 'test'); + } + + public function testSupportsPartialUpdates() + { + $this->assertFalse($this->storage->supportsPartialUpdates()); + } + + public function testSupportsCompositePrimaryKeys() + { + $this->assertFalse($this->storage->supportsCompositePrimaryKeys()); + } + + public function testRequiresCompositePrimaryKeys() + { + $this->assertFalse($this->storage->requiresCompositePrimaryKeys()); + } + + public function testInsert() + { + $bucket = $this->getMockBuilder('Riak\Bucket') + ->disableOriginalConstructor() + ->getMock(); + + $this->riak->expects($this->once()) + ->method('bucket') + ->will($this->returnValue($bucket)); + + $objectMock = $this->getMockBuilder('Riak\Object') + ->disableOriginalConstructor() + ->getMock(); + + $objectMock->expects($this->once()) + ->method('store'); + + + $that = $this; + $bucket->expects($this->once()) + ->method('newObject') + ->will($this->returnCallback(function($key, $data) use ($objectMock, $that) { + $that->assertEquals('foobar', $key); + $that->assertEquals(array('title' => 'Riak test'), $data); + return $objectMock; + })); + + $this->storage->insert('riak-test', 'foobar', array('title' => 'Riak test')); + } + + public function testUpdate() + { + $objectMock = $this->getMockBuilder('Riak\Object') + ->disableOriginalConstructor() + ->getMock(); + + $bucket = $this->getMockBuilder('Riak\Bucket') + ->disableOriginalConstructor() + ->getMock(); + + $this->riak->expects($this->once()) + ->method('bucket') + ->will($this->returnValue($bucket)); + + $bucket->expects($this->once()) + ->method('get') + ->will($this->returnValue($objectMock)); + + + $that = $this; + $objectMock->expects($this->once()) + ->method('setData') + ->will($this->returnCallback(function($data) use ($that) { + $that->assertEquals(array('title' => 'Riak cookbook'), $data); + })); + + $objectMock->expects($this->once()) + ->method('store'); + + $this->storage->update('riak-test', 'foobar', array('title' => 'Riak cookbook')); + } + + public function testDelete() + { + $objectMock = $this->getMockBuilder('Riak\Object') + ->disableOriginalConstructor() + ->getMock(); + + $bucket = $this->getMockBuilder('Riak\Bucket') + ->disableOriginalConstructor() + ->getMock(); + + $this->riak->expects($this->once()) + ->method('bucket') + ->will($this->returnValue($bucket)); + + $bucket->expects($this->once()) + ->method('get') + ->with('foobar') + ->will($this->returnValue($objectMock)); + + $objectMock->expects($this->once()) + ->method('exists') + ->will($this->returnValue(true)); + + $objectMock->expects($this->once()) + ->method('delete'); + + $this->storage->delete('riak-test', 'foobar'); + } + + public function testDeleteWithNotExistKey() + { + $objectMock = $this->getMockBuilder('Riak\Object') + ->disableOriginalConstructor() + ->getMock(); + + $bucket = $this->getMockBuilder('Riak\Bucket') + ->disableOriginalConstructor() + ->getMock(); + + $this->riak->expects($this->once()) + ->method('bucket') + ->will($this->returnValue($bucket)); + + $bucket->expects($this->once()) + ->method('get') + ->with('foobar') + ->will($this->returnValue($objectMock)); + + $objectMock->expects($this->once()) + ->method('exists') + ->will($this->returnValue(false)); + + $objectMock->expects($this->never()) + ->method('delete'); + + $this->storage->delete('riak-test', 'foobar'); + } + + public function testFind() + { + $objectMock = $this->getMockBuilder('Riak\Object') + ->disableOriginalConstructor() + ->getMock(); + + $bucket = $this->getMockBuilder('Riak\Bucket') + ->disableOriginalConstructor() + ->getMock(); + + $this->riak->expects($this->once()) + ->method('bucket') + ->will($this->returnValue($bucket)); + + $bucket->expects($this->once()) + ->method('get') + ->with('foobar') + ->will($this->returnValue($objectMock)); + + $objectMock->expects($this->once()) + ->method('exists') + ->will($this->returnValue(true)); + + $objectMock->expects($this->once()) + ->method('getData') + ->will($this->returnValue(array('title' => 'Riak Test'))); + + $this->assertEquals(array('title' => 'Riak Test'), $this->storage->find('riaktest', 'foobar')); + } + + /** + * @expectedException Doctrine\KeyValueStore\NotFoundException + */ + public function testFindWithNotExistKey() + { + $objectMock = $this->getMockBuilder('Riak\Object') + ->disableOriginalConstructor() + ->getMock(); + + $bucket = $this->getMockBuilder('Riak\Bucket') + ->disableOriginalConstructor() + ->getMock(); + + $this->riak->expects($this->once()) + ->method('bucket') + ->will($this->returnValue($bucket)); + + $bucket->expects($this->once()) + ->method('get') + ->with('foobar') + ->will($this->returnValue($objectMock)); + + $objectMock->expects($this->once()) + ->method('exists') + ->will($this->returnValue(false)); + + $objectMock->expects($this->never()) + ->method('getData'); + + $this->storage->find('riak-test', 'foobar'); + } + + public function testGetName() + { + $this->assertEquals('riak', $this->storage->getName()); + } +} \ No newline at end of file From 692e0ca38deace3640c4bad3f197651ff30ade9e Mon Sep 17 00:00:00 2001 From: Markus Bachmann Date: Tue, 19 Jun 2012 21:53:28 +0200 Subject: [PATCH 2/6] CS --- lib/Doctrine/KeyValueStore/Storage/RiakStorage.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/KeyValueStore/Storage/RiakStorage.php b/lib/Doctrine/KeyValueStore/Storage/RiakStorage.php index 63f8109..b0da4de 100644 --- a/lib/Doctrine/KeyValueStore/Storage/RiakStorage.php +++ b/lib/Doctrine/KeyValueStore/Storage/RiakStorage.php @@ -68,7 +68,7 @@ public function supportsPartialUpdates() * * @return bool */ - function supportsCompositePrimaryKeys() + public function supportsCompositePrimaryKeys() { return false; } @@ -78,7 +78,7 @@ function supportsCompositePrimaryKeys() * * @return bool */ - function requiresCompositePrimaryKeys() + public function requiresCompositePrimaryKeys() { return false; } From 76530e8b5d0416b6ff11e0c5a63ec79da7cf6e0b Mon Sep 17 00:00:00 2001 From: Markus Bachmann Date: Wed, 20 Jun 2012 21:10:40 +0200 Subject: [PATCH 3/6] Replace docblocks with {@inheritDoc} --- .../KeyValueStore/Storage/RiakStorage.php | 64 ++++--------------- 1 file changed, 13 insertions(+), 51 deletions(-) diff --git a/lib/Doctrine/KeyValueStore/Storage/RiakStorage.php b/lib/Doctrine/KeyValueStore/Storage/RiakStorage.php index b0da4de..51f52df 100644 --- a/lib/Doctrine/KeyValueStore/Storage/RiakStorage.php +++ b/lib/Doctrine/KeyValueStore/Storage/RiakStorage.php @@ -34,29 +34,19 @@ class RiakStorage implements Storage */ protected $client; - /** - * @var string - */ - protected $bucketName; - /** * Constructor * * @param \Riak\Client $riak * @param string $bucketName */ - public function __construct(Client $riak, $bucketName) + public function __construct(Client $riak) { $this->client = $riak; - $this->bucketName = $bucketName; } /** - * Determine if the storage supports updating only a subset of properties, - * or if all properties have to be set, even if only a subset of properties - * changed. - * - * @return bool + * {@inheritDoc} */ public function supportsPartialUpdates() { @@ -64,9 +54,7 @@ public function supportsPartialUpdates() } /** - * Does this storage support composite primary keys? - * - * @return bool + * {@inheritDoc} */ public function supportsCompositePrimaryKeys() { @@ -74,9 +62,7 @@ public function supportsCompositePrimaryKeys() } /** - * Does this storage require composite primary keys? - * - * @return bool + * {@inheritDoc} */ public function requiresCompositePrimaryKeys() { @@ -84,31 +70,21 @@ public function requiresCompositePrimaryKeys() } /** - * Insert data into the storage key specified. - * - * @param string $storageName - * @param array|string $key - * @param array $data - * @return void + * {@inheritDoc} */ public function insert($storageName, $key, array $data) { - $bucket = $this->client->bucket($this->bucketName); + $bucket = $this->client->bucket($storageName); $object = $bucket->newObject($key, $data); $object->store(); } /** - * Update data into the given key. - * - * @param string $storageName - * @param array|string $key - * @param array $data - * @return void + * {@inheritDoc} */ public function update($storageName, $key, array $data) { - $bucket = $this->client->bucket($this->bucketName); + $bucket = $this->client->bucket($storageName); /** @var $object \Riak\Object */ $object = $bucket->get($key); @@ -117,15 +93,11 @@ public function update($storageName, $key, array $data) } /** - * Delete data at key - * - * @param string $storageName - * @param array|string $key - * @return void + * {@inheritDoc} */ public function delete($storageName, $key) { - $bucket = $this->client->bucket($this->bucketName); + $bucket = $this->client->bucket($storageName); /** @var $object \Riak\Object */ $object = $bucket->get($key); @@ -139,19 +111,11 @@ public function delete($storageName, $key) } /** - * Find data at key - * - * Important note: The returned array does contain the identifier (again)! - * - * @throws Doctrine\KeyValueStore\NotFoundException When data with key is not found. - * - * @param string $storageName - * @param array|string $key - * @return array + * {@inheritDoc} */ public function find($storageName, $key) { - $bucket = $this->client->bucket($this->bucketName); + $bucket = $this->client->bucket($storageName); /** @var $object \Riak\Object */ $object = $bucket->get($key); @@ -164,9 +128,7 @@ public function find($storageName, $key) } /** - * Return a name of the underlying storage. - * - * @return string + * {@inheritDoc} */ public function getName() { From f4ba01af295b6270845ce2e37118dd609998572a Mon Sep 17 00:00:00 2001 From: Markus Bachmann Date: Wed, 20 Jun 2012 21:10:59 +0200 Subject: [PATCH 4/6] Use the right indention --- .../KeyValueStore/Storage/RiakStorageTest.php | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/tests/Doctrine/Tests/KeyValueStore/Storage/RiakStorageTest.php b/tests/Doctrine/Tests/KeyValueStore/Storage/RiakStorageTest.php index 8097c8c..e24ff12 100644 --- a/tests/Doctrine/Tests/KeyValueStore/Storage/RiakStorageTest.php +++ b/tests/Doctrine/Tests/KeyValueStore/Storage/RiakStorageTest.php @@ -22,11 +22,11 @@ class RiakStorageTest extends \PHPUnit_Framework_TestCase protected function setup() { $this->riak = $this->getMockBuilder('Riak\\Client') - ->disableOriginalConstructor() - ->getMock(); + ->disableOriginalConstructor() + ->getMock(); - $this->storage = new RiakStorage($this->riak, 'test'); + $this->storage = new RiakStorage($this->riak); } public function testSupportsPartialUpdates() @@ -55,21 +55,21 @@ public function testInsert() ->will($this->returnValue($bucket)); $objectMock = $this->getMockBuilder('Riak\Object') - ->disableOriginalConstructor() - ->getMock(); + ->disableOriginalConstructor() + ->getMock(); $objectMock->expects($this->once()) - ->method('store'); + ->method('store'); $that = $this; $bucket->expects($this->once()) - ->method('newObject') - ->will($this->returnCallback(function($key, $data) use ($objectMock, $that) { - $that->assertEquals('foobar', $key); - $that->assertEquals(array('title' => 'Riak test'), $data); - return $objectMock; - })); + ->method('newObject') + ->will($this->returnCallback(function($key, $data) use ($objectMock, $that) { + $that->assertEquals('foobar', $key); + $that->assertEquals(array('title' => 'Riak test'), $data); + return $objectMock; + })); $this->storage->insert('riak-test', 'foobar', array('title' => 'Riak test')); } @@ -77,8 +77,8 @@ public function testInsert() public function testUpdate() { $objectMock = $this->getMockBuilder('Riak\Object') - ->disableOriginalConstructor() - ->getMock(); + ->disableOriginalConstructor() + ->getMock(); $bucket = $this->getMockBuilder('Riak\Bucket') ->disableOriginalConstructor() @@ -95,13 +95,13 @@ public function testUpdate() $that = $this; $objectMock->expects($this->once()) - ->method('setData') - ->will($this->returnCallback(function($data) use ($that) { - $that->assertEquals(array('title' => 'Riak cookbook'), $data); - })); + ->method('setData') + ->will($this->returnCallback(function($data) use ($that) { + $that->assertEquals(array('title' => 'Riak cookbook'), $data); + })); $objectMock->expects($this->once()) - ->method('store'); + ->method('store'); $this->storage->update('riak-test', 'foobar', array('title' => 'Riak cookbook')); } @@ -126,11 +126,11 @@ public function testDelete() ->will($this->returnValue($objectMock)); $objectMock->expects($this->once()) - ->method('exists') - ->will($this->returnValue(true)); + ->method('exists') + ->will($this->returnValue(true)); $objectMock->expects($this->once()) - ->method('delete'); + ->method('delete'); $this->storage->delete('riak-test', 'foobar'); } @@ -184,12 +184,12 @@ public function testFind() ->will($this->returnValue($objectMock)); $objectMock->expects($this->once()) - ->method('exists') - ->will($this->returnValue(true)); + ->method('exists') + ->will($this->returnValue(true)); $objectMock->expects($this->once()) - ->method('getData') - ->will($this->returnValue(array('title' => 'Riak Test'))); + ->method('getData') + ->will($this->returnValue(array('title' => 'Riak Test'))); $this->assertEquals(array('title' => 'Riak Test'), $this->storage->find('riaktest', 'foobar')); } From f8c3d9d7250206f97321202cb15999fd071a3775 Mon Sep 17 00:00:00 2001 From: Markus Bachmann Date: Wed, 20 Jun 2012 21:11:40 +0200 Subject: [PATCH 5/6] Update composer file and add riak/riak-client to the "suggest" section --- composer.json | 5 ++++- composer.lock | 22 +++++----------------- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/composer.json b/composer.json index 5c98bc5..39262f4 100644 --- a/composer.json +++ b/composer.json @@ -3,8 +3,11 @@ "require": { "doctrine/common": "*" }, + "suggest": { + "riak/riak-client": "dev-master" + }, "require-dev": { - "riak/riak-client": "*" + "riak/riak-client": "dev-master" }, "description": "Simple Key-Value Store Abstraction Layer that maps to PHP objects, allowing for many backends.", "license": "MIT", diff --git a/composer.lock b/composer.lock index 1f4765d..9bea926 100644 --- a/composer.lock +++ b/composer.lock @@ -1,30 +1,18 @@ { - "hash": "23665765c3ec49f9478cd201eaf36803", + "hash": "a5d60fb3fe6f38269e7733d90fa16eb4", "packages": [ - { - "package": "doctrine/common", - "version": "dev-master", - "alias-pretty-version": "2.3.x-dev", - "alias-version": "2.3.9999999.9999999-dev" - }, { "package": "doctrine/common", "version": "dev-master", "source-reference": "f0b548aa55bb7dac36d79061270a085dd38635b4" } ], - "packages-dev": [ - { - "package": "riak/riak-client", - "version": "dev-master", - "source-reference": "cea446f04d759caaef44020a2064a38b41cbdfab" - } - ], + "packages-dev": null, "aliases": [ ], "minimum-stability": "dev", - "stability-flags": [ - - ] + "stability-flags": { + "riak/riak-client": 20 + } } From c7050ddb968651cb4bce2055063fb49921d5dbe9 Mon Sep 17 00:00:00 2001 From: Markus Bachmann Date: Wed, 20 Jun 2012 21:30:43 +0200 Subject: [PATCH 6/6] Replace version constraint in suggest section --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 39262f4..922fe79 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,7 @@ "doctrine/common": "*" }, "suggest": { - "riak/riak-client": "dev-master" + "riak/riak-client": "to use the Riak storage" }, "require-dev": { "riak/riak-client": "dev-master"