Skip to content

Commit

Permalink
add an instance config trait
Browse files Browse the repository at this point in the history
  • Loading branch information
AD7six committed Mar 24, 2014
1 parent f4ff29b commit 474fdb3
Show file tree
Hide file tree
Showing 2 changed files with 378 additions and 0 deletions.
145 changes: 145 additions & 0 deletions src/Core/InstanceConfigTrait.php
@@ -0,0 +1,145 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since 3.0.0
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
namespace Cake\Core;

use Cake\Error;

/**
* A trait for reading and writing instance config
*
* Implementing objects are expected to declare a `$_config` property.
*/
trait InstanceConfigTrait {

/**
* Runtime config
*
* @var array
*/
protected $_config = [];

/**
* ### Usage
*
* Reading the whole config:
*
* `$this->config();`
*
* Reading a specific value:
*
* `$this->config('key');`
*
* Reading a nested value:
*
* `$this->config('some.nested.key');`
*
* Setting a specific value:
*
* `$this->config('key', $value);`
*
* Setting a nested value:
*
* `$this->config('some.nested.key', $value);`
*
* Updating multiple config settings at the same time:
*
* `$this->config(['one' => 'value', 'another' => 'value']);`
*
* @param string|array|null $key the key to get/set, or a complete array of configs
* @param mixed|null $value the value to set
* @return mixed|null null for write, or whatever is in the config on read
* @throws \Cake\Error\Exception When trying to set a key that is invalid
*/
public function config($key = null, $value = null) {
if ($this->_config === [] && $this->_defaultConfig) {
$this->_config = $this->_defaultConfig;
}

if ($value !== null || is_array($key)) {
return $this->_configWrite($key, $value);
}

return $this->_configRead($key);
}

/**
* Read a config variable
*
* @param string|null $key
* @return mixed
*/
protected function _configRead($key) {
if ($key === null) {
return $this->_config;
}

if (strpos($key, '.') === false) {
return isset($this->_config[$key]) ? $this->_config[$key] : null;
}

$return = $this->_config;

foreach(explode('.', $key) as $k) {
if (!is_array($return) || !isset($return[$k])) {
$return = null;
break;
}

$return = $return[$k];

}

return $return;
}

/**
* Write a config variable
*
* @throws Error\Exception if attempting to clobber existing config
* @param string|array $key
* @param mixed|null $value
* @return void
*/
protected function _configWrite($key, $value = null) {
if (is_array($key)) {
foreach($key as $k => $val) {
$this->_configWrite($k, $val);
}
return;
}

if (strpos($key, '.') === false) {
$this->_config[$key] = $value;
return;
}

$update =& $this->_config;

foreach(explode('.', $key) as $k) {
if (!is_array($update)) {
throw new Error\Exception(sprintf('Cannot set %s value', $key));
}

if (!isset($update[$k])) {
$update[$k] = [];
}

$update =& $update[$k];
}

$update = $value;
}

}
233 changes: 233 additions & 0 deletions tests/TestCase/Core/InstanceConfigTrait.php
@@ -0,0 +1,233 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since 3.0.0
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/
namespace Cake\Test\TestCase\Core;

use Cake\Core\InstanceConfigTrait;
use Cake\TestSuite\TestCase;

/**
* TestObject class
*/
class TestInstanceConfig {

use InstanceConfigTrait;

/**
* _defaultConfig
*
* Some default config
*
* @var mixed
*/
protected $_defaultConfig = [
'some' => 'string',
'a' => ['nested' => 'value']
];
}

/**
* InstanceConfigTraitTest
*
*/
class InstanceConfigTraitTest extends TestCase {

/**
* setUp method
*
* @return void
*/
public function setUp() {
parent::setUp();
$this->object = new TestInstanceConfig();
}

/**
* testDefaultsAreSet
*
* @return void
*/
public function testDefaultsAreSet() {
$this->assertSame(
[
'some' => 'string',
'a' => ['nested' => 'value']
],
$this->object->config(),
'runtime config should match the defaults if not overriden'
);
}

/**
* testGetSimple
*
* @return void
*/
public function testGetSimple() {
$this->assertSame(
'string',
$this->object->config('some'),
'should return the key value only'
);

$this->assertSame(
['nested' => 'value'],
$this->object->config('a'),
'should return the key value only'
);
}

/**
* testGetDot
*
* @return void
*/
public function testGetDot() {
$this->assertSame(
'value',
$this->object->config('a.nested'),
'should return the nested value only'
);
}

/**
* testSetSimple
*
* @return void
*/
public function testSetSimple() {
$this->object->config('foo', 'bar');
$this->assertSame(
'bar',
$this->object->config('foo'),
'should return the same value just set'
);

$this->object->config('some', 'zum');
$this->assertSame(
'zum',
$this->object->config('some'),
'should return the overritten value'
);

$this->assertSame(
[
'some' => 'zum',
'a' => ['nested' => 'value'],
'foo' => 'bar',
],
$this->object->config(),
'updates should be merged with existing config'
);
}

/**
* testSetNested
*
* @return void
*/
public function testSetNested() {
$this->object->config('new.foo', 'bar');
$this->assertSame(
'bar',
$this->object->config('new.foo'),
'should return the same value just set'
);

$this->object->config('a.nested', 'zum');
$this->assertSame(
'zum',
$this->object->config('a.nested'),
'should return the overritten value'
);

$this->assertSame(
[
'some' => 'string',
'a' => ['nested' => 'zum'],
'new' => ['foo' => 'bar']
],
$this->object->config(),
'updates should be merged with existing config'
);
}

/**
* testSetNested
*
* @return void
*/
public function testSetArray() {
$this->object->config(['foo' => 'bar']);
$this->assertSame(
'bar',
$this->object->config('foo'),
'should return the same value just set'
);

$this->assertSame(
[
'some' => 'string',
'a' => ['nested' => 'value'],
'foo' => 'bar',
],
$this->object->config(),
'updates should be merged with existing config'
);

$this->object->config(['new.foo' => 'bar']);
$this->assertSame(
'bar',
$this->object->config('new.foo'),
'should return the same value just set'
);

$this->assertSame(
[
'some' => 'string',
'a' => ['nested' => 'value'],
'foo' => 'bar',
'new' => ['foo' => 'bar']
],
$this->object->config(),
'updates should be merged with existing config'
);

$this->object->config(['multiple' => 'different', 'a.values.to' => 'set']);

$this->assertSame(
[
'some' => 'string',
'a' => ['nested' => 'value', 'values' => ['to' => 'set']],
'foo' => 'bar',
'new' => ['foo' => 'bar'],
'multiple' => 'different'
],
$this->object->config(),
'updates should be merged with existing config'
);
}

/**
* testSetClobber
*
* @expectedException \Exception
* @expectedExceptionMessage Cannot set a.nested.value
* @return void
*/
public function testSetClobber() {
$this->object->config(['a.nested.value' => 'not possible']);
}

}

0 comments on commit 474fdb3

Please sign in to comment.