Browse Source

Fixes #881:

- Adds Announcer >> on:do:for:
- Adds associated tests
Nicolas Petton 10 years ago
parent
commit
e03ed64cee
4 changed files with 250 additions and 14 deletions
  1. 142 10
      src/Kernel-Announcements.js
  2. 42 1
      src/Kernel-Announcements.st
  3. 46 2
      src/Kernel-Tests.js
  4. 20 1
      src/Kernel-Tests.st

+ 142 - 10
src/Kernel-Announcements.js

@@ -175,6 +175,110 @@ globals.AnnouncementSubscription);
 
 
 
+smalltalk.addClass('AnnouncementValuable', globals.Object, ['valuable', 'receiver'], 'Kernel-Announcements');
+globals.AnnouncementValuable.comment="I wrap `valuable` objects (typically instances of `BlockClosure`) with a `receiver` to be able to unregister subscriptions based on a `receiver`.";
+smalltalk.addMethod(
+smalltalk.method({
+selector: "receiver",
+protocol: 'accessing',
+fn: function (){
+var self=this;
+var $1;
+$1=self["@receiver"];
+return $1;
+},
+args: [],
+source: "receiver\x0a\x09^ receiver",
+messageSends: [],
+referencedClasses: []
+}),
+globals.AnnouncementValuable);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "receiver:",
+protocol: 'accessing',
+fn: function (anObject){
+var self=this;
+self["@receiver"]=anObject;
+return self},
+args: ["anObject"],
+source: "receiver: anObject\x0a\x09receiver := anObject",
+messageSends: [],
+referencedClasses: []
+}),
+globals.AnnouncementValuable);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "valuable",
+protocol: 'accessing',
+fn: function (){
+var self=this;
+var $1;
+$1=self["@valuable"];
+return $1;
+},
+args: [],
+source: "valuable\x0a\x09^ valuable",
+messageSends: [],
+referencedClasses: []
+}),
+globals.AnnouncementValuable);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "valuable:",
+protocol: 'accessing',
+fn: function (anObject){
+var self=this;
+self["@valuable"]=anObject;
+return self},
+args: ["anObject"],
+source: "valuable: anObject\x0a\x09valuable := anObject",
+messageSends: [],
+referencedClasses: []
+}),
+globals.AnnouncementValuable);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "value",
+protocol: 'evaluating',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(self._valuable())._value();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"value",{},globals.AnnouncementValuable)})},
+args: [],
+source: "value\x0a\x09^ self valuable value",
+messageSends: ["value", "valuable"],
+referencedClasses: []
+}),
+globals.AnnouncementValuable);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "value:",
+protocol: 'evaluating',
+fn: function (anObject){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(self._valuable())._value_(anObject);
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"value:",{anObject:anObject},globals.AnnouncementValuable)})},
+args: ["anObject"],
+source: "value: anObject\x0a\x09^ self valuable value: anObject",
+messageSends: ["value:", "valuable"],
+referencedClasses: []
+}),
+globals.AnnouncementValuable);
+
+
+
 smalltalk.addClass('Announcer', globals.Object, ['registry', 'subscriptions'], 'Kernel-Announcements');
 globals.Announcer.comment="I hold annoncement subscriptions (instances of `AnnouncementSubscription`) in a private registry.\x0aI announce (trigger) announces, which are then dispatched to all subscriptions.\x0a\x0aThe code is based on the announcements as [described by Vassili Bykov](http://www.cincomsmalltalk.com/userblogs/vbykov/blogView?searchCategory=Announcements%20Framework).\x0a\x0a## API\x0a\x0aUse `#announce:` to trigger an announcement.\x0a\x0aUse `#on:do:` or `#on:send:to:` to register subscriptions.\x0a\x0aWhen using `#on:send:to:`, unregistration can be done with `#unregister:`.\x0a\x0a## Usage example:\x0a\x0a    SystemAnnouncer current\x0a        on: ClassAdded\x0a        do: [ :ann | window alert: ann theClass name, ' added' ].";
 smalltalk.addMethod(
@@ -220,19 +324,47 @@ selector: "on:do:",
 protocol: 'subscribing',
 fn: function (aClass,aBlock){
 var self=this;
-function $AnnouncementSubscription(){return globals.AnnouncementSubscription||(typeof AnnouncementSubscription=="undefined"?nil:AnnouncementSubscription)}
 return smalltalk.withContext(function($ctx1) { 
-var $1,$2;
-$1=_st($AnnouncementSubscription())._new();
-_st($1)._valuable_(aBlock);
-_st($1)._announcementClass_(aClass);
-$2=_st($1)._yourself();
-_st(self["@subscriptions"])._add_($2);
+self._on_do_for_(aClass,aBlock,nil);
 return self}, function($ctx1) {$ctx1.fill(self,"on:do:",{aClass:aClass,aBlock:aBlock},globals.Announcer)})},
 args: ["aClass", "aBlock"],
-source: "on: aClass do: aBlock\x0a\x09subscriptions add: (AnnouncementSubscription new\x0a\x09\x09valuable: aBlock;\x0a\x09\x09announcementClass: aClass;\x0a\x09\x09yourself)",
-messageSends: ["add:", "valuable:", "new", "announcementClass:", "yourself"],
-referencedClasses: ["AnnouncementSubscription"]
+source: "on: aClass do: aBlock\x0a\x09self on: aClass do: aBlock for: nil",
+messageSends: ["on:do:for:"],
+referencedClasses: []
+}),
+globals.Announcer);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "on:do:for:",
+protocol: 'subscribing',
+fn: function (aClass,aBlock,aReceiver){
+var self=this;
+function $AnnouncementSubscription(){return globals.AnnouncementSubscription||(typeof AnnouncementSubscription=="undefined"?nil:AnnouncementSubscription)}
+function $AnnouncementValuable(){return globals.AnnouncementValuable||(typeof AnnouncementValuable=="undefined"?nil:AnnouncementValuable)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$3,$4,$6,$7,$5,$8,$2;
+$1=self["@subscriptions"];
+$3=_st($AnnouncementSubscription())._new();
+$ctx1.sendIdx["new"]=1;
+$4=$3;
+$6=_st($AnnouncementValuable())._new();
+_st($6)._valuable_(aBlock);
+_st($6)._receiver_(aReceiver);
+$7=_st($6)._yourself();
+$ctx1.sendIdx["yourself"]=1;
+$5=$7;
+_st($4)._valuable_($5);
+$ctx1.sendIdx["valuable:"]=1;
+_st($3)._announcementClass_(aClass);
+$8=_st($3)._yourself();
+$2=$8;
+_st($1)._add_($2);
+return self}, function($ctx1) {$ctx1.fill(self,"on:do:for:",{aClass:aClass,aBlock:aBlock,aReceiver:aReceiver},globals.Announcer)})},
+args: ["aClass", "aBlock", "aReceiver"],
+source: "on: aClass do: aBlock for: aReceiver\x0a\x09subscriptions add: (AnnouncementSubscription new\x0a\x09\x09valuable: (AnnouncementValuable new\x0a\x09\x09\x09valuable: aBlock;\x0a\x09\x09\x09receiver: aReceiver;\x0a\x09\x09\x09yourself);\x0a\x09\x09announcementClass: aClass;\x0a\x09\x09yourself)",
+messageSends: ["add:", "valuable:", "new", "receiver:", "yourself", "announcementClass:"],
+referencedClasses: ["AnnouncementSubscription", "AnnouncementValuable"]
 }),
 globals.Announcer);
 

