jenkins-bot has submitted this change and it was merged.

Change subject: \SMW\ObservableDispatcher dispatches state changes from a 
source to an Observer
......................................................................


\SMW\ObservableDispatcher dispatches state changes from a source to an Observer

Code coverage: 100%
CRAP: 2

Instead of using the inheritance model or implementing an Observable interface
to communicate with attached Observers, the ObservableDispatcher can be used
as intermediary transmitter to notify Observers. The dispatcher inherits
all methods necessary (ObservableSubject) to communicate with an Observer,
and freeing the emitter of the state change from being an ObservableSubject
itself.

This also allows an Observer to become a DispatchableSource which can
inform other Observers independently from its own status.

## Example
Previous to this change PropertyChangeNotifier was a ObservableSubject which
would communicate/initiate direct communiction with Observers, now
that PropertyChangeNotifier is injected with an ObservableDispatcher it
does no longer need to establish any link rather expects state changes to
be dispatchd through ObservableSubjectDispatcher to those Observers that
are registered with it (in this case with ChangeObserver()).

- Before
$changeNotifier = new PropertyChangeNotifier( ... );
$changeNotifier->attach( new ChangeObserver() );

- After
$changeNotifier = new PropertyChangeNotifier( ... );
$changeNotifier->setDispatcher( new ObservableSubjectDispatcher( new 
ChangeObserver() ) );

Doing so avoids PropertyChangeNotifier being directly linked to the
Observer/Subject pattern and instead using a setter injection to establish
a connection such as:

SMW\ParserData->updateStore( )
SMW\PropertyChangeNotifier->detectChanges( )
SMW\PropertyChangeNotifier->compareConversionFactors( )
SMW\PropertyChangeNotifier->addDispatchJob( )
SMW\ObservableSubject->setState( )
SMW\ObservableSubject->notify( )
SMW\Observer->update( )
SMW\ChangeObserver->runUpdateDispatcher( )

Change-Id: I64c156006e9340aba57aba6b86d3b3c93a4b5c82
---
M docs/doxygen.group.definitions.php
M includes/BasePropertyAnnotator.php
A includes/ObservableSubject.php
A includes/ObservableSubjectDispatcher.php
M includes/Observer.php
M includes/ParserData.php
M includes/PropertyChangeNotifier.php
D includes/Publisher.php
M includes/Setup.php
D includes/Subject.php
D includes/Subscriber.php
A tests/phpunit/includes/ObservableSubjectDispatcherTest.php
M tests/phpunit/includes/ObserverTest.php
M tests/phpunit/includes/PropertyChangeNotifierTest.php
14 files changed, 693 insertions(+), 337 deletions(-)

Approvals:
  Mwjames: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/docs/doxygen.group.definitions.php 
b/docs/doxygen.group.definitions.php
index 0aa00f4..16b1dfb 100644
--- a/docs/doxygen.group.definitions.php
+++ b/docs/doxygen.group.definitions.php
@@ -10,102 +10,9 @@
  */
 
 /**
- * This group contains members that are related to message, error, and 
parameter
- * formatting
- *
- * @defgroup Formatter Formatter
- * @ingroup SMW
- */
-
-/**
- * This group contains members that are related to jobs
- *
- * @defgroup Job Job
- * @ingroup SMW
- */
-
-/**
- * This group contains members that are related to special pages
- *
- * @see https://www.mediawiki.org/wiki/Manual:Special_pages
- *
- * @defgroup SpecialPage SpecialPage
- * @ingroup SMW
- */
-
-/**
- * This group contains members that are related to hooks, hook events, and
- * hook registrations
- *
- * @see https://www.mediawiki.org/wiki/Manual:Hooks
- * @see https://semantic-mediawiki.org/wiki/Hooks‎
- *
- * @defgroup Hook Hook
- * @ingroup SMW
- */
-
-/**
- * This group contains members that are related to accessors
- *
- * @defgroup Accessor Accessor
- * @ingroup SMW
- */
-
-/**
- * This group contains members that are related to parser hooks and functions
- *
- * @defgroup ParserFunction ParserFunction
- * @ingroup SMW
- */
-
-/**
- * This group contains members that are related to unit tests
- *
- * @defgroup Test Test
- * @ingroup SMW
- */
-
-/**
- * This group contains members that are related to the QueryPrinter unit tests
- *
- * @defgroup QueryPrinterTest QueryPrinterTest
- * @ingroup Test
- */
-
-/**
- * This group contains members that are related to the Store unit tests
- *
- * @defgroup StoreTest StoreTest
- * @ingroup Test
- */
-
-/**
- * This group contains members that are related to the SQLStore unit tests
- *
- * @defgroup SQLStoreTest SQLStoreTest
- * @ingroup StoreTest
- */
-
-/**
  * This group contains members that are related to the Semantic MediaWiki Api
  *
  * @defgroup Api Api
- * @ingroup SMW
- */
-
-/**
- * This group contains members that are related to handler classes (including
- * object handler, hooks handler, instance handler etc.)
- *
- * @defgroup Handler Handler
- * @ingroup SMW
- */
-
-/**
- * This group contains members that are related to utility classes and support
- * functions
- *
- * @defgroup Utility Utility
  * @ingroup SMW
  */
 
