ソースを参照

Blackboards renamed to 'XxxTrapper'

Herbert Vojčík 10 年 前
コミット
2ec9805cac

+ 2 - 2
example-counter/src/Trapped-Counter.js

@@ -1,10 +1,10 @@
-define("trapped-counter/Trapped-Counter", ["amber/boot", "trapped/Trapped-Backend", "amber_core/Kernel-Objects"], function($boot){
+define("trapped-counter/Trapped-Counter", ["amber/boot", "amber_core/Kernel-Objects", "trapped/Trapped-Backend"], function($boot){
 var $core=$boot.api,nil=$boot.nil,$recv=$boot.asReceiver,$globals=$boot.globals;
 $core.addPackage('Trapped-Counter');
 $core.packages["Trapped-Counter"].innerEval = function (expr) { return eval(expr); };
 $core.packages["Trapped-Counter"].transport = {"type":"amd","amdNamespace":"trapped-counter"};
 
-$core.addClass('App', $globals.ListKeyedIsolatedEntity, [], 'Trapped-Counter');
+$core.addClass('App', $globals.IsolatingTrapper, [], 'Trapped-Counter');
 //>>excludeStart("ide", pragmas.excludeIdeData);
 $globals.App.comment="// Code from AngularJS Todo example, http://angularjs.org/#todo-js\x0afunction TodoCtrl($scope) {\x0a  $scope.todos = [\x0a    {text:'learn angular', done:true},\x0a    {text:'build an angular app', done:false}];\x0a \x0a  $scope.addTodo = function() {\x0a    $scope.todos.push({text:$scope.todoText, done:false});\x0a    $scope.todoText = '';\x0a  };\x0a \x0a  $scope.remaining = function() {\x0a    var count = 0;\x0a    angular.forEach($scope.todos, function(todo) {\x0a      count += todo.done ? 0 : 1;\x0a    });\x0a    return count;\x0a  };\x0a \x0a  $scope.archive = function() {\x0a    var oldTodos = $scope.todos;\x0a    $scope.todos = [];\x0a    angular.forEach(oldTodos, function(todo) {\x0a      if (!todo.done) $scope.todos.push(todo);\x0a    });\x0a  };\x0a}";
 //>>excludeEnd("ide");

+ 1 - 1
example-counter/src/Trapped-Counter.st

@@ -1,5 +1,5 @@
 Smalltalk createPackage: 'Trapped-Counter'!
-ListKeyedIsolatedEntity subclass: #App
+IsolatingTrapper subclass: #App
 	instanceVariableNames: ''
 	package: 'Trapped-Counter'!
 !App commentStamp!

+ 1 - 1
example-todo/src/Trapped-Todo.js

@@ -4,7 +4,7 @@ $core.addPackage('Trapped-Todo');
 $core.packages["Trapped-Todo"].innerEval = function (expr) { return eval(expr); };
 $core.packages["Trapped-Todo"].transport = {"type":"amd","amdNamespace":"trapped-todo"};
 
-$core.addClass('App', $globals.ListKeyedIsolatedEntity, [], 'Trapped-Todo');
+$core.addClass('App', $globals.IsolatingTrapper, [], 'Trapped-Todo');
 //>>excludeStart("ide", pragmas.excludeIdeData);
 $globals.App.comment="// Code from AngularJS Todo example, http://angularjs.org/#todo-js\x0afunction TodoCtrl($scope) {\x0a  $scope.todos = [\x0a    {text:'learn angular', done:true},\x0a    {text:'build an angular app', done:false}];\x0a \x0a  $scope.addTodo = function() {\x0a    $scope.todos.push({text:$scope.todoText, done:false});\x0a    $scope.todoText = '';\x0a  };\x0a \x0a  $scope.remaining = function() {\x0a    var count = 0;\x0a    angular.forEach($scope.todos, function(todo) {\x0a      count += todo.done ? 0 : 1;\x0a    });\x0a    return count;\x0a  };\x0a \x0a  $scope.archive = function() {\x0a    var oldTodos = $scope.todos;\x0a    $scope.todos = [];\x0a    angular.forEach(oldTodos, function(todo) {\x0a      if (!todo.done) $scope.todos.push(todo);\x0a    });\x0a  };\x0a}";
 //>>excludeEnd("ide");

+ 1 - 1
example-todo/src/Trapped-Todo.st

@@ -1,5 +1,5 @@
 Smalltalk createPackage: 'Trapped-Todo'!
-ListKeyedIsolatedEntity subclass: #App
+IsolatingTrapper subclass: #App
 	instanceVariableNames: ''
 	package: 'Trapped-Todo'!
 !App commentStamp!

+ 69 - 21
src/Trapped-Backend.js

@@ -365,9 +365,9 @@ messageSends: ["root:", "new"]
 $globals.Isolator.klass);
 
 
-$core.addClass('ListKeyedEntity', $globals.AxonizedObject, ['payload'], 'Trapped-Backend');
+$core.addClass('Trapper', $globals.AxonizedObject, ['payload'], 'Trapped-Backend');
 //>>excludeStart("ide", pragmas.excludeIdeData);
-$globals.ListKeyedEntity.comment="I am base class for #('string-at-index' #selector numeric-at-index)-array-path-keyed entities,\x0athat moderate access to the wrapped model object via read;do and modify:do:\x0aand allow pub-sub via watch:do:.\x0aThe wrapped model can be any smalltalk object.\x0a\x0aMy subclasses need to provide implementation for:\x0a\x0a - read:do:\x0a - modify:do:\x0a\x0aand must issue these calls when initializing:\x0a\x0a - axon: (with a subclass of `AxonBase`)\x0a - model: (with a wrapped object, after `axon:`)";
+$globals.Trapper.comment="A portmanteau of 'Trapped wrapper', I am base class for model objects wrapped by Trapped.\x0a\x0aWrapped object is indexed by #('string-at-index' #selector numeric-at-index) array paths. Operations using this indexing are:\x0a\x0a - `read:do` to get the indexed content\x0a - `modify:do:` to get and modify the indexed content, and\x0a - `watch:do:` to subscribe to changes of the indexed content.\x0a\x0aThe wrapped model can be any smalltalk object.\x0a\x0aMy subclasses need to provide implementation for:\x0a\x0a - read:do:\x0a - modify:do:\x0a\x0aand must issue these calls when initializing:\x0a\x0a - axon: (with a subclass of `AxonBase`)\x0a - model: (with a wrapped object, after `axon:`)";
 //>>excludeEnd("ide");
 $core.addMethod(
 $core.method({
@@ -422,7 +422,7 @@ self["@payload"]=anObject;
 self._changed_([]);
 return self;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx1) {$ctx1.fill(self,"model:",{anObject:anObject},$globals.ListKeyedEntity)});
+}, function($ctx1) {$ctx1.fill(self,"model:",{anObject:anObject},$globals.Trapper)});
 //>>excludeEnd("ctx");
 },
 //>>excludeStart("ide", pragmas.excludeIdeData);
@@ -432,7 +432,55 @@ referencedClasses: ["InterestedInTrapPathSubtree", "InterestedInTrapPath"],
 //>>excludeEnd("ide");
 messageSends: ["interestFactory:", "axon", "ifTrue:ifFalse:", "and:", "notEmpty", "isNil", "last", "aspect:block:", "new", "allButLast", "yourself", "changed:"]
 }),
