3 Commits e227741785 ... 346447e15e

Author SHA1 Message Date
  Herbert Vojčík 346447e15e Isolator->Axxord; Backend,Tests empty, deleted. 6 years ago
  Herbert Vojčík 4ed84f7659 Move Interested...Axes into Axxord. 6 years ago
  Herbert Vojčík 02893a45c6 Root modify only failing on actual change. 6 years ago
4 changed files with 1076 additions and 25 deletions
  1. 647 6
      src/Axxord-Tests.js
  2. 145 3
      src/Axxord-Tests.st
  3. 223 12
      src/Axxord.js
  4. 61 4
      src/Axxord.st

+ 647 - 6
src/Axxord-Tests.js

@@ -7,6 +7,609 @@ $core.addPackage("Axxord-Tests");
 $core.packages["Axxord-Tests"].innerEval = function (expr) { return eval(expr); };
 $core.packages["Axxord-Tests"].transport = {"type":"amd","amdNamespace":"axxord"};
 
+$core.addClass("AxolatorTest", $globals.TestCase, ["rootModel"], "Axxord-Tests");
+$core.addMethod(
+$core.method({
+selector: "setUp",
+protocol: "running",
+fn: function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $1;
+$1=$recv($globals.EavModel)._new();
+$recv($1)._getBlock_((function(x){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv(x)._root();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({x:x},$ctx1,1)});
+//>>excludeEnd("ctx");
+}));
+$self["@rootModel"]=$recv($1)._putBlock_((function(x,y){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv(x)._root_(y);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({x:x,y:y},$ctx1,2)});
+//>>excludeEnd("ctx");
+}));
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"setUp",{},$globals.AxolatorTest)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "setUp\x0a\x0arootModel := EavModel new\x0a\x09getBlock: [:x | x root];\x0a    putBlock: [:x :y | x root: y].",
+referencedClasses: ["EavModel"],
+//>>excludeEnd("ide");
+messageSends: ["getBlock:", "new", "root", "putBlock:", "root:"]
+}),
+$globals.AxolatorTest);
+
+$core.addMethod(
+$core.method({
+selector: "testNontrivialModelGetsAppropriateValueForModification",
+protocol: "tests",
+fn: function (){
+var self=this,$self=this;
+var isolator,model,result;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $1,$3,$2;
+result=nil;
+isolator=$recv($globals.Axolator)._on_($globals.HashedCollection._newFromPairs_(["foo",["bar", [(1), [(2), (5)]], "baz"],"moo","zoo"]));
+$1=$recv($globals.EavModel)._new();
+$recv($1)._getBlock_((function(x){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+$3=$recv(x)._root();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx2.sendIdx["root"]=1;
+//>>excludeEnd("ctx");
+$2=$recv($3)._at_("foo");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx2.sendIdx["at:"]=2;
+//>>excludeEnd("ctx");
+return $recv($2)._at_((2));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx2.sendIdx["at:"]=1;
+//>>excludeEnd("ctx");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({x:x},$ctx1,1)});
+//>>excludeEnd("ctx");
+}));
+model=$recv($1)._putBlock_((function(x,y){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv($recv($recv(x)._root())._at_("foo"))._at_put_((2),y);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({x:x,y:y},$ctx1,2)});
+//>>excludeEnd("ctx");
+}));
+$recv(isolator)._model_modify_(model,(function(r){
+result=r;
+return result;
+
+}));
+$self._assert_equals_([(1), [(2), (5)]],result);
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testNontrivialModelGetsAppropriateValueForModification",{isolator:isolator,model:model,result:result},$globals.AxolatorTest)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testNontrivialModelGetsAppropriateValueForModification\x0a| isolator model result |\x0aresult := nil.\x0aisolator := Axolator on: #{ 'foo' -> #('bar' #(1 #(2 5)) 'baz'). 'moo' -> 'zoo' }.\x0amodel := EavModel new\x0a\x09getBlock: [ :x | (x root at: 'foo') at: 2 ];\x0a\x09putBlock: [ :x :y | (x root at: 'foo') at: 2 put: y].\x0aisolator model: model modify: [:r|result := r].\x0aself assert: #(1 #(2 5)) equals: result",
+referencedClasses: ["Axolator", "EavModel"],
+//>>excludeEnd("ide");
+messageSends: ["on:", "getBlock:", "new", "at:", "root", "putBlock:", "at:put:", "model:modify:", "assert:equals:"]
+}),
+$globals.AxolatorTest);
+
+$core.addMethod(
+$core.method({
+selector: "testNontrivialModelModifiesAppropriateValue",
+protocol: "tests",
+fn: function (){
+var self=this,$self=this;
+var isolator,model,result;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $1,$3,$2;
+result=nil;
+isolator=$recv($globals.Axolator)._on_($globals.HashedCollection._newFromPairs_(["foo",["bar", [(1), [(2), (3)]], "baz"],"moo","zoo"]));
+$1=$recv($globals.EavModel)._new();
+$recv($1)._getBlock_((function(x){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+$3=$recv(x)._root();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx2.sendIdx["root"]=1;
+//>>excludeEnd("ctx");
+$2=$recv($3)._at_("foo");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx2.sendIdx["at:"]=2;
+//>>excludeEnd("ctx");
+return $recv($2)._at_((2));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx2.sendIdx["at:"]=1;
+//>>excludeEnd("ctx");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({x:x},$ctx1,1)});
+//>>excludeEnd("ctx");
+}));
+model=$recv($1)._putBlock_((function(x,y){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv($recv($recv(x)._root())._at_("foo"))._at_put_((2),y);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({x:x,y:y},$ctx1,2)});
+//>>excludeEnd("ctx");
+}));
+$recv(isolator)._model_modify_(model,(function(r){
+return "new";
+
+}));
+$recv(isolator)._model_read_(model,(function(r){
+result=r;
+return result;
+
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["model:read:"]=1;
+//>>excludeEnd("ctx");
+$self._assert_equals_("new",result);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["assert:equals:"]=1;
+//>>excludeEnd("ctx");
+$recv(isolator)._model_read_($self["@rootModel"],(function(r){
+result=r;
+return result;
+
+}));
+$self._assert_equals_($globals.HashedCollection._newFromPairs_(["foo",["bar", "new", "baz"],"moo","zoo"]),result);
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testNontrivialModelModifiesAppropriateValue",{isolator:isolator,model:model,result:result},$globals.AxolatorTest)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testNontrivialModelModifiesAppropriateValue\x0a| isolator model result |\x0aresult := nil.\x0aisolator := Axolator on: #{ 'foo' -> #('bar' #(1 #(2 3)) 'baz'). 'moo' -> 'zoo' }.\x0amodel := EavModel new\x0a\x09getBlock: [ :x | (x root at: 'foo') at: 2 ];\x0a\x09putBlock: [ :x :y | (x root at: 'foo') at: 2 put: y].\x0aisolator model: model modify: [:r|#new].\x0aisolator model: model read: [:r|result := r].\x0aself assert: #new equals: result.\x0aisolator model: rootModel read: [:r|result := r].\x0aself assert: #{ 'foo' -> #('bar' #new 'baz'). 'moo' -> 'zoo' } equals: result",
+referencedClasses: ["Axolator", "EavModel"],
+//>>excludeEnd("ide");
+messageSends: ["on:", "getBlock:", "new", "at:", "root", "putBlock:", "at:put:", "model:modify:", "model:read:", "assert:equals:"]
+}),
+$globals.AxolatorTest);
+
+$core.addMethod(
+$core.method({
+selector: "testNontrivialModelReturnsAppropriateValue",
+protocol: "tests",
+fn: function (){
+var self=this,$self=this;
+var isolator,model,result;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+result=nil;
+isolator=$recv($globals.Axolator)._on_($globals.HashedCollection._newFromPairs_(["foo",["bar", [(1), [(2), (3)]], "baz"],"moo","zoo"]));
+model=$recv($recv($globals.EavModel)._new())._getBlock_((function(x){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv($recv($recv(x)._root())._at_("foo"))._at_((2));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx2.sendIdx["at:"]=1;
+//>>excludeEnd("ctx");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({x:x},$ctx1,1)});
+//>>excludeEnd("ctx");
+}));
+$recv(isolator)._model_read_(model,(function(r){
+result=r;
+return result;
+
+}));
+$self._assert_equals_([(1), [(2), (3)]],result);
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testNontrivialModelReturnsAppropriateValue",{isolator:isolator,model:model,result:result},$globals.AxolatorTest)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testNontrivialModelReturnsAppropriateValue\x0a| isolator model result |\x0aresult := nil.\x0aisolator := Axolator on: #{ 'foo' -> #('bar' #(1 #(2 3)) 'baz'). 'moo' -> 'zoo' }.\x0amodel := EavModel new getBlock: [ :x | (x root at: 'foo') at: 2 ].\x0aisolator model: model read: [:r|result := r].\x0aself assert: #(1 #(2 3)) equals: result",
+referencedClasses: ["Axolator", "EavModel"],
+//>>excludeEnd("ide");
+messageSends: ["on:", "getBlock:", "new", "at:", "root", "model:read:", "assert:equals:"]
+}),
+$globals.AxolatorTest);
+
+$core.addMethod(
+$core.method({
+selector: "testRootModelExaminesThenModifiesRoot",
+protocol: "tests",
+fn: function (){
+var self=this,$self=this;
+var isolator,result;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+result=nil;
+isolator=$recv($globals.Axolator)._on_([(1), [(2), (3)]]);
+$recv(isolator)._model_modify_($self["@rootModel"],(function(r){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv(r)._second();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({r:r},$ctx1,1)});
+//>>excludeEnd("ctx");
+}));
+$recv(isolator)._model_read_($self["@rootModel"],(function(r){
+result=r;
+return result;
+
+}));
+$self._assert_equals_([(2), (3)],result);
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testRootModelExaminesThenModifiesRoot",{isolator:isolator,result:result},$globals.AxolatorTest)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testRootModelExaminesThenModifiesRoot\x0a| isolator result |\x0aresult := nil.\x0aisolator := Axolator on: #(1 #(2 3)).\x0aisolator model: rootModel modify: [:r|r second].\x0aisolator model: rootModel read: [:r|result := r].\x0aself assert: #(2 3) equals: result",
+referencedClasses: ["Axolator"],
+//>>excludeEnd("ide");
+messageSends: ["on:", "model:modify:", "second", "model:read:", "assert:equals:"]
+}),
+$globals.AxolatorTest);
+
+$core.addMethod(
+$core.method({
+selector: "testRootModelGetsRootForModification",
+protocol: "tests",
+fn: function (){
+var self=this,$self=this;
+var isolator,result;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+result=nil;
+isolator=$recv($globals.Axolator)._on_([(2), [(1), (0)]]);
+$recv(isolator)._model_modify_($self["@rootModel"],(function(r){
+result=r;
+return result;
+
+}));
+$self._assert_equals_([(2), [(1), (0)]],result);
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testRootModelGetsRootForModification",{isolator:isolator,result:result},$globals.AxolatorTest)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testRootModelGetsRootForModification\x0a| isolator result |\x0aresult := nil.\x0aisolator := Axolator on: #(2 #(1 0)).\x0aisolator model: rootModel modify: [:r|result := r].\x0aself assert: #(2 #(1 0)) equals: result",
+referencedClasses: ["Axolator"],
+//>>excludeEnd("ide");
+messageSends: ["on:", "model:modify:", "assert:equals:"]
+}),
+$globals.AxolatorTest);
+
+$core.addMethod(
+$core.method({
+selector: "testRootModelModifiesAndDeeplyIsolatesInPlaceModifiedRoot",
+protocol: "tests",
+fn: function (){
+var self=this,$self=this;
+var isolator,result,newValue;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+result=nil;
+newValue=nil;
+isolator=$recv($globals.Axolator)._on_([(1), [(2), (3)]]);
+$recv(isolator)._model_modify_($self["@rootModel"],(function(r){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+newValue=r;
+newValue;
+$recv(r)._at_put_((1),(4));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx2.sendIdx["at:put:"]=1;
+//>>excludeEnd("ctx");
+return r;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({r:r},$ctx1,1)});
+//>>excludeEnd("ctx");
+}));
+$recv(newValue)._at_put_((2),"bar");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["at:put:"]=2;
+//>>excludeEnd("ctx");
+$recv(isolator)._model_read_($self["@rootModel"],(function(r){
+result=r;
+return result;
+
+}));
+$recv(newValue)._at_put_((2),"baz");
+$self._assert_equals_([(4), [(2), (3)]],result);
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testRootModelModifiesAndDeeplyIsolatesInPlaceModifiedRoot",{isolator:isolator,result:result,newValue:newValue},$globals.AxolatorTest)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testRootModelModifiesAndDeeplyIsolatesInPlaceModifiedRoot\x0a| isolator result newValue |\x0aresult := nil. newValue := nil.\x0aisolator := Axolator on: #(1 #(2 3)).\x0aisolator model: rootModel modify: [:r|newValue := r. r at: 1 put: 4. r].\x0anewValue at: 2 put: 'bar'.\x0aisolator model: rootModel read: [:r|result := r].\x0anewValue at: 2 put: 'baz'.\x0aself assert: #(4 #(2 3)) equals: result",
+referencedClasses: ["Axolator"],
+//>>excludeEnd("ide");
+messageSends: ["on:", "model:modify:", "at:put:", "model:read:", "assert:equals:"]
+}),
+$globals.AxolatorTest);
+
+$core.addMethod(
+$core.method({
+selector: "testRootModelModifiesAndDeeplyIsolatesRoot",
+protocol: "tests",
+fn: function (){
+var self=this,$self=this;
+var isolator,result,newValue;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $1;
+result=nil;
+isolator=$recv($globals.Axolator)._on_([(1), [(2), (3)]]);
+newValue=$globals.HashedCollection._newFromPairs_(["foo",[(4), (5), (6)]]);
+$recv(isolator)._model_modify_($self["@rootModel"],(function(r){
+return newValue;
+
+}));
+$1=$recv(newValue)._at_("foo");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["at:"]=1;
+//>>excludeEnd("ctx");
+$recv($1)._at_put_((1),"bar");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["at:put:"]=1;
+//>>excludeEnd("ctx");
+$recv(isolator)._model_read_($self["@rootModel"],(function(r){
+result=r;
+return result;
+
+}));
+$recv($recv(newValue)._at_("foo"))._at_put_((3),"baz");
+$self._assert_equals_($globals.HashedCollection._newFromPairs_(["foo",[(4), (5), (6)]]),result);
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testRootModelModifiesAndDeeplyIsolatesRoot",{isolator:isolator,result:result,newValue:newValue},$globals.AxolatorTest)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testRootModelModifiesAndDeeplyIsolatesRoot\x0a| isolator result newValue |\x0aresult := nil.\x0aisolator := Axolator on: #(1 #(2 3)).\x0anewValue := #{'foo'->#(4 5 6)}.\x0aisolator model: rootModel modify: [:r|newValue].\x0a(newValue at: 'foo') at: 1 put: 'bar'.\x0aisolator model: rootModel read: [:r|result := r].\x0a(newValue at: 'foo') at: 3 put: 'baz'.\x0aself assert: #{'foo'->#(4 5 6)} equals: result",
+referencedClasses: ["Axolator"],
+//>>excludeEnd("ide");
+messageSends: ["on:", "model:modify:", "at:put:", "at:", "model:read:", "assert:equals:"]
+}),
+$globals.AxolatorTest);
+
+$core.addMethod(
+$core.method({
+selector: "testRootModelModifiesAndIsolatesRoot",
+protocol: "tests",
+fn: function (){
+var self=this,$self=this;
+var isolator,result,newValue;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+result=nil;
+isolator=$recv($globals.Axolator)._on_([(1), [(2), (3)]]);
+newValue=$globals.HashedCollection._newFromPairs_(["foo",[(4), (5), (6)]]);
+$recv(isolator)._model_modify_($self["@rootModel"],(function(r){
+return newValue;
+
+}));
+$recv(newValue)._at_put_("foo","bar");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["at:put:"]=1;
+//>>excludeEnd("ctx");
+$recv(isolator)._model_read_($self["@rootModel"],(function(r){
+result=r;
+return result;
+
+}));
+$recv(newValue)._at_put_("foo","baz");
+$self._assert_equals_($globals.HashedCollection._newFromPairs_(["foo",[(4), (5), (6)]]),result);
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testRootModelModifiesAndIsolatesRoot",{isolator:isolator,result:result,newValue:newValue},$globals.AxolatorTest)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testRootModelModifiesAndIsolatesRoot\x0a| isolator result newValue |\x0aresult := nil.\x0aisolator := Axolator on: #(1 #(2 3)).\x0anewValue := #{'foo'->#(4 5 6)}.\x0aisolator model: rootModel modify: [:r|newValue].\x0anewValue at: 'foo' put: 'bar'.\x0aisolator model: rootModel read: [:r|result := r].\x0anewValue at: 'foo' put: 'baz'.\x0aself assert: #{'foo'->#(4 5 6)} equals: result",
+referencedClasses: ["Axolator"],
+//>>excludeEnd("ide");
+messageSends: ["on:", "model:modify:", "at:put:", "model:read:", "assert:equals:"]
+}),
+$globals.AxolatorTest);
+
+$core.addMethod(
+$core.method({
+selector: "testRootModelModifiesRoot",
+protocol: "tests",
+fn: function (){
+var self=this,$self=this;
+var isolator,result;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+result=nil;
+isolator=$recv($globals.Axolator)._on_([(1), [(2), (3)]]);
+$recv(isolator)._model_modify_($self["@rootModel"],(function(r){
+return $globals.HashedCollection._newFromPairs_(["foo",[(4), (5), (6)]]);
+
+}));
+$recv(isolator)._model_read_($self["@rootModel"],(function(r){
+result=r;
+return result;
+
+}));
+$self._assert_equals_($globals.HashedCollection._newFromPairs_(["foo",[(4), (5), (6)]]),result);
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testRootModelModifiesRoot",{isolator:isolator,result:result},$globals.AxolatorTest)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testRootModelModifiesRoot\x0a| isolator result |\x0aresult := nil.\x0aisolator := Axolator on: #(1 #(2 3)).\x0aisolator model: rootModel modify: [:r|#{'foo'->#(4 5 6)}].\x0aisolator model: rootModel read: [:r|result := r].\x0aself assert: #{'foo'->#(4 5 6)} equals: result",
+referencedClasses: ["Axolator"],
+//>>excludeEnd("ide");
+messageSends: ["on:", "model:modify:", "model:read:", "assert:equals:"]
+}),
+$globals.AxolatorTest);
+
+$core.addMethod(
+$core.method({
+selector: "testRootModelReturnsDeeplyIsolatedRoot",
+protocol: "tests",
+fn: function (){
+var self=this,$self=this;
+var isolator,result;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+result=nil;
+isolator=$recv($globals.Axolator)._on_([(1), [(2), (3)]]);
+$recv(isolator)._model_read_($self["@rootModel"],(function(r){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv($recv(r)._at_((2)))._at_put_((1),(0));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({r:r},$ctx1,1)});
+//>>excludeEnd("ctx");
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["model:read:"]=1;
+//>>excludeEnd("ctx");
+$recv(isolator)._model_read_($self["@rootModel"],(function(r){
+result=r;
+return result;
+
+}));
+$self._assert_equals_([(1), [(2), (3)]],result);
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testRootModelReturnsDeeplyIsolatedRoot",{isolator:isolator,result:result},$globals.AxolatorTest)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testRootModelReturnsDeeplyIsolatedRoot\x0a| isolator result |\x0aresult := nil.\x0aisolator := Axolator on: #(1 #(2 3)).\x0aisolator model: rootModel read: [:r|(r at: 2) at: 1 put: 0].\x0aisolator model: rootModel read: [:r|result := r].\x0aself assert: #(1 #(2 3)) equals: result",
+referencedClasses: ["Axolator"],
+//>>excludeEnd("ide");
+messageSends: ["on:", "model:read:", "at:put:", "at:", "assert:equals:"]
+}),
+$globals.AxolatorTest);
+
+$core.addMethod(
+$core.method({
+selector: "testRootModelReturnsIsolatedRoot",
+protocol: "tests",
+fn: function (){
+var self=this,$self=this;
+var isolator,result;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+result=nil;
+isolator=$recv($globals.Axolator)._on_([(1), [(2), (4)]]);
+$recv(isolator)._model_read_($self["@rootModel"],(function(r){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv(r)._at_put_((2),nil);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({r:r},$ctx1,1)});
+//>>excludeEnd("ctx");
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["model:read:"]=1;
+//>>excludeEnd("ctx");
+$recv(isolator)._model_read_($self["@rootModel"],(function(r){
+result=r;
+return result;
+
+}));
+$self._assert_equals_([(1), [(2), (4)]],result);
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testRootModelReturnsIsolatedRoot",{isolator:isolator,result:result},$globals.AxolatorTest)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testRootModelReturnsIsolatedRoot\x0a| isolator result |\x0aresult := nil.\x0aisolator := Axolator on: #(1 #(2 4)).\x0aisolator model: rootModel read: [:r|r at: 2 put: nil].\x0aisolator model: rootModel read: [:r|result := r].\x0aself assert: #(1 #(2 4)) equals: result",
+referencedClasses: ["Axolator"],
+//>>excludeEnd("ide");
+messageSends: ["on:", "model:read:", "at:put:", "assert:equals:"]
+}),
+$globals.AxolatorTest);
+
+$core.addMethod(
+$core.method({
+selector: "testRootModelReturnsRoot",
+protocol: "tests",
+fn: function (){
+var self=this,$self=this;
+var isolator,result;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+result=nil;
+isolator=$recv($globals.Axolator)._on_([(1), [(2), (3)]]);
+$recv(isolator)._model_read_($self["@rootModel"],(function(r){
+result=r;
+return result;
+
+}));
+$self._assert_equals_([(1), [(2), (3)]],result);
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testRootModelReturnsRoot",{isolator:isolator,result:result},$globals.AxolatorTest)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testRootModelReturnsRoot\x0a| isolator result |\x0aresult := nil.\x0aisolator := Axolator on: #(1 #(2 3)).\x0aisolator model: rootModel read: [:r|result := r].\x0aself assert: #(1 #(2 3)) equals: result",
+referencedClasses: ["Axolator"],
+//>>excludeEnd("ide");
+messageSends: ["on:", "model:read:", "assert:equals:"]
+}),
+$globals.AxolatorTest);
+
+
+
 $core.addClass("PlainConsumeTransformTest", $globals.TestCase, [], "Axxord-Tests");
 $core.addMethod(
 $core.method({
@@ -269,7 +872,7 @@ $globals.PlainConsumeTransformTest);
 
 $core.addMethod(
 $core.method({
-selector: "testRootTransformBlockIsNotRun",
+selector: "testRootTransformBlockIsRun",
 protocol: "tests",
 fn: function (){
 var self=this,$self=this;
@@ -285,7 +888,8 @@ return $core.withContext(function($ctx2) {
 //>>excludeEnd("ctx");
 return $recv(model)._axes_transform_([],(function(r){
 result=r;
-return result;
+result;
+return model;
 
 }));
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
@@ -294,18 +898,55 @@ return result;
 }))._on_do_($globals.Error,(function(){
 
 }));
-$self._assert_($recv(result)._isNil());
+$self._assert_equals_(result,model);
 return self;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx1) {$ctx1.fill(self,"testRootTransformBlockIsNotRun",{model:model,result:result},$globals.PlainConsumeTransformTest)});
+}, function($ctx1) {$ctx1.fill(self,"testRootTransformBlockIsRun",{model:model,result:result},$globals.PlainConsumeTransformTest)});
 //>>excludeEnd("ctx");
 },
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: [],
-source: "testRootTransformBlockIsNotRun\x0a| model result |\x0aresult := nil.\x0amodel := #(2 #(1 0)).\x0a[model axes: #() transform: [:r | result := r]] on: Error do: [].\x0aself assert: result isNil",
+source: "testRootTransformBlockIsRun\x0a| model result |\x0aresult := nil.\x0amodel := #(2 #(1 0)).\x0a[model axes: #() transform: [:r | result := r. model]] on: Error do: [].\x0aself assert: result equals: model",
 referencedClasses: ["Error"],
 //>>excludeEnd("ide");
-messageSends: ["on:do:", "axes:transform:", "assert:", "isNil"]
+messageSends: ["on:do:", "axes:transform:", "assert:equals:"]
+}),
+$globals.PlainConsumeTransformTest);
+
+$core.addMethod(
+$core.method({
+selector: "testRootTransformFailsOnActualChange",
+protocol: "tests",
+fn: function (){
+var self=this,$self=this;
+var model;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+model=[(2), [(1), (0)]];
+$self._should_raise_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv(model)._axes_transform_([],(function(r){
+return "new";
+
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
+//>>excludeEnd("ctx");
+}),$globals.Error);
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testRootTransformFailsOnActualChange",{model:model},$globals.PlainConsumeTransformTest)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testRootTransformFailsOnActualChange\x0a| model |\x0amodel := #(2 #(1 0)).\x0aself should: [model axes: #() transform: [:r | #new]] raise: Error",
+referencedClasses: ["Error"],
+//>>excludeEnd("ide");
+messageSends: ["should:raise:", "axes:transform:"]
 }),
 $globals.PlainConsumeTransformTest);
 

+ 145 - 3
src/Axxord-Tests.st

@@ -1,4 +1,140 @@
 Smalltalk createPackage: 'Axxord-Tests'!
+TestCase subclass: #AxolatorTest
+	instanceVariableNames: 'rootModel'
+	package: 'Axxord-Tests'!
+
+!AxolatorTest methodsFor: 'running'!
+
+setUp
+
+rootModel := EavModel new
+	getBlock: [:x | x root];
+    putBlock: [:x :y | x root: y].
+! !
+
+!AxolatorTest methodsFor: 'tests'!
+
+testNontrivialModelGetsAppropriateValueForModification
+| isolator model result |
+result := nil.
+isolator := Axolator on: #{ 'foo' -> #('bar' #(1 #(2 5)) 'baz'). 'moo' -> 'zoo' }.
+model := EavModel new
+	getBlock: [ :x | (x root at: 'foo') at: 2 ];
+	putBlock: [ :x :y | (x root at: 'foo') at: 2 put: y].
+isolator model: model modify: [:r|result := r].
+self assert: #(1 #(2 5)) equals: result
+!
+
+testNontrivialModelModifiesAppropriateValue
+| isolator model result |
+result := nil.
+isolator := Axolator on: #{ 'foo' -> #('bar' #(1 #(2 3)) 'baz'). 'moo' -> 'zoo' }.
+model := EavModel new
+	getBlock: [ :x | (x root at: 'foo') at: 2 ];
+	putBlock: [ :x :y | (x root at: 'foo') at: 2 put: y].
+isolator model: model modify: [:r|#new].
+isolator model: model read: [:r|result := r].
+self assert: #new equals: result.
+isolator model: rootModel read: [:r|result := r].
+self assert: #{ 'foo' -> #('bar' #new 'baz'). 'moo' -> 'zoo' } equals: result
+!
+
+testNontrivialModelReturnsAppropriateValue
+| isolator model result |
+result := nil.
+isolator := Axolator on: #{ 'foo' -> #('bar' #(1 #(2 3)) 'baz'). 'moo' -> 'zoo' }.
+model := EavModel new getBlock: [ :x | (x root at: 'foo') at: 2 ].
+isolator model: model read: [:r|result := r].
+self assert: #(1 #(2 3)) equals: result
+!
+
+testRootModelExaminesThenModifiesRoot
+| isolator result |
+result := nil.
+isolator := Axolator on: #(1 #(2 3)).
+isolator model: rootModel modify: [:r|r second].
+isolator model: rootModel read: [:r|result := r].
+self assert: #(2 3) equals: result
+!
+
+testRootModelGetsRootForModification
+| isolator result |
+result := nil.
+isolator := Axolator on: #(2 #(1 0)).
+isolator model: rootModel modify: [:r|result := r].
+self assert: #(2 #(1 0)) equals: result
+!
+
+testRootModelModifiesAndDeeplyIsolatesInPlaceModifiedRoot
+| isolator result newValue |
+result := nil. newValue := nil.
+isolator := Axolator on: #(1 #(2 3)).
+isolator model: rootModel modify: [:r|newValue := r. r at: 1 put: 4. r].
+newValue at: 2 put: 'bar'.
+isolator model: rootModel read: [:r|result := r].
+newValue at: 2 put: 'baz'.
+self assert: #(4 #(2 3)) equals: result
+!
+
+testRootModelModifiesAndDeeplyIsolatesRoot
+| isolator result newValue |
+result := nil.
+isolator := Axolator on: #(1 #(2 3)).
+newValue := #{'foo'->#(4 5 6)}.
+isolator model: rootModel modify: [:r|newValue].
+(newValue at: 'foo') at: 1 put: 'bar'.
+isolator model: rootModel read: [:r|result := r].
+(newValue at: 'foo') at: 3 put: 'baz'.
+self assert: #{'foo'->#(4 5 6)} equals: result
+!
+
+testRootModelModifiesAndIsolatesRoot
+| isolator result newValue |
+result := nil.
+isolator := Axolator on: #(1 #(2 3)).
+newValue := #{'foo'->#(4 5 6)}.
+isolator model: rootModel modify: [:r|newValue].
+newValue at: 'foo' put: 'bar'.
+isolator model: rootModel read: [:r|result := r].
+newValue at: 'foo' put: 'baz'.
+self assert: #{'foo'->#(4 5 6)} equals: result
+!
+
+testRootModelModifiesRoot
+| isolator result |
+result := nil.
+isolator := Axolator on: #(1 #(2 3)).
+isolator model: rootModel modify: [:r|#{'foo'->#(4 5 6)}].
+isolator model: rootModel read: [:r|result := r].
+self assert: #{'foo'->#(4 5 6)} equals: result
+!
+
+testRootModelReturnsDeeplyIsolatedRoot
+| isolator result |
+result := nil.
+isolator := Axolator on: #(1 #(2 3)).
+isolator model: rootModel read: [:r|(r at: 2) at: 1 put: 0].
+isolator model: rootModel read: [:r|result := r].
+self assert: #(1 #(2 3)) equals: result
+!
+
+testRootModelReturnsIsolatedRoot
+| isolator result |
+result := nil.
+isolator := Axolator on: #(1 #(2 4)).
+isolator model: rootModel read: [:r|r at: 2 put: nil].
+isolator model: rootModel read: [:r|result := r].
+self assert: #(1 #(2 4)) equals: result
+!
+
+testRootModelReturnsRoot
+| isolator result |
+result := nil.
+isolator := Axolator on: #(1 #(2 3)).
+isolator model: rootModel read: [:r|result := r].
+self assert: #(1 #(2 3)) equals: result
+! !
+
 TestCase subclass: #PlainConsumeTransformTest
 	instanceVariableNames: ''
 	package: 'Axxord-Tests'!
@@ -66,12 +202,18 @@ model axes: #() consume: [:r | result := r].
 self assert: #(1 #(2 3)) equals: result
 !
 
-testRootTransformBlockIsNotRun
+testRootTransformBlockIsRun
 | model result |
 result := nil.
 model := #(2 #(1 0)).
-[model axes: #() transform: [:r | result := r]] on: Error do: [].
-self assert: result isNil
+[model axes: #() transform: [:r | result := r. model]] on: Error do: [].
+self assert: result equals: model
+!
+
+testRootTransformFailsOnActualChange
+| model |
+model := #(2 #(1 0)).
+self should: [model axes: #() transform: [:r | #new]] raise: Error
 ! !
 
 DumbAxon subclass: #TestSpyAxon

+ 223 - 12
src/Axxord.js

@@ -208,6 +208,119 @@ messageSends: ["tokenize:", "do:", "whileTrue:", "and:", "notEmpty", "=", "first
 $globals.Axes.a$cls);
 
 
+$core.addClass("Axolator", $globals.Object, ["root"], "Axxord");
+$core.addMethod(
+$core.method({
+selector: "model:modify:",
+protocol: "action",
+fn: function (anEavModel,aBlock){
+var self=this,$self=this;
+var newValue;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+newValue=$recv(aBlock)._value_($recv(anEavModel)._on_(self));
+$recv(anEavModel)._on_put_(self,$recv(newValue)._deepCopy());
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"model:modify:",{anEavModel:anEavModel,aBlock:aBlock,newValue:newValue},$globals.Axolator)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["anEavModel", "aBlock"],
+source: "model: anEavModel modify: aBlock\x0a\x0a| newValue |\x0anewValue := aBlock value: (anEavModel on: self).\x0aanEavModel on: self put: newValue deepCopy",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["value:", "on:", "on:put:", "deepCopy"]
+}),
+$globals.Axolator);
+
+$core.addMethod(
+$core.method({
+selector: "model:read:",
+protocol: "action",
+fn: function (anEavModel,aBlock){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$recv(aBlock)._value_($recv($recv(anEavModel)._on_(self))._deepCopy());
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"model:read:",{anEavModel:anEavModel,aBlock:aBlock},$globals.Axolator)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["anEavModel", "aBlock"],
+source: "model: anEavModel read: aBlock\x0a\x0aaBlock value: (anEavModel on: self) deepCopy",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["value:", "deepCopy", "on:"]
+}),
+$globals.Axolator);
+
+$core.addMethod(
+$core.method({
+selector: "root",
+protocol: "accessing",
+fn: function (){
+var self=this,$self=this;
+return $self["@root"];
+
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "root\x0a\x0a^root",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: []
+}),
+$globals.Axolator);
+
+$core.addMethod(
+$core.method({
+selector: "root:",
+protocol: "accessing",
+fn: function (anObject){
+var self=this,$self=this;
+$self["@root"]=anObject;
+return self;
+
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["anObject"],
+source: "root: anObject\x0a\x0aroot := anObject",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: []
+}),
+$globals.Axolator);
+
+
+$core.addMethod(
+$core.method({
+selector: "on:",
+protocol: "instance creation",
+fn: function (anObject){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+return $recv($self._new())._root_(anObject);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"on:",{anObject:anObject},$globals.Axolator.a$cls)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["anObject"],
+source: "on: anObject\x0a^self new root: anObject",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["root:", "new"]
+}),
+$globals.Axolator.a$cls);
+
+
 $core.addClass("Axon", $globals.Object, ["factory"], "Axxord");
 //>>excludeStart("ide", pragmas.excludeIdeData);
 $globals.Axon.comment="I represent a pub-sub based on a key (called 'aspect').\x0aI manage aspect-block subscriptions (called 'interests') as well as run blocks of dirtied interests.\x0aThe interest objects are responsible of decision if the change of an aspect is relevant for them.\x0aInterest object must be subclasses of `AxonInterest`.\x0a\x0aMy subclasses must provide implementation for:\x0a\x0a - add:\x0a - do:\x0a - clean";
@@ -822,6 +935,102 @@ $globals.InterestedInEqual);
 
 
 
+$core.addClass("InterestedThruAxes", $globals.AxonInterest, [], "Axxord");
+$core.addMethod(
+$core.method({
+selector: "accepts:",
+protocol: "testing",
+fn: function (anAspect){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $2,$3,$1,$5,$6,$4;
+$2=$recv(anAspect)._size();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["size"]=1;
+//>>excludeEnd("ctx");
+$3=$recv($self["@aspect"])._size();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["size"]=2;
+//>>excludeEnd("ctx");
+$1=$recv($2).__lt_eq($3);
+if($core.assert($1)){
+$5=$self["@aspect"];
+$6=$recv(anAspect)._size();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["size"]=3;
+//>>excludeEnd("ctx");
+$4=$recv($5)._copyFrom_to_((1),$6);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["copyFrom:to:"]=1;
+//>>excludeEnd("ctx");
+return $recv(anAspect).__eq($4);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["="]=1;
+//>>excludeEnd("ctx");
+} else {
+return $recv($self["@aspect"]).__eq($recv(anAspect)._copyFrom_to_((1),$recv($self["@aspect"])._size()));
+}
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"accepts:",{anAspect:anAspect},$globals.InterestedThruAxes)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["anAspect"],
+source: "accepts: anAspect\x0a    ^anAspect size <= aspect size\x0a\x09\x09ifTrue: [anAspect = (aspect copyFrom: 1 to: anAspect size)]\x0a\x09\x09ifFalse: [aspect = (anAspect copyFrom: 1 to: aspect size)]",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["ifTrue:ifFalse:", "<=", "size", "=", "copyFrom:to:"]
+}),
+$globals.InterestedThruAxes);
+
+
+
+$core.addClass("InterestedUpToAxes", $globals.AxonInterest, [], "Axxord");
+$core.addMethod(
+$core.method({
+selector: "accepts:",
+protocol: "testing",
+fn: function (anAspect){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $2,$3,$1;
+$2=$recv(anAspect)._size();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["size"]=1;
+//>>excludeEnd("ctx");
+$3=$recv($self["@aspect"])._size();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["size"]=2;
+//>>excludeEnd("ctx");
+$1=$recv($2).__lt_eq($3);
+return $recv($1)._and_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv(anAspect).__eq($recv($self["@aspect"])._copyFrom_to_((1),$recv(anAspect)._size()));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
+//>>excludeEnd("ctx");
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"accepts:",{anAspect:anAspect},$globals.InterestedUpToAxes)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["anAspect"],
+source: "accepts: anAspect\x0a    ^anAspect size <= aspect size and: [anAspect = (aspect copyFrom: 1 to: anAspect size)]",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["and:", "<=", "size", "=", "copyFrom:to:"]
+}),
+$globals.InterestedUpToAxes);
+
+
+
 $core.addClass("AxonOff", $globals.Error, [], "Axxord");
 //>>excludeStart("ide", pragmas.excludeIdeData);
 $globals.AxonOff.comment="Signal me from the subscription block to unsubscribe it.";
@@ -1217,26 +1426,28 @@ selector: "axes:transform:",
 protocol: "*Axxord",
 fn: function (aCollection,aBlock){
 var self=this,$self=this;
-var value;
+var value,newValue;
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
-var $1,$receiver;
+var $1,$2,$receiver;
 var $early={};
 try {
-$recv(aCollection)._last();
 value=$self._atAxes_ifAbsent_(aCollection,(function(){
 throw $early=[self];
 
 }));
-value=$recv(aBlock)._value_(value);
-value=$self._atAxes_ifAbsent_put_(aCollection,(function(){
+newValue=$recv(aBlock)._value_(value);
+$1=$recv(value).__eq_eq(newValue);
+if(!$core.assert($1)){
+$self._atAxes_ifAbsent_put_(aCollection,(function(){
 throw $early=[self];
 
-}),value);
-$1=$self._registeredAxon();
-if(($receiver = $1) == null || $receiver.a$nil){
-$1;
+}),newValue);
+}
+$2=$self._registeredAxon();
+if(($receiver = $2) == null || $receiver.a$nil){
+$2;
 } else {
 var axon;
 axon=$receiver;
@@ -1246,15 +1457,15 @@ return self;
 }
 catch(e) {if(e===$early)return e[0]; throw e}
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx1) {$ctx1.fill(self,"axes:transform:",{aCollection:aCollection,aBlock:aBlock,value:value},$globals.Object)});
+}, function($ctx1) {$ctx1.fill(self,"axes:transform:",{aCollection:aCollection,aBlock:aBlock,value:value,newValue:newValue},$globals.Object)});
 //>>excludeEnd("ctx");
 },
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: ["aCollection", "aBlock"],
-source: "axes: aCollection transform: aBlock\x0a\x09| value |\x0a\x09aCollection last. \x22raise if empty\x22\x0a\x09value := self atAxes: aCollection ifAbsent: [ ^self ].\x0a\x09value := aBlock value: value.\x0a\x09value := self atAxes: aCollection ifAbsent: [ ^self ] put: value.\x0a\x09self registeredAxon ifNotNil: [:axon | axon changed: aCollection]",
+source: "axes: aCollection transform: aBlock\x0a\x09| value newValue |\x0a\x09value := self atAxes: aCollection ifAbsent: [ ^self ].\x0a\x09newValue := aBlock value: value.\x0a\x09value == newValue ifFalse: [ self atAxes: aCollection ifAbsent: [ ^self ] put: newValue ].\x0a\x09self registeredAxon ifNotNil: [:axon | axon changed: aCollection]",
 referencedClasses: [],
 //>>excludeEnd("ide");
-messageSends: ["last", "atAxes:ifAbsent:", "value:", "atAxes:ifAbsent:put:", "ifNotNil:", "registeredAxon", "changed:"]
+messageSends: ["atAxes:ifAbsent:", "value:", "ifFalse:", "==", "atAxes:ifAbsent:put:", "ifNotNil:", "registeredAxon", "changed:"]
 }),
 $globals.Object);
 

+ 61 - 4
src/Axxord.st

@@ -24,6 +24,42 @@ parse: message
 	^ result
 ! !
 
+Object subclass: #Axolator
+	instanceVariableNames: 'root'
+	package: 'Axxord'!
+
+!Axolator methodsFor: 'accessing'!
+
+root
+
+^root
+!
+
+root: anObject
+
+root := anObject
+! !
+
+!Axolator methodsFor: 'action'!
+
+model: anEavModel modify: aBlock
+
+| newValue |
+newValue := aBlock value: (anEavModel on: self).
+anEavModel on: self put: newValue deepCopy
+!
+
+model: anEavModel read: aBlock
+
+aBlock value: (anEavModel on: self) deepCopy
+! !
+
+!Axolator class methodsFor: 'instance creation'!
+
+on: anObject
+^self new root: anObject
+! !
+
 Object subclass: #Axon
 	instanceVariableNames: 'factory'
 	package: 'Axxord'!
@@ -193,6 +229,28 @@ accepts: anAspect
     ^ anAspect = aspect
 ! !
 
+AxonInterest subclass: #InterestedThruAxes
+	instanceVariableNames: ''
+	package: 'Axxord'!
+
+!InterestedThruAxes methodsFor: 'testing'!
+
+accepts: anAspect
+    ^anAspect size <= aspect size
+		ifTrue: [anAspect = (aspect copyFrom: 1 to: anAspect size)]
+		ifFalse: [aspect = (anAspect copyFrom: 1 to: aspect size)]
+! !
+
+AxonInterest subclass: #InterestedUpToAxes
+	instanceVariableNames: ''
+	package: 'Axxord'!
+
+!InterestedUpToAxes methodsFor: 'testing'!
+
+accepts: anAspect
+    ^anAspect size <= aspect size and: [anAspect = (aspect copyFrom: 1 to: anAspect size)]
+! !
+
 Error subclass: #AxonOff
 	instanceVariableNames: ''
 	package: 'Axxord'!
@@ -274,11 +332,10 @@ axes: aCollection consume: aBlock
 !
 
 axes: aCollection transform: aBlock
-	| value |
-	aCollection last. "raise if empty"
+	| value newValue |
 	value := self atAxes: aCollection ifAbsent: [ ^self ].
-	value := aBlock value: value.
-	value := self atAxes: aCollection ifAbsent: [ ^self ] put: value.
+	newValue := aBlock value: value.
+	value == newValue ifFalse: [ self atAxes: aCollection ifAbsent: [ ^self ] put: newValue ].
 	self registeredAxon ifNotNil: [:axon | axon changed: aCollection]
 !