diff --git a/src/RJSObject.mm b/src/RJSObject.mm index ca765145..440daae1 100644 --- a/src/RJSObject.mm +++ b/src/RJSObject.mm @@ -120,6 +120,16 @@ template<> JSValueRef RJSAccessor::dict_value_for_key(JSContextRef ctx, JSValueR return ret; } +template<> bool RJSAccessor::has_default_value_for_property(JSContextRef ctx, const ObjectSchema &object_schema, const std::string &prop_name) { + ObjectDefaults &defaults = RJSDefaultsForClassName(object_schema.name); + return defaults.find(prop_name) != defaults.end(); +} + +template<> JSValueRef RJSAccessor::default_value_for_property(JSContextRef ctx, const ObjectSchema &object_schema, const std::string &prop_name) { + ObjectDefaults &defaults = RJSDefaultsForClassName(object_schema.name); + return defaults[prop_name]; +} + template<> bool RJSAccessor::is_null(JSContextRef ctx, JSValueRef &val) { return JSValueIsUndefined(ctx, val) || JSValueIsNull(ctx, val); } diff --git a/src/RJSSchema.hpp b/src/RJSSchema.hpp index 4618b240..8faa8f5a 100644 --- a/src/RJSSchema.hpp +++ b/src/RJSSchema.hpp @@ -17,9 +17,11 @@ //////////////////////////////////////////////////////////////////////////// #import "RJSUtil.hpp" +#import namespace realm { class Schema; + using ObjectDefaults = std::map; } JSClassRef RJSSchemaClass(); @@ -28,3 +30,4 @@ JSObjectRef RJSSchemaCreate(JSContextRef ctx, realm::Schema *schema); realm::Schema RJSParseSchema(JSContextRef ctx, JSObjectRef jsonObject); JSValueRef RJSPrototypeForClassName(const std::string &className); +realm::ObjectDefaults &RJSDefaultsForClassName(const std::string &className); diff --git a/src/RJSSchema.mm b/src/RJSSchema.mm index 0fba76df..17ddc727 100644 --- a/src/RJSSchema.mm +++ b/src/RJSSchema.mm @@ -45,6 +45,11 @@ JSObjectRef RJSSchemaCreate(JSContextRef ctx, Schema &schema) { return RJSWrapObject(ctx, RJSSchemaClass(), wrapper); } +static std::map s_defaults; +ObjectDefaults &RJSDefaultsForClassName(const std::string &className) { + return s_defaults[className]; +} + static std::map s_prototypes; JSValueRef RJSPrototypeForClassName(const std::string &className) { return s_prototypes[className]; @@ -117,15 +122,24 @@ static inline ObjectSchema RJSParseObjectSchema(JSContextRef ctx, JSObjectRef ob JSObjectRef propertiesObject = RJSValidatedObjectProperty(ctx, objectSchemaObject, propertiesString, "ObjectSchema object must have a 'properties' array."); ObjectSchema objectSchema; - static JSStringRef nameString = JSStringCreateWithUTF8CString("name"); + ObjectDefaults defaults; + static JSStringRef nameString = JSStringCreateWithUTF8CString("name"); objectSchema.name = RJSValidatedStringProperty(ctx, objectSchemaObject, nameString); size_t numProperties = RJSValidatedArrayLength(ctx, propertiesObject); for (unsigned int p = 0; p < numProperties; p++) { JSObjectRef property = RJSValidatedObjectAtIndex(ctx, propertiesObject, p); objectSchema.properties.emplace_back(RJSParseProperty(ctx, property)); + + static JSStringRef defaultString = JSStringCreateWithUTF8CString("default"); + JSValueRef defaultValue = JSObjectGetProperty(ctx, property, defaultString, NULL); + if (!JSValueIsUndefined(ctx, defaultValue)) { + JSValueProtect(ctx, defaultValue); + defaults.emplace(objectSchema.properties.back().name, defaultValue); + } } + s_defaults.emplace(objectSchema.name, std::move(defaults)); static JSStringRef primaryString = JSStringCreateWithUTF8CString("primaryKey"); JSValueRef primaryValue = RJSValidatedPropertyValue(ctx, objectSchemaObject, primaryString); @@ -141,7 +155,7 @@ static inline ObjectSchema RJSParseObjectSchema(JSContextRef ctx, JSObjectRef ob // store prototype if (prototypeObject) { JSValueProtect(ctx, prototypeObject); - s_prototypes[objectSchema.name] = prototypeObject; + s_prototypes[objectSchema.name] = std::move(prototypeObject); } return objectSchema; @@ -158,3 +172,4 @@ realm::Schema RJSParseSchema(JSContextRef ctx, JSObjectRef jsonObject) { return schema; } + diff --git a/src/object-store/object_accessor.hpp b/src/object-store/object_accessor.hpp index f318a2f5..dfb24f69 100644 --- a/src/object-store/object_accessor.hpp +++ b/src/object-store/object_accessor.hpp @@ -32,6 +32,9 @@ namespace realm { static bool dict_has_value_for_key(ContextType ctx, ValueType dict, const std::string &prop_name); static ValueType dict_value_for_key(ContextType ctx, ValueType dict, const std::string &prop_name); + static bool has_default_value_for_property(ContextType ctx, const ObjectSchema &object_schema, const std::string &prop_name); + static ValueType default_value_for_property(ContextType ctx, const ObjectSchema &object_schema, const std::string &prop_name); + static bool to_bool(ContextType ctx, ValueType &val); static long long to_long(ContextType ctx, ValueType &val); static float to_float(ContextType ctx, ValueType &val); @@ -184,14 +187,18 @@ namespace realm { // populate Object object(realm, object_schema, table->get(row_index)); for (Property &prop : object_schema.properties) { - if (Accessor::dict_has_value_for_key(ctx, value, prop.name)) { - if (created || !prop.is_primary) { - ValueType prop_value = Accessor::dict_value_for_key(ctx, value, prop.name); - object.set_property_value_impl(ctx, prop, prop_value, try_update); + if (created || !prop.is_primary) { + if (Accessor::dict_has_value_for_key(ctx, value, prop.name)) { + object.set_property_value_impl(ctx, prop, Accessor::dict_value_for_key(ctx, value, prop.name), try_update); + } + else if (created) { + if (Accessor::has_default_value_for_property(ctx, object_schema, prop.name)) { + object.set_property_value_impl(ctx, prop, Accessor::default_value_for_property(ctx, object_schema, prop.name), try_update); + } + else { + throw std::runtime_error("Missing property value for property " + prop.name); + } } - } - else if (created) { - throw std::runtime_error("Missing property value for property " + prop.name); } } return object; diff --git a/tests/ObjectTests.js b/tests/ObjectTests.js index aca1be41..e4f65525 100644 --- a/tests/ObjectTests.js +++ b/tests/ObjectTests.js @@ -86,7 +86,7 @@ var ObjectTests = { var realm = new Realm({schema: [LinkTypesObjectSchema, TestObjectSchema]}); var obj = null; realm.write(function() { - obj = realm.create('LinkTypesObject', [[1], undefined, [[3]]]); + obj = realm.create('LinkTypesObject', [[1], null, [[3]]]); }); TestCase.assertEqual(realm.objects('TestObject').length, 2); @@ -99,7 +99,7 @@ var ObjectTests = { TestCase.assertEqual(realm.objects('TestObject').length, 2); realm.write(function() { - obj.objectCol = undefined; + obj.objectCol = null; obj.objectCol1 = null; }); TestCase.assertEqual(obj.objectCol, null); diff --git a/tests/RealmTests.js b/tests/RealmTests.js index 560208f8..58ad0323 100644 --- a/tests/RealmTests.js +++ b/tests/RealmTests.js @@ -142,6 +142,24 @@ var RealmTests = { }); }, + testRealmCreateWithDefaults: function() { + var realm = new Realm({schema: [DefaultValuesObjectSchema, TestObjectSchema]}); + realm.write(function() { + var obj = realm.create('DefaultValuesObject', {}); + TestCase.assertEqual(obj.boolCol, DefaultValuesObjectSchema.properties[0].default); + TestCase.assertEqual(obj.intCol, DefaultValuesObjectSchema.properties[1].default); + TestCase.assertEqualWithTolerance(obj.floatCol, DefaultValuesObjectSchema.properties[2].default, 0.000001); + TestCase.assertEqual(obj.doubleCol, DefaultValuesObjectSchema.properties[3].default); + TestCase.assertEqual(obj.stringCol, DefaultValuesObjectSchema.properties[4].default); + TestCase.assertEqual(obj.dateCol.getTime(), DefaultValuesObjectSchema.properties[5].default.getTime()); + TestCase.assertEqual(obj.dataCol, DefaultValuesObjectSchema.properties[6].default); + TestCase.assertEqual(obj.objectCol.doubleCol, DefaultValuesObjectSchema.properties[7].default[0]); + TestCase.assertEqual(obj.nullObjectCol, null); + TestCase.assertEqual(obj.arrayCol.length, DefaultValuesObjectSchema.properties[9].default.length); + TestCase.assertEqual(obj.arrayCol[0].doubleCol, DefaultValuesObjectSchema.properties[9].default[0][0]); + }); + }, + testRealmDelete: function() { var realm = new Realm({schema: [TestObjectSchema]}); realm.write(function() { diff --git a/tests/TestObjects.js b/tests/TestObjects.js index c1eaf1bc..8d2e7ba1 100644 --- a/tests/TestObjects.js +++ b/tests/TestObjects.js @@ -85,3 +85,19 @@ var AllTypesObjectSchema = { ] }; +var DefaultValuesObjectSchema = { + name: 'DefaultValuesObject', + properties: [ + {name: 'boolCol', type: RealmType.Bool, default: true}, + {name: 'intCol', type: RealmType.Int, default: -1}, + {name: 'floatCol', type: RealmType.Float, default: -1.1}, + {name: 'doubleCol', type: RealmType.Double, default: -1.11}, + {name: 'stringCol', type: RealmType.String, default: 'defaultString'}, + {name: 'dateCol', type: RealmType.Date, default: new Date(1.111)}, + {name: 'dataCol', type: RealmType.Data, default: 'defaultData'}, + {name: 'objectCol', type: 'TestObject', default: [1]}, + {name: 'nullObjectCol', type: 'TestObject', default: null}, + {name: 'arrayCol', type: RealmType.Array, objectType: 'TestObject', default: [[2]]}, + ] +}; +