Prechádzať zdrojové kódy

axons monkeypatched into objs; Axon & LYst BAsed BlAckboard

Herbert Vojčík 9 rokov pred
rodič
commit
8cae687cec
11 zmenil súbory, kde vykonal 790 pridanie a 4 odobranie
  1. 9 1
      Gruntfile.js
  2. 1 0
      local.amd.json
  3. 414 0
      src/Alybaba-Tests.js
  4. 106 0
      src/Alybaba-Tests.st
  5. 97 0
      src/Alybaba.js
  6. 19 0
      src/Alybaba.st
  7. 108 0
      src/Axon.js
  8. 32 0
      src/Axon.st
  9. 2 2
      src/Trapped-Backend.js
  10. 1 1
      src/Trapped-Backend.st
  11. 1 0
      testing.js

+ 9 - 1
Gruntfile.js

@@ -11,7 +11,7 @@ module.exports = function (grunt) {
         helpers = require('amber-dev').helpers;
 
     // Default task.
-    grunt.registerTask('default', ['amdconfig:app', 'amberc:axon', 'amberc:all']);
+    grunt.registerTask('default', ['amdconfig:app', 'amberc:axon', 'amberc:alybaba', 'amberc:all']);
     grunt.registerTask('test', ['amdconfig:app', 'requirejs:test_runner', 'exec:test_runner', 'clean:test_runner']);
     grunt.registerTask('devel', ['amdconfig:app', 'requirejs:devel']);
     grunt.registerTask('deploy', ['amdconfig:app', 'requirejs:deploy']);
@@ -55,6 +55,14 @@ module.exports = function (grunt) {
                 amd_namespace: 'trapped',
                 libraries: ['axon/Axon', 'amber_core/SUnit', 'amber/web/Web']
             },
+            alybaba: {
+                src: [
+                    'src/Alybaba.st',  // list all sources in dependency order
+                    // list all tests in dependency order
+                ],
+                amd_namespace: 'alybaba',
+                libraries: ['amber_core/SUnit']
+            },
             counter: {
                 src: [
                     'example-counter/src/Trapped-Counter.st'

+ 1 - 0
local.amd.json

@@ -2,6 +2,7 @@
     "paths": {
         "trapped": "src",
         "axon": "src",
+        "alybaba": "src",
         "trapped-todo": "example-todo/src",
         "trapped-counter": "example-counter/src"
     }

+ 414 - 0
src/Alybaba-Tests.js

@@ -0,0 +1,414 @@
+define("alybaba/Alybaba-Tests", ["amber/boot", "amber_core/SUnit", "axon/Axon"], function($boot){"use strict";
+var $core=$boot.api,nil=$boot.nil,$recv=$boot.asReceiver,$globals=$boot.globals;
+$core.addPackage('Alybaba-Tests');
+$core.packages["Alybaba-Tests"].innerEval = function (expr) { return eval(expr); };
+$core.packages["Alybaba-Tests"].transport = {"type":"amd","amdNamespace":"alybaba"};
+
+$core.addClass('PlainConsumeTransformTest', $globals.TestCase, [], 'Alybaba-Tests');
+$core.addMethod(
+$core.method({
+selector: "testModelTransformSentToAxon",
+protocol: 'tests',
+fn: function (){
+var self=this;
+var model,result,axon;
+function $TestSpyAxon(){return $globals.TestSpyAxon||(typeof TestSpyAxon=="undefined"?nil:TestSpyAxon)}
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+result=nil;
+model=$globals.HashedCollection._newFromPairs_(["foo",["bar", [(1), [(2), (3)]], "baz"],"moo","zoo"]);
+axon=$recv($TestSpyAxon())._new();
+$recv(axon)._registerIn_(model);
+$recv(model)._atLyst_transform_(["foo", (2)],(function(r){
+return "new";
+
+}));
+self._assert_equals_($recv(axon)._changedAspectLog(),[["foo", (2)]]);
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testModelTransformSentToAxon",{model:model,result:result,axon:axon},$globals.PlainConsumeTransformTest)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testModelTransformSentToAxon\x0a| model result axon |\x0aresult := nil.\x0amodel := #{ 'foo' -> #('bar' #(1 #(2 3)) 'baz'). 'moo' -> 'zoo' }.\x0aaxon := TestSpyAxon new.\x0aaxon registerIn: model.\x0amodel atLyst: #(foo 2) transform: [:r | #new].\x0aself assert: axon changedAspectLog equals: #((foo 2))",
+referencedClasses: ["TestSpyAxon"],
+//>>excludeEnd("ide");
+messageSends: ["new", "registerIn:", "atLyst:transform:", "assert:equals:", "changedAspectLog"]
+}),
+$globals.PlainConsumeTransformTest);
+
+$core.addMethod(
+$core.method({
+selector: "testNontrivialModelGetsAppropriateValueForTransform",
+protocol: 'tests',
+fn: function (){
+var self=this;
+var model,result;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+result=nil;
+model=$globals.HashedCollection._newFromPairs_(["foo",["bar", [(1), [(2), (5)]], "baz"],"moo","zoo"]);
+$recv(model)._atLyst_transform_(["foo", (2)],(function(r){
+result=r;
+return result;
+
+}));
+self._assert_equals_([(1), [(2), (5)]],result);
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testNontrivialModelGetsAppropriateValueForTransform",{model:model,result:result},$globals.PlainConsumeTransformTest)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testNontrivialModelGetsAppropriateValueForTransform\x0a| model result |\x0aresult := nil.\x0amodel := #{ 'foo' -> #('bar' #(1 #(2 5)) 'baz'). 'moo' -> 'zoo' }.\x0amodel atLyst: #(foo 2) transform: [:r | result := r].\x0aself assert: #(1 #(2 5)) equals: result",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["atLyst:transform:", "assert:equals:"]
+}),
+$globals.PlainConsumeTransformTest);
+
+$core.addMethod(
+$core.method({
+selector: "testNontrivialModelReturnsAppropriateValue",
+protocol: 'tests',
+fn: function (){
+var self=this;
+var model,result;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+result=nil;
+model=$globals.HashedCollection._newFromPairs_(["foo",["bar", [(1), [(2), (3)]], "baz"],"moo","zoo"]);
+$recv(model)._atLyst_consume_(["foo", (2)],(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",{model:model,result:result},$globals.PlainConsumeTransformTest)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testNontrivialModelReturnsAppropriateValue\x0a| model result |\x0aresult := nil.\x0amodel := #{ 'foo' -> #('bar' #(1 #(2 3)) 'baz'). 'moo' -> 'zoo' }.\x0amodel atLyst: #(foo 2) consume: [:r | result := r].\x0aself assert: #(1 #(2 3)) equals: result",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["atLyst:consume:", "assert:equals:"]
+}),
+$globals.PlainConsumeTransformTest);
+
+$core.addMethod(
+$core.method({
+selector: "testNontrivialModelTransformsAppropriateValue",
+protocol: 'tests',
+fn: function (){
+var self=this;
+var model,result;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+result=nil;
+model=$globals.HashedCollection._newFromPairs_(["foo",["bar", [(1), [(2), (3)]], "baz"],"moo","zoo"]);
+$recv(model)._atLyst_transform_(["foo", (2)],(function(r){
+return "new";
+
+}));
+$recv(model)._atLyst_consume_(["foo", (2)],(function(r){
+result=r;
+return result;
+
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["atLyst:consume:"]=1;
+//>>excludeEnd("ctx");
+self._assert_equals_("new",result);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["assert:equals:"]=1;
+//>>excludeEnd("ctx");
+$recv(model)._atLyst_consume_([],(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,"testNontrivialModelTransformsAppropriateValue",{model:model,result:result},$globals.PlainConsumeTransformTest)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testNontrivialModelTransformsAppropriateValue\x0a| model result |\x0aresult := nil.\x0amodel := #{ 'foo' -> #('bar' #(1 #(2 3)) 'baz'). 'moo' -> 'zoo' }.\x0amodel atLyst: #(foo 2) transform: [:r | #new].\x0amodel atLyst: #(foo 2) consume: [:r | result := r].\x0aself assert: #new equals: result.\x0amodel atLyst: #() consume: [:r | result := r].\x0aself assert: #{ 'foo' -> #('bar' #new 'baz'). 'moo' -> 'zoo' } equals: result",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["atLyst:transform:", "atLyst:consume:", "assert:equals:"]
+}),
+$globals.PlainConsumeTransformTest);
+
+$core.addMethod(
+$core.method({
+selector: "testRootModelCannotTransform",
+protocol: 'tests',
+fn: function (){
+var self=this;
+var model,result;
+function $Error(){return $globals.Error||(typeof Error=="undefined"?nil:Error)}
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+result=nil;
+model=[(1), [(2), (3)]];
+self._should_raise_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv(model)._atLyst_transform_([],(function(r){
+return $globals.HashedCollection._newFromPairs_(["foo",[(4), (5), (6)]]);
+
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
+//>>excludeEnd("ctx");
+}),$Error());
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testRootModelCannotTransform",{model:model,result:result},$globals.PlainConsumeTransformTest)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testRootModelCannotTransform\x0a| model result |\x0aresult := nil.\x0amodel := #(1 #(2 3)).\x0aself should: [ model atLyst: #() transform: [:r | #{'foo'->#(4 5 6)}] ] raise: Error",
+referencedClasses: ["Error"],
+//>>excludeEnd("ide");
+messageSends: ["should:raise:", "atLyst:transform:"]
+}),
+$globals.PlainConsumeTransformTest);
+
+$core.addMethod(
+$core.method({
+selector: "testRootModelReturnsDirectRoot",
+protocol: 'tests',
+fn: function (){
+var self=this;
+var model,result;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+result=nil;
+model=[(1), [(2), (4)]];
+$recv(model)._atLyst_consume_([],(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["atLyst:consume:"]=1;
+//>>excludeEnd("ctx");
+$recv(model)._atLyst_consume_([],(function(r){
+result=r;
+return result;
+
+}));
+self._assert_equals_([(1), nil],result);
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testRootModelReturnsDirectRoot",{model:model,result:result},$globals.PlainConsumeTransformTest)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testRootModelReturnsDirectRoot\x0a| model result |\x0aresult := nil.\x0amodel := #(1 #(2 4)).\x0amodel atLyst: #() consume: [:r | r at: 2 put: nil].\x0amodel atLyst: #() consume: [:r | result := r].\x0aself assert: #(1 nil) equals: result",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["atLyst:consume:", "at:put:", "assert:equals:"]
+}),
+$globals.PlainConsumeTransformTest);
+
+$core.addMethod(
+$core.method({
+selector: "testRootModelReturnsRoot",
+protocol: 'tests',
+fn: function (){
+var self=this;
+var model,result;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+result=nil;
+model=[(1), [(2), (3)]];
+$recv(model)._atLyst_consume_([],(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",{model:model,result:result},$globals.PlainConsumeTransformTest)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "testRootModelReturnsRoot\x0a| model result |\x0aresult := nil.\x0amodel := #(1 #(2 3)).\x0amodel atLyst: #() consume: [:r | result := r].\x0aself assert: #(1 #(2 3)) equals: result",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["atLyst:consume:", "assert:equals:"]
+}),
+$globals.PlainConsumeTransformTest);
+
+$core.addMethod(
+$core.method({
+selector: "testRootTransformBlockIsNotRun",
+protocol: 'tests',
+fn: function (){
+var self=this;
+var model,result;
+function $Error(){return $globals.Error||(typeof Error=="undefined"?nil:Error)}
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+result=nil;
+model=[(2), [(1), (0)]];
+$recv((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv(model)._atLyst_transform_([],(function(r){
+result=r;
+return result;
+
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
+//>>excludeEnd("ctx");
+}))._on_do_($Error(),(function(){
+
+}));
+self._assert_($recv(result)._isNil());
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testRootTransformBlockIsNotRun",{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 atLyst: #() transform: [:r | result := r]] on: Error do: [].\x0aself assert: result isNil",
+referencedClasses: ["Error"],
+//>>excludeEnd("ide");
+messageSends: ["on:do:", "atLyst:transform:", "assert:", "isNil"]
+}),
+$globals.PlainConsumeTransformTest);
+
+
+
+$core.addClass('TestSpyAxon', $globals.DumbAxon, ['changedAspectLog'], 'Alybaba-Tests');
+//>>excludeStart("ide", pragmas.excludeIdeData);
+$globals.TestSpyAxon.comment="I am an axon that logs changed aspects. I am useful in tests.";
+//>>excludeEnd("ide");
+$core.addMethod(
+$core.method({
+selector: "changed:",
+protocol: 'action',
+fn: function (anAspect){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$recv(self["@changedAspectLog"])._add_(anAspect);
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"changed:",{anAspect:anAspect},$globals.TestSpyAxon)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["anAspect"],
+source: "changed: anAspect\x0a\x09changedAspectLog add: anAspect",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["add:"]
+}),
+$globals.TestSpyAxon);
+
+$core.addMethod(
+$core.method({
+selector: "changedAspectLog",
+protocol: 'accessing',
+fn: function (){
+var self=this;
+var $1;
+$1=self["@changedAspectLog"];
+return $1;
+
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "changedAspectLog\x0a\x09^ changedAspectLog",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: []
+}),
+$globals.TestSpyAxon);
+
+$core.addMethod(
+$core.method({
+selector: "changedAspectLog:",
+protocol: 'accessing',
+fn: function (anObject){
+var self=this;
+self["@changedAspectLog"]=anObject;
+return self;
+
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["anObject"],
+source: "changedAspectLog: anObject\x0a\x09changedAspectLog := anObject",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: []
+}),
+$globals.TestSpyAxon);
+
+$core.addMethod(
+$core.method({
+selector: "initialize",
+protocol: 'initialization',
+fn: function (){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+(
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.supercall = true, 
+//>>excludeEnd("ctx");
+$globals.TestSpyAxon.superclass.fn.prototype._initialize.apply($recv(self), []));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.supercall = false;
+//>>excludeEnd("ctx");;
+self["@changedAspectLog"]=[];
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"initialize",{},$globals.TestSpyAxon)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "initialize\x0a\x09super initialize.\x0a\x0a\x09changedAspectLog := #()",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["initialize"]
+}),
+$globals.TestSpyAxon);
+
+
+});

+ 106 - 0
src/Alybaba-Tests.st

@@ -0,0 +1,106 @@
+Smalltalk createPackage: 'Alybaba-Tests'!
+TestCase subclass: #PlainConsumeTransformTest
+	instanceVariableNames: ''
+	package: 'Alybaba-Tests'!
+
+!PlainConsumeTransformTest methodsFor: 'tests'!
+
+testModelTransformSentToAxon
+| model result axon |
+result := nil.
+model := #{ 'foo' -> #('bar' #(1 #(2 3)) 'baz'). 'moo' -> 'zoo' }.
+axon := TestSpyAxon new.
+axon registerIn: model.
+model atLyst: #(foo 2) transform: [:r | #new].
+self assert: axon changedAspectLog equals: #((foo 2))
+!
+
+testNontrivialModelGetsAppropriateValueForTransform
+| model result |
+result := nil.
+model := #{ 'foo' -> #('bar' #(1 #(2 5)) 'baz'). 'moo' -> 'zoo' }.
+model atLyst: #(foo 2) transform: [:r | result := r].
+self assert: #(1 #(2 5)) equals: result
+!
+
+testNontrivialModelReturnsAppropriateValue
+| model result |
+result := nil.
+model := #{ 'foo' -> #('bar' #(1 #(2 3)) 'baz'). 'moo' -> 'zoo' }.
+model atLyst: #(foo 2) consume: [:r | result := r].
+self assert: #(1 #(2 3)) equals: result
+!
+
+testNontrivialModelTransformsAppropriateValue
+| model result |
+result := nil.
+model := #{ 'foo' -> #('bar' #(1 #(2 3)) 'baz'). 'moo' -> 'zoo' }.
+model atLyst: #(foo 2) transform: [:r | #new].
+model atLyst: #(foo 2) consume: [:r | result := r].
+self assert: #new equals: result.
+model atLyst: #() consume: [:r | result := r].
+self assert: #{ 'foo' -> #('bar' #new 'baz'). 'moo' -> 'zoo' } equals: result
+!
+
+testRootModelCannotTransform
+| model result |
+result := nil.
+model := #(1 #(2 3)).
+self should: [ model atLyst: #() transform: [:r | #{'foo'->#(4 5 6)}] ] raise: Error
+!
+
+testRootModelReturnsDirectRoot
+| model result |
+result := nil.
+model := #(1 #(2 4)).
+model atLyst: #() consume: [:r | r at: 2 put: nil].
+model atLyst: #() consume: [:r | result := r].
+self assert: #(1 nil) equals: result
+!
+
+testRootModelReturnsRoot
+| model result |
+result := nil.
+model := #(1 #(2 3)).
+model atLyst: #() consume: [:r | result := r].
+self assert: #(1 #(2 3)) equals: result
+!
+
+testRootTransformBlockIsNotRun
+| model result |
+result := nil.
+model := #(2 #(1 0)).
+[model atLyst: #() transform: [:r | result := r]] on: Error do: [].
+self assert: result isNil
+! !
+
+DumbAxon subclass: #TestSpyAxon
+	instanceVariableNames: 'changedAspectLog'
+	package: 'Alybaba-Tests'!
+!TestSpyAxon commentStamp!
+I am an axon that logs changed aspects. I am useful in tests.!
+
+!TestSpyAxon methodsFor: 'accessing'!
+
+changedAspectLog
+	^ changedAspectLog
+!
+
+changedAspectLog: anObject
+	changedAspectLog := anObject
+! !
+
+!TestSpyAxon methodsFor: 'action'!
+
+changed: anAspect
+	changedAspectLog add: anAspect
+! !
+
+!TestSpyAxon methodsFor: 'initialization'!
+
+initialize
+	super initialize.
+
+	changedAspectLog := #()
+! !
+

+ 97 - 0
src/Alybaba.js

@@ -0,0 +1,97 @@
+define(["amber/boot"
+//>>excludeStart("imports", pragmas.excludeImports);
+, "axon/Axon", "lyst/Lyst"
+//>>excludeEnd("imports");
+, "amber_core/Kernel-Objects"], function($boot
+//>>excludeStart("imports", pragmas.excludeImports);
+
+//>>excludeEnd("imports");
+){"use strict";
+if(!$boot.nilAsReceiver)$boot.nilAsReceiver=$boot.nil;
+if(!("nilAsValue" in $boot))$boot.nilAsValue=$boot.nilAsReceiver;
+var $core=$boot.api,nil=$boot.nilAsValue,$nil=$boot.nilAsReceiver,$recv=$boot.asReceiver,$globals=$boot.globals;
+if(!$boot.nilAsClass)$boot.nilAsClass=$boot.dnu;
+$core.addPackage("Alybaba");
+$core.packages["Alybaba"].innerEval = function (expr) { return eval(expr); };
+$core.packages["Alybaba"].imports = ["axon/Axon", "lyst/Lyst"];
+$core.packages["Alybaba"].transport = {"type":"amd","amdNamespace":"alybaba"};
+$core.addMethod(
+$core.method({
+selector: "atLyst:consume:",
+protocol: "*Alybaba",
+fn: function (aCollection,aBlock){
+var self=this,$self=this;
+var value;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $early={};
+try {
+value=$self._atLyst_ifAbsent_(aCollection,(function(){
+throw $early=[self];
+
+}));
+return $recv(aBlock)._value_(value);
+}
+catch(e) {if(e===$early)return e[0]; throw e}
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"atLyst:consume:",{aCollection:aCollection,aBlock:aBlock,value:value},$globals.Object)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["aCollection", "aBlock"],
+source: "atLyst: aCollection consume: aBlock\x0a\x09| value |\x0a\x09value := self atLyst: aCollection ifAbsent: [ ^self ].\x0a\x09^ aBlock value: value",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["atLyst:ifAbsent:", "value:"]
+}),
+$globals.Object);
+
+$core.addMethod(
+$core.method({
+selector: "atLyst:transform:",
+protocol: "*Alybaba",
+fn: function (aCollection,aBlock){
+var self=this,$self=this;
+var value;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $1,$receiver;
+var $early={};
+try {
+$recv(aCollection)._last();
+value=$self._atLyst_ifAbsent_(aCollection,(function(){
+throw $early=[self];
+
+}));
+value=$recv(aBlock)._value_(value);
+value=$self._atLyst_ifAbsent_put_(aCollection,(function(){
+throw $early=[self];
+
+}),value);
+$1=$self._registeredAxon();
+if(($receiver = $1) == null || $receiver.a$nil){
+$1;
+} else {
+var axon;
+axon=$receiver;
+$recv(axon)._changed_(aCollection);
+}
+return self;
+}
+catch(e) {if(e===$early)return e[0]; throw e}
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"atLyst:transform:",{aCollection:aCollection,aBlock:aBlock,value:value},$globals.Object)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["aCollection", "aBlock"],
+source: "atLyst: aCollection transform: aBlock\x0a\x09| value |\x0a\x09aCollection last. \x22raise if empty\x22\x0a\x09value := self atLyst: aCollection ifAbsent: [ ^self ].\x0a\x09value := aBlock value: value.\x0a\x09value := self atLyst: aCollection ifAbsent: [ ^self ] put: value.\x0a\x09self registeredAxon ifNotNil: [:axon | axon changed: aCollection]",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["last", "atLyst:ifAbsent:", "value:", "atLyst:ifAbsent:put:", "ifNotNil:", "registeredAxon", "changed:"]
+}),
+$globals.Object);
+
+});

+ 19 - 0
src/Alybaba.st

@@ -0,0 +1,19 @@
+Smalltalk createPackage: 'Alybaba'!
+(Smalltalk packageAt: 'Alybaba') imports: {'axon/Axon'. 'lyst/Lyst'}!
+!Object methodsFor: '*Alybaba'!
+
+atLyst: aCollection consume: aBlock
+	| value |
+	value := self atLyst: aCollection ifAbsent: [ ^self ].
+	^ aBlock value: value
+!
+
+atLyst: aCollection transform: aBlock
+	| value |
+	aCollection last. "raise if empty"
+	value := self atLyst: aCollection ifAbsent: [ ^self ].
+	value := aBlock value: value.
+	value := self atLyst: aCollection ifAbsent: [ ^self ] put: value.
+	self registeredAxon ifNotNil: [:axon | axon changed: aCollection]
+! !
+

+ 108 - 0
src/Axon.js

@@ -151,6 +151,30 @@ messageSends: ["ifTrue:", "fork", "run"]
 }),
 $globals.Axon);
 
+$core.addMethod(
+$core.method({
+selector: "registerIn:",
+protocol: "injecting",
+fn: function (anObject){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+anObject.$axon$=self;
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"registerIn:",{anObject:anObject},$globals.Axon)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["anObject"],
+source: "registerIn: anObject\x0a<inlineJS: 'anObject.$axon$=self'>",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: []
+}),
+$globals.Axon);
+
 $core.addMethod(
 $core.method({
 selector: "run",
@@ -217,6 +241,66 @@ $globals.Axon);
 
 
 
+$core.addClass("DumbAxon", $globals.Axon, [], "Axon");
+//>>excludeStart("ide", pragmas.excludeIdeData);
+$globals.DumbAxon.comment="I am an axon that does nothing.";
+//>>excludeEnd("ide");
+$core.addMethod(
+$core.method({
+selector: "add:",
+protocol: "as yet unclassified",
+fn: function (anInterest){
+var self=this,$self=this;
+return self;
+
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["anInterest"],
+source: "add: anInterest\x0a\x09\x22pass\x22",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: []
+}),
+$globals.DumbAxon);
+
+$core.addMethod(
+$core.method({
+selector: "clean",
+protocol: "as yet unclassified",
+fn: function (){
+var self=this,$self=this;
+return self;
+
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "clean\x0a\x09\x22pass\x22",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: []
+}),
+$globals.DumbAxon);
+
+$core.addMethod(
+$core.method({
+selector: "do:",
+protocol: "as yet unclassified",
+fn: function (aBlock){
+var self=this,$self=this;
+return self;
+
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["aBlock"],
+source: "do: aBlock\x0a\x09\x22pass\x22",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: []
+}),
+$globals.DumbAxon);
+
+
+
 $core.addClass("SimpleAxon", $globals.Axon, ["queue"], "Axon");
 $core.addMethod(
 $core.method({
@@ -609,4 +693,28 @@ messageSends: ["changed:", "axon"]
 $globals.AxonizedObject);
 
 
+$core.addMethod(
+$core.method({
+selector: "registeredAxon",
+protocol: "*Axon",
+fn: function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+return self.$axon$;
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"registeredAxon",{},$globals.Object)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "registeredAxon\x0a<inlineJS: 'return self.$axon$'>",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: []
+}),
+$globals.Object);
+
 });

+ 32 - 0
src/Axon.st

@@ -57,6 +57,32 @@ run
 	] on: Error do: [ self dirty: true ]
 ! !
 
+!Axon methodsFor: 'injecting'!
+
+registerIn: anObject
+<inlineJS: 'anObject.$axon$=self'>
+! !
+
+Axon subclass: #DumbAxon
+	instanceVariableNames: ''
+	package: 'Axon'!
+!DumbAxon commentStamp!
+I am an axon that does nothing.!
+
+!DumbAxon methodsFor: 'as yet unclassified'!
+
+add: anInterest
+	"pass"
+!
+
+clean
+	"pass"
+!
+
+do: aBlock
+	"pass"
+! !
+
 Axon subclass: #SimpleAxon
 	instanceVariableNames: 'queue'
 	package: 'Axon'!
@@ -174,3 +200,9 @@ changed: anAspect
 	self axon changed: anAspect
 ! !
 
+!Object methodsFor: '*Axon'!
+
+registeredAxon
+<inlineJS: 'return self.$axon$'>
+! !
+

+ 2 - 2
src/Trapped-Backend.js

@@ -1,6 +1,6 @@
 define(["amber/boot"
 //>>excludeStart("imports", pragmas.excludeImports);
-, "lyst/Lyst"
+, "alybaba/Alybaba", "lyst/Lyst"
 //>>excludeEnd("imports");
 , "amber_core/Kernel-Collections", "amber_core/Kernel-Objects", "axon/Axon"], function($boot
 //>>excludeStart("imports", pragmas.excludeImports);
@@ -13,7 +13,7 @@ var $core=$boot.api,nil=$boot.nilAsValue,$nil=$boot.nilAsReceiver,$recv=$boot.as
 if(!$boot.nilAsClass)$boot.nilAsClass=$boot.dnu;
 $core.addPackage("Trapped-Backend");
 $core.packages["Trapped-Backend"].innerEval = function (expr) { return eval(expr); };
-$core.packages["Trapped-Backend"].imports = ["lyst/Lyst"];
+$core.packages["Trapped-Backend"].imports = ["alybaba/Alybaba", "lyst/Lyst"];
 $core.packages["Trapped-Backend"].transport = {"type":"amd","amdNamespace":"trapped"};
 
 $core.addClass("EavModel", $globals.Object, ["getBlock", "putBlock"], "Trapped-Backend");

+ 1 - 1
src/Trapped-Backend.st

@@ -1,5 +1,5 @@
 Smalltalk createPackage: 'Trapped-Backend'!
-(Smalltalk packageAt: 'Trapped-Backend') imports: {'lyst/Lyst'}!
+(Smalltalk packageAt: 'Trapped-Backend') imports: {'alybaba/Alybaba'. 'lyst/Lyst'}!
 Object subclass: #EavModel
 	instanceVariableNames: 'getBlock putBlock'
 	package: 'Trapped-Backend'!

+ 1 - 0
testing.js

@@ -2,6 +2,7 @@ define([
     './deploy',
     'amber_core/SUnit',
     // --- packages used only during automated testing begin here ---
+    'alybaba/Alybaba-Tests',
     'trapped/Trapped-Tests',
     // --- packages used only during automated testing end here ---
 ], function (amber) {