+ 42 - 1
src/Kernel-Announcements.st

@@ -58,6 +58,40 @@ handlesAnnouncement: anAnnouncement
 		(Smalltalk globals at: anAnnouncement class theNonMetaClass name) includesBehavior: class ]
 ! !
 
+Object subclass: #AnnouncementValuable
+	instanceVariableNames: 'valuable receiver'
+	package: 'Kernel-Announcements'!
+!AnnouncementValuable commentStamp!
+I wrap `valuable` objects (typically instances of `BlockClosure`) with a `receiver` to be able to unregister subscriptions based on a `receiver`.!
+
+!AnnouncementValuable methodsFor: 'accessing'!
+
+receiver
+	^ receiver
+!
+
+receiver: anObject
+	receiver := anObject
+!
+
+valuable
+	^ valuable
+!
+
+valuable: anObject
+	valuable := anObject
+! !
+
+!AnnouncementValuable methodsFor: 'evaluating'!
+
+value
+	^ self valuable value
+!
+
+value: anObject
+	^ self valuable value: anObject
+! !
+
 Object subclass: #Announcer
 	instanceVariableNames: 'registry subscriptions'
 	package: 'Kernel-Announcements'!
@@ -98,8 +132,15 @@ initialize
 !Announcer methodsFor: 'subscribing'!
 
 on: aClass do: aBlock
+	self on: aClass do: aBlock for: nil
+!
+
+on: aClass do: aBlock for: aReceiver
 	subscriptions add: (AnnouncementSubscription new
-		valuable: aBlock;
+		valuable: (AnnouncementValuable new
+			valuable: aBlock;
+			receiver: aReceiver;
+			yourself);
 		announcementClass: aClass;
 		yourself)
 !

