# Copyright: Copyright (c) 2004 Nicolas Despres. All rights reserved. # Author: Nicolas Despres . # License: Gnu General Public License. # $LastChangedBy: ertai $ # $Id: observable_pool.rb 186 2005-04-03 00:07:45Z ertai $ require 'observable' require 'thread' module ObservablePool class Observer include Observable attr_reader :observable def initialize(observable, mutex) @observable = observable @observable.add_observer(self) @mutex = mutex end def update(*args, &block) @mutex.synchronize do changed notify_observers(@observable, *args, &block) end end def method_missing(meth, *args, &block) @observable.send(meth, *args, &block) end end # class Observer def add_observable(observable) unless observable.is_a?(Observable) raise(ArgumentError, "observable object must be Observale") end @observable_peer_mutex = Mutex.new unless defined? @observable_peer_mutex @observable_peers = {} unless defined? @observable_peers sync_observer = Observer.new(observable, @observable_peer_mutex) @observable_peers[observable] = sync_observer @global_observer_peers = [] unless defined? @global_observer_peers @global_observer_peers.each do |observer| sync_observer.add_observer(observer) end sync_observer end def add_observables(*observables) observables.each { |observable| add_observable(observable) } end def delete_observable(observable) @observable_peers.delete(observable) if defined? @observable_peers end def delete_observables @observable_peers.clear if defined? @observable_peers end def observable(observable) @observable_peers[observable] if defined? @observable_peers end def observable?(observable) @observable_peers.has_key?(observable) if defined? @observable_peers end def list_observables if defined? @observable_peers @observable_peers.keys else [] end end def list_observables_observed(observer) observables = [] if defined? @observable_peers @observable_peers.each do |observable, obsv| if obsv.list_observers.include?(observer) observables << observable end end end observables end def count_observables if defined? @observable_peers @observable_peers.size else 0 end end def each_observables(&block) if defined? @observable_peers @observable_peers.each do |observable, observer| block[observable, observer] end end end def list_all_observers observers = [] if defined? @observable_peers @observable_peers.each do |observable, observer| observers += observer.list_observers end end observers.uniq end def count_all_observers list_observers.size end def add_global_observer(observer) if defined? @observable_peers @observable_peers.each do |observable, obsv| obsv.add_observer(observer) unless observable.observer?(observer) end end @global_observer_peers = [] unless defined? @global_observer_peers @global_observer_peers << observer end def add_global_observers(*observers) observers.each { |observer| add_global_observer(observer) } end def delete_global_observer(observer) if defined? @observable_peers @observable_peers.each do |observable, obsv| obsv.delete_observer(observer) end end @global_observer_peers.delete(observer) if defined? @global_observer_peers end def delete_global_observers if defined? @global_observer_peers @global_observer_peers.dup.each do |global_observer| delete_global_observer(global_observer) end end end def list_global_observers if defined? @global_observer_peers @global_observer_peers.dup else [] end end def count_global_observers if defined? @global_observer_peers @globale.observer_peers.size else 0 end end def global_observer?(observer) if defined? @global_observer_peers @global_observer_peers.include?(observer) else false end end end # class ObservablePool if defined? TEST_MODE or __FILE__ == $0 require 'test/unit/ui/yaml/testrunner' class ObservablePoolTest < Test::Unit::TestCase class Notifier include Observable end class NotifierPool include ObservablePool end class Obsv attr_reader :last_msg def initialize @last_msg = nil end def update(*args, &block) @last_msg = args end end def test_simple n1 = Notifier.new n2 = Notifier.new o1 = Obsv.new o2 = Obsv.new o3 = Obsv.new op = NotifierPool.new assert_equal(0, op.count_observables) assert_equal([], op.list_observables) op.add_observables(n1) op.add_observable(n2) op.observable(n2).add_observers(o1, o3) assert_equal(2, op.count_observables) [ n1, n2 ].each { |x| assert(op.list_observables.include?(x)) } assert_equal(1, n1.count_observers) assert_equal([], n1.list_observers[0].list_observers) assert_equal(1, n2.count_observers) assert_equal(2, n2.list_observers[0].count_observers) [o1, o3].each do |x| assert(n2.list_observers[0].list_observers.include?(x), "#{x} not observer") end op.observable(n1).add_observers(o2, o3) assert_equal(2, op.observable(n1).count_observers) [o1, o2, o3].each do |x| assert(op.list_all_observers.include?(x), "#{x} not observer") end n2.changed n2.notify_observers('msg2') assert_equal([ n2, 'msg2'], o1.last_msg) assert_nil(o2.last_msg) assert_equal([ n2, 'msg2'], o3.last_msg) n1.changed n1.notify_observers('msg1') assert_equal([n2, 'msg2'], o1.last_msg) assert_equal([n1, 'msg1'], o2.last_msg) assert_equal([n1, 'msg1'], o3.last_msg) [n1, n2].each do |x| assert(op.list_observables_observed(o3).include?(x), "#{x} not observer") end end def test_global_observer n1 = Notifier.new n2 = Notifier.new n3 = Notifier.new o1 = Obsv.new o2 = Obsv.new o3 = Obsv.new op = NotifierPool.new assert_equal(0, op.count_global_observers) assert_equal([], op.list_global_observers) op.add_observables(n1) op.add_observable(n2) op.observable(n1).add_observers(o1) op.observable(n2).add_observers(o2) op.add_global_observers(o3) [o1, o3].each do |x| assert(n1.list_observers[0].list_observers.include?(x), "#{x} not observer") end [o2, o3].each do |x| assert(n2.list_observers[0].list_observers.include?(x), "#{x} not observer") end assert_equal([o3], op.list_global_observers) op.add_observable(n3) assert([o3], n3.list_observers[0].list_observers) op.delete_global_observers assert_equal([], op.list_global_observers) assert([], n3.list_observers[0].list_observers) assert([o2], n2.list_observers[0].list_observers) assert([o1], n1.list_observers[0].list_observers) end end # class ObservablePoolTest end