@@ -128,4 +35,120 @@
  *
  * @defgroup Collector Collector
  * @ingroup Store
- */
\ No newline at end of file
+ */
+
+/**
+ * This group contains members that are related to message, error, and 
parameter
+ * formatting
+ *
+ * @defgroup Formatter Formatter
+ * @ingroup SMW
+ */
+
+/**
+ * This group contains members that are related to hooks, hook events, and
+ * hook registrations
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks
+ * @see https://semantic-mediawiki.org/wiki/Hooks‎
+ *
+ * @defgroup Hook Hook
+ * @ingroup SMW
+ */
+
+/**
+ * This group contains members that are related to parser hooks and functions
+ *
+ * @defgroup ParserFunction ParserFunction
+ * @ingroup SMW
+ */
+
+/**
+ * This group contains members that are related to special pages
+ *
+ * @see https://www.mediawiki.org/wiki/Manual:Special_pages
+ *
+ * @defgroup SpecialPage SpecialPage
+ * @ingroup SMW
+ */
+
+/**
+ * This group contains members that are related to jobs
+ *
+ * @defgroup Job Job
+ * @ingroup SMW
+ */
+
+/**
+ * This group contains members that are related to maintenance scripts
+ *
+ * @defgroup Maintenance Maintenance
+ * @ingroup SMW
+ */
+
+/**
+ * This group contains members that are related to utility classes and support
+ * functions
+ *
+ * @defgroup Utility Utility
+ * @ingroup SMW
+ */
+
+/**
+ * This group contains members that are related to supporting the Observer
+ *
+ * @defgroup Observer Observer
+ * @ingroup Utility
+ */
+
+/**
+ * This group contains members that are related to handler classes (including
+ * object handler, hooks handler, instance handler etc.)
+ *
+ * @defgroup Handler Handler
+ * @ingroup Utility
+ */
+
+/**
+ * This group contains members that are related to accessors
+ *
+ * @defgroup Accessor Accessor
+ * @ingroup Utility
+ */
+
+
+/**
+ * This group contains members that are related to unit tests
+ *
+ * @defgroup Test Test
+ * @ingroup SMW
+ */
+
+/**
+ * This group contains members that are related to the QueryPrinter unit tests
+ *
+ * @defgroup QueryPrinterTest QueryPrinterTest
+ * @ingroup Test
+ */
+
+/**
+ * This group contains members that are related to the maintenance script unit
+ * tests
+ *
+ * @defgroup MaintenanceTest MaintenanceTest
+ * @ingroup Test
+ */
+
+/**
+ * This group contains members that are related to the Store unit tests
+ *
+ * @defgroup StoreTest StoreTest
+ * @ingroup Test
+ */
+
+/**
+ * This group contains members that are related to the SQLStore unit tests
+ *
+ * @defgroup SQLStoreTest SQLStoreTest
+ * @ingroup StoreTest
+ */
diff --git a/includes/BasePropertyAnnotator.php 
b/includes/BasePropertyAnnotator.php
index 9c12be4..fc0d5de 100644
--- a/includes/BasePropertyAnnotator.php
+++ b/includes/BasePropertyAnnotator.php
@@ -28,7 +28,7 @@
  *
  * @ingroup Annotator
  */