+ 46 - 2
src/Kernel-Tests.js

@@ -44,7 +44,7 @@ smalltalk.addClass('AnnouncerTest', globals.TestCase, [], 'Kernel-Tests');
 smalltalk.addMethod(
 smalltalk.method({
 selector: "testOnDo",
-protocol: 'not yet classified',
+protocol: 'tests',
 fn: function (){
 var self=this;
 var counter,announcer;
@@ -77,10 +77,54 @@ referencedClasses: ["Announcer", "SystemAnnouncement"]
 }),
 globals.AnnouncerTest);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testOnDoFor",
+protocol: 'tests',
+fn: function (){
+var self=this;
+var counter,announcer;
+function $Announcer(){return globals.Announcer||(typeof Announcer=="undefined"?nil:Announcer)}
+function $SystemAnnouncement(){return globals.SystemAnnouncement||(typeof SystemAnnouncement=="undefined"?nil:SystemAnnouncement)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2,$3,$4;
+counter=(0);
+announcer=_st($Announcer())._new();
+$ctx1.sendIdx["new"]=1;
+_st(announcer)._on_do_for_($SystemAnnouncement(),(function(){
+return smalltalk.withContext(function($ctx2) {
+counter=_st(counter).__plus((1));
+return counter;
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)})}),self);
+$1=announcer;
+$2=_st($SystemAnnouncement())._new();
+$ctx1.sendIdx["new"]=2;
+_st($1)._announce_($2);
+$ctx1.sendIdx["announce:"]=1;
+self._assert_equals_(counter,(1));
+$ctx1.sendIdx["assert:equals:"]=1;
+$3=announcer;
+$4=_st($SystemAnnouncement())._new();
+$ctx1.sendIdx["new"]=3;
+_st($3)._announce_($4);
+$ctx1.sendIdx["announce:"]=2;
+self._assert_equals_(counter,(2));
+$ctx1.sendIdx["assert:equals:"]=2;
+_st(announcer)._unsubscribe_(self);
+_st(announcer)._announce_(_st($SystemAnnouncement())._new());
+self._assert_equals_(counter,(2));
+return self}, function($ctx1) {$ctx1.fill(self,"testOnDoFor",{counter:counter,announcer:announcer},globals.AnnouncerTest)})},
+args: [],
+source: "testOnDoFor\x0a\x09| counter announcer |\x0a\x09\x0a\x09counter := 0.\x0a\x09announcer := Announcer new.\x0a\x09announcer on: SystemAnnouncement do: [ counter := counter + 1 ] for: self.\x0a\x0a\x09announcer announce: (SystemAnnouncement new).\x0a\x09self assert: counter equals: 1.\x0a\x0a\x09announcer announce: (SystemAnnouncement new).\x0a\x09self assert: counter equals: 2.\x0a\x09\x0a\x09announcer unsubscribe: self.\x0a\x09\x0a\x09announcer announce: (SystemAnnouncement new).\x0a\x09self assert: counter equals: 2.",
+messageSends: ["new", "on:do:for:", "+", "announce:", "assert:equals:", "unsubscribe:"],
+referencedClasses: ["Announcer", "SystemAnnouncement"]
+}),
+globals.AnnouncerTest);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "testOnDoOnce",
-protocol: 'not yet classified',
+protocol: 'tests',
 fn: function (){
 var self=this;
 var counter,announcer;

+ 20 - 1
src/Kernel-Tests.st

@@ -26,7 +26,7 @@ TestCase subclass: #AnnouncerTest
 	instanceVariableNames: ''
 	package: 'Kernel-Tests'!
 
-!AnnouncerTest methodsFor: 'not yet classified'!
+!AnnouncerTest methodsFor: 'tests'!
 
 testOnDo
 	| counter announcer |
@@ -42,6 +42,25 @@ testOnDo
 	self assert: counter equals: 2.
 !
 
+testOnDoFor
+	| counter announcer |
+	
+	counter := 0.
+	announcer := Announcer new.
+	announcer on: SystemAnnouncement do: [ counter := counter + 1 ] for: self.
+
+	announcer announce: (SystemAnnouncement new).
+	self assert: counter equals: 1.
+
+	announcer announce: (SystemAnnouncement new).
+	self assert: counter equals: 2.
+	
+	announcer unsubscribe: self.
+	
+	announcer announce: (SystemAnnouncement new).
+	self assert: counter equals: 2.
+!
+
 testOnDoOnce
 	| counter announcer |