Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Created plugin architecture for models

Updated README.txt and TODO
  • Loading branch information...
commit a79ecb1ad5afc4b5c810adce69e459453eca5deb 1 parent 0f87f19
@weierophinney authored
View
16 README.txt
@@ -18,12 +18,12 @@ INSTALLATION
installation (1.6.0RC1 or later, or current trunk) to library/Zend/
-- this is done to keep the tarball size down.
- Alternately, you can grab current trunk or the 1.6 release branch
+ Alternately, you can grab current trunk or the 1.7 release branch
from SVN using:
svn co http://framework.zend.com/svn/framework/standard/trunk/library/Zend
- svn co http://framework.zend.com/svn/framework/standard/branches/release-1.6/library/Zend
+ svn co http://framework.zend.com/svn/framework/standard/branches/release-1.7/library/Zend
4. If you are on Windows, rename the public/js-src directory to
public/js; on *nix, verify that public/js is a symlink to
@@ -34,10 +34,10 @@ INSTALLATION
chmod -R a+rwX <packagedir>/application/data
-6. Make the directory public/api/paste/v1/content world writeable; this can be
- accomplished on *nix systems using:
+6. Make the directory public/api/spindle/paste/content world writeable; this can
+ be accomplished on *nix systems using:
- chmod a+rwX <packagedir>/public/api/paste/v1/content
+ chmod a+rwX <packagedir>/public/api/spindle/paste/content
This will only affect you when you set the application environment to
"production", at which time artifacts will be written to the
@@ -75,10 +75,14 @@ This application shows off the following Dojo features:
* BorderContainer
* TabContainer
+ * ExpandoPane
+ * AccordionContainer
* dojox.Grid
* dojox.highlight
+ * dojo.back
* A variety of dijits: ValidationTextBox, SimpleTextarea,
and FilteringSelect
+ * dojo.xhr
* JSON-RPC
ZF specific features include:
@@ -95,7 +99,7 @@ CUSTOM DOJO BUILDS
For the adventurous, I have provided a profile for creating a custom
Dojo build for the pastebin application. You will need to copy the
public/js-src/paste directory and contents to your Dojo source
-installation, and then use the misc/paste.profile.js build profile to
+installation, and then use the misc/spindle.profile.js build profile to
create the build. Further instructions are in misc/README.txt.
View
15 TODO
@@ -3,16 +3,15 @@
- If available, redirect to JS-enabled version
- Add detection in active-grid action to use Zend_Paginator if JS is
disabled
- - Plugin system for Paste model
- - allow registering plugins statically and/or per instance
- - surrounding add() call
- - Allowing such things as:
- - sending updates to an IRC channel
- - sending updates to Growl or growl-like notifiers
- - triggering caching
- - triggering indexing
- spindle layer
- Add ability to update page title (hook this in pastebin module)
- Import bugapp into Spindle module
- Navbar
- links should load main content frame dynamically, w/o page refresh
+- Create an install script
+ - should set appropriate permissions
+ - data directory (world writeable)
+ - api/spindle/paste/content directory (world writeable)
+ - should optionally allow specifying directory containing ZF to symlink in
+ - should symlink js-src js
+ - should create dev database (if ZF is detected)
View
436 application/modules/spindle/models/Model.php
@@ -0,0 +1,436 @@
+<?php
+/**
+ * Base model
+ *
+ * Defines methods for setting options, retrieving resource loader, creating
+ * and manipulating plugin hooks, and registering and manipulating plugins.
+ *
+ * @package Spindle
+ * @subpackage Model
+ * @license New BSD {@link http://framework.zend.com/license/new-bsd}
+ * @version $Id: $
+ */
+abstract class Spindle_Model_Model
+{
+ /**
+ * @var array Class methods
+ */
+ protected $_classMethods;
+
+ /**
+ * @var array Registered hooks
+ */
+ protected $_hooks = array();
+
+ /**
+ * @var array registered plugins
+ */
+ protected $_plugins = array();
+
+ /**
+ * @var My_Controller_Helper_ResourceLoader
+ */
+ protected $_resourceLoader;
+
+ /**
+ * Constructor
+ *
+ * @param array|Zend_Config|null $options
+ * @return void
+ */
+ public function __construct($options = null)
+ {
+ if ($options instanceof Zend_Config) {
+ $options = $options->toArray();
+ }
+
+ if (is_array($options)) {
+ $this->setOptions($options);
+ }
+ }
+
+ /**
+ * Set options using setter methods
+ *
+ * @param array $options
+ * @return Spindle_Model_Paste
+ */
+ public function setOptions(array $options)
+ {
+ if (null === $this->_classMethods) {
+ $this->_classMethods = get_class_methods($this);
+ }
+ foreach ($options as $key => $value) {
+ $method = 'set' . ucfirst($key);
+ if (in_array($method, $this->_classMethods)) {
+ $this->$method($value);
+ }
+ }
+ return $this;
+ }
+
+ /**
+ * Set resource loader
+ *
+ * @param object $loader
+ * @return Spindle_Model_DbTable_Paste
+ */
+ public function setResourceLoader($loader)
+ {
+ if (!is_object($loader)) {
+ throw new Exception('Invalid resource loader provided to ' . __CLASS__);
+ }
+ $this->_resourceLoader = $loader;
+ return $this;
+ }
+
+ /**
+ * Retrieve resource loader
+ *
+ * @return object
+ */
+ public function getResourceLoader()
+ {
+ if (null === $this->_resourceLoader) {
+ $this->_resourceLoader = new My_Controller_Helper_ResourceLoader;
+ $this->_resourceLoader->initModule('spindle');
+ }
+ return $this->_resourceLoader;
+ }
+
+ /**
+ * Add a plugin hook
+ *
+ * @param string $hook
+ * @return Spindle_Model_Model
+ */
+ public function addHook($hook)
+ {
+ $hook = (string) $hook;
+ if (!$this->hasHook($hook)) {
+ $this->_hooks[] = $hook;
+ }
+ return $this;
+ }
+
+ /**
+ * Add multiple hooks
+ *
+ * @param array $hooks
+ * @return Spindle_Model_Model
+ */
+ public function addHooks(array $hooks)
+ {
+ foreach ($hooks as $hook) {
+ $this->addHook($hook);
+ }
+ return $this;
+ }
+
+ /**
+ * Overwrite all hooks
+ *
+ * @param array $hooks
+ * @return Spindle_Model_Model
+ */
+ public function setHooks(array $hooks)
+ {
+ $this->clearHooks();
+ return $this->addHooks($hooks);
+ }
+
+ /**
+ * Retrive all hooks
+ *
+ * @return array
+ */
+ public function getHooks()
+ {
+ return $this->_hooks;
+ }
+
+ /**
+ * Does the given hook exist?
+ *
+ * @param string $hook
+ * @return bool
+ */
+ public function hasHook($hook)
+ {
+ return in_array((string) $hook, $this->_hooks);
+ }
+
+ /**
+ * Remove a hook
+ *
+ * @param string $hook
+ * @return Spindle_Model_Model
+ */
+ public function removeHook($hook)
+ {
+ $hook = (string) $hook;
+ if ($this->hasHook($hook)) {
+ $index = array_search($hook, $this->_hooks);
+ unset($this->_hooks[$index]);
+ }
+ return $this;
+ }
+
+ /**
+ * Clear all hooks
+ *
+ * @return Spindle_Model_Model
+ */
+ public function clearHooks()
+ {
+ $this->_hooks = array();
+ return $this;
+ }
+
+ /**
+ * Add a plugin to a hook
+ *
+ * @param string|object $plugin
+ * @param array|string|null $hook
+ * @return Spindle_Model_Model
+ */
+ public function addPlugin($plugin, $hook = null)
+ {
+ if (is_string($plugin)) {
+ $plugin = $this->getResourceLoader()->load(ucfirst($plugin));
+ } elseif (!is_object($plugin)) {
+ require_once dirname(__FILE__) . '/Exception.php';
+ throw new Spindle_Model_Exception('Invalid plugin "' . var_export($plugin, 1) . '" specified');
+ }
+
+ if (null === $hook) {
+ $hooks = $this->getHooks();
+ } elseif (is_array($hook)) {
+ $hooks = $hook;
+ } elseif (is_string($hook) && $this->hasHook($hook)) {
+ $hooks = array($hook);
+ } else {
+ require_once dirname(__FILE__) . '/Exception.php';
+ throw new Spindle_Model_Exception('Invalid hook "' . $hook . '" specified');
+ }
+ foreach ($this->getHooks() as $hook) {
+ if (!isset($this->_plugins[$hook])) {
+ $this->_plugins[$hook] = array();
+ }
+ $pluginName = get_class($plugin);
+ $this->_plugins[$hook][$pluginName] = $plugin;
+ }
+ return $this;
+ }
+
+ /**
+ * Add multiple plugins at once.
+ *
+ * Each plugin should be an assoc array with the key "plugin" and
+ * optionally the key "hook".
+ *
+ * @param array $plugins
+ * @return Spindle_Model_Model
+ */
+ public function addPlugins(array $plugins)
+ {
+ foreach ($plugins as $spec) {
+ if (!is_array($spec)
+ || !array_key_exists('plugin', $spec)
+ ) {
+ require_once dirname(__FILE__) . '/Exception.php';
+ throw new Spindle_Model_Exception('Plugins passed to addPlugins must be arrays with key/value pairs for "plugin" and optionally "hook"');
+ }
+ $plugin = $spec['plugin'];
+ $hook = null;
+ if (!empty($spec['hook'])) {
+ $hook = $spec['hook'];
+ }
+ $this->addPlugin($plugin, $hook);
+ }
+ return $this;
+ }
+
+ /**
+ * Overwrite and set plugins
+ *
+ * See {@link addPlugins()} for format.
+ *
+ * @param array $plugins
+ * @return Spindle_Model_Model
+ */
+ public function setPlugins(array $plugins)
+ {
+ $this->clearPlugins();
+ return $this->addPlugins();
+ }
+
+ /**
+ * Return all plugins (optionally for the given hook or hooks)
+ *
+ * @param null|string|array $hook
+ * @return array
+ */
+ public function getPlugins($hook = null)
+ {
+ if (null === $hook) {
+ return $this->_plugins;
+ }
+
+ if (is_array($hook)) {
+ $plugins = array();
+ foreach ($hook as $h) {
+ if (!$this->hasHook($h)) {
+ continue;
+ }
+ if (isset($this->_plugins[$h])) {
+ $plugins[$h] = $this->_plugins[$h];
+ }
+ }
+ return $plugins;
+ }
+
+ if (is_string($hook) && $this->hasHook($hook)) {
+ if (isset($this->_plugins[$hook])) {
+ return $this->_plugins[$hook];
+ }
+ }
+
+ return array();
+ }
+
+ /**
+ * Retrieve a given plugin by hook
+ *
+ * @param string $plugin
+ * @param string $hook
+ * @return false|object
+ */
+ public function getPlugin($plugin, $hook)
+ {
+ $hook = (string) $hook;
+ if (!$this->hasHook($hook)) {
+ return false;
+ }
+ $plugin = ucfirst((string) $plugin);
+ $plugins = $this->getPlugins($hook);
+ foreach ($plugins as $name => $object) {
+ if ($name == $plugin) {
+ return $object;
+ }
+
+ $len = strlen($plugin);
+ if (($len < strlen($name))
+ && ($plugin == substr($name, -$len))
+ ) {
+ return $object;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Does the given hook (or any hook) have the given plugin?
+ *
+ * @param string $plugin
+ * @param null|string|array $hook
+ * @return bool
+ */
+ public function hasPlugin($plugin, $hook = null)
+ {
+ if (null === $hook) {
+ $hooks = $this->getHooks();
+ } elseif (is_array($hook)) {
+ $hooks = $hook;
+ } elseif (is_string($hook)) {
+ $hooks = array($hook);
+ } else {
+ require_once dirname(__FILE__) . '/Exception.php';
+ throw new Spindle_Model_Exception('Invalid hook "' . $hook . '" specified');
+ }
+
+ foreach ($hooks as $hook) {
+ if (false !== $this->getPlugin($plugin, $hook)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Remove a plugin by name and optionally hook(s)
+ *
+ * @param string $plugin
+ * @param null|string|array $hook
+ * @return Spindle_Model_Model
+ */
+ public function removePlugin($plugin, $hook = null)
+ {
+ if (null === $hook) {
+ $hooks = $this->getHooks();
+ } elseif (is_array($hook)) {
+ $hooks = $hook;
+ } elseif (is_string($hook)) {
+ $hooks = array($hook);
+ } else {
+ require_once dirname(__FILE__) . '/Exception.php';
+ throw new Spindle_Model_Exception('Invalid hook "' . $hook . '" specified');
+ }
+
+ foreach ($hooks as $hook) {
+ if (false !== ($object = $this->getPlugin($plugin, $hook))) {
+ $name = get_class($plugin);
+ unset($this->_plugins[$hook][$name]);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Clear plugins for a given hook, set of hooks, or all hooks
+ *
+ * @param null|string|array $hook
+ * @return Spindle_Model_Model
+ */
+ public function clearPlugins($hook = null)
+ {
+ if (null === $hook) {
+ $hooks = $this->getHooks();
+ } elseif (is_array($hook)) {
+ $hooks = $hook;
+ } elseif (is_string($hook)) {
+ $hooks = array($hook);
+ } else {
+ require_once dirname(__FILE__) . '/Exception.php';
+ throw new Spindle_Model_Exception('Invalid hook "' . $hook . '" specified');
+ }
+
+ foreach ($hooks as $hook) {
+ if (isset($this->_plugins[$hook])) {
+ unset($this->_plugins[$hook]);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Call a plugin hook
+ *
+ * @param string $hook
+ * @param array $args
+ * @return void
+ */
+ public function callHook($hook, array $args)
+ {
+ if (!$this->hasHook($hook)) {
+ return;
+ }
+
+ $plugins = $this->getPlugins($hook);
+ foreach ($plugins as $plugin) {
+ call_user_func_array(array($plugin, $hook), $args);
+ }
+ }
+}
View
78 application/modules/spindle/models/Paste.php
@@ -1,13 +1,18 @@
<?php
+
+/** Spindle_Model_Model */
+require_once dirname(__FILE__) . '/Model.php';
+
/**
* Pastebin model
*
+ * @uses Spindle_Model_Model
* @package Spindle
* @subpackage Model
* @license New BSD {@link http://framework.zend.com/license/new-bsd}
* @version $Id: $
*/
-class Spindle_Model_Paste
+class Spindle_Model_Paste extends Spindle_Model_Model
{
/**
* Table fields
@@ -28,79 +33,23 @@ class Spindle_Model_Paste
protected $_table;
/**
- * @var array Class methods
- */
- protected $_classMethods;
-
- /**
- * @var My_Controller_Helper_ResourceLoader
- */
- protected $_resourceLoader;
-
- /**
* Constructor
*
- * @param array|Zend_Config|null $options
+ * @param mixed $options
* @return void
*/
public function __construct($options = null)
{
+ $this->addHook('preAdd')
+ ->addHook('postAdd');
+
if ($options instanceof Zend_Config) {
$options = $options->toArray();
}
-
if (is_array($options)) {
$this->setOptions($options);
}
- }
-
- /**
- * Set options using setter methods
- *
- * @param array $options
- * @return Spindle_Model_Paste
- */
- public function setOptions(array $options)
- {
- if (null === $this->_classMethods) {
- $this->_classMethods = get_class_methods($this);
- }
- foreach ($options as $key => $value) {
- $method = 'set' . ucfirst($key);
- if (in_array($method, $this->_classMethods)) {
- $this->$method($value);
- }
- }
- return $this;
- }
- /**
- * Set resource loader
- *
- * @param object $loader
- * @return Spindle_Model_DbTable_Paste
- */
- public function setResourceLoader($loader)
- {
- if (!is_object($loader)) {
- throw new Exception('Invalid resource loader provided to ' . __CLASS__);
- }
- $this->_resourceLoader = $loader;
- return $this;
- }
-
- /**
- * Retrieve resource loader
- *
- * @return object
- */
- public function getResourceLoader()
- {
- if (null === $this->_resourceLoader) {
- $this->_resourceLoader = new My_Controller_Helper_ResourceLoader;
- $this->_resourceLoader->initModule('spindle');
- }
- return $this->_resourceLoader;
}
/**
@@ -111,6 +60,7 @@ public function getResourceLoader()
*/
public function add(array $data)
{
+ $this->callHook('preAdd', array($data));
$form = $this->getForm();
$belongTo = $form->getElementsBelongTo();
@@ -127,7 +77,11 @@ public function add(array $data)
$values = $values[$belongTo];
}
- return $this->getTable()->insert($values);
+ $id = $this->getTable()->insert($values);
+
+ $this->callHook('postAdd', array($id));
+
+ return $id;
}
/**
View
30 tests/models/Spindle/Model/PasteTest.php
@@ -11,6 +11,8 @@
/**
* Test class for Paste.
*
+ * @todo Test registering/manipulating plugins (need plugins, first)
+ *
* @group Spindle
* @group Paste
* @group Models
@@ -241,6 +243,34 @@ public function testFetchingActivePastesShouldAllowSorting()
rsort($users);
$this->assertEquals($users, $test);
}
+
+ /**
+ * @group hooks
+ */
+ public function testPasteModelShouldDefinePreAndPostAddHooks()
+ {
+ $this->assertTrue($this->model->hasHook('preAdd'));
+ $this->assertTrue($this->model->hasHook('postAdd'));
+ }
+
+ /**
+ * @group hooks
+ */
+ public function testModelShouldAllowOverwritingHooks()
+ {
+ $hooks = array('preNothing', 'postNothing');
+ $this->model->setHooks($hooks);
+ $this->assertSame($hooks, $this->model->getHooks());
+ }
+
+ /**
+ * @group hooks
+ */
+ public function testModelShouldAllowRemovingInvididualHooks()
+ {
+ $this->model->removeHook('postAdd');
+ $this->assertFalse($this->model->hasHook('postAdd'));
+ }
}
// Call Spindle_Model_PasteTest::main() if this source file is executed directly.
Please sign in to comment.
Something went wrong with that request. Please try again.