-$globals.ListKeyedEntity);
+$globals.Trapper);
+
+$core.addMethod(
+$core.method({
+selector: "modify:do:",
+protocol: 'action',
+fn: function (path,aBlock){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+self._subclassResponsibility();
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"modify:do:",{path:path,aBlock:aBlock},$globals.Trapper)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["path", "aBlock"],
+source: "modify: path do: aBlock\x0a\x09self subclassResponsibility",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["subclassResponsibility"]
+}),
+$globals.Trapper);
+
+$core.addMethod(
+$core.method({
+selector: "read:do:",
+protocol: 'action',
+fn: function (path,aBlock){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+self._subclassResponsibility();
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"read:do:",{path:path,aBlock:aBlock},$globals.Trapper)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["path", "aBlock"],
+source: "read: path do: aBlock\x0a\x09self subclassResponsibility",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["subclassResponsibility"]
+}),
+$globals.Trapper);
 
 $core.addMethod(
 $core.method({
@@ -454,7 +502,7 @@ return self._read_do_(path,aBlock);
 }));
 return self;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx1) {$ctx1.fill(self,"watch:do:",{path:path,aBlock:aBlock},$globals.ListKeyedEntity)});
+}, function($ctx1) {$ctx1.fill(self,"watch:do:",{path:path,aBlock:aBlock},$globals.Trapper)});
 //>>excludeEnd("ctx");
 },
 //>>excludeStart("ide", pragmas.excludeIdeData);
