vadimtsushko avatar vadimtsushko committed afb0e0c

Objectory: Added compound objects

Comments (0)

Files changed (18)

 examples/types.dart
+lib/objectory/ObjectoryLib_vm.dart
 tests/allTests.dart
 tests/CursorTest.dart
 tests/DbCommandTest.dart
-tests/bson/clientTest/clientTest.dart
+tests/objectory/PersistentObjectTest.dart
 lib/bson/bson.dart
 examples/queries.dart
 third_party/dart-crypto-lib/test/vm/tests.dart
 examples/simpleUpdate.dart
 third_party/dart-crypto-lib/src/md5.dart
 tests/bson/BsonImplTest.dart
+tests/bson/ClientTest/ClientTest.dart
+tests/objectory/ObjectoryBaseImplTest.dart
 third_party/testing/unittest/unittest_node.dart
 tests/ConnectionTest.dart
 third_party/dart-crypto-lib/test/browser/test.dart
 lib/networking/logger.dart
 third_party/testing/unittest/unittest_vm.dart
 lib/mongo.dart
+tests/objectory/ObjectoryImplVmTest.dart
 tests/bson/BsonBinaryTest.dart
 tests/SelectorBuilderTest.dart

lib/bson/bson.dart

 #library("bson.dart");
-//#import("../../../../../../dartrepo/dart-read-only/dart/utils/string_encoding/utf8.dart");
 #import("dart:utf");
 #import("dart:uri");
 #source("bson_type.dart");

lib/bson/objectid.dart

   }
   ObjectId.fromSeconds(int seconds): id=new Binary(12){
     id.writeInt(seconds,4,forceBigEndian:true);
+    /* Todo - restore whan Math.random would work
     id.writeInt(Statics.MachineId,3);
     id.writeInt(Statics.Pid,2);    
+    */
+    id.writeInt((seconds & 0xFFFFFF).floor().toInt(),3);
+    id.writeInt((seconds & 0xFFFF).floor().toInt(),2);
     id.writeInt(Statics.nextIncrement,3,forceBigEndian:true);
   }  
   String toString()=>"ObjectId(${id.toHexString()})";

lib/dbcollection.dart

   DbCollection(this.db, this.collectionName){}  
   String fullName() => "${db.databaseName}.$collectionName";
   save(Map document){
+    var id;
+    bool createId = false;
     if (document.containsKey("_id")){
-      update({"_id": document["_id"]}, document);
+      id = document["_id"];      
+      if (id === null){
+        createId = true;
+      }
+    }
+    if (id !== null){
+      update({"_id": id}, document);
     } 
     else{
+      if (createId) {
+        document["_id"] = new ObjectId();
+      }     
       insert(document);
     }
   }

lib/helpers/MapProxy.dart

