{ import: Object } { import: Utility } { import: TestCase } "It privides a simple notification protocol for one to many relationship. Now I intentionally limit the event handler to accept just one argument, and #triggerSignal:with: returns nothing from from its listener. It is because I can see this mechanism as if it is a special case of blocking function (I will consider this semantics deeply later)." "Constant" SignalTables : IdentityDictionary() [ SignalTables := SignalTables new ] "----------------------------------------------------------------" Object actionMap [ "#actionMap can be overrided by subclass if necessary" ^ SignalTables at: self ifAbsentPut: IdentityDictionary new. ] Object when: aSymbol do: listener [ "Register a listener. Listener should respond #value: and #receiver (BlockClosure or MessageSend typically)." (self actionMap at: aSymbol ifAbsentPut: OrderedCollection new) add: listener. ] Object triggerSignal: aSymbol with: anArg [ "Notify to all listener waiting the symbol with an argument." (self actionMap at: aSymbol ifAbsent: [^self]) do: [:each | each value: anArg]. ] Object removeSignals: aSymbol receiver: anObject [ "Remove events matched the receiver" | signals matched | signals := self actionMap at: aSymbol ifAbsent: [^self]. signals removeAll: (signals select: [:each | each receiver == anObject ]). ] "----------------------------------------------------------------" "MessageSend encapsulates receiver and selector." MessageSend : Object (receiver selector) MessageSend receiver [ ^receiver ] BlockClosure receiver [ ^nil ] MessageSend receiver: anObject selector: aSymbol [ receiver := anObject. selector := aSymbol. ] MessageSend value: argument [ receiver perform: selector with: argument ] Object >>> aSymbol [ ^MessageSend new receiver: self selector: aSymbol ] "----------------------------------------------------------------" "Test Cases" SignalTest : TestCase (status) SignalTest status [ ^status ] SignalTest status: value [ status := value ] SignalTest new [ self := super new. status := 'beginning'. ^ self ] SignalTest testObjectSignal [ | model listener | model := Object new. listener := 0. model when: #fire do: [:aNumber | listener := listener + aNumber ]. model triggerSignal: #fire with: 1. self assert: listener equals: 1. model when: #fire do: [:aNumber | listener := listener + aNumber ]. model triggerSignal: #fire with: 1. self assert: listener equals: 3. "Added same listner twice" ] SignalTest testMessageSend [ | model listener | model := Object new. listener := self new. model when: #fire do: listener >>> #status:. model triggerSignal: #fire with: 'triggered'. self assert: listener status equals: 'triggered'. ] SignalTest testAddRemove [ | model listener | model := Object new. listener := self new. model when: #fire do: listener >>> #status:. model removeSignals: #fire receiver: listener. model triggerSignal: #fire with: 'triggered'. self assert: listener status equals: 'beginning'. "Nothing should happen." 1 removeSignals: #fire receiver: listener. "Nothing should happen." ]