@@ -464,13 +512,13 @@ referencedClasses: [],
 //>>excludeEnd("ide");
 messageSends: ["on:hook:", "axon", "read:do:"]
 }),
-$globals.ListKeyedEntity);
+$globals.Trapper);
 
 
 
-$core.addClass('ListKeyedDirectEntity', $globals.ListKeyedEntity, [], 'Trapped-Backend');
+$core.addClass('DirectTrapper', $globals.Trapper, [], 'Trapped-Backend');
 //>>excludeStart("ide", pragmas.excludeIdeData);
-$globals.ListKeyedDirectEntity.comment="I am ListKeyedEntity that directly manipulate\x0athe wrapped model object.";
+$globals.DirectTrapper.comment="I am Trapper that directly manipulate\x0athe wrapped model object.";
 //>>excludeEnd("ide");
 $core.addMethod(
 $core.method({
@@ -503,7 +551,7 @@ return self._changed_(path);
 }));
 return self;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx1) {$ctx1.fill(self,"modify:do:",{path:path,aBlock:aBlock,newValue:newValue,eavModel:eavModel},$globals.ListKeyedDirectEntity)});
+}, function($ctx1) {$ctx1.fill(self,"modify:do:",{path:path,aBlock:aBlock,newValue:newValue,eavModel:eavModel},$globals.DirectTrapper)});
 //>>excludeEnd("ctx");
 },
 //>>excludeStart("ide", pragmas.excludeIdeData);
@@ -513,7 +561,7 @@ referencedClasses: [],
 //>>excludeEnd("ide");
 messageSends: ["asEavModel", "value:", "on:", "ensure:", "on:put:", "changed:"]
 }),
-$globals.ListKeyedDirectEntity);
+$globals.DirectTrapper);
 
 $core.addMethod(
 $core.method({
@@ -529,7 +577,7 @@ eavModel=$recv(path)._asEavModel();
 $recv(aBlock)._value_($recv(eavModel)._on_(self["@payload"]));
 return self;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx1) {$ctx1.fill(self,"read:do:",{path:path,aBlock:aBlock,eavModel:eavModel},$globals.ListKeyedDirectEntity)});
+}, function($ctx1) {$ctx1.fill(self,"read:do:",{path:path,aBlock:aBlock,eavModel:eavModel},$globals.DirectTrapper)});
 //>>excludeEnd("ctx");
 },
 //>>excludeStart("ide", pragmas.excludeIdeData);
@@ -539,13 +587,13 @@ referencedClasses: [],
 //>>excludeEnd("ide");
 messageSends: ["asEavModel", "value:", "on:"]
 }),
-$globals.ListKeyedDirectEntity);
+$globals.DirectTrapper);
 
 
 