-class BasePropertyAnnotator extends Subject {
+class BasePropertyAnnotator extends ObservableSubject {
 
        /** @var SemanticData */
        protected $semanticData;
diff --git a/includes/ObservableSubject.php b/includes/ObservableSubject.php
new file mode 100644
index 0000000..1ef9e4c
--- /dev/null
+++ b/includes/ObservableSubject.php
@@ -0,0 +1,222 @@
+<?php
+
+namespace SMW;
+
+/**
+ * Interface and abstract class defining the operations for
+ * attaching and de-attaching observers
+ *
+ * @file
+ *
+ * @license GNU GPL v2+
+ * @since   1.9
+ *
+ * @author mwjames
+ */
+
+/**
+ * Base Publisher interface
+ *
+ * @ingroup Observer
+ */
+interface Publisher {
+
+       /**
+        * Attaches a Subscriber
+        *
+        * @since  1.9
+        *
+        * @param Subscriber $subscriber
+        */
+       public function attach( Subscriber $subscriber );
+
+       /**
+        * Detaches a Subscriber
+        *
+        * @since  1.9
+        *
+        * @param Subscriber $subscriber
+        */
+       public function detach( Subscriber $subscriber );
+
+       /**
+        * Notifies attached Subscribers
+        *
+        * @since  1.9
+        */
+       public function notify();
+
+}
+
+/**
+ * Extended Publisher interface specifying access to
+ * source and state changes
+ *
+ * @ingroup Observer
+ */
+interface Observable extends Publisher {
+
+       /**
+        * Returns the invoked state change
+        *
+        * @since 1.9
+        */
+       public function getState();
+
+       /**
+        * Registers a state change
+        *
+        * @since 1.9
+        */
+       public function setState( $state );
+
+       /**
+        * Returns the emitter of the state change
+        *
+        * @since 1.9
+        */
+       public function getSource();
+
+}
+
+/**
+ * Implements the Pubisher/Observable interface as base class
+ *
+ * @note In the GOF book this class/interface is known as Subject
+ *
+ * @note We will avoid referring to it as Subject otherwise it could be
+ * mistaken with Semantic MediaWiki's own "Subject" (DIWikiPage) object
+ *
+ * @ingroup Observer
+ */
+abstract class ObservableSubject implements Observable {
+
+       /** @var Subscriber[] */
+       protected $observers = array();
+
+       /** string */
+       protected $state = null;
+
+       /**
+        * @since  1.9
+        *
+        * @param Subscriber|null $subject
+        */
+       public function __construct( Subscriber $observer = null ) {
+               if ( $observer instanceof Subscriber ) {
+                       $this->attach( $observer );
+               }
+       }
+
+       /**
+        * @see Observable::attach
+        *
+        * @since 1.9
+        *
+        * @param Subscriber $observer
+        */
+       public function attach( Subscriber $observer ) {
+
+               if ( $this->contains( $observer ) === null ) {
+                       $this->observers[] = $observer;
+               }
+
+               return $this;
+       }
+
+       /**
+        * @see Observable::attach
+        *
+        * @since  1.9
+        *
+        * @param Subscriber $observer
+        */
+       public function detach( Subscriber $observer ) {
+
+               $index = $this->contains( $observer );
+
+               if ( $index !== null ) {
+                       unset( $this->observers[$index] );
+               }
+
+               return $this;
+       }
+
+       /**
+        * @see Observable::getState
+        *
+        * @since 1.9
+        *
+        * @return string
+        */
+       public function getState() {
+               return $this->state;
+       }
+
+       /**
+        * Set a state variable state and initiates to notify
+        * the attached Subscribers (Observers)
+        *
+        * @since 1.9
+        *
+        * @param string $state
+        */
+       public function setState( $state ) {
+               $this->state = $state;
+               $this->notify();
+       }
+
+       /**
+        * @see Observable::getSource
+        *
+        * @since 1.9
+        *
+        * @return Observable
+        */
+       public function getSource() {
+               return $this;
+       }
+
+       /**
+        * Notifies the updater of all invoked Subscribers
+        *
+        * @since  1.9
+        */
+       public function notify() {
+               foreach ( $this->observers as $observer ) {
+                       $observer->update( $this );
+               }
+       }
+
+       /**
+        * Returns registered Observers
+        *
+        * @since 1.9
+        *
+        * @return array
+        */
+       public function getObservers() {
+               return $this->observers;
+       }
+
+       /**
+        * Returns an index (or null) of a registered Observer
+        *
+        * @since  1.9
+        *
+        * @param Subscriber $observer
+        *
+        * @return integer|null
+        */
+       private function contains( Subscriber $observer ) {
+
+               foreach ( $this->observers as $key => $obs ) {
+                       if ( $obs === $observer ) {
+                               return $key;
+                       }
+               }
+
+               return null;
+       }
+
+}
diff --git a/includes/ObservableSubjectDispatcher.php 
b/includes/ObservableSubjectDispatcher.php
new file mode 100644
index 0000000..f8e5d29
--- /dev/null
+++ b/includes/ObservableSubjectDispatcher.php
@@ -0,0 +1,104 @@
+<?php
+
+namespace SMW;
+
+/**
+ * Dispatches notifification (state changes) from a client to registered
+ * Observers
+ *
+ * @file
+ *
+ * @license GNU GPL v2+
+ * @since   1.9
+ *
+ * @author mwjames
+ */
+
+/**
+ * Interface describing a source that is dispatchable
+ *
+ * @ingroup Observer
+ */
+interface DispatchableSource {
+
+       /**
+        * Frowards requests fo
+        *
+        * @since  1.9
+        *
+        * @param Dispatchable $dispatcher
+        */
+       public function setDispatcher( ObservableDispatcher $dispatcher );
+
+}
+
+/**
+ * Extends the Observable interface to forward the source
+ *
+ * This ObservableDispatcher enables the emitter (client) of an event not being
+ * directly linked to an Observer, freeing it from implementing methods to
+ * communicate with it while maintaining capabability to transmitt state 
changes
+ * to Observers that are registered with this dispatcher.
+ *
+ * @ingroup Observer
+ */
+interface ObservableDispatcher extends Observable {
+
+       /**
+        * Specifies the source from which events are transmitted
+        *
+        * @since  1.9
+        *
+        * @param mixed $source
+        */
+       public function setSource( $source );
+
+}
+
+/**
+ * Implementation of the ObservableDispatcher
+ *
+ * ObservableDispatcher inherits all methods from ObservableSubject in order to
+ * communicate with its Observers.
+ *
+ * @par Example:
+ * @code
+ *  $changeNotifier = new PropertyChangeNotifier( ... );
+ *  $changeNotifier->setDispatcher( new ObservableSubjectDispatcher( new 
ChangeObserver() ) );
+ * @endcode
+ *
+ * @ingroup Observer
+ */
+class ObservableSubjectDispatcher extends ObservableSubject implements 
ObservableDispatcher {
+
+       /** @var mixed */
+       protected $source = null;
+
+       /**
+        * Registeres the DispatchableSource
+        *
+        * @since 1.9
+        *
+        * @param $source
+        *
+        * @return ObservableSubjectDispatcher
+        */
+       public function setSource( $source ) {
+               $this->source = $source;
+               return $this;
+       }
+
+       /**
+        * Overrides ObservableSubject::getSource to ensure that the emitting 
client
+        * is identified as source and not the ObservableDispatcher, in order 
for
+        * the Observer to act on the clients behalf
+        *
+        * @since 1.9
+        *
+        * @return mixed
+        */
+       public function getSource() {
+               return $this->source;
+       }
+
+}
diff --git a/includes/Observer.php b/includes/Observer.php
index 9f0d0ac..90b3617 100644
--- a/includes/Observer.php
+++ b/includes/Observer.php
@@ -3,10 +3,8 @@
 namespace SMW;
 
 /**
- * Contains interfaces and implementation classes to
- * enable a Observer-Subject (or Publisher-Subcriber) pattern where
- * objects can indepentanly be notfied about a state change and initiate
- * an update of its registered Publisher
+ * Specifies interface and abstract class for operations that are used to 
execute
+ * registered operations
  *
  * @file
  *
@@ -17,8 +15,26 @@
  */
 
 /**
- * Implement the Subsriber interface resutling in an Observer base class
- * that accomodates necessary methods to update an invoked publisher
+ * Interface describing a Subsriber
+ *
+ * @ingroup Observer
+ */
+interface Subscriber {
+
+       /**
+        * Receive update from a publishable source
+        *
+        * @since  1.9
+        *
+        * @param Observable $source
+        */
+       public function update( Observable $source );
+
+}
+
+/**
+ * Implements the Subsriber interface resutling in an Observer base class
+ * that accomodates necessary methods to operate according the invoked state
  *
  * @ingroup Observer
  */
@@ -27,26 +43,31 @@
        /**
         * @since  1.9
         *
-        * @param Publisher|null $subject
+        * @param Observable|null $subject
         */
-       public function __construct( Publisher $subject = null ) {
-               if ( $subject instanceof Publisher ) {
+       public function __construct( Observable $subject = null ) {
+
+               if ( $subject instanceof Observable ) {
                        $subject->attach( $this );
                }
+
        }
 
        /**
-        * Update handling of an invoked publisher by relying
-        * on the state object to carry out the task
+        * Operates according the invoked state and source
         *
         * @since 1.9
         *
-        * @param Publisher|null $subject
+        * @param Observable|null $subject
         */
-       public function update( Publisher $subject ) {
+       public function update( Observable $subject ) {
 
                if ( method_exists( $this, $subject->getState() ) ) {
-                       call_user_func_array( array( $this, 
$subject->getState() ), array( $subject ) );
+                       call_user_func_array(
+                               array( $this, $subject->getState() ),
+                               array( $subject->getSource() )
+                       );
                }
        }
+
 }
diff --git a/includes/ParserData.php b/includes/ParserData.php
index 19c5937..9b7b401 100644
--- a/includes/ParserData.php
+++ b/includes/ParserData.php
@@ -405,8 +405,7 @@
                // even finding uses of a property fails after its type was 
changed.
                if ( $this->updateJobs ) {
                        $changeNotifier = new PropertyChangeNotifier( $store, 
$this->semanticData, Settings::newFromGlobals() );
-                       $changeNotifier->attach( new ChangeObserver() );
-                       $changeNotifier->detectChanges();
+                       $changeNotifier->setDispatcher( new 
ObservableSubjectDispatcher( new ChangeObserver() ) )->detectChanges();
                }
 
                // Actually store semantic data, or at least clear it if needed
diff --git a/includes/PropertyChangeNotifier.php 
b/includes/PropertyChangeNotifier.php
index 22b7ab9..072e0b4 100644
--- a/includes/PropertyChangeNotifier.php
+++ b/includes/PropertyChangeNotifier.php
@@ -19,7 +19,7 @@
  *
  * @ingroup SMW
  */
-class PropertyChangeNotifier extends Subject implements TitleAccess {
+class PropertyChangeNotifier implements TitleAccess, DispatchableSource {
 
        /** @var Store */
        protected $store;
@@ -29,6 +29,9 @@
 
        /** @var Settings */
        protected $settings;
+
+       /** @var ObservableDispatcher */
+       protected $dispatcher;
 
        /** @var boolean */
        protected $hasDisparity = false;
@@ -55,6 +58,18 @@
         */
        public function getTitle() {
                return $this->semanticData->getSubject()->getTitle();
+       }
+
+       /**
+        * Invokes an ObservableDispatcher object to deploy state changes to an 
Observer
+        *
+        * @since 1.9
+        *
+        * @param ObservableDispatcher $dispatcher
+        */
+       public function setDispatcher( ObservableDispatcher $dispatcher ) {
+               $this->dispatcher = $dispatcher->setSource( $this );
+               return $this;
        }
 
        /**
@@ -89,7 +104,7 @@
        }
 
        /**
-        * Compare and find change related to the property type
+        * Compare and find changes related to the property type
         *
         * @since 1.9
         */
@@ -133,7 +148,7 @@
        }
 
        /**
-        * Compare and find change related to conversion factor
+        * Compare and find changes related to conversion factor
         *
         * @since 1.9
         */
@@ -142,11 +157,11 @@
 
                $pconversion  = new DIProperty( DIProperty::TYPE_CONVERSION );
 
+               $newfactors = $this->semanticData->getPropertyValues( 
$pconversion );
                $oldfactors = $this->store->getPropertyValues(
                        $this->semanticData->getSubject(),
                        $pconversion
                );
-               $newfactors = $this->semanticData->getPropertyValues( 
$pconversion );
 
                $this->addDispatchJob( !$this->isEqual( $oldfactors, 
$newfactors ) );
 
@@ -162,7 +177,7 @@
         */
        protected function addDispatchJob( $addJob = true ) {
                if ( $addJob && !$this->hasDisparity ) {
-                       $this->setState( 'runUpdateDispatcher' );
+                       $this->dispatcher->setState( 'runUpdateDispatcher' );
                        $this->hasDisparity = true;
                }
        }
@@ -200,4 +215,5 @@
 
                return ( $oldDataValueHash == $newDataValueHash );
        }
+
 }
diff --git a/includes/Publisher.php b/includes/Publisher.php
deleted file mode 100644
index 234db51..0000000
--- a/includes/Publisher.php
+++ /dev/null
@@ -1,51 +0,0 @@
-<?php
-
-namespace SMW;
-
-/**
- * Contains interfaces and implementation classes to
- * enable a Observer-Subject (or Publisher-Subcriber) pattern where
- * objects can indepentanly be notfied about a state change and initiate
- * an update of its registered Publisher
- *
- * @file
- *
- * @license GNU GPL v2+
- * @since   1.9
- *
- * @author mwjames
- */
-
-/**
- * Interface describing a Publisher
- *
- * @ingroup Observer
- */
-interface Publisher {
-
-       /**
-        * Attach an Subscriber
-        *
-        * @since  1.9
-        *
-        * @param Subscriber $subscriber
-        */
-       public function attach( Subscriber $subscriber );
-
-       /**
-        * Detach an Subscriber
-        *
-        * @since  1.9
-        *
-        * @param Subscriber $subscriber
-        */
-       public function detach( Subscriber $subscriber );
-
-       /**
-        * Notify an Subscriber
-        *
-        * @since  1.9
-        */
-       public function notify();
-
-}
diff --git a/includes/Setup.php b/includes/Setup.php
index dac5534..eb6f5cb 100644
--- a/includes/Setup.php
+++ b/includes/Setup.php
@@ -161,10 +161,16 @@
        $wgAutoloadClasses['SMW\ChangeObserver']            = $incDir . 
'ChangeObserver.php';
        $wgAutoloadClasses['SMW\TitleAccess']               = $incDir . 
'TitleAccess.php';
 
-       $wgAutoloadClasses['SMW\Publisher']                 = $incDir . 
'Publisher.php';
-       $wgAutoloadClasses['SMW\Subject']                   = $incDir . 
'Subject.php';
+       $wgAutoloadClasses['SMW\Observable']                = $incDir . 
'ObservableSubject.php';
+       $wgAutoloadClasses['SMW\Publisher']                 = $incDir . 
'ObservableSubject.php';
+       $wgAutoloadClasses['SMW\ObservableSubject']         = $incDir . 
'ObservableSubject.php';
+
        $wgAutoloadClasses['SMW\Observer']                  = $incDir . 
'Observer.php';
-       $wgAutoloadClasses['SMW\Subscriber']                = $incDir . 
'Subscriber.php';
+       $wgAutoloadClasses['SMW\Subscriber']                = $incDir . 
'Observer.php';
+
+       $wgAutoloadClasses['SMW\ObservableDispatcher']        = $incDir . 
'ObservableSubjectDispatcher.php';
+       $wgAutoloadClasses['SMW\DispatchableSource']          = $incDir . 
'ObservableSubjectDispatcher.php';
+       $wgAutoloadClasses['SMW\ObservableSubjectDispatcher'] = $incDir . 
'ObservableSubjectDispatcher.php';
 
        $wgAutoloadClasses['SMW\Cacheable']                 = $incDir . 
'Cacheable.php';
        $wgAutoloadClasses['SMW\Configurable']              = $incDir . 
'Configurable.php';
diff --git a/includes/Subject.php b/includes/Subject.php
deleted file mode 100644
index 5b70349..0000000
--- a/includes/Subject.php
+++ /dev/null
@@ -1,122 +0,0 @@
-<?php
-
-namespace SMW;
-
-/**
- * Contains interfaces and implementation classes to
- * enable a Observer-Subject (or Publisher-Subcriber) pattern where
- * objects can indepentanly be notfied about a state change and initiate
- * an update of its registered Publisher
- *
- * @file
- *
- * @license GNU GPL v2+
- * @since   1.9
- *
- * @author mwjames
- */
-
-/**
- * Implement the Publisher interface resulting in an Subject base class
- *
- * @ingroup Observer
- */
-abstract class Subject implements Publisher {
-
-       /** @var Subscriber[] */
-       protected $observers = array();
-
-       /** string */
-       protected $state = null;
-
-       /**
-        * @see Publisher::attach
-        *
-        * @since 1.9
-        *
-        * @param Subscriber $observer
-        */
-       public function attach( Subscriber $observer ) {
-               if ( $this->contains( $observer ) === null ) {
-                       $this->observers[] = $observer;
-               }
-       }
-
-       /**
-        * @since  1.9
-        *
-        * @param Subscriber $observer
-        */
-       public function detach( Subscriber $observer ) {
-               $index = $this->contains( $observer );
-               if ( $index !== null ) {
-                       unset( $this->observers[$index] );
-               }
-       }
-
-       /**
-        * Returns a string which represents an updateable
-        * Publisher object method
-        *
-        * @since 1.9
-        *
-        * @return string
-        */
-       public function getState() {
-               return $this->state;
-       }
-
-       /**
-        * Set a state variable state and initiates to notify
-        * the attached Subscribers
-        *
-        * @since 1.9
-        *
-        * @param string $state
-        */
-       public function setState( $state ) {
-               $this->state = $state;
-               $this->notify();
-       }
-
-       /**
-        * Notifies the updater of all invoked Subscribers
-        *
-        * @since  1.9
-        */
-       public function notify() {
-               foreach ( $this->observers as $observer ) {
-                       $observer->update( $this );
-               }
-       }
-
-       /**
-        * Returns registered Observers
-        *
-        * @since 1.9
-        *
-        * @return array
-        */
-       public function getObservers() {
-               return $this->observers;
-       }
-
-       /**
-        * Returns an index (or null) of a registered Observer
-        *
-        * @since  1.9
-        *
-        * @param Subscriber $observer
-        *
-        * @return integer|null
-        */
-       protected function contains( Subscriber $observer ) {
-               foreach ( $this->observers as $key => $obs ) {
-                       if ( $obs === $observer ) {
-                               return $key;
-                       }
-               }
-               return null;
-       }
-
-}
diff --git a/includes/Subscriber.php b/includes/Subscriber.php
deleted file mode 100644
index 1f6476f..0000000
--- a/includes/Subscriber.php
+++ /dev/null
@@ -1,35 +0,0 @@
-<?php
-
-namespace SMW;
-
-/**
- * Contains interfaces and implementation classes to
- * enable a Observer-Subject (or Publisher-Subcriber) pattern where
- * objects can indepentanly be notfied about a state change and initiate
- * an update of its registered Publisher
- *
- * @file
- *
- * @license GNU GPL v2+
- * @since   1.9
- *
- * @author mwjames
- */
-
-/**
- * Interface describing a Subsriber
- *
- * @ingroup Observer
- */
-interface Subscriber {
-
-       /**
-        * Receive update from a Publisher
-        *
-        * @since  1.9
-        *
-        * @param Publisher $publisher
-        */
-       public function update( Publisher $publisher );
-
-}
diff --git a/tests/phpunit/includes/ObservableSubjectDispatcherTest.php 
b/tests/phpunit/includes/ObservableSubjectDispatcherTest.php
new file mode 100644
index 0000000..575a66f
--- /dev/null
+++ b/tests/phpunit/includes/ObservableSubjectDispatcherTest.php
@@ -0,0 +1,170 @@
+<?php
+
+namespace SMW\Test;
+
+use SMW\ObservableSubjectDispatcher;
+use SMW\ObservableDispatcher;
+
+/**
+ * Tests for the Observer/Subject pattern
+ *
+ * @file
+ *
+ * @license GNU GPL v2+
+ * @since   1.9
+ *
+ * @author mwjames
+ */
+
+/**
+ * @covers \SMW\Observer
+ * @covers \SMW\ObservableSubject
+ * @covers \SMW\ObservableSubjectDispatcher
+ *
+ * @ingroup Test
+ *
+ * @group SMW
+ * @group SMWExtension
+ */
+class ObservableSubjectDispatcherTest extends SemanticMediaWikiTestCase {
+
+       /**
+        * Returns the name of the class to be tested
+        *
+        * @return string|false
+        */
+       public function getClass() {
+               return '\SMW\ObservableSubjectDispatcher';
+       }
+
+       /**
+        * Helper method that returns a DispatchableSource object
+        *
+        * @since 1.9
+        *
+        * @param $data
+        *
+        * @return DispatchableSource
+        */
+       private function newDispatchableSource() {
+
+               $source = $this->getMockBuilder( '\SMW\DispatchableSource' )
+                       ->setMethods( array( 'setDispatcher' ) )
+                       ->getMock();
+
+               $source->expects( $this->any() )
+                       ->method( 'setDispatcher' )
+                       ->will( $this->returnCallback( array( $this, 
'setDispatcherCallback' ) ) );
+
+               return $source;
+
+       }
+
+       /**
+        * Helper method that returns a Observer object
+        *
+        * @since 1.9
+        *
+        * @param $data
+        *
+        * @return Observer
+        */
+       private function newObserver() {
+
+               $observer = $this->getMockBuilder( '\SMW\Observer' )
+                       ->setMethods( array( 'lila' ) )
+                       ->getMock();
+
+               $observer->expects( $this->any() )
+                       ->method( 'lila' )
+                       ->will( $this->returnCallback( array( $this, 
'lilaObserverCallback' ) ) );
+
+               return $observer;
+
+       }
+
+       /**
+        * Helper method that returns a ObservableSubjectDispatcher object
+        *
+        * @since 1.9
+        *
+        * @param $data
+        *
+        * @return ObservableSubjectDispatcher
+        */
+       private function getInstance( $observer = null ) {
+               return new ObservableSubjectDispatcher( $observer );
+
+       }
+
+       /**
+        * @since 1.9
+        */
+       public function testConstructor() {
+               $this->assertInstanceOf( $this->getClass(), 
$this->getInstance() );
+       }
+
+       /**
+        * @since 1.9
+        */
+       public function testAttachDetach() {
+
+               $dispatcher = $this->getInstance();
+
+               $observer = $this->newObserver();
+               $dispatcher->attach( $observer );
+
+               $this->assertCount( 1, $dispatcher->getObservers() );
+               $dispatcher->detach( $observer );
+               $this->assertCount( 0, $dispatcher->getObservers() );
+
+               $dispatcher = $this->getInstance( $this->newObserver() );
+               $this->assertCount( 1, $dispatcher->getObservers() );
+
+       }
+
+       /**
+        * @since 1.9
+        */
+       public function testSourceDispatching() {
+
+               // Register Observer with the dispatcher
+               $dispatcher = $this->getInstance( $this->newObserver() );
+
+               // Register dispatcher with a source
+               $source = $this->newDispatchableSource();
+               $source->setDispatcher( $dispatcher );
+
+               // Rather being the source itself, the dispatcher returns the 
invoked instance
+               $this->assertEquals( $this, $dispatcher->getSource() );
+
+               // The dipatchers forwards a registered state change but the 
Observer
+               // is using the invoked source ($this) as means to communicate
+               $dispatcher->setState( 'lila' );
+               $this->assertEquals( 'lilaObserver was informed by source', 
$this->ObserverSentAMessage );
+
+       }
+
+       /**
+        * Verifies that the Observer was acting on the invoked Subject
+        *
+        * @since 1.9
+        *
+        * @param $subject
+        */
+       public function lilaObserverCallback( $subject ) {
+               return $subject->ObserverSentAMessage = 'lilaObserver was 
informed by source';
+       }
+
+       /**
+        * Sets the source
+        *
+        * @since 1.9
+        *
+        * @param ObservableDispatcher $dispatcher
+        */
+       public function setDispatcherCallback( ObservableDispatcher $dispatcher 
) {
+               $dispatcher->setSource( $this );
+       }
+
+}
diff --git a/tests/phpunit/includes/ObserverTest.php 
b/tests/phpunit/includes/ObserverTest.php
index 31c11d8..4efe1f3 100644
--- a/tests/phpunit/includes/ObserverTest.php
+++ b/tests/phpunit/includes/ObserverTest.php
@@ -15,7 +15,7 @@
 
 /**
  * @covers \SMW\Observer
- * @covers \SMW\Subject
+ * @covers \SMW\ObservableSubject
  *
  * @ingroup Test
  *
@@ -57,17 +57,17 @@
        }
 
        /**
-        * Helper method that returns a Subject object
+        * Helper method that returns a ObservableSubject object
         *
         * @since 1.9
         *
         * @param $data
         *
-        * @return Subject
+        * @return ObservableSubject
         */
-       private function newObserverSubject() {
+       private function newObservableSubject() {
 
-               $subject = $this->getMockBuilder( '\SMW\Subject' )
+               $subject = $this->getMockBuilder( '\SMW\ObservableSubject' )
                        ->setMethods( array( 'lulu' ) )
                        ->getMock();
 
@@ -84,7 +84,7 @@
         */
        public function testConstructor() {
                $this->assertInstanceOf( '\SMW\Observer', $this->newObserver() 
);
-               $this->assertInstanceOf( '\SMW\Subject', 
$this->newObserverSubject() );
+               $this->assertInstanceOf( '\SMW\ObservableSubject', 
$this->newObservableSubject() );
        }
 
        /**
@@ -92,7 +92,7 @@
         */
        public function testInvokeAndDetach() {
 
-               $subject  = $this->getMockForAbstractClass( '\SMW\Subject' );
+               $subject  = $this->getMockForAbstractClass( 
'\SMW\ObservableSubject' );
 
                // Same Observer instance attached twice results in only one 
registered object
                $observer = $this->getMockForAbstractClass( '\SMW\Observer', 
array( $subject ) );
@@ -117,7 +117,7 @@
         */
        public function testNotifyAndUpdate() {
 
-               $subject = $this->newObserverSubject();
+               $subject = $this->newObservableSubject();
                $subject->attach( $this->newObserver() );
 
                $this->assertNull( $subject->getState() );
diff --git a/tests/phpunit/includes/PropertyChangeNotifierTest.php 
b/tests/phpunit/includes/PropertyChangeNotifierTest.php
index ff3def7..96a3664 100644
--- a/tests/phpunit/includes/PropertyChangeNotifierTest.php
+++ b/tests/phpunit/includes/PropertyChangeNotifierTest.php
@@ -3,6 +3,7 @@
 namespace SMW\Test;
 
 use SMW\PropertyChangeNotifier;
+use SMW\ObservableSubjectDispatcher;
 use SMW\DIProperty;
 
 use Title;
@@ -77,7 +78,7 @@
         *
         * @since 1.9
         */
-       public function testFindDisparity( $storeValues, $dataValues, 
$settings, $expected ) {
+       public function testDetectChanges( $storeValues, $dataValues, 
$settings, $expected ) {
 
                $subject = $this->newSubject( $this->newTitle( SMW_NS_PROPERTY 
) );
                $this->storeValues = $storeValues;
@@ -92,7 +93,9 @@
                );
 
                $instance = $this->getInstance( $store, $data, $settings );
-               $observer = new MockChangeObserver( $instance );
+               $observer = new MockChangeObserver();
+
+               $instance->setDispatcher( new ObservableSubjectDispatcher( 
$observer ) );
 
                $this->assertInstanceOf( $this->getClass(), 
$instance->detectChanges() );
                $this->assertEquals( $subject->getTitle(), 
$instance->getTitle() );

-- 
To view, visit https://gerrit.wikimedia.org/r/77565
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I64c156006e9340aba57aba6b86d3b3c93a4b5c82
Gerrit-PatchSet: 3
Gerrit-Project: mediawiki/extensions/SemanticMediaWiki
Gerrit-Branch: master
Gerrit-Owner: Mwjames <[email protected]>
Gerrit-Reviewer: Mwjames <[email protected]>
Gerrit-Reviewer: jenkins-bot

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to