Refactor out some Interdependency between Ion and AnimeClient namespaces

This commit is contained in:
Timothy Warren 2016-07-27 13:18:52 -04:00
parent cc7046f0ec
commit 563adace2f
30 changed files with 473 additions and 338 deletions

View File

@ -41,7 +41,7 @@ return function(array $config_array = []) {
$container->set('config', $config);
// Create Cache Object
$container->set('cache', new CacheManager($container));
$container->set('cache', new CacheManager($config));
// Create Aura Router Object
$container->set('aura-router', new RouterContainer);
@ -71,9 +71,9 @@ return function(array $config_array = []) {
$container->set('session', $session);
// Miscellaneous helper methods
$anime_client = new AnimeClient();
$anime_client->setContainer($container);
$container->set('anime-client', $anime_client);
$util = new Util($container);
$container->set('anime-client', $util);
$container->set('util', $util);
// Models
$container->set('api-model', new Model\API($container));

View File

@ -50,8 +50,8 @@
<?= $helper->menu($menu_name) ?>
<br />
<ul>
<li class="<?= AnimeClient::is_not_selected('list', $urlGenerator->last_segment()) ?>"><a href="<?= $urlGenerator->url($route_path) ?>">Cover View</a></li>
<li class="<?= AnimeClient::is_selected('list', $urlGenerator->last_segment()) ?>"><a href="<?= $urlGenerator->url("{$route_path}/list") ?>">List View</a></li>
<li class="<?= Util::is_not_selected('list', $urlGenerator->last_segment()) ?>"><a href="<?= $urlGenerator->url($route_path) ?>">Cover View</a></li>
<li class="<?= Util::is_selected('list', $urlGenerator->last_segment()) ?>"><a href="<?= $urlGenerator->url("{$route_path}/list") ?>">List View</a></li>
</ul>
<?php endif ?>
</nav>

View File

@ -18,12 +18,11 @@ use Yosymfony\Toml\Toml;
define('SRC_DIR', realpath(__DIR__ . '/../../'));
/**
* Odds and Ends class
* Application constants
*/
class AnimeClient {
use \Aviat\Ion\Di\ContainerAware;
const HUMMINGBIRD_AUTH_URL = 'https://hummingbird.me/api/v1/users/authenticate';
const SESSION_SEGMENT = 'Aviat\AnimeClient\Auth';
const DEFAULT_CONTROLLER_NAMESPACE = 'Aviat\AnimeClient\Controller';
const DEFAULT_CONTROLLER = 'Aviat\AnimeClient\Controller\Anime';
@ -32,67 +31,6 @@ class AnimeClient {
const ERROR_MESSAGE_METHOD = 'error_page';
const SRC_DIR = SRC_DIR;
private static $form_pages = [
'edit',
'add',
'update',
'update_form',
'login',
'logout',
'details'
];
/**
* HTML selection helper function
*
* @param string $a - First item to compare
* @param string $b - Second item to compare
* @return string
*/
public static function is_selected($a, $b)
{
return ($a === $b) ? 'selected' : '';
}
/**
* Inverse of selected helper function
*
* @param string $a - First item to compare
* @param string $b - Second item to compare
* @return string
*/
public static function is_not_selected($a, $b)
{
return ($a !== $b) ? 'selected' : '';
}
/**
* Determine whether to show the sub-menu
*
* @return bool
*/
public function is_view_page()
{
$url = $this->container->get('request')
->getUri();
$page_segments = explode("/", $url);
$intersect = array_intersect($page_segments, self::$form_pages);
return empty($intersect);
}
/**
* Determine whether the page is a page with a form, and
* not suitable for redirection
*
* @return boolean
*/
public function is_form_page()
{
return ! $this->is_view_page();
}
/**
* Load configuration options from .toml files
*

View File

@ -15,7 +15,6 @@ namespace Aviat\AnimeClient\Auth;
use Aviat\Ion\Di\ContainerInterface;
use Aviat\AnimeClient\AnimeClient;
use Aviat\AnimeClient\Model\API;
/**
* Hummingbird API Authentication
@ -102,6 +101,5 @@ class HummingbirdAuth {
{
return $this->segment->get('auth_token', FALSE);
}
}
// End of HummingbirdAuth.php

View File

@ -51,7 +51,7 @@ class BaseCommand extends Command {
*/
protected function setupContainer()
{
$CONF_DIR = __DIR__ . '/../../../../app/config/';
$CONF_DIR = realpath(__DIR__ . '/../../../../app/config/');
require_once $CONF_DIR . '/base_config.php'; // $base_config
$config = AnimeClient::load_toml($CONF_DIR);
@ -65,7 +65,7 @@ class BaseCommand extends Command {
$container->set('config', $config);
// Create Cache Object
$container->set('cache', new CacheManager($container));
$container->set('cache', new CacheManager($config));
// Create session Object
$session = (new SessionFactory())->newInstance($_COOKIE);

View File

@ -13,12 +13,14 @@
namespace Aviat\AnimeClient;
use Aviat\Ion\ConfigInterface;
use InvalidArgumentException;
/**
* Wrapper for configuration values
*/
class Config {
class Config implements ConfigInterface {
use \Aviat\Ion\ArrayWrapper;

View File

@ -17,7 +17,6 @@ use Aura\Web\Response;
use Aviat\Ion\Di\ContainerInterface;
use Aviat\Ion\Friend;
use Aviat\AnimeClient\AnimeClient;
/**

View File

@ -19,7 +19,8 @@ use GuzzleHttp\Psr7\ResponseInterface;
use GuzzleHttp\Exception\ClientException;
use Aviat\Ion\Di\ContainerInterface;
use Aviat\AnimeClient\Model as BaseModel;
use Aviat\Ion\Model;
use Aviat\AnimeClient\AnimeClient;
/**
* Base model for api interaction
@ -32,7 +33,15 @@ use Aviat\AnimeClient\Model as BaseModel;
* @method ResponseInterface post(string $uri, array $options);
* @method ResponseInterface put(string $uri, array $options);
*/
class API extends BaseModel {
class API extends Model {
use \Aviat\Ion\Di\ContainerAware;
/**
* Config manager
* @var ConfigInterface
*/
protected $config;
/**
* Base url for making api requests
@ -65,7 +74,8 @@ class API extends BaseModel {
*/
public function __construct(ContainerInterface $container)
{
parent::__construct($container);
$this->container = $container;
$this->config = $container->get('config');
$this->cache = $container->get('cache');
$this->init();
}
@ -171,7 +181,7 @@ class API extends BaseModel {
*/
public function authenticate($username, $password)
{
$response = $this->post('https://hummingbird.me/api/v1/users/authenticate', [
$response = $this->post(AnimeClient::HUMMINGBIRD_AUTH_URL, [
'form_params' => [
'username' => $username,
'password' => $password

View File

@ -226,9 +226,10 @@ class Anime extends API {
$response = $this->get("users/{$username}/library", $config);
$output = $this->transform($status, $response);
$util = $this->container->get('util');
foreach ($output as &$row)
{
$row['anime']['image'] = $this->get_cached_image($row['anime']['image'], $row['anime']['slug'], 'anime');
$row['anime']['image'] = $util->get_cached_image($row['anime']['image'], $row['anime']['slug'], 'anime');
}
return $output;

View File

@ -36,7 +36,7 @@ class Collection extends DB {
* Create a new collection object
*
* @param ContainerInterface $container
* @return boolean
* @return void
*/
public function __construct(ContainerInterface $container)
{

View File

@ -13,12 +13,14 @@
namespace Aviat\AnimeClient\Model;
use Aviat\Ion\Di\ContainerInterface;
use Aviat\AnimeClient\Model as BaseModel;
use Aviat\Ion\Model;
/**
* Base model for database interaction
*/
class DB extends BaseModel {
class DB extends Model {
use \Aviat\Ion\Di\ContainerAware;
/**
* The query builder object
* @var object $db
@ -38,8 +40,8 @@ class DB extends BaseModel {
*/
public function __construct(ContainerInterface $container)
{
parent::__construct($container);
$this->db_config = (array)$this->config->get('database');
$this->db_config = $container->get('config')->get('database');
$this->setContainer($container);
}
}
// End of BaseDBModel.php
// End of DB.php

View File

@ -252,9 +252,11 @@ class Manga extends API {
self::COMPLETED => [],
];
$util = $this->container->get('util');
foreach ($data as &$entry)
{
$entry['manga']['image'] = $this->get_cached_image(
$entry['manga']['image'] = $util->get_cached_image(
$entry['manga']['image'],
$entry['manga']['slug'],
'manga'

View File

@ -13,38 +13,94 @@
namespace Aviat\AnimeClient;
use abeautifulsite\SimpleImage;
use Aviat\Ion\ConfigInterface;
use Aviat\Ion\Di\ContainerInterface;
/**
* Common base for all Models
* Utility method class
*/
class Model {
class Util {
use \Aviat\Ion\StringWrapper;
use \Aviat\Ion\Di\ContainerAware;
private static $form_pages = [
'edit',
'add',
'update',
'update_form',
'login',
'logout',
'details'
];
/**
* The global configuration object
* @var Config
* The config manager
* @var ConfigInterface
*/
protected $config;
private $config;
/**
* The container object
* @var ContainerInterface
*/
protected $container;
/**
* Constructor
* Set up the Util class
*
* @param ContainerInterface $container
*/
public function __construct(ContainerInterface $container)
{
$this->container = $container;
$this->setContainer($container);
$this->config = $container->get('config');
}
/**
* HTML selection helper function
*
* @param string $a - First item to compare
* @param string $b - Second item to compare
* @return string
*/
public static function is_selected($a, $b)
{
return ($a === $b) ? 'selected' : '';
}
/**
* Inverse of selected helper function
*
* @param string $a - First item to compare
* @param string $b - Second item to compare
* @return string
*/
public static function is_not_selected($a, $b)
{
return ($a !== $b) ? 'selected' : '';
}
/**
* Determine whether to show the sub-menu
*
* @return bool
*/
public function is_view_page()
{
$url = $this->container->get('request')
->getUri();
$page_segments = explode("/", $url);
$intersect = array_intersect($page_segments, self::$form_pages);
return empty($intersect);
}
/**
* Determine whether the page is a page with a form, and
* not suitable for redirection
*
* @return boolean
*/
public function is_form_page()
{
return ! $this->is_view_page();
}
/**
* Get the path of the cached version of the image. Create the cached image
* if the file does not already exist
@ -100,7 +156,7 @@ class Model {
}
else
{
throw new DomainException("Couldn't cache images because they couldn't be downloaded.");
throw new \DomainException("Couldn't cache images because they couldn't be downloaded.");
}
// Resize the image
@ -137,4 +193,3 @@ class Model {
}
}
}
// End of BaseModel.php

View File

@ -12,7 +12,7 @@
namespace Aviat\Ion\Cache;
use \Aviat\Ion\Di\ContainerInterface;
use Aviat\Ion\ConfigInterface;
/**
* Class proxying cached and fresh values from the selected cache driver
@ -25,11 +25,12 @@ class CacheManager implements CacheInterface {
protected $driver;
/**
* Retreive the appropriate driver from the container
* Retrieve the appropriate driver from the container
*
* @param ConfigInterface $config The configuration management class
*/
public function __construct(ContainerInterface $container)
public function __construct(ConfigInterface $config)
{
$config = $container->get('config');
$driverConf = $config->get('cache_driver');
if (empty($driverConf))
@ -38,13 +39,13 @@ class CacheManager implements CacheInterface {
}
$driverClass = __NAMESPACE__ . "\\Driver\\{$driverConf}";
$driver = new $driverClass($container);
$driver = new $driverClass($config);
$this->driver = $driver;
}
/**
* Retreive a cached value if it exists, otherwise, get the value
* Retrieve a cached value if it exists, otherwise, get the value
* from the passed arguments
*
* @param object $object - object to retrieve fresh value from
@ -68,7 +69,7 @@ class CacheManager implements CacheInterface {
}
/**
* Retreive a fresh value from the method, and update the cache
* Retrieve a fresh value from the method, and update the cache
* @param object $object - object to retrieve fresh value from
* @param string $method - method name to call
* @param [array] $args - the arguments to pass to the retrieval method

View File

@ -12,12 +12,13 @@
namespace Aviat\Ion\Cache\Driver;
use Aviat\Ion\Di\ContainerInterface;
use Aviat\Ion\ConfigInterface;
use Aviat\Ion\Cache\CacheDriverInterface;
/**
* The Driver for no real cache
*/
class NullDriver implements \Aviat\Ion\Cache\CacheDriverInterface {
class NullDriver implements CacheDriverInterface {
/**
* 'Cache' for Null data store
@ -25,9 +26,11 @@ class NullDriver implements \Aviat\Ion\Cache\CacheDriverInterface {
protected $data;
/**
* Create the Redis cache driver
* Create the Null cache driver
*
* @param ConfigInterface $config The configuration management class
*/
public function __construct(ContainerInterface $container)
public function __construct(ConfigInterface $config)
{
$this->data = [];
}

View File

@ -12,7 +12,7 @@
namespace Aviat\Ion\Cache\Driver;
use Aviat\Ion\Di\ContainerInterface;
use Aviat\Ion\ConfigInterface;
use Aviat\Ion\Cache\CacheDriverInterface;
use Predis\Client;
@ -20,17 +20,19 @@ use Predis\Client;
class RedisDriver implements CacheDriverInterface {
/**
* The redis extension class instance
* @var Redis
* THe Predis library instance
*
* @var Client
*/
protected $redis;
/**
* Create the Redis cache driver
*
* @param ConfigInterface $config The configuration management class
*/
public function __construct(ContainerInterface $container)
public function __construct(ConfigInterface $config)
{
$config = $container->get('config');
$redisConfig = $config->get('redis');
if (array_key_exists('password', $redisConfig) && $redisConfig['password'] === '')
@ -50,7 +52,7 @@ class RedisDriver implements CacheDriverInterface {
}
/**
* Retreive a value from the cache backend
* Retrieve a value from the cache backend
*
* @param string $key
* @return mixed

View File

@ -12,9 +12,9 @@
namespace Aviat\Ion\Cache\Driver;
use Aviat\Ion\Di\ContainerInterface;
use Aviat\Ion\ConfigInterface;
use Aviat\Ion\Cache\CacheDriverInterface;
use Aviat\AnimeClient\Model\DB;
use Aviat\Ion\Model\DB;
/**
* Driver for caching via a traditional SQL database
@ -29,15 +29,17 @@ class SQLDriver extends DB implements CacheDriverInterface {
/**
* Create the driver object
*
* @param ConfigInterface $config
*/
public function __construct(ContainerInterface $container)
public function __construct(ConfigInterface $config)
{
parent::__construct($container);
parent::__construct($config);
$this->db = \Query($this->db_config['collection']);
}
/**
* Retreive a value from the cache backend
* Retrieve a value from the cache backend
*
* @param string $key
* @return mixed

View File

@ -0,0 +1,41 @@
<?php
/**
* Ion
*
* Building blocks for web development
*
* @package Ion
* @author Timothy J. Warren
* @copyright Copyright (c) 2015 - 2016
* @license MIT
*/
namespace Aviat\Ion;
interface ConfigInterface {
/**
* Get a config value
*
* @param array|string $key
* @return mixed
*/
public function get($key);
/**
* Set a config value
*
* @param integer|string|array $key
* @param mixed $value
* @throws \InvalidArgumentException
* @return ConfigInterface
*/
public function set($key, $value);
/**
* Remove a config value
*
* @param string|array $key
* @return void
*/
public function delete($key);
}

21
src/Aviat/Ion/Model.php Normal file
View File

@ -0,0 +1,21 @@
<?php
/**
* Ion
*
* Building blocks for web development
*
* @package Ion
* @author Timothy J. Warren
* @copyright Copyright (c) 2015 - 2016
* @license MIT
*/
namespace Aviat\Ion;
/**
* Common base for all Models
*/
class Model {
use StringWrapper;
}
// End of Model.php

View File

@ -0,0 +1,51 @@
<?php
/**
* Ion
*
* Building blocks for web development
*
* @package Ion
* @author Timothy J. Warren
* @copyright Copyright (c) 2015 - 2016
* @license MIT
*/
namespace Aviat\Ion\Model;
use Aviat\Ion\ConfigInterface;
use Aviat\Ion\Model as BaseModel;
/**
* Base model for database interaction
*/
class DB extends BaseModel {
/**
* The query builder object
* @var object $db
*/
protected $db;
/**
* The config manager
* @var ConfigInterface
*/
protected $config;
/**
* The database connection information array
* @var array $db_config
*/
protected $db_config;
/**
* Constructor
*
* @param ConfigInterface $config
*/
public function __construct(ConfigInterface $config)
{
$this->config = $config;
$this->db_config = (array)$config->get('database');
}
}
// End of DB.php

View File

@ -5,13 +5,6 @@ use Aviat\AnimeClient\AnimeClient;
class AnimeClientTest extends AnimeClient_TestCase {
public function setUp()
{
parent::setUp();
$this->anime_client = new AnimeClient();
$this->anime_client->setContainer($this->container);
}
/**
* Basic sanity test for _dir function
*/
@ -19,70 +12,4 @@ class AnimeClientTest extends AnimeClient_TestCase {
{
$this->assertEquals('foo' . DIRECTORY_SEPARATOR . 'bar', \_dir('foo', 'bar'));
}
public function testIsSelected()
{
// Failure to match
$this->assertEquals('', AnimeClient::is_selected('foo', 'bar'));
// Matches
$this->assertEquals('selected', AnimeClient::is_selected('foo', 'foo'));
}
public function testIsNotSelected()
{
// Failure to match
$this->assertEquals('selected', AnimeClient::is_not_selected('foo', 'bar'));
// Matches
$this->assertEquals('', AnimeClient::is_not_selected('foo', 'foo'));
}
public function dataIsViewPage()
{
return [
[
'uri' => '/anime/update',
'expected' => FALSE
],
[
'uri' => '/anime/watching',
'expected' => TRUE
],
[
'uri' => '/manga/reading',
'expected' => TRUE
],
[
'uri' => '/manga/update',
'expected' => FALSE
]
];
}
/**
* @dataProvider dataIsViewPage
*/
public function testIsViewPage($uri, $expected)
{
$this->setSuperGlobals([
'_SERVER' => [
'REQUEST_URI' => $uri
]
]);
$this->assertEquals($expected, $this->anime_client->is_view_page());
}
/**
* @dataProvider dataIsViewPage
*/
public function testIsFormPage($uri, $expected)
{
$this->setSuperGlobals([
'_SERVER' => [
'REQUEST_URI' => $uri
]
]);
$this->assertEquals(!$expected, $this->anime_client->is_form_page());
}
}

View File

@ -12,6 +12,7 @@ class MangaModelTest extends AnimeClient_TestCase {
public function setUp()
{
parent::setUp();
$this->container->set('util', new MockUtil($this->container));
$this->model = new Friend(new TestMangaModel($this->container));
$this->mockDir = __DIR__ . '/../../test_data/manga_list';
}

View File

@ -0,0 +1,78 @@
<?php
use Aviat\AnimeClient\Util;
class UtilTest extends AnimeClient_TestCase {
public function setUp()
{
parent::setUp();
$this->util = new Util($this->container);
}
public function testIsSelected()
{
// Failure to match
$this->assertEquals('', Util::is_selected('foo', 'bar'));
// Matches
$this->assertEquals('selected', Util::is_selected('foo', 'foo'));
}
public function testIsNotSelected()
{
// Failure to match
$this->assertEquals('selected', Util::is_not_selected('foo', 'bar'));
// Matches
$this->assertEquals('', Util::is_not_selected('foo', 'foo'));
}
public function dataIsViewPage()
{
return [
[
'uri' => '/anime/update',
'expected' => FALSE
],
[
'uri' => '/anime/watching',
'expected' => TRUE
],
[
'uri' => '/manga/reading',
'expected' => TRUE
],
[
'uri' => '/manga/update',
'expected' => FALSE
]
];
}
/**
* @dataProvider dataIsViewPage
*/
public function testIsViewPage($uri, $expected)
{
$this->setSuperGlobals([
'_SERVER' => [
'REQUEST_URI' => $uri
]
]);
$this->assertEquals($expected, $this->util->is_view_page());
}
/**
* @dataProvider dataIsViewPage
*/
public function testIsFormPage($uri, $expected)
{
$this->setSuperGlobals([
'_SERVER' => [
'REQUEST_URI' => $uri
]
]);
$this->assertEquals(!$expected, $this->util->is_form_page());
}
}

View File

@ -1,12 +1,12 @@
<?php
use Aviat\AnimeClient\Model as BaseModel;
use Aviat\Ion\Model as BaseModel;
class BaseModelTest extends AnimeClient_TestCase {
public function testBaseModelSanity()
{
$baseModel = new BaseModel($this->container);
$baseModel = new BaseModel();
$this->assertTrue(is_object($baseModel));
}
}

View File

@ -15,7 +15,7 @@ class CacheManagerText extends AnimeClient_TestCase {
public function setUp()
{
parent::setUp();
$this->cache = new CacheManager($this->container);
$this->cache = new CacheManager($this->container->get('config'), $this->container);
$this->friend = new Friend($this->cache);
}

View File

@ -12,6 +12,6 @@ class CacheNullDriverTest extends AnimeClient_TestCase {
public function setUp()
{
parent::setUp();
$this->driver = new NullDriver($this->container);
$this->driver = new NullDriver($this->container->get('config'));
}
}

View File

@ -16,16 +16,15 @@ class CacheRedisDriverTestTwo extends AnimeClient_TestCase {
parent::setUp();
// Setup config with port and password
$container = new Container();
$container->set('config', new Config([
$config = new Config([
'redis' => [
'host' => (array_key_exists('REDIS_HOST', $_ENV)) ? $_ENV['REDIS_HOST'] : 'localhost',
'port' => 6379,
'password' => '',
'database' => 13,
]
]));
$this->driver = new RedisDriver($container);
]);
$this->driver = new RedisDriver($config);
}
public function tearDown()

View File

@ -13,7 +13,7 @@ class CacheRedisDriverTest extends AnimeClient_TestCase {
{
parent::setUp();
$this->driver = new RedisDriver($this->container);
$this->driver = new RedisDriver($this->container->get('config'));
}
public function tearDown()

View File

@ -13,7 +13,7 @@ class CacheSQLDriverTest extends AnimeClient_TestCase {
public function setUp()
{
parent::setUp();
$this->driver = new SQLDriver($this->container);
$this->driver = new SQLDriver($this->container->get('config'));
$friend = new Friend($this->driver);
$friend->db->query('CREATE TABLE IF NOT EXISTS "cache" ("key" TEXT NULL, "value" TEXT NULL, PRIMARY KEY ("key"))');
}

View File

@ -99,6 +99,13 @@ trait MockViewOutputTrait {
}
}
class MockUtil {
public function get_cached_image($api_path, $series_slug, $type = "anime")
{
return "/public/images/{$type}/{$series_slug}.jpg";
}
}
class TestView extends View {
public function send() {}
protected function output()
@ -157,11 +164,6 @@ class TestAnimeModel extends AnimeModel {
class TestMangaModel extends MangaModel {
use MockInjectionTrait;
public function get_cached_image($api_path, $series_slug, $type = "anime")
{
return "/public/images/{$type}/{$series_slug}.jpg";
}
protected function _check_cache($response)
{
$file = __DIR__ . '/test_data/manga_list/manga-transformed.json';