-$core.addClass('ListKeyedIsolatedEntity', $globals.ListKeyedEntity, [], 'Trapped-Backend');
+$core.addClass('IsolatingTrapper', $globals.Trapper, [], 'Trapped-Backend');
 //>>excludeStart("ide", pragmas.excludeIdeData);
-$globals.ListKeyedIsolatedEntity.comment="I am ListKeyedEntity that guards access\x0ato the wrapped model object via Isolator.";
+$globals.IsolatingTrapper.comment="I am Trapper that guards access\x0ato the wrapped model object via Isolator.\x0a\x0aIOW, read:do: gets always its own deep copy,\x0amodify:do: is not reentrant\x0aand upon writing the written part is deep-copied as well\x0a(so modifier does not hold the source of truth\x0aand can change it later).\x0a\x0aThis also means, a wrapped object and all its parts\x0amust understand `#deepCopy`.";
 //>>excludeEnd("ide");
 $core.addMethod(
 $core.method({
@@ -561,13 +609,13 @@ return $core.withContext(function($ctx1) {
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 $ctx1.supercall = true, 
 //>>excludeEnd("ctx");
-$globals.ListKeyedIsolatedEntity.superclass.fn.prototype._model_.apply($recv(self), [$recv($Isolator())._on_(anObject)]));
+$globals.IsolatingTrapper.superclass.fn.prototype._model_.apply($recv(self), [$recv($Isolator())._on_(anObject)]));
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 $ctx1.supercall = false;
 //>>excludeEnd("ctx");;
 return self;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx1) {$ctx1.fill(self,"model:",{anObject:anObject},$globals.ListKeyedIsolatedEntity)});
+}, function($ctx1) {$ctx1.fill(self,"model:",{anObject:anObject},$globals.IsolatingTrapper)});
 //>>excludeEnd("ctx");
 },
 //>>excludeStart("ide", pragmas.excludeIdeData);
@@ -577,7 +625,7 @@ referencedClasses: ["Isolator"],
 //>>excludeEnd("ide");
 messageSends: ["model:", "on:"]
 }),
-$globals.ListKeyedIsolatedEntity);
+$globals.IsolatingTrapper);
 
 $core.addMethod(
 $core.method({
@@ -609,7 +657,7 @@ return self._changed_(path);
 }));
 return self;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx1) {$ctx1.fill(self,"modify:do:",{path:path,aBlock:aBlock,eavModel:eavModel},$globals.ListKeyedIsolatedEntity)});
+}, function($ctx1) {$ctx1.fill(self,"modify:do:",{path:path,aBlock:aBlock,eavModel:eavModel},$globals.IsolatingTrapper)});
 //>>excludeEnd("ctx");
 },
 //>>excludeStart("ide", pragmas.excludeIdeData);
@@ -619,7 +667,7 @@ referencedClasses: [],
 //>>excludeEnd("ide");
 messageSends: ["asEavModel", ",", "ensure:", "model:modify:", "changed:"]
 }),
-$globals.ListKeyedIsolatedEntity);
+$globals.IsolatingTrapper);
 
 $core.addMethod(
 $core.method({
@@ -635,7 +683,7 @@ eavModel=$recv($recv([["root"]]).__comma(path))._asEavModel();
 $recv(self["@payload"])._model_read_(eavModel,aBlock);
 return self;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx1) {$ctx1.fill(self,"read:do:",{path:path,aBlock:aBlock,eavModel:eavModel},$globals.ListKeyedIsolatedEntity)});
+}, function($ctx1) {$ctx1.fill(self,"read:do:",{path:path,aBlock:aBlock,eavModel:eavModel},$globals.IsolatingTrapper)});
 //>>excludeEnd("ctx");
 },
 //>>excludeStart("ide", pragmas.excludeIdeData);
@@ -645,7 +693,7 @@ referencedClasses: [],
 //>>excludeEnd("ide");
 messageSends: ["asEavModel", ",", "model:read:"]
 }),
