Sfoglia il codice sorgente

Reorganization.

ModelWrappers decouple from frontend,
renamed to ListKeyedEntities and moved to backend.

TrappedDispatcher renamed to ListKeyedPubSub
and moved to backend.

Trivial dispatcher impl from demo moved to backend and renamed.
Herbert Vojčík 11 anni fa
parent
commit
25b678a89a

+ 22 - 11
README.md

@@ -23,13 +23,24 @@ What is missing:
  - optimizations ;-)
  - view -> viewmodel change propagation everywhere
 
+Enhancements for the future:
+ - better change/update model (implicit, not explicit).
+
 How can I try it?
 ----
 
+**Easy way** (but you cannot save and reload with new code):
+
+Visit http://www.herby.sk/trapped/demo.html.
+
+**Hard way** (but you can save and reload with new code):
+
 Clone this repo, with submodules as well (amber is bundled as submodule).
 Then start the server: `node vendor/amber/server/server.js`. It start on port 4000.
 Visit `http://localhost:4000/demo.html` in your browser. Amber IDE opens.
 
+**Play with it:**
+
 The Todo example from AngularJS is ported into the demo page.
 
 Trapped itself is in `Trapped-Frontend` and `Trapped-Backend` packages.
@@ -39,14 +50,14 @@ Other classes in `Trapped-Demo` are just prototype implemenations of Trapped
 building blocks. They may be deleted in the future or move to frontend/backend packages
 when they mature.
 
-`App` is the view model wrapper (its instance is put
-into global variable `AppVM` in `demo.html`)
+`App` is the view model entity (its instance is put
+into global variable `AppEntity` in `demo.html`), that is,
+facade and wrapper around the real object,
 and `AppView` is the view. `AppModel` is plain Smalltalk class
 holding data and having some behaviour. Instance of this class
-is wrapper by `App`. It shows any plain object can be used
-for a view model and wrapped by trapped.
+is wrapped by `App`.
 
-The view model wraps any object (via `model:`, as seen in `App >> initialize`).
+The entity wraps any object (via `model:`, as seen in `App >> initialize`).
 The view is subclass of plain `Widget`, but inside it, uses of `trap:`
 (and others of  `trap:xxx:` family) on `TagBrush`
 and `path trapDescend: block` allows you to bind data from view model.
@@ -55,22 +66,22 @@ You can also iterate arrays in the model using `TagBrush >> trapIter:tag:do:`.
 To see viewmodel->view update working, try this in Workspace:
 
 ```smalltalk
-AppVM modify: #(#todos) do: [ :old | old, { #{'text'->'try the guts'. 'done'->true} } ]
+AppEntity modify: #(#todos) do: [ :old | old, { #{'text'->'try the guts'. 'done'->true} } ]
 ```
 
 The number and list of items should update. If you do
 
 ```smalltalk
-AppVM modify: #(#title) do: [ 'My title' ]
+AppEntity modify: #(#title) do: [ 'My title' ]
 ```
 
 The title of the page as well as header should be updated.
 
 The `modify:do:` should be used for update since it changes as well as signals the change.
-When using `TrappedMWIsolated` wrapper class,  `read:do:` and `modify:do:`
+When using `ListKeyedIsolatedEntity` subclass as wrapper entity,  `read:do:` and `modify:do:`
 guard the data by doing deep copies behind the scene.
 
 If you wish to, you can change the raw data you put into `model:` by hand,
-but then be sure to call `AppVM dispatcher changed: #(#title)` or similar
-(you can do `AppVM dispatcher changed: #()` to signal everything in `AppVM` has changed,
-but then everything depending upon it will redraw).
+but then be sure to call `AppEntity dispatcher changed: #(#title)` or similar
+(you can do `AppEntity dispatcher changed: #()` to signal everything in `AppVM` has changed,
+but then everything depending upon it will redraw).

+ 3 - 3
demo.html

@@ -6,15 +6,15 @@
 </head>
 <body>
 <script type="text/javascript">
-    var AppVM;
+    var AppEntity;
     loadAmber({
         packages:['Trapped-Backend', 'Trapped-Frontend', 'Trapped-Demo'],
         packageHome:'./',
         ready:function () {
             $(function() {
                 smalltalk.Browser._openOn_(smalltalk.App);
-                AppVM = smalltalk.App._start();
-                smalltalk.Trapped._start();
+                AppEntity = smalltalk.App._new();
+                smalltalk.Trapped._start_(AppEntity); // any number of args
             });
         }
     });

+ 216 - 0
js/Trapped-Backend.deploy.js

@@ -219,6 +219,75 @@ smalltalk.KeyedPubSubBase);
 
 
 
+smalltalk.addClass('ListKeyedPubSubBase', smalltalk.KeyedPubSubBase, [], 'Trapped-Backend');
+smalltalk.addMethod(
+"_subscriptionKey_block_",
+smalltalk.method({
+selector: "subscriptionKey:block:",
+fn: function (key,aBlock){
+var self=this;
+var $2,$3,$1;
+$2=smalltalk.send((smalltalk.ListKeyedSubscription || ListKeyedSubscription),"_new",[]);
+smalltalk.send($2,"_key_block_",[key,aBlock]);
+$3=smalltalk.send($2,"_yourself",[]);
+$1=$3;
+return $1;
+}
+}),
+smalltalk.ListKeyedPubSubBase);
+
+
+
+smalltalk.addClass('SimpleListKeyedPubSub', smalltalk.ListKeyedPubSubBase, ['queue'], 'Trapped-Backend');
+smalltalk.addMethod(
+"_add_",
+smalltalk.method({
+selector: "add:",
+fn: function (aSubscription){
+var self=this;
+smalltalk.send(self["@queue"],"_add_",[aSubscription]);
+return self}
+}),
+smalltalk.SimpleListKeyedPubSub);
+
+smalltalk.addMethod(
+"_clean",
+smalltalk.method({
+selector: "clean",
+fn: function (){
+var self=this;
+self["@queue"]=smalltalk.send(self["@queue"],"_select_",[(function(each){
+return smalltalk.send(each,"_isEnabled",[]);
+})]);
+return self}
+}),
+smalltalk.SimpleListKeyedPubSub);
+
+smalltalk.addMethod(
+"_do_",
+smalltalk.method({
+selector: "do:",
+fn: function (aBlock){
+var self=this;
+smalltalk.send(self["@queue"],"_do_",[aBlock]);
+return self}
+}),
+smalltalk.SimpleListKeyedPubSub);
+
+smalltalk.addMethod(
+"_initialize",
+smalltalk.method({
+selector: "initialize",
+fn: function (){
+var self=this;
+smalltalk.send(self,"_initialize",[],smalltalk.ListKeyedPubSubBase);
+self["@queue"]=smalltalk.send((smalltalk.OrderedCollection || OrderedCollection),"_new",[]);
+return self}
+}),
+smalltalk.SimpleListKeyedPubSub);
+
+
+
 smalltalk.addClass('KeyedPubSubUnsubscribe', smalltalk.Error, [], 'Trapped-Backend');
 
 
@@ -318,6 +387,153 @@ smalltalk.KeyedSubscriptionBase);
 
 
 