-class _MapProxy<K,V> implements Map<K,V>{
+class MapProxy<K,V> implements Map<K,V>{
   LinkedHashMap map;
   Queue keys;
-  _MapProxy(): map = new LinkedHashMap();
+  MapProxy(): map = new LinkedHashMap();
   bool containsValue(V value)=>map.containsValue(value);
   
   bool containsKey(K key)=>map.containsKey(key);

lib/helpers/SelectorBuilder.dart

 SelectorBuilder query(){
   return new SelectorBuilder();
 }
-class SelectorBuilder<K,V> extends _MapProxy<K,V>{
+class SelectorBuilder<K,V> extends MapProxy<K,V>{
   toString()=>"SelectorBuilder($map)";
   SelectorBuilder eq(String fieldName,value){
     map[fieldName] = value;

lib/objectory/Objectory.dart

 typedef PersistentObject FactoryMethod();
 interface Objectory{  
   void registerClass(String className, FactoryMethod fm);
-  Future<PersistentObject> findOne(String className,[Map query]);
-  Future<List<PersistentObject>> find(String className,[Map query]);
+  PersistentObject newInstance(String className);
+  Future<PersistentObject> findOne(String className,[Map selector]);
+  Future<List<PersistentObject>> find(String className,[Map selector]);
   void save(PersistentObject persistentObject);
   void remove(PersistentObject persistentObject);
-  Future<bool> connect(String database, [String url]);
+  Future<bool> open(String database, [String url]);
   Future<bool> dropDb();
 }
-class ObjectoryBaseImpl implements Objectory{
+abstract class ObjectoryBaseImpl implements Objectory{
   Map<String,FactoryMethod> factories;
   ObjectoryBaseImpl(){
     factories = new  Map<String,FactoryMethod>();
   PersistentObject map2Object(String className, Map map){
     PersistentObject result = newInstance(className);
     result.map = map;
+    if (result.isRoot()){
+      result.id = map["_id"];    
+    }      
+    for (var key in map.getKeys()){
+      var value = map[key];
+      if (value is Map){
+        if (value.containsKey("_pt")){
+          PersistentObject subComponent = map2Object(value["_pt"],value);
+          result.setProperty(key,subComponent);  
+          result.clearDirtyStatus();
+        }
+      }
+    }
     return result;
   }
+  void clearFactories(){
+    factories.clear();
+  }
   void registerClass(String className, FactoryMethod fm){
     factories[className] = fm;
   }

lib/objectory/ObjectoryDirectConnectionImpl.dart

 Objectory get objectory() => new ObjectorySingleton._singleton();
-class ObjectorySingleton extends ObjectoryBaseImpl{
+abstract class ObjectorySingleton extends ObjectoryBaseImpl{
   static Objectory _objectory;
   ObjectorySingleton._internal();
   factory ObjectorySingleton._singleton(){
   }
   void save(PersistentObject persistentObject){
     db.collection(persistentObject.type).save(persistentObject);
+    persistentObject.id = persistentObject["_id"];
   }
   void remove(PersistentObject persistentObject){
-    db.save(persistentObject);
+    if (persistentObject.id === null){
+      return;
+    }
+    db.collection(persistentObject.type).remove({"_id":persistentObject.id});
   }
-  Future<List<PersistentObject>> find(String className,[Map query]){
+  Future<List<PersistentObject>> find(String className,[Map selector]){
     Completer completer = new Completer();
     List<PersistentObject> result = new List<PersistentObject>();
     db.collection(className)
-      .find(query)
+      .find(selector)
       .each((map){
-        PersistentObject obj = objectory.newInstance(className);
-        obj.map = map;
-        obj.id = map["_id"];
+        PersistentObject obj = objectory.map2Object(className,map);
         result.add(obj);
       }).then((_) => completer.complete(result));
     return completer.future;  
   }
+  Future<PersistentObject> findOne(String className,[Map selector]){
+    Completer completer = new Completer();    
+    db.collection(className)
+      .findOne(selector)
+      .then((map){              
+        PersistentObject obj = objectory.map2Object(className,map);
+        completer.complete(obj);
+      });
+    return completer.future;  
+  }
+  
+  Future<bool> dropDb(){
+    db.drop();
+  }
 }

lib/objectory/ObjectoryLib_vm.dart

 #library("objectory");
 #import("../mongo.dart");
 #import("../bson/bson.dart");
-#source("../helpers/MapProxy.dart");
+//#source("../helpers/MapProxy.dart");
 #source("PersistentObject.dart");
 #source("Objectory.dart");
 #source("ObjectoryDirectConnectionImpl.dart");

lib/objectory/PersistentObject.dart

 // noSuchMethod() borrowed from Chris Buckett (chrisbuckett@gmail.com)
 // http://github.com/chrisbu/dartwatch-JsonObject
-class PersistentObject extends _MapProxy{  
-  PersistentObject.fromMap(Map map): super.fromMap(map);
+interface PersistentObject{
+  noSuchMethod(String function_name, List args);
+  void setProperty(String property, value);
+  void init();
+  String get type();
+  void clearDirtyStatus();
+}
+abstract class PersistentObjectBase extends MapProxy implements PersistentObject{  
+  bool setupMode;
   Set<String> dirtyFields;
-  ObjectId id;
-  PersistentObject(){        
-    map = null;
-    dirtyFields = new Set<String>();      
+  Set<String> _properties;
+  void setPropertyList(List<String> propertyList){
+    _properties = new Set<String>.from(propertyList);
+  }  
+  PersistentObjectBase(){        
+    if (isRoot()){
+      map["_id"] = null;
+    }                
     init();
-//    abstract bool isRoot();
-  }
-  PersistentObject.makeTransient(){
-
-  }
-  setDirty(String fieldName){
+    dirtyFields = new Set<String>();
+    if (_properties === null){
+      throw "$type: Properties list must be set in method init()";
+    }
+  }  
+  void setDirty(String fieldName){
+    if (dirtyFields === null){
+      return;
+    }
     dirtyFields.add(fieldName);
-  }
+  }  
+void clearDirtyStatus(){
+  dirtyFields.clear();
+}
   onValueChanging(String fieldName, newValue){
     setDirty(fieldName);
   }
   isDirty(){
     return !dirtyFields.isEmpty();
   }
-
   noSuchMethod(String function_name, List args) {
-    if (map === null){
-       map =  new LinkedHashMap();
-    }
     if (args.length == 0 && function_name.startsWith("get:")) {
       //synthetic getter
       var property = function_name.replaceFirst("get:", "");
-      if (this.containsKey(property)) {
+      if (_properties.contains(property)) {
         return this[property];
       }
       else{
-        return null;
+        super.noSuchMethod(function_name, args);
       }
     }
     else if (args.length == 1 && function_name.startsWith("set:")) {
       //synthetic setter
-      var property = function_name.replaceFirst("set:", "");
-      onValueChanging(property, args[0]);
-      this[property] = args[0];
-      return this[property];
-    }
-    
+      var value = args[0];
+      var property = function_name.replaceFirst("set:", "");      
+      if (_properties.contains(property)) {
+        onValueChanging(property, value);
+        this[property] = value;
+        if (value is InnerPersistentObject){
+          value.pathToMe = property;
+          value.parent = this;
+        } 
+        return this[property];
+      }
+      else {       
+        super.noSuchMethod(function_name, args);
+      }        
+    }    
     //if we get here, then we've not found it - throw.
     super.noSuchMethod(function_name, args);
   }
     return noSuchMethod('get:$property');
   }  
   String toString()=>"$type($map)";
-  void init(){
+  void init(){}  
+  abstract String get type();
+  abstract bool isRoot();
+}
+abstract class RootPersistentObject extends PersistentObjectBase{
+   ObjectId id;
+   bool isRoot()=>true;
+}
+abstract class InnerPersistentObject extends PersistentObjectBase{
+  PersistentObject parent;
+  String pathToMe;
+  InnerPersistentObject():super(){
+    map["_pt"] = type;
   }  
-  abstract String get type();
-  static Future<List<PersistentObject>>find(Map query){
-  }
-  static Future<PersistentObject>findOne(Map query){
-  }  
+  bool isRoot()=>false;
 }

tests/DbCollectionTest.dart

     asyncTest("testFindEach",1,testFindEach);
     asyncTest("testDrop",1,testDrop);
     asyncTest("testSaveWithIntegerId",1,testSaveWithIntegerId);
-    asyncTest("testSaveWithObjectId",1,testSaveWithObjectId);    
+/*    asyncTest("testSaveWithObjectId",1,testSaveWithObjectId);    */
   });
 }

tests/allTests.dart

 #import("DbCollectionTest.dart",prefix:"dbcollection");
 #import("DbTest.dart",prefix:"dbtest");
 #import("bson/allBsonTests.dart",prefix:"bson");
+#import("objectory/allobjectoryTests.dart",prefix:"objectory");
 #import("SelectorBuilderTest.dart",prefix:"helper");
 main(){
   bson.main();
   dbcollection.main();
   dbtest.main();
   helper.main();        
+  objectory.main();
 }

tests/objectory/Author.dart

-interface IAuthor{
-  String name;
-  int age;
-  String email;
-}
-class Author extends PersistentObject implements IAuthor{  
-  String get type()=>'Author';
-  set name(String value){
-    if (value is String){
-      value = value.toUpperCase();
-    }      
-    setProperty('name', value);
-  }
-  /*
-  init(){
-    name = null;
-    age = null;
-    email = null;
-  }
-  */
-}

tests/objectory/DomainModel.dart

+interface IAuthor{
+  String name;
+  int age;
+  String email;
+}
+interface IPerson{
+  String firstName;
+  String lastName;
+  Date birthday;
+  Address address;  
+}
+interface IAddress{
+  String cityName;
+  String zipcode;
+  String streetName;
+}
+
+
+class Author extends RootPersistentObject implements IAuthor{  
+  String get type()=>'Author';
+  init(){
+    setPropertyList(["name","age","email"]);
+  }
+  set name(String value){
+    if (value is String){
+      value = value.toUpperCase();
+    }      
+    setProperty('name', value);
+  }
+}
+class Person extends RootPersistentObject implements IPerson{  
+  String get type()=>"Person";
+  init(){    
+    setPropertyList(["firstName","lastName","birthday","address"]);
+    address = new Address();
+  }
+}  
+class Address extends InnerPersistentObject implements IAddress{  
+  String get type()=>"Address";
+  init(){
+    setPropertyList(["cityName","zipCode","streetName"]);
+  }
+}

tests/objectory/ObjectoryBaseImplTest.dart

-#library("ObjectoryBaseImplTest");
-#import("../../lib/objectory/ObjectoryLib_vm.dart");
-#import('../../third_party/testing/unittest/unittest_vm.dart');
-#source("author.dart");
-setUpObjectory(){
-  objectory.registerClass('Author',()=>new Author());
-}
-testNewInstanceMethod(){
-  Author author = objectory.newInstance('Author');
-  expect(author is Author).isTrue();
-  print(author);
-}
-testMap2ObjectMethod(){
-  Map map = {
-    "name": "Vadim",
-    "age": 300,
-    "email": "nobody@know.it"};
-  Author author = objectory.map2Object("Author",map);
-  // Not converted to upperCase because setter has not been invoked
-  expect(author.name).equals("Vadim"); 
-  expect(author.age).equals(300);
-  expect(author.email).equals("nobody@know.it");
-}
-
-main(){  
-  setUpObjectory();
-  group("ObjectoryTests", ()  {
-    test("testNewInstanceMethod",testNewInstanceMethod);   
-    test("testMap2ObjectMethod",testMap2ObjectMethod);       
-  });  
-}

tests/objectory/ObjectoryImplVmTest.dart

-#library("ObjectoryBaseImplTest");
+#library("ObjectoryVM");
 #import("../../lib/objectory/ObjectoryLib_vm.dart");
 #import('../../third_party/testing/unittest/unittest_vm.dart');
-#source("author.dart");
+#source("DomainModel.dart");
+#source("../../lib/helpers/SelectorBuilder.dart");
+#source("../../lib/helpers/MapProxy.dart");
 Future<bool> setUpObjectory(){
   var res = new Completer();
-  objectory.open("ObjectoryTest").then((_){
-    objectory.registerClass('Author',()=>new Author());
-    print("I'm here");
+  objectory.clearFactories();
+  objectory.open("ObjectoryTest").then((_){    
+    objectory.dropDb();
+    objectory.registerClass('Author',()=>new Author());    
+    objectory.registerClass('Person',()=>new Person());
+    objectory.registerClass('Address',()=>new Address());
     res.complete(true);
   });    
   return res.future;
 }
-void testInsertion(){
+void testInsertionAndUpdate(){
   Author author = new Author();  
   author.name = 'Dan';
   author.age = 3;
-  author.email = 'who@cares.net';
-  objectory.save(author);
+  author.email = 'who@cares.net';  
+  objectory.save(author); // First insert;
+  author.age = 4;
+  objectory.save(author); // Then update;
   objectory.find('Author').then((coll){
-    for (Author auth in coll){
-      print("Author: ${auth.id} ${auth.name} ${auth.age}");
+    expect(coll.length).equals(1);
+    Author authFromMongo = coll[0];
+    expect(authFromMongo.age).equals(4);    
+    callbackDone();
+  });
+}
+testNewInstanceMethod(){
+  Author author = objectory.newInstance('Author');
+  expect(author is Author).isTrue();
+}
+testMap2ObjectMethod(){
+  Map map = {
+    "name": "Vadim",
+    "age": 300,
+    "email": "nobody@know.it"};
+  Author author = objectory.map2Object("Author",map);
+  // Not converted to upperCase because setter has not been invoked
+  expect(author.name).equals("Vadim"); 
+  expect(author.age).equals(300);
+  expect(author.email).equals("nobody@know.it");
+  map = {
+    "streetName": "333",
+    "cityName": "44444"
     };
+  Address address = objectory.map2Object("Address",map);  
+  expect(address.cityName).equals("44444");   
+}
+testCompoundObject(){
+  Person person = new Person();  
+  person.address.cityName = 'Tyumen';
+  person.address.streetName = 'Elm';  
+  person.firstName = 'Dick';
+  objectory.save(person); ;
+  objectory.findOne('Person',query().id(person.id)).then((savedPerson){
+    expect(savedPerson.firstName).equals('Dick');
+    expect(savedPerson.address.streetName).equals('Elm');
+    expect(savedPerson.address.cityName).equals('Tyumen');
     callbackDone();
   });
 }
 
+
 main(){  
-  setUpObjectory().then((_) {
-    print("And now here. ${objectory.db}");
-    group("ObjectoryTests", ()  {
-      asyncTest("testInsertion",1,testInsertion);   
+  setUpObjectory().then((_) {    
+    group("ObjectoryVM", ()  {
+      asyncTest("testCompoundObject",1,testCompoundObject);   
+      asyncTest("testInsertionAndUpdate",1,testInsertionAndUpdate);         
+      test("testNewInstanceMethod",testNewInstanceMethod);   
+      test("testMap2ObjectMethod",testMap2ObjectMethod);       
     });  
   });    
 }

tests/objectory/PersistentObjectTest.dart

 #library("PersistenObjectTests");
 #import("../../lib/objectory/ObjectoryLib_vm.dart");
 #import('../../third_party/testing/unittest/unittest_vm.dart');
-#source("author.dart");
+#source("DomainModel.dart");
 testAuthorCreation(){
   Author author = new Author();
   author.name = 'vadim';
   author.age = 99;
   author.email = 'sdf';
-  expect(author.getKeys()[0]).equals("name");
-  expect(author.getKeys()[1]).equals("age");
+  expect(author.getKeys()[0]).equals("_id");
+  expect(author.getKeys()[1]).equals("name");
+  expect(author.getKeys()[2]).equals("age");
   expect(author.getKeys().last()).equals("email");
-  expect(author.getKeys().length).equals(3);
+  expect(author.getKeys().length).equals(4);
   expect(author.name).equals('VADIM'); // converted to uppercase by custom  setter;
 }
+
 testSetDirty(){
   Author author = new Author();
   author.name = "Vadim";
   expect(author.dirtyFields.length).equals(1);
   expect(author.isDirty()).isTrue();  
 }
+testCompoundObject(){
+  Person person = new Person();  
+  person.address.cityName = 'Tyumen';
+  person.address.streetName = 'Elm';  
+  person.firstName = 'Dick';
+  Map map = person.map;
+  expect(map["address"]["streetName"]).equals("Elm");
+  expect(person.address.parent).equals(person);
+  expect(person.address.pathToMe).equals("address");
+  expect(person.isDirty()).isTrue();
+  expect(person.address.isDirty()).isTrue();
+}
+testFailOnAbsentProperty(){
+  IAuthor author = new Author();
+  Expect.throws(()=>author.sdfsdfsdfgdfgdf,reason:"Must fail on missing property getter");
+}
 
 main(){
   group("PersistenObjectTests", ()  {
     test("testAuthorCreation",testAuthorCreation);
     test("testSetDirty",testSetDirty);
+    test("testCompoundObject",testCompoundObject);
+    test("testFailOnAbsentProperty",testFailOnAbsentProperty);
   });  
 
 }

tests/objectory/allObjectoryTests.dart

+#library("all_objectory_tests");
+#import("ObjectoryImplVmTest.dart",prefix:"impl");
+#import("PersistentObjectTest.dart",prefix:"persistentObject");
+main(){  
+  impl.main();
+  persistentObject.main();
+}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.