-$globals.ListKeyedIsolatedEntity);
+$globals.IsolatingTrapper);
 
 
 $core.addMethod(

+ 39 - 17
src/Trapped-Backend.st

@@ -95,13 +95,18 @@ on: anObject
 ^self new root: anObject
 ! !
 
-AxonizedObject subclass: #ListKeyedEntity
+AxonizedObject subclass: #Trapper
 	instanceVariableNames: 'payload'
 	package: 'Trapped-Backend'!
-!ListKeyedEntity commentStamp!
-I am base class for #('string-at-index' #selector numeric-at-index)-array-path-keyed entities,
-that moderate access to the wrapped model object via read;do and modify:do:
-and allow pub-sub via watch:do:.
+!Trapper commentStamp!
+A portmanteau of 'Trapped wrapper', I am base class for model objects wrapped by Trapped.
+
+Wrapped object is indexed by #('string-at-index' #selector numeric-at-index) array paths. Operations using this indexing are:
+
+ - `read:do` to get the indexed content
+ - `modify:do:` to get and modify the indexed content, and
+ - `watch:do:` to subscribe to changes of the indexed content.
+
 The wrapped model can be any smalltalk object.
 
 My subclasses need to provide implementation for:
@@ -114,7 +119,7 @@ and must issue these calls when initializing:
  - axon: (with a subclass of `AxonBase`)
  - model: (with a wrapped object, after `axon:`)!
 
-!ListKeyedEntity methodsFor: 'accessing'!
+!Trapper methodsFor: 'accessing'!
 
 model: anObject
 	self axon
@@ -126,20 +131,28 @@ model: anObject
     self changed: #()
 ! !
 
-!ListKeyedEntity methodsFor: 'action'!
+!Trapper methodsFor: 'action'!
+
+modify: path do: aBlock
+	self subclassResponsibility
+!
+
+read: path do: aBlock
+	self subclassResponsibility
+!
 
 watch: path do: aBlock
 	self axon on: path hook: [ self read: path do: aBlock ]
 ! !
 
-ListKeyedEntity subclass: #ListKeyedDirectEntity
+Trapper subclass: #DirectTrapper
 	instanceVariableNames: ''
 	package: 'Trapped-Backend'!
-!ListKeyedDirectEntity commentStamp!
-I am ListKeyedEntity that directly manipulate
+!DirectTrapper commentStamp!
+I am Trapper that directly manipulate
 the wrapped model object.!
 
-!ListKeyedDirectEntity methodsFor: 'action'!
+!DirectTrapper methodsFor: 'action'!
 
 modify: path do: aBlock
     | newValue eavModel |
@@ -154,20 +167,29 @@ read: path do: aBlock
     aBlock value: (eavModel on: payload)
 ! !
 
-ListKeyedEntity subclass: #ListKeyedIsolatedEntity
+Trapper subclass: #IsolatingTrapper
 	instanceVariableNames: ''
 	package: 'Trapped-Backend'!
-!ListKeyedIsolatedEntity commentStamp!
-I am ListKeyedEntity that guards access
-to the wrapped model object via Isolator.!
+!IsolatingTrapper commentStamp!
+I am Trapper that guards access
+to the wrapped model object via Isolator.
+
+IOW, read:do: gets always its own deep copy,
+modify:do: is not reentrant
+and upon writing the written part is deep-copied as well
+(so modifier does not hold the source of truth
+and can change it later).
+
+This also means, a wrapped object and all its parts
+must understand `#deepCopy`.!
 
-!ListKeyedIsolatedEntity methodsFor: 'accessing'!
+!IsolatingTrapper methodsFor: 'accessing'!
 
 model: anObject
 	super model: (Isolator on: anObject)
 ! !
 
-!ListKeyedIsolatedEntity methodsFor: 'action'!
+!IsolatingTrapper methodsFor: 'action'!
 
 modify: path do: aBlock
     | eavModel |