+smalltalk.addClass('ListKeyedSubscription', smalltalk.KeyedSubscriptionBase, [], 'Trapped-Backend');
+smalltalk.addMethod(
+"_accepts_",
+smalltalk.method({
+selector: "accepts:",
+fn: function (aKey){
+var self=this;
+var $1;
+$1=smalltalk.send(smalltalk.send(smalltalk.send(aKey,"_size",[]),"__lt_eq",[smalltalk.send(self["@key"],"_size",[])]),"_and_",[(function(){
+return smalltalk.send(aKey,"__eq",[smalltalk.send(self["@key"],"_copyFrom_to_",[(1),smalltalk.send(aKey,"_size",[])])]);
+})]);
+return $1;
+}
+}),
+smalltalk.ListKeyedSubscription);
+
+
+
+smalltalk.addClass('ListKeyedEntity', smalltalk.Object, ['dispatcher', 'payload'], 'Trapped-Backend');
+smalltalk.addMethod(
+"_dispatcher",
+smalltalk.method({
+selector: "dispatcher",
+fn: function (){
+var self=this;
+return self["@dispatcher"];
+}
+}),
+smalltalk.ListKeyedEntity);
+
+smalltalk.addMethod(
+"_dispatcher_",
+smalltalk.method({
+selector: "dispatcher:",
+fn: function (aDispatcher){
+var self=this;
+self["@dispatcher"]=aDispatcher;
+return self}
+}),
+smalltalk.ListKeyedEntity);
+
+smalltalk.addMethod(
+"_model_",
+smalltalk.method({
+selector: "model:",
+fn: function (anObject){
+var self=this;
+self["@payload"]=anObject;
+smalltalk.send(smalltalk.send(self,"_dispatcher",[]),"_changed_",[[]]);
+return self}
+}),
+smalltalk.ListKeyedEntity);
+
+smalltalk.addMethod(
+"_watch_do_",
+smalltalk.method({
+selector: "watch:do:",
+fn: function (path,aBlock){
+var self=this;
+smalltalk.send(smalltalk.send(self,"_dispatcher",[]),"_on_hook_",[path,(function(){
+return smalltalk.send(self,"_read_do_",[path,aBlock]);
+})]);
+return self}
+}),
+smalltalk.ListKeyedEntity);
+
+
+
+smalltalk.addClass('ListKeyedDirectEntity', smalltalk.ListKeyedEntity, [], 'Trapped-Backend');
+smalltalk.addMethod(
+"_modify_do_",
+smalltalk.method({
+selector: "modify:do:",
+fn: function (path,aBlock){
+var self=this;
+var newValue;
+var eavModel;
+eavModel=smalltalk.send(path,"_asEavModel",[]);
+newValue=smalltalk.send(aBlock,"_value_",[smalltalk.send(eavModel,"_on_",[self["@payload"]])]);
+smalltalk.send((function(){
+return smalltalk.send(eavModel,"_on_put_",[self["@payload"],newValue]);
+}),"_ensure_",[(function(){
+return smalltalk.send(smalltalk.send(self,"_dispatcher",[]),"_changed_",[path]);
+})]);
+return self}
+}),
+smalltalk.ListKeyedDirectEntity);
+
+smalltalk.addMethod(
+"_read_do_",
+smalltalk.method({
+selector: "read:do:",
+fn: function (path,aBlock){
+var self=this;
+var eavModel;
+eavModel=smalltalk.send(path,"_asEavModel",[]);
+smalltalk.send(aBlock,"_value_",[smalltalk.send(eavModel,"_on_",[self["@payload"]])]);
+return self}
+}),
+smalltalk.ListKeyedDirectEntity);
+
+
+
+smalltalk.addClass('ListKeyedIsolatedEntity', smalltalk.ListKeyedEntity, [], 'Trapped-Backend');
+smalltalk.addMethod(
+"_model_",
+smalltalk.method({
+selector: "model:",
+fn: function (anObject){
+var self=this;
+smalltalk.send(self,"_model_",[smalltalk.send((smalltalk.Isolator || Isolator),"_on_",[anObject])],smalltalk.ListKeyedEntity);
+return self}
+}),
+smalltalk.ListKeyedIsolatedEntity);
+
+smalltalk.addMethod(
+"_modify_do_",
+smalltalk.method({
+selector: "modify:do:",
+fn: function (path,aBlock){
+var self=this;
+var eavModel;
+eavModel=smalltalk.send(smalltalk.send([smalltalk.symbolFor("root")],"__comma",[path]),"_asEavModel",[]);
+smalltalk.send((function(){
+return smalltalk.send(self["@payload"],"_model_modify_",[eavModel,aBlock]);
+}),"_ensure_",[(function(){
+return smalltalk.send(smalltalk.send(self,"_dispatcher",[]),"_changed_",[path]);
+})]);
+return self}
+}),
+smalltalk.ListKeyedIsolatedEntity);
+
+smalltalk.addMethod(
+"_read_do_",
+smalltalk.method({
+selector: "read:do:",
+fn: function (path,aBlock){
+var self=this;
+var eavModel;
+eavModel=smalltalk.send(smalltalk.send([smalltalk.symbolFor("root")],"__comma",[path]),"_asEavModel",[]);
+smalltalk.send(self["@payload"],"_model_read_",[eavModel,aBlock]);
+return self}
+}),
+smalltalk.ListKeyedIsolatedEntity);
+
+
+
 smalltalk.addMethod(
 "_reverseTrapAt_",
 smalltalk.method({

+ 296 - 0
js/Trapped-Backend.js

@@ -181,6 +181,7 @@ smalltalk.Isolator.klass);
 
 
 smalltalk.addClass('KeyedPubSubBase', smalltalk.Object, [], 'Trapped-Backend');
+smalltalk.KeyedPubSubBase.comment="I represent a pub-sub based on a key.\x0aI manage key-block subscriptions as well as running blocks that are dirty.\x0aThe subscription objects are reponsible of decision if the change is relevant for them.\x0aSubscription object must be subclasses of KeyedSubscriptionBase.\x0a\x0aMy subclasses must provide implementation for:\x0a\x09subscriptionKey:block: (factory method for creating appropriate subscription object)\x0a\x0aas well as bookkeeping of subscriptions:\x0a\x09add:\x0a    do:\x0a    clean\x0a    (optionally) run\x0a"
 smalltalk.addMethod(
 "_changed_",
 smalltalk.method({
@@ -295,6 +296,101 @@ smalltalk.KeyedPubSubBase);
 
 
 
+smalltalk.addClass('ListKeyedPubSubBase', smalltalk.KeyedPubSubBase, [], 'Trapped-Backend');
+smalltalk.ListKeyedPubSubBase.comment="I am base class list-keyed pub-sub.\x0a\x0aMy subclasses need to provide implementation for:\x0a\x09add:\x0a    do:\x0a    clean\x0a    (optionally) run\x0a"
+smalltalk.addMethod(
+"_subscriptionKey_block_",
+smalltalk.method({
+selector: "subscriptionKey:block:",
+category: 'action',
+fn: function (key,aBlock){
+var self=this;
+var $2,$3,$1;
+$2=smalltalk.send((smalltalk.ListKeyedSubscription || ListKeyedSubscription),"_new",[]);
+smalltalk.send($2,"_key_block_",[key,aBlock]);
+$3=smalltalk.send($2,"_yourself",[]);
+$1=$3;
+return $1;
+},
+args: ["key", "aBlock"],
+source: "subscriptionKey: key block: aBlock\x0a\x09^ListKeyedSubscription new key: key block: aBlock; yourself\x0a",
+messageSends: ["key:block:", "new", "yourself"],
+referencedClasses: ["ListKeyedSubscription"]
+}),
+smalltalk.ListKeyedPubSubBase);
+
+
+
+smalltalk.addClass('SimpleListKeyedPubSub', smalltalk.ListKeyedPubSubBase, ['queue'], 'Trapped-Backend');
+smalltalk.addMethod(
+"_add_",
+smalltalk.method({
+selector: "add:",
+category: 'accessing',
+fn: function (aSubscription){
+var self=this;
+smalltalk.send(self["@queue"],"_add_",[aSubscription]);
+return self},
+args: ["aSubscription"],
+source: "add: aSubscription\x0a\x09queue add: aSubscription.\x0a",
+messageSends: ["add:"],
+referencedClasses: []
+}),
+smalltalk.SimpleListKeyedPubSub);
+
+smalltalk.addMethod(
+"_clean",
+smalltalk.method({
+selector: "clean",
+category: 'bookkeeping',
+fn: function (){
+var self=this;
+self["@queue"]=smalltalk.send(self["@queue"],"_select_",[(function(each){
+return smalltalk.send(each,"_isEnabled",[]);
+})]);
+return self},
+args: [],
+source: "clean\x0a\x09queue := queue select: [ :each | each isEnabled ]",
+messageSends: ["select:", "isEnabled"],
+referencedClasses: []
+}),
+smalltalk.SimpleListKeyedPubSub);
+
+smalltalk.addMethod(
+"_do_",
+smalltalk.method({
+selector: "do:",
+category: 'enumeration',
+fn: function (aBlock){
+var self=this;
+smalltalk.send(self["@queue"],"_do_",[aBlock]);
+return self},
+args: ["aBlock"],
+source: "do: aBlock\x0a\x09queue do: aBlock",
+messageSends: ["do:"],
+referencedClasses: []
+}),
+smalltalk.SimpleListKeyedPubSub);
+
+smalltalk.addMethod(
+"_initialize",
+smalltalk.method({
+selector: "initialize",
+category: 'initialization',
+fn: function (){
+var self=this;
+smalltalk.send(self,"_initialize",[],smalltalk.ListKeyedPubSubBase);
+self["@queue"]=smalltalk.send((smalltalk.OrderedCollection || OrderedCollection),"_new",[]);
+return self},
+args: [],
+source: "initialize\x0a    super initialize.\x0a\x09queue := OrderedCollection new",
+messageSends: ["initialize", "new"],
+referencedClasses: ["OrderedCollection"]
+}),
+smalltalk.SimpleListKeyedPubSub);
+
+
+
 smalltalk.addClass('KeyedPubSubUnsubscribe', smalltalk.Error, [], 'Trapped-Backend');
 smalltalk.KeyedPubSubUnsubscribe.comment="SIgnal me from the subscription block to unsubscribe it."
 
@@ -430,6 +526,206 @@ smalltalk.KeyedSubscriptionBase);
 
 
 
+smalltalk.addClass('ListKeyedSubscription', smalltalk.KeyedSubscriptionBase, [], 'Trapped-Backend');
+smalltalk.addMethod(
+"_accepts_",
+smalltalk.method({
+selector: "accepts:",
+category: 'testing',
+fn: function (aKey){
+var self=this;
+var $1;
+$1=smalltalk.send(smalltalk.send(smalltalk.send(aKey,"_size",[]),"__lt_eq",[smalltalk.send(self["@key"],"_size",[])]),"_and_",[(function(){
+return smalltalk.send(aKey,"__eq",[smalltalk.send(self["@key"],"_copyFrom_to_",[(1),smalltalk.send(aKey,"_size",[])])]);
+})]);
+return $1;
+},
+args: ["aKey"],
+source: "accepts: aKey\x0a    ^aKey size <= key size and: [aKey = (key copyFrom: 1 to: aKey size)]",
+messageSends: ["and:", "=", "copyFrom:to:", "size", "<="],
+referencedClasses: []
+}),
+smalltalk.ListKeyedSubscription);
+
+
+
+smalltalk.addClass('ListKeyedEntity', smalltalk.Object, ['dispatcher', 'payload'], 'Trapped-Backend');
+smalltalk.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:.\x0aThis wrapped model can be any smalltalk object.\x0a\x0aMy subclasses need to provide implementation for:\x0a\x09read:do:\x0a    modify:do:\x0a\x0aand must issue these calls when initializing:\x0a\x09model: (with a wrapped object)\x0a\x09dispatcher: (with a subclass of ListKeyedPubSubBase)\x0a"
+smalltalk.addMethod(
+"_dispatcher",
+smalltalk.method({
+selector: "dispatcher",
+category: 'accessing',
+fn: function (){
+var self=this;
+return self["@dispatcher"];
+},
+args: [],
+source: "dispatcher\x0a\x09^dispatcher",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.ListKeyedEntity);
+
+smalltalk.addMethod(
+"_dispatcher_",
+smalltalk.method({
+selector: "dispatcher:",
+category: 'accessing',
+fn: function (aDispatcher){
+var self=this;
+self["@dispatcher"]=aDispatcher;
+return self},
+args: ["aDispatcher"],
+source: "dispatcher: aDispatcher\x0a\x09dispatcher := aDispatcher",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.ListKeyedEntity);
+
+smalltalk.addMethod(
+"_model_",
+smalltalk.method({
+selector: "model:",
+category: 'accessing',
+fn: function (anObject){
+var self=this;
+self["@payload"]=anObject;
+smalltalk.send(smalltalk.send(self,"_dispatcher",[]),"_changed_",[[]]);
+return self},
+args: ["anObject"],
+source: "model: anObject\x0a\x09payload := anObject.\x0a    self dispatcher changed: #()",
+messageSends: ["changed:", "dispatcher"],
+referencedClasses: []
+}),
+smalltalk.ListKeyedEntity);
+
+smalltalk.addMethod(
+"_watch_do_",
+smalltalk.method({
+selector: "watch:do:",
+category: 'action',
+fn: function (path,aBlock){
+var self=this;
+smalltalk.send(smalltalk.send(self,"_dispatcher",[]),"_on_hook_",[path,(function(){
+return smalltalk.send(self,"_read_do_",[path,aBlock]);
+})]);
+return self},
+args: ["path", "aBlock"],
+source: "watch: path do: aBlock\x0a\x09self dispatcher on: path hook: [ self read: path do: aBlock ]\x0a",
+messageSends: ["on:hook:", "read:do:", "dispatcher"],
+referencedClasses: []
+}),
+smalltalk.ListKeyedEntity);
+
+
+
+smalltalk.addClass('ListKeyedDirectEntity', smalltalk.ListKeyedEntity, [], 'Trapped-Backend');
+smalltalk.ListKeyedDirectEntity.comment="I am ListKeyedEntity that directly manipulate\x0athe wrapped model object."
+smalltalk.addMethod(
+"_modify_do_",
+smalltalk.method({
+selector: "modify:do:",
+category: 'action',
+fn: function (path,aBlock){
+var self=this;
+var newValue;
+var eavModel;
+eavModel=smalltalk.send(path,"_asEavModel",[]);
+newValue=smalltalk.send(aBlock,"_value_",[smalltalk.send(eavModel,"_on_",[self["@payload"]])]);
+smalltalk.send((function(){
+return smalltalk.send(eavModel,"_on_put_",[self["@payload"],newValue]);
+}),"_ensure_",[(function(){
+return smalltalk.send(smalltalk.send(self,"_dispatcher",[]),"_changed_",[path]);
+})]);
+return self},
+args: ["path", "aBlock"],
+source: "modify: path do: aBlock\x0a    | newValue eavModel |\x0a    eavModel := path asEavModel.\x0a    newValue := aBlock value: (eavModel on: payload).\x0a    [ eavModel on: payload put: newValue ] ensure: [ self dispatcher changed: path ]\x0a",
+messageSends: ["asEavModel", "value:", "on:", "ensure:", "changed:", "dispatcher", "on:put:"],
+referencedClasses: []
+}),
+smalltalk.ListKeyedDirectEntity);
+
+smalltalk.addMethod(
+"_read_do_",
+smalltalk.method({
+selector: "read:do:",
+category: 'action',
+fn: function (path,aBlock){
+var self=this;
+var eavModel;
+eavModel=smalltalk.send(path,"_asEavModel",[]);
+smalltalk.send(aBlock,"_value_",[smalltalk.send(eavModel,"_on_",[self["@payload"]])]);
+return self},
+args: ["path", "aBlock"],
+source: "read: path do: aBlock\x0a    | eavModel |\x0a    eavModel := path asEavModel.\x0a    aBlock value: (eavModel on: payload)\x0a",
+messageSends: ["asEavModel", "value:", "on:"],
+referencedClasses: []
+}),
+smalltalk.ListKeyedDirectEntity);
+
+
+
+smalltalk.addClass('ListKeyedIsolatedEntity', smalltalk.ListKeyedEntity, [], 'Trapped-Backend');
+smalltalk.ListKeyedIsolatedEntity.comment="I am ListKeyedEntity that guards access\x0ato the wrapped model object via Isolator."
+smalltalk.addMethod(
+"_model_",
+smalltalk.method({
+selector: "model:",
+category: 'accessing',
+fn: function (anObject){
+var self=this;
+smalltalk.send(self,"_model_",[smalltalk.send((smalltalk.Isolator || Isolator),"_on_",[anObject])],smalltalk.ListKeyedEntity);
+return self},
+args: ["anObject"],
+source: "model: anObject\x0a\x09super model: (Isolator on: anObject)",
+messageSends: ["model:", "on:"],
+referencedClasses: ["Isolator"]
+}),
+smalltalk.ListKeyedIsolatedEntity);
+
+smalltalk.addMethod(
+"_modify_do_",
+smalltalk.method({
+selector: "modify:do:",
+category: 'action',
+fn: function (path,aBlock){
+var self=this;
+var eavModel;
+eavModel=smalltalk.send(smalltalk.send([smalltalk.symbolFor("root")],"__comma",[path]),"_asEavModel",[]);
+smalltalk.send((function(){
+return smalltalk.send(self["@payload"],"_model_modify_",[eavModel,aBlock]);
+}),"_ensure_",[(function(){
+return smalltalk.send(smalltalk.send(self,"_dispatcher",[]),"_changed_",[path]);
+})]);
+return self},
+args: ["path", "aBlock"],
+source: "modify: path do: aBlock\x0a    | eavModel |\x0a    eavModel := ({#root},path) asEavModel.\x0a    [ payload model: eavModel modify: aBlock ] ensure: [ self dispatcher changed: path ]\x0a",
+messageSends: ["asEavModel", ",", "ensure:", "changed:", "dispatcher", "model:modify:"],
+referencedClasses: []
+}),
+smalltalk.ListKeyedIsolatedEntity);
+
+smalltalk.addMethod(
+"_read_do_",
+smalltalk.method({
+selector: "read:do:",
+category: 'action',
+fn: function (path,aBlock){
+var self=this;
+var eavModel;
+eavModel=smalltalk.send(smalltalk.send([smalltalk.symbolFor("root")],"__comma",[path]),"_asEavModel",[]);
+smalltalk.send(self["@payload"],"_model_read_",[eavModel,aBlock]);
+return self},
+args: ["path", "aBlock"],
+source: "read: path do: aBlock\x0a    | eavModel |\x0a    eavModel := ({#root},path) asEavModel.\x0a    payload model: eavModel read: aBlock\x0a",
+messageSends: ["asEavModel", ",", "model:read:"],
+referencedClasses: []
+}),
+smalltalk.ListKeyedIsolatedEntity);
+
+
+
 smalltalk.addMethod(
 "_reverseTrapAt_",
 smalltalk.method({

+ 3 - 52
js/Trapped-Demo.deploy.js

@@ -1,13 +1,13 @@
 smalltalk.addPackage('Trapped-Demo', {});
-smalltalk.addClass('App', smalltalk.TrappedMWIsolated, [], 'Trapped-Demo');
+smalltalk.addClass('App', smalltalk.ListKeyedIsolatedEntity, [], 'Trapped-Demo');
 smalltalk.addMethod(
 "_initialize",
 smalltalk.method({
 selector: "initialize",
 fn: function (){
 var self=this;
-smalltalk.send(self,"_initialize",[],smalltalk.TrappedMWIsolated);
-smalltalk.send(self,"_dispatcher_",[smalltalk.send((smalltalk.TrappedDumbDispatcher || TrappedDumbDispatcher),"_new",[])]);
+smalltalk.send(self,"_initialize",[],smalltalk.ListKeyedIsolatedEntity);
+smalltalk.send(self,"_dispatcher_",[smalltalk.send((smalltalk.SimpleListKeyedPubSub || SimpleListKeyedPubSub),"_new",[])]);
 smalltalk.send(self,"_model_",[smalltalk.send(smalltalk.send((smalltalk.AppModel || AppModel),"_new",[]),"_title_",["Todo"])]);
 smalltalk.send((function(){
 return smalltalk.send(self,"_modify_do_",[[smalltalk.symbolFor("todos")],(function(){
@@ -216,52 +216,3 @@ smalltalk.AppView);
 
 
 
-smalltalk.addClass('TrappedDumbDispatcher', smalltalk.TrappedDispatcher, ['queue'], 'Trapped-Demo');
-smalltalk.addMethod(
-"_add_",
-smalltalk.method({
-selector: "add:",
-fn: function (aSubscription){
-var self=this;
-smalltalk.send(self["@queue"],"_add_",[aSubscription]);
-return self}
-}),
-smalltalk.TrappedDumbDispatcher);
-
-smalltalk.addMethod(
-"_clean",
-smalltalk.method({
-selector: "clean",
-fn: function (){
-var self=this;
-self["@queue"]=smalltalk.send(self["@queue"],"_select_",[(function(each){
-return smalltalk.send(each,"_isEnabled",[]);
-})]);
-return self}
-}),
-smalltalk.TrappedDumbDispatcher);
-
-smalltalk.addMethod(
-"_do_",
-smalltalk.method({
-selector: "do:",
-fn: function (aBlock){
-var self=this;
-smalltalk.send(self["@queue"],"_do_",[aBlock]);
-return self}
-}),
-smalltalk.TrappedDumbDispatcher);
-
-smalltalk.addMethod(
-"_initialize",
-smalltalk.method({
-selector: "initialize",
-fn: function (){
-var self=this;
-self["@queue"]=smalltalk.send((smalltalk.OrderedCollection || OrderedCollection),"_new",[]);
-return self}
-}),
-smalltalk.TrappedDumbDispatcher);
-
-
-

+ 5 - 74
js/Trapped-Demo.js

@@ -1,5 +1,5 @@
 smalltalk.addPackage('Trapped-Demo', {});
-smalltalk.addClass('App', smalltalk.TrappedMWIsolated, [], 'Trapped-Demo');
+smalltalk.addClass('App', smalltalk.ListKeyedIsolatedEntity, [], 'Trapped-Demo');
 smalltalk.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}"
 smalltalk.addMethod(
 "_initialize",
@@ -8,8 +8,8 @@ selector: "initialize",
 category: 'initialization',
 fn: function (){
 var self=this;
-smalltalk.send(self,"_initialize",[],smalltalk.TrappedMWIsolated);
-smalltalk.send(self,"_dispatcher_",[smalltalk.send((smalltalk.TrappedDumbDispatcher || TrappedDumbDispatcher),"_new",[])]);
+smalltalk.send(self,"_initialize",[],smalltalk.ListKeyedIsolatedEntity);
+smalltalk.send(self,"_dispatcher_",[smalltalk.send((smalltalk.SimpleListKeyedPubSub || SimpleListKeyedPubSub),"_new",[])]);
 smalltalk.send(self,"_model_",[smalltalk.send(smalltalk.send((smalltalk.AppModel || AppModel),"_new",[]),"_title_",["Todo"])]);
 smalltalk.send((function(){
 return smalltalk.send(self,"_modify_do_",[[smalltalk.symbolFor("todos")],(function(){
@@ -18,9 +18,9 @@ return [smalltalk.HashedCollection._fromPairs_([smalltalk.send("text","__minus_g
 }),"_valueWithTimeout_",[(2000)]);
 return self},
 args: [],
-source: "initialize\x0a\x09super initialize.\x0a    self dispatcher: TrappedDumbDispatcher new.\x0a    self model: (AppModel new title: 'Todo').\x0a    [ self modify: #(#todos) do: [{\x0a        #{'text'->'learn trapped'. 'done'->true}.\x0a        #{'text'->'build a trapped app'. 'done'->false}\x0a    }]] valueWithTimeout: 2000\x0a",
+source: "initialize\x0a\x09super initialize.\x0a    self dispatcher: SimpleListKeyedPubSub new.\x0a    self model: (AppModel new title: 'Todo').\x0a    [ self modify: #(#todos) do: [{\x0a        #{'text'->'learn trapped'. 'done'->true}.\x0a        #{'text'->'build a trapped app'. 'done'->false}\x0a    }]] valueWithTimeout: 2000\x0a",
 messageSends: ["initialize", "dispatcher:", "new", "model:", "title:", "valueWithTimeout:", "modify:do:", "->"],
-referencedClasses: ["TrappedDumbDispatcher", "AppModel"]
+referencedClasses: ["SimpleListKeyedPubSub", "AppModel"]
 }),
 smalltalk.App);
 
@@ -279,72 +279,3 @@ smalltalk.AppView);
 
 
 
-smalltalk.addClass('TrappedDumbDispatcher', smalltalk.TrappedDispatcher, ['queue'], 'Trapped-Demo');
-smalltalk.addMethod(
-"_add_",
-smalltalk.method({
-selector: "add:",
-category: 'accessing',
-fn: function (aSubscription){
-var self=this;
-smalltalk.send(self["@queue"],"_add_",[aSubscription]);
-return self},
-args: ["aSubscription"],
-source: "add: aSubscription\x0a\x09queue add: aSubscription.\x0a",
-messageSends: ["add:"],
-referencedClasses: []
-}),
-smalltalk.TrappedDumbDispatcher);
-
-smalltalk.addMethod(
-"_clean",
-smalltalk.method({
-selector: "clean",
-category: 'bookkeeping',
-fn: function (){
-var self=this;
-self["@queue"]=smalltalk.send(self["@queue"],"_select_",[(function(each){
-return smalltalk.send(each,"_isEnabled",[]);
-})]);
-return self},
-args: [],
-source: "clean\x0a\x09queue := queue select: [ :each | each isEnabled ]",
-messageSends: ["select:", "isEnabled"],
-referencedClasses: []
-}),
-smalltalk.TrappedDumbDispatcher);
-
-smalltalk.addMethod(
-"_do_",
-smalltalk.method({
-selector: "do:",
-category: 'enumeration',
-fn: function (aBlock){
-var self=this;
-smalltalk.send(self["@queue"],"_do_",[aBlock]);
-return self},
-args: ["aBlock"],
-source: "do: aBlock\x0a\x09queue do: aBlock",
-messageSends: ["do:"],
-referencedClasses: []
-}),
-smalltalk.TrappedDumbDispatcher);
-
-smalltalk.addMethod(
-"_initialize",
-smalltalk.method({
-selector: "initialize",
-category: 'initialization',
-fn: function (){
-var self=this;
-self["@queue"]=smalltalk.send((smalltalk.OrderedCollection || OrderedCollection),"_new",[]);
-return self},
-args: [],
-source: "initialize\x0a\x09queue := OrderedCollection new",
-messageSends: ["new"],
-referencedClasses: ["OrderedCollection"]
-}),
-smalltalk.TrappedDumbDispatcher);
-
-
-

+ 29 - 218
js/Trapped-Frontend.deploy.js

@@ -140,25 +140,6 @@ smalltalk.TrappedValBinder);
 
 
 
-smalltalk.addClass('TrappedDispatcher', smalltalk.KeyedPubSubBase, [], 'Trapped-Frontend');
-smalltalk.addMethod(
-"_subscriptionKey_block_",
-smalltalk.method({
-selector: "subscriptionKey:block:",
-fn: function (key,aBlock){
-var self=this;
-var $2,$3,$1;
-$2=smalltalk.send((smalltalk.TrappedSubscription || TrappedSubscription),"_new",[]);
-smalltalk.send($2,"_key_block_",[key,aBlock]);
-$3=smalltalk.send($2,"_yourself",[]);
-$1=$3;
-return $1;
-}
-}),
-smalltalk.TrappedDispatcher);
-
-
-
 smalltalk.addClass('TrappedDumbView', smalltalk.Widget, [], 'Trapped-Frontend');
 smalltalk.addMethod(
 "_renderOn_",
@@ -173,181 +154,12 @@ smalltalk.TrappedDumbView);
 
 
 
-smalltalk.addClass('TrappedModelWrapper', smalltalk.Object, ['dispatcher', 'payload'], 'Trapped-Frontend');
-smalltalk.addMethod(
-"_dispatcher",
-smalltalk.method({
-selector: "dispatcher",
-fn: function (){
-var self=this;
-return self["@dispatcher"];
-}
-}),
-smalltalk.TrappedModelWrapper);
-
-smalltalk.addMethod(
-"_dispatcher_",
-smalltalk.method({
-selector: "dispatcher:",
-fn: function (aDispatcher){
-var self=this;
-self["@dispatcher"]=aDispatcher;
-return self}
-}),
-smalltalk.TrappedModelWrapper);
-
-smalltalk.addMethod(
-"_model_",
-smalltalk.method({
-selector: "model:",
-fn: function (anObject){
-var self=this;
-self["@payload"]=anObject;
-smalltalk.send(smalltalk.send(self,"_dispatcher",[]),"_changed_",[[]]);
-return self}
-}),
-smalltalk.TrappedModelWrapper);
-
-smalltalk.addMethod(
-"_name",
-smalltalk.method({
-selector: "name",
-fn: function (){
-var self=this;
-var $1;
-$1=smalltalk.send(smalltalk.send(self,"_class",[]),"_name",[]);
-return $1;
-}
-}),
-smalltalk.TrappedModelWrapper);
-
-smalltalk.addMethod(
-"_start",
-smalltalk.method({
-selector: "start",
-fn: function (){
-var self=this;
-smalltalk.send(smalltalk.send((smalltalk.Trapped || Trapped),"_current",[]),"_register_name_",[self,smalltalk.send(self,"_name",[])]);
-return self}
-}),
-smalltalk.TrappedModelWrapper);
-
-smalltalk.addMethod(
-"_watch_do_",
-smalltalk.method({
-selector: "watch:do:",
-fn: function (path,aBlock){
-var self=this;
-smalltalk.send(smalltalk.send(self,"_dispatcher",[]),"_on_hook_",[path,(function(){
-return smalltalk.send(self,"_read_do_",[path,aBlock]);
-})]);
-return self}
-}),
-smalltalk.TrappedModelWrapper);
-
-
-smalltalk.addMethod(
-"_start",
-smalltalk.method({
-selector: "start",
-fn: function (){
-var self=this;
-var $2,$3,$1;
-$2=smalltalk.send(self,"_new",[]);
-smalltalk.send($2,"_start",[]);
-$3=smalltalk.send($2,"_yourself",[]);
-$1=$3;
-return $1;
-}
-}),
-smalltalk.TrappedModelWrapper.klass);
-
-
-smalltalk.addClass('TrappedMWDirect', smalltalk.TrappedModelWrapper, [], 'Trapped-Frontend');
-smalltalk.addMethod(
-"_modify_do_",
-smalltalk.method({
-selector: "modify:do:",
-fn: function (path,aBlock){
-var self=this;
-var newValue;
-var eavModel;
-eavModel=smalltalk.send(path,"_asEavModel",[]);
-newValue=smalltalk.send(aBlock,"_value_",[smalltalk.send(eavModel,"_on_",[self["@payload"]])]);
-smalltalk.send((function(){
-return smalltalk.send(eavModel,"_on_put_",[self["@payload"],newValue]);
-}),"_ensure_",[(function(){
-return smalltalk.send(smalltalk.send(self,"_dispatcher",[]),"_changed_",[path]);
-})]);
-return self}
-}),
-smalltalk.TrappedMWDirect);
-
-smalltalk.addMethod(
-"_read_do_",
-smalltalk.method({
-selector: "read:do:",
-fn: function (path,aBlock){
-var self=this;
-var eavModel;
-eavModel=smalltalk.send(path,"_asEavModel",[]);
-smalltalk.send(aBlock,"_value_",[smalltalk.send(eavModel,"_on_",[self["@payload"]])]);
-return self}
-}),
-smalltalk.TrappedMWDirect);
-
-
-
-smalltalk.addClass('TrappedMWIsolated', smalltalk.TrappedModelWrapper, [], 'Trapped-Frontend');
-smalltalk.addMethod(
-"_model_",
-smalltalk.method({
-selector: "model:",
-fn: function (anObject){
-var self=this;
-smalltalk.send(self,"_model_",[smalltalk.send((smalltalk.Isolator || Isolator),"_on_",[anObject])],smalltalk.TrappedModelWrapper);
-return self}
-}),
-smalltalk.TrappedMWIsolated);
-
-smalltalk.addMethod(
-"_modify_do_",
-smalltalk.method({
-selector: "modify:do:",
-fn: function (path,aBlock){
-var self=this;
-var eavModel;
-eavModel=smalltalk.send(smalltalk.send([smalltalk.symbolFor("root")],"__comma",[path]),"_asEavModel",[]);
-smalltalk.send((function(){
-return smalltalk.send(self["@payload"],"_model_modify_",[eavModel,aBlock]);
-}),"_ensure_",[(function(){
-return smalltalk.send(smalltalk.send(self,"_dispatcher",[]),"_changed_",[path]);
-})]);
-return self}
-}),
-smalltalk.TrappedMWIsolated);
-
-smalltalk.addMethod(
-"_read_do_",
-smalltalk.method({
-selector: "read:do:",
-fn: function (path,aBlock){
-var self=this;
-var eavModel;
-eavModel=smalltalk.send(smalltalk.send([smalltalk.symbolFor("root")],"__comma",[path]),"_asEavModel",[]);
-smalltalk.send(self["@payload"],"_model_read_",[eavModel,aBlock]);
-return self}
-}),
-smalltalk.TrappedMWIsolated);
-
-
-
 smalltalk.addClass('TrappedSingleton', smalltalk.Object, [], 'Trapped-Frontend');
 smalltalk.addMethod(
-"_start",
+"_start_",
 smalltalk.method({
-selector: "start",
-fn: function (){
+selector: "start:",
+fn: function (args){
 var self=this;
 var $1;
 $1=smalltalk.send(self,"_subclassResponsibility",[]);
@@ -377,12 +189,12 @@ return $1;
 smalltalk.TrappedSingleton.klass);
 
 smalltalk.addMethod(
-"_start",
+"_start_",
 smalltalk.method({
-selector: "start",
-fn: function (){
+selector: "start:",
+fn: function (zzz){
 var self=this;
-smalltalk.send(smalltalk.send(self,"_current",[]),"_start",[]);
+smalltalk.send(smalltalk.send(self,"_current",[]),"_perform_withArguments_",[smalltalk.symbolFor("start:"),arguments]);
 return self}
 }),
 smalltalk.TrappedSingleton.klass);
@@ -475,24 +287,41 @@ return self}
 }),
 smalltalk.Trapped);
 
+smalltalk.addMethod(
+"_register_",
+smalltalk.method({
+selector: "register:",
+fn: function (aListKeyedEntity){
+var self=this;
+smalltalk.send(self,"_register_name_",[aListKeyedEntity,smalltalk.send(smalltalk.send(aListKeyedEntity,"_class",[]),"_name",[])]);
+return self}
+}),
+smalltalk.Trapped);
+
 smalltalk.addMethod(
 "_register_name_",
 smalltalk.method({
 selector: "register:name:",
-fn: function (aFly,aString){
+fn: function (aListKeyedEntity,aString){
 var self=this;
-smalltalk.send(self["@registry"],"_at_put_",[aString,aFly]);
+smalltalk.send(self["@registry"],"_at_put_",[aString,aListKeyedEntity]);
 return self}
 }),
 smalltalk.Trapped);
 
 smalltalk.addMethod(
-"_start",
+"_start_",
 smalltalk.method({
-selector: "start",
-fn: function (){
+selector: "start:",
+fn: function (zzz){
 var self=this;
 var $1;
+var args;
+args = [].slice.call(arguments);
+;
+smalltalk.send(args,"_do_",[(function(each){
+return smalltalk.send(self,"_register_",[each]);
+})]);
 smalltalk.send(smalltalk.send("[data-trap]","_asJQuery",[]),"_each_",[(function(index,elem){
 var trap;
 var jq;
@@ -677,24 +506,6 @@ smalltalk.TrappedSnapshot);
 
 
 
-smalltalk.addClass('TrappedSubscription', smalltalk.KeyedSubscriptionBase, [], 'Trapped-Frontend');
-smalltalk.addMethod(
-"_accepts_",
-smalltalk.method({
-selector: "accepts:",
-fn: function (aKey){
-var self=this;
-var $1;
-$1=smalltalk.send(smalltalk.send(smalltalk.send(aKey,"_size",[]),"__lt_eq",[smalltalk.send(self["@key"],"_size",[])]),"_and_",[(function(){
-return smalltalk.send(aKey,"__eq",[smalltalk.send(self["@key"],"_copyFrom_to_",[(1),smalltalk.send(aKey,"_size",[])])]);
-})]);
-return $1;
-}
-}),
-smalltalk.TrappedSubscription);
-
-
-
 smalltalk.addMethod(
 "_trapDescend_",
 smalltalk.method({

+ 44 - 302
js/Trapped-Frontend.js

@@ -180,31 +180,6 @@ smalltalk.TrappedValBinder);
 
 
 
-smalltalk.addClass('TrappedDispatcher', smalltalk.KeyedPubSubBase, [], 'Trapped-Frontend');
-smalltalk.TrappedDispatcher.comment="I am base class for change event dispatchers.\x0aI manage changed path - action block subscriptions.\x0aThese subscription are instances of TrappedSubscription\x0a\x0aMy subclasses need to provide implementation for:\x0a\x09add:\x0a    do:\x0a    clean\x0a    (optionally) run\x0a"
-smalltalk.addMethod(
-"_subscriptionKey_block_",
-smalltalk.method({
-selector: "subscriptionKey:block:",
-category: 'action',
-fn: function (key,aBlock){
-var self=this;
-var $2,$3,$1;
-$2=smalltalk.send((smalltalk.TrappedSubscription || TrappedSubscription),"_new",[]);
-smalltalk.send($2,"_key_block_",[key,aBlock]);
-$3=smalltalk.send($2,"_yourself",[]);
-$1=$3;
-return $1;
-},
-args: ["key", "aBlock"],
-source: "subscriptionKey: key block: aBlock\x0a\x09^TrappedSubscription new key: key block: aBlock; yourself\x0a",
-messageSends: ["key:block:", "new", "yourself"],
-referencedClasses: ["TrappedSubscription"]
-}),
-smalltalk.TrappedDispatcher);
-
-
-
 smalltalk.addClass('TrappedDumbView', smalltalk.Widget, [], 'Trapped-Frontend');
 smalltalk.TrappedDumbView.comment="I just read and show an actual path."
 smalltalk.addMethod(
@@ -225,252 +200,20 @@ smalltalk.TrappedDumbView);
 
 
 
-smalltalk.addClass('TrappedModelWrapper', smalltalk.Object, ['dispatcher', 'payload'], 'Trapped-Frontend');
-smalltalk.TrappedModelWrapper.comment="I am base class for model wrappers.\x0aI wrap a model which can be any object.\x0a\x0aMy subclasses need to provide implementation for:\x0a\x09read:do:\x0a    modify:do:\x0a\x09(optionally) name\x0a\x0aand must issue these call when initializing:\x0a\x09model:\x0a\x09dispatcher: (with a subclass of TrappedDispatcher)\x0a"
-smalltalk.addMethod(
-"_dispatcher",
-smalltalk.method({
-selector: "dispatcher",
-category: 'accessing',
-fn: function (){
-var self=this;
-return self["@dispatcher"];
-},
-args: [],
-source: "dispatcher\x0a\x09^dispatcher",
-messageSends: [],
-referencedClasses: []
-}),
-smalltalk.TrappedModelWrapper);
-
-smalltalk.addMethod(
-"_dispatcher_",
-smalltalk.method({
-selector: "dispatcher:",
-category: 'accessing',
-fn: function (aDispatcher){
-var self=this;
-self["@dispatcher"]=aDispatcher;
-return self},
-args: ["aDispatcher"],
-source: "dispatcher: aDispatcher\x0a\x09dispatcher := aDispatcher",
-messageSends: [],
-referencedClasses: []
-}),
-smalltalk.TrappedModelWrapper);
-
-smalltalk.addMethod(
-"_model_",
-smalltalk.method({
-selector: "model:",
-category: 'accessing',
-fn: function (anObject){
-var self=this;
-self["@payload"]=anObject;
-smalltalk.send(smalltalk.send(self,"_dispatcher",[]),"_changed_",[[]]);
-return self},
-args: ["anObject"],
-source: "model: anObject\x0a\x09payload := anObject.\x0a    self dispatcher changed: #()",
-messageSends: ["changed:", "dispatcher"],
-referencedClasses: []
-}),
-smalltalk.TrappedModelWrapper);
-
-smalltalk.addMethod(
-"_name",
-smalltalk.method({
-selector: "name",
-category: 'accessing',
-fn: function (){
-var self=this;
-var $1;
-$1=smalltalk.send(smalltalk.send(self,"_class",[]),"_name",[]);
-return $1;
-},
-args: [],
-source: "name\x0a\x09^ self class name",
-messageSends: ["name", "class"],
-referencedClasses: []
-}),
-smalltalk.TrappedModelWrapper);
-
-smalltalk.addMethod(
-"_start",
-smalltalk.method({
-selector: "start",
-category: 'action',
-fn: function (){
-var self=this;
-smalltalk.send(smalltalk.send((smalltalk.Trapped || Trapped),"_current",[]),"_register_name_",[self,smalltalk.send(self,"_name",[])]);
-return self},
-args: [],
-source: "start\x0a\x09Trapped current register: self name: self name",
-messageSends: ["register:name:", "name", "current"],
-referencedClasses: ["Trapped"]
-}),
-smalltalk.TrappedModelWrapper);
-
-smalltalk.addMethod(
-"_watch_do_",
-smalltalk.method({
-selector: "watch:do:",
-category: 'action',
-fn: function (path,aBlock){
-var self=this;
-smalltalk.send(smalltalk.send(self,"_dispatcher",[]),"_on_hook_",[path,(function(){
-return smalltalk.send(self,"_read_do_",[path,aBlock]);
-})]);
-return self},
-args: ["path", "aBlock"],
-source: "watch: path do: aBlock\x0a\x09self dispatcher on: path hook: [ self read: path do: aBlock ]\x0a",
-messageSends: ["on:hook:", "read:do:", "dispatcher"],
-referencedClasses: []
-}),
-smalltalk.TrappedModelWrapper);
-
-
-smalltalk.addMethod(
-"_start",
-smalltalk.method({
-selector: "start",
-category: 'action',
-fn: function (){
-var self=this;
-var $2,$3,$1;
-$2=smalltalk.send(self,"_new",[]);
-smalltalk.send($2,"_start",[]);
-$3=smalltalk.send($2,"_yourself",[]);
-$1=$3;
-return $1;
-},
-args: [],
-source: "start\x0a\x09^self new start; yourself",
-messageSends: ["start", "new", "yourself"],
-referencedClasses: []
-}),
-smalltalk.TrappedModelWrapper.klass);
-
-
-smalltalk.addClass('TrappedMWDirect', smalltalk.TrappedModelWrapper, [], 'Trapped-Frontend');
-smalltalk.TrappedMWDirect.comment="I am TrappedModelWrapper that directly manipulate\x0athe object passed to model:"
-smalltalk.addMethod(
-"_modify_do_",
-smalltalk.method({
-selector: "modify:do:",
-category: 'action',
-fn: function (path,aBlock){
-var self=this;
-var newValue;
-var eavModel;
-eavModel=smalltalk.send(path,"_asEavModel",[]);
-newValue=smalltalk.send(aBlock,"_value_",[smalltalk.send(eavModel,"_on_",[self["@payload"]])]);
-smalltalk.send((function(){
-return smalltalk.send(eavModel,"_on_put_",[self["@payload"],newValue]);
-}),"_ensure_",[(function(){
-return smalltalk.send(smalltalk.send(self,"_dispatcher",[]),"_changed_",[path]);
-})]);
-return self},
-args: ["path", "aBlock"],
-source: "modify: path do: aBlock\x0a    | newValue eavModel |\x0a    eavModel := path asEavModel.\x0a    newValue := aBlock value: (eavModel on: payload).\x0a    [ eavModel on: payload put: newValue ] ensure: [ self dispatcher changed: path ]\x0a",
-messageSends: ["asEavModel", "value:", "on:", "ensure:", "changed:", "dispatcher", "on:put:"],
-referencedClasses: []
-}),
-smalltalk.TrappedMWDirect);
-
-smalltalk.addMethod(
-"_read_do_",
-smalltalk.method({
-selector: "read:do:",
-category: 'action',
-fn: function (path,aBlock){
-var self=this;
-var eavModel;
-eavModel=smalltalk.send(path,"_asEavModel",[]);
-smalltalk.send(aBlock,"_value_",[smalltalk.send(eavModel,"_on_",[self["@payload"]])]);
-return self},
-args: ["path", "aBlock"],
-source: "read: path do: aBlock\x0a    | eavModel |\x0a    eavModel := path asEavModel.\x0a    aBlock value: (eavModel on: payload)\x0a",
-messageSends: ["asEavModel", "value:", "on:"],
-referencedClasses: []
-}),
-smalltalk.TrappedMWDirect);
-
-
-
-smalltalk.addClass('TrappedMWIsolated', smalltalk.TrappedModelWrapper, [], 'Trapped-Frontend');
-smalltalk.TrappedMWIsolated.comment="I am TrappedModelWrapper than wrap access\x0ato an object passed to model: via Isolator."
-smalltalk.addMethod(
-"_model_",
-smalltalk.method({
-selector: "model:",
-category: 'accessing',
-fn: function (anObject){
-var self=this;
-smalltalk.send(self,"_model_",[smalltalk.send((smalltalk.Isolator || Isolator),"_on_",[anObject])],smalltalk.TrappedModelWrapper);
-return self},
-args: ["anObject"],
-source: "model: anObject\x0a\x09super model: (Isolator on: anObject)",
-messageSends: ["model:", "on:"],
-referencedClasses: ["Isolator"]
-}),
-smalltalk.TrappedMWIsolated);
-
-smalltalk.addMethod(
-"_modify_do_",
-smalltalk.method({
-selector: "modify:do:",
-category: 'action',
-fn: function (path,aBlock){
-var self=this;
-var eavModel;
-eavModel=smalltalk.send(smalltalk.send([smalltalk.symbolFor("root")],"__comma",[path]),"_asEavModel",[]);
-smalltalk.send((function(){
-return smalltalk.send(self["@payload"],"_model_modify_",[eavModel,aBlock]);
-}),"_ensure_",[(function(){
-return smalltalk.send(smalltalk.send(self,"_dispatcher",[]),"_changed_",[path]);
-})]);
-return self},
-args: ["path", "aBlock"],
-source: "modify: path do: aBlock\x0a    | eavModel |\x0a    eavModel := ({#root},path) asEavModel.\x0a    [ payload model: eavModel modify: aBlock ] ensure: [ self dispatcher changed: path ]\x0a",
-messageSends: ["asEavModel", ",", "ensure:", "changed:", "dispatcher", "model:modify:"],
-referencedClasses: []
-}),
-smalltalk.TrappedMWIsolated);
-
-smalltalk.addMethod(
-"_read_do_",
-smalltalk.method({
-selector: "read:do:",
-category: 'action',
-fn: function (path,aBlock){
-var self=this;
-var eavModel;
-eavModel=smalltalk.send(smalltalk.send([smalltalk.symbolFor("root")],"__comma",[path]),"_asEavModel",[]);
-smalltalk.send(self["@payload"],"_model_read_",[eavModel,aBlock]);
-return self},
-args: ["path", "aBlock"],
-source: "read: path do: aBlock\x0a    | eavModel |\x0a    eavModel := ({#root},path) asEavModel.\x0a    payload model: eavModel read: aBlock\x0a",
-messageSends: ["asEavModel", ",", "model:read:"],
-referencedClasses: []
-}),
-smalltalk.TrappedMWIsolated);
-
-
-
 smalltalk.addClass('TrappedSingleton', smalltalk.Object, [], 'Trapped-Frontend');
 smalltalk.addMethod(
-"_start",
+"_start_",
 smalltalk.method({
-selector: "start",
+selector: "start:",
 category: 'action',
-fn: function (){
+fn: function (args){
 var self=this;
 var $1;
 $1=smalltalk.send(self,"_subclassResponsibility",[]);
 return $1;
 },
-args: [],
-source: "start\x0a\x09^ self subclassResponsibility",
+args: ["args"],
+source: "start: args\x0a\x09^ self subclassResponsibility",
 messageSends: ["subclassResponsibility"],
 referencedClasses: []
 }),
@@ -502,17 +245,17 @@ referencedClasses: []
 smalltalk.TrappedSingleton.klass);
 
 smalltalk.addMethod(
-"_start",
+"_start_",
 smalltalk.method({
-selector: "start",
+selector: "start:",
 category: 'action',
-fn: function (){
+fn: function (zzz){
 var self=this;
-smalltalk.send(smalltalk.send(self,"_current",[]),"_start",[]);
+smalltalk.send(smalltalk.send(self,"_current",[]),"_perform_withArguments_",[smalltalk.symbolFor("start:"),arguments]);
 return self},
-args: [],
-source: "start\x0a\x09self current start",
-messageSends: ["start", "current"],
+args: ["zzz"],
+source: "start: zzz\x0a\x09self current perform: #start: withArguments: arguments",
+messageSends: ["perform:withArguments:", "current"],
 referencedClasses: []
 }),
 smalltalk.TrappedSingleton.klass);
@@ -625,30 +368,52 @@ referencedClasses: []
 }),
 smalltalk.Trapped);
 
+smalltalk.addMethod(
+"_register_",
+smalltalk.method({
+selector: "register:",
+category: 'accessing',
+fn: function (aListKeyedEntity){
+var self=this;
+smalltalk.send(self,"_register_name_",[aListKeyedEntity,smalltalk.send(smalltalk.send(aListKeyedEntity,"_class",[]),"_name",[])]);
+return self},
+args: ["aListKeyedEntity"],
+source: "register: aListKeyedEntity\x0a\x09self register: aListKeyedEntity name: aListKeyedEntity class name",
+messageSends: ["register:name:", "name", "class"],
+referencedClasses: []
+}),
+smalltalk.Trapped);
+
 smalltalk.addMethod(
 "_register_name_",
 smalltalk.method({
 selector: "register:name:",
 category: 'accessing',
-fn: function (aFly,aString){
+fn: function (aListKeyedEntity,aString){
 var self=this;
-smalltalk.send(self["@registry"],"_at_put_",[aString,aFly]);
+smalltalk.send(self["@registry"],"_at_put_",[aString,aListKeyedEntity]);
 return self},
-args: ["aFly", "aString"],
-source: "register: aFly name: aString\x0a\x09registry at: aString put: aFly",
+args: ["aListKeyedEntity", "aString"],
+source: "register: aListKeyedEntity name: aString\x0a\x09registry at: aString put: aListKeyedEntity",
 messageSends: ["at:put:"],
 referencedClasses: []
 }),
 smalltalk.Trapped);
 
 smalltalk.addMethod(
-"_start",
+"_start_",
 smalltalk.method({
-selector: "start",
+selector: "start:",
 category: 'action',
-fn: function (){
+fn: function (zzz){
 var self=this;
 var $1;
+var args;
+args = [].slice.call(arguments);
+;
+smalltalk.send(args,"_do_",[(function(each){
+return smalltalk.send(self,"_register_",[each]);
+})]);
 smalltalk.send(smalltalk.send("[data-trap]","_asJQuery",[]),"_each_",[(function(index,elem){
 var trap;
 var jq;
@@ -682,9 +447,9 @@ return smalltalk.send(smalltalk.send(smalltalk.send(smalltalk.send((smalltalk.Sm
 })]);
 })]);
 return self},
-args: [],
-source: "start\x0a\x09'[data-trap]' asJQuery each: [ :index :elem |\x0a    \x09| trap jq viewName modelName tokens path |\x0a        jq := elem asJQuery.\x0a        trap := jq attr: 'data-trap'.\x0a        tokens := trap tokenize: ':'.\x0a        tokens size = 1 ifTrue: [ tokens := { 'TrappedDumbView' }, tokens ].\x0a        viewName := tokens first.\x0a        tokens := (tokens second tokenize: ' ') select: [ :each | each notEmpty ].\x0a        modelName := tokens first.\x0a        path := Trapped parse: tokens allButFirst.\x0a        { modelName }, path trapDescend: [(Smalltalk current at: viewName) new appendToJQuery: jq].\x0a    ]",
-messageSends: ["each:", "asJQuery", "attr:", "tokenize:", "ifTrue:", ",", "=", "size", "first", "select:", "notEmpty", "second", "parse:", "allButFirst", "trapDescend:", "appendToJQuery:", "new", "at:", "current"],
+args: ["zzz"],
+source: "start: zzz\x0a\x09| args |\x0a    <args = [].slice.call(arguments)>.\x0a    args do: [ :each | self register: each ].\x0a\x09'[data-trap]' asJQuery each: [ :index :elem |\x0a    \x09| trap jq viewName modelName tokens path |\x0a        jq := elem asJQuery.\x0a        trap := jq attr: 'data-trap'.\x0a        tokens := trap tokenize: ':'.\x0a        tokens size = 1 ifTrue: [ tokens := { 'TrappedDumbView' }, tokens ].\x0a        viewName := tokens first.\x0a        tokens := (tokens second tokenize: ' ') select: [ :each | each notEmpty ].\x0a        modelName := tokens first.\x0a        path := Trapped parse: tokens allButFirst.\x0a        { modelName }, path trapDescend: [(Smalltalk current at: viewName) new appendToJQuery: jq].\x0a    ]",
+messageSends: ["do:", "register:", "each:", "asJQuery", "attr:", "tokenize:", "ifTrue:", ",", "=", "size", "first", "select:", "notEmpty", "second", "parse:", "allButFirst", "trapDescend:", "appendToJQuery:", "new", "at:", "current"],
 referencedClasses: ["Trapped", "Smalltalk"]
 }),
 smalltalk.Trapped);
@@ -887,29 +652,6 @@ smalltalk.TrappedSnapshot);
 
 
 
-smalltalk.addClass('TrappedSubscription', smalltalk.KeyedSubscriptionBase, [], 'Trapped-Frontend');
-smalltalk.addMethod(
-"_accepts_",
-smalltalk.method({
-selector: "accepts:",
-category: 'testing',
-fn: function (aKey){
-var self=this;
-var $1;
-$1=smalltalk.send(smalltalk.send(smalltalk.send(aKey,"_size",[]),"__lt_eq",[smalltalk.send(self["@key"],"_size",[])]),"_and_",[(function(){
-return smalltalk.send(aKey,"__eq",[smalltalk.send(self["@key"],"_copyFrom_to_",[(1),smalltalk.send(aKey,"_size",[])])]);
-})]);
-return $1;
-},
-args: ["aKey"],
-source: "accepts: aKey\x0a    ^aKey size <= key size and: [aKey = (key copyFrom: 1 to: aKey size)]",
-messageSends: ["and:", "=", "copyFrom:to:", "size", "<="],
-referencedClasses: []
-}),
-smalltalk.TrappedSubscription);
-
-
-
 smalltalk.addMethod(
 "_trapDescend_",
 smalltalk.method({

+ 158 - 0
st/Trapped-Backend.st

@@ -76,6 +76,20 @@ on: anObject
 Object subclass: #KeyedPubSubBase
 	instanceVariableNames: ''
 	package: 'Trapped-Backend'!
+!KeyedPubSubBase commentStamp!
+I represent a pub-sub based on a key.
+I manage key-block subscriptions as well as running blocks that are dirty.
+The subscription objects are reponsible of decision if the change is relevant for them.
+Subscription object must be subclasses of KeyedSubscriptionBase.
+
+My subclasses must provide implementation for:
+	subscriptionKey:block: (factory method for creating appropriate subscription object)
+
+as well as bookkeeping of subscriptions:
+	add:
+    do:
+    clean
+    (optionally) run!
 
 !KeyedPubSubBase methodsFor: 'action'!
 
@@ -117,6 +131,53 @@ subscriptionKey: key block: aBlock
     self subclassReponsibility
 ! !
 
+KeyedPubSubBase subclass: #ListKeyedPubSubBase
+	instanceVariableNames: ''
+	package: 'Trapped-Backend'!
+!ListKeyedPubSubBase commentStamp!
+I am base class list-keyed pub-sub.
+
+My subclasses need to provide implementation for:
+	add:
+    do:
+    clean
+    (optionally) run!
+
+!ListKeyedPubSubBase methodsFor: 'action'!
+
+subscriptionKey: key block: aBlock
+	^ListKeyedSubscription new key: key block: aBlock; yourself
+! !
+
+ListKeyedPubSubBase subclass: #SimpleListKeyedPubSub
+	instanceVariableNames: 'queue'
+	package: 'Trapped-Backend'!
+
+!SimpleListKeyedPubSub methodsFor: 'accessing'!
+
+add: aSubscription
+	queue add: aSubscription.
+! !
+
+!SimpleListKeyedPubSub methodsFor: 'bookkeeping'!
+
+clean
+	queue := queue select: [ :each | each isEnabled ]
+! !
+
+!SimpleListKeyedPubSub methodsFor: 'enumeration'!
+
+do: aBlock
+	queue do: aBlock
+! !
+
+!SimpleListKeyedPubSub methodsFor: 'initialization'!
+
+initialize
+    super initialize.
+	queue := OrderedCollection new
+! !
+
 Error subclass: #KeyedPubSubUnsubscribe
 	instanceVariableNames: ''
 	package: 'Trapped-Backend'!
@@ -169,6 +230,103 @@ isFlagged
 	^flagged
 ! !
 
+KeyedSubscriptionBase subclass: #ListKeyedSubscription
+	instanceVariableNames: ''
+	package: 'Trapped-Backend'!
+
+!ListKeyedSubscription methodsFor: 'testing'!
+
+accepts: aKey
+    ^aKey size <= key size and: [aKey = (key copyFrom: 1 to: aKey size)]
+! !
+
+Object subclass: #ListKeyedEntity
+	instanceVariableNames: 'dispatcher 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:.
+This wrapped model can be any smalltalk object.
+
+My subclasses need to provide implementation for:
+	read:do:
+    modify:do:
+
+and must issue these calls when initializing:
+	model: (with a wrapped object)
+	dispatcher: (with a subclass of ListKeyedPubSubBase)!
+
+!ListKeyedEntity methodsFor: 'accessing'!
+
+dispatcher
+	^dispatcher
+!
+
+dispatcher: aDispatcher
+	dispatcher := aDispatcher
+!
+
+model: anObject
+	payload := anObject.
+    self dispatcher changed: #()
+! !
+
+!ListKeyedEntity methodsFor: 'action'!
+
+watch: path do: aBlock
+	self dispatcher on: path hook: [ self read: path do: aBlock ]
+! !
+
+ListKeyedEntity subclass: #ListKeyedDirectEntity
+	instanceVariableNames: ''
+	package: 'Trapped-Backend'!
+!ListKeyedDirectEntity commentStamp!
+I am ListKeyedEntity that directly manipulate
+the wrapped model object.!
+
+!ListKeyedDirectEntity methodsFor: 'action'!
+
+modify: path do: aBlock
+    | newValue eavModel |
+    eavModel := path asEavModel.
+    newValue := aBlock value: (eavModel on: payload).
+    [ eavModel on: payload put: newValue ] ensure: [ self dispatcher changed: path ]
+!
+
+read: path do: aBlock
+    | eavModel |
+    eavModel := path asEavModel.
+    aBlock value: (eavModel on: payload)
+! !
+
+ListKeyedEntity subclass: #ListKeyedIsolatedEntity
+	instanceVariableNames: ''
+	package: 'Trapped-Backend'!
+!ListKeyedIsolatedEntity commentStamp!
+I am ListKeyedEntity that guards access
+to the wrapped model object via Isolator.!
+
+!ListKeyedIsolatedEntity methodsFor: 'accessing'!
+
+model: anObject
+	super model: (Isolator on: anObject)
+! !
+
+!ListKeyedIsolatedEntity methodsFor: 'action'!
+
+modify: path do: aBlock
+    | eavModel |
+    eavModel := ({#root},path) asEavModel.
+    [ payload model: eavModel modify: aBlock ] ensure: [ self dispatcher changed: path ]
+!
+
+read: path do: aBlock
+    | eavModel |
+    eavModel := ({#root},path) asEavModel.
+    payload model: eavModel read: aBlock
+! !
+
 !Object methodsFor: '*Trapped-Backend'!
 
 reverseTrapAt: anObject

+ 2 - 30
st/Trapped-Demo.st

@@ -1,5 +1,5 @@
 Smalltalk current createPackage: 'Trapped-Demo' properties: #{}!
-TrappedMWIsolated subclass: #App
+ListKeyedIsolatedEntity subclass: #App
 	instanceVariableNames: ''
 	package: 'Trapped-Demo'!
 !App commentStamp!
@@ -35,7 +35,7 @@ function TodoCtrl($scope) {
 
 initialize
 	super initialize.
-    self dispatcher: TrappedDumbDispatcher new.
+    self dispatcher: SimpleListKeyedPubSub new.
     self model: (AppModel new title: 'Todo').
     [ self modify: #(#todos) do: [{
         #{'text'->'learn trapped'. 'done'->true}.
@@ -174,31 +174,3 @@ renderOn: html
     ]] ifNotPresent: [ html with: 'Loading ...' ]]
 ! !
 
-TrappedDispatcher subclass: #TrappedDumbDispatcher
-	instanceVariableNames: 'queue'
-	package: 'Trapped-Demo'!
-
-!TrappedDumbDispatcher methodsFor: 'accessing'!
-
-add: aSubscription
-	queue add: aSubscription.
-! !
-
-!TrappedDumbDispatcher methodsFor: 'bookkeeping'!
-
-clean
-	queue := queue select: [ :each | each isEnabled ]
-! !
-
-!TrappedDumbDispatcher methodsFor: 'enumeration'!
-
-do: aBlock
-	queue do: aBlock
-! !
-
-!TrappedDumbDispatcher methodsFor: 'initialization'!
-
-initialize
-	queue := OrderedCollection new
-! !
-

+ 13 - 136
st/Trapped-Frontend.st

@@ -63,26 +63,6 @@ showBlock
 	^[ :model | brush asJQuery val: (model ifNotNil: [self prim: model] ifNil: [[]]) ]
 ! !
 
-KeyedPubSubBase subclass: #TrappedDispatcher
-	instanceVariableNames: ''
-	package: 'Trapped-Frontend'!
-!TrappedDispatcher commentStamp!
-I am base class for change event dispatchers.
-I manage changed path - action block subscriptions.
-These subscription are instances of TrappedSubscription
-
-My subclasses need to provide implementation for:
-	add:
-    do:
-    clean
-    (optionally) run!
-
-!TrappedDispatcher methodsFor: 'action'!
-
-subscriptionKey: key block: aBlock
-	^TrappedSubscription new key: key block: aBlock; yourself
-! !
-
 Widget subclass: #TrappedDumbView
 	instanceVariableNames: ''
 	package: 'Trapped-Frontend'!
@@ -95,113 +75,13 @@ renderOn: html
 	html root trap: #()
 ! !
 
-Object subclass: #TrappedModelWrapper
-	instanceVariableNames: 'dispatcher payload'
-	package: 'Trapped-Frontend'!
-!TrappedModelWrapper commentStamp!
-I am base class for model wrappers.
-I wrap a model which can be any object.
-
-My subclasses need to provide implementation for:
-	read:do:
-    modify:do:
-	(optionally) name
-
-and must issue these call when initializing:
-	model:
-	dispatcher: (with a subclass of TrappedDispatcher)!
-
-!TrappedModelWrapper methodsFor: 'accessing'!
-
-dispatcher
-	^dispatcher
-!
-
-dispatcher: aDispatcher
-	dispatcher := aDispatcher
-!
-
-model: anObject
-	payload := anObject.
-    self dispatcher changed: #()
-!
-
-name
-	^ self class name
-! !
-
-!TrappedModelWrapper methodsFor: 'action'!
-
-start
-	Trapped current register: self name: self name
-!
-
-watch: path do: aBlock
-	self dispatcher on: path hook: [ self read: path do: aBlock ]
-! !
-
-!TrappedModelWrapper class methodsFor: 'action'!
-
-start
-	^self new start; yourself
-! !
-
-TrappedModelWrapper subclass: #TrappedMWDirect
-	instanceVariableNames: ''
-	package: 'Trapped-Frontend'!
-!TrappedMWDirect commentStamp!
-I am TrappedModelWrapper that directly manipulate
-the object passed to model:!
-
-!TrappedMWDirect methodsFor: 'action'!
-
-modify: path do: aBlock
-    | newValue eavModel |
-    eavModel := path asEavModel.
-    newValue := aBlock value: (eavModel on: payload).
-    [ eavModel on: payload put: newValue ] ensure: [ self dispatcher changed: path ]
-!
-
-read: path do: aBlock
-    | eavModel |
-    eavModel := path asEavModel.
-    aBlock value: (eavModel on: payload)
-! !
-
-TrappedModelWrapper subclass: #TrappedMWIsolated
-	instanceVariableNames: ''
-	package: 'Trapped-Frontend'!
-!TrappedMWIsolated commentStamp!
-I am TrappedModelWrapper than wrap access
-to an object passed to model: via Isolator.!
-
-!TrappedMWIsolated methodsFor: 'accessing'!
-
-model: anObject
-	super model: (Isolator on: anObject)
-! !
-
-!TrappedMWIsolated methodsFor: 'action'!
-
-modify: path do: aBlock
-    | eavModel |
-    eavModel := ({#root},path) asEavModel.
-    [ payload model: eavModel modify: aBlock ] ensure: [ self dispatcher changed: path ]
-!
-
-read: path do: aBlock
-    | eavModel |
-    eavModel := ({#root},path) asEavModel.
-    payload model: eavModel read: aBlock
-! !
-
 Object subclass: #TrappedSingleton
 	instanceVariableNames: ''
 	package: 'Trapped-Frontend'!
 
 !TrappedSingleton methodsFor: 'action'!
 
-start
+start: args
 	^ self subclassResponsibility
 ! !
 
@@ -215,8 +95,8 @@ current
 
 !TrappedSingleton class methodsFor: 'action'!
 
-start
-	self current start
+start: zzz
+	self current perform: #start: withArguments: arguments
 ! !
 
 TrappedSingleton subclass: #Trapped
@@ -229,8 +109,12 @@ byName: aString
 	^ registry at: aString
 !
 
-register: aFly name: aString
-	registry at: aString put: aFly
+register: aListKeyedEntity
+	self register: aListKeyedEntity name: aListKeyedEntity class name
+!
+
+register: aListKeyedEntity name: aString
+	registry at: aString put: aListKeyedEntity
 ! !
 
 !Trapped methodsFor: 'action'!
@@ -246,7 +130,10 @@ descend: anArray snapshotDo: aBlock
     ]
 !
 
-start
+start: zzz
+	| args |
+    <args = [].slice.call(arguments)>.
+    args do: [ :each | self register: each ].
 	'[data-trap]' asJQuery each: [ :index :elem |
     	| trap jq viewName modelName tokens path |
         jq := elem asJQuery.
@@ -353,16 +240,6 @@ modify: aBlock
 	self model modify: self path allButFirst do: aBlock
 ! !
 
-KeyedSubscriptionBase subclass: #TrappedSubscription
-	instanceVariableNames: ''
-	package: 'Trapped-Frontend'!
-
-!TrappedSubscription methodsFor: 'testing'!
-
-accepts: aKey
-    ^aKey size <= key size and: [aKey = (key copyFrom: 1 to: aKey size)]
-! !
-
 !Array methodsFor: '*Trapped-Frontend'!
 
 trapDescend: aBlock