Change schema API to take properties as an object

The keys are names of the properties, which is more natural in JS, but will cause issues with ability to create objects where arrays of values. This feature will be removed in a subsequent commit.
This commit is contained in:
Scott Kyle 2015-10-30 12:08:31 -07:00
parent 358e5dacf3
commit 43e14093cc
5 changed files with 166 additions and 111 deletions

View File

@ -32,7 +32,7 @@ JSClassRef RJSRealmTypeClass() {
{ "DATE", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "DATA", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "OBJECT", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "LIST", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ "LIST", RJSTypeGet, NULL, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
{ NULL, NULL, NULL, 0 }
};
realmTypesDefinition.staticValues = types;

View File

@ -31,25 +31,34 @@ JSObjectRef RJSSchemaCreate(JSContextRef ctx, Schema &schema) {
return RJSWrapObject(ctx, RJSSchemaClass(), wrapper);
}
static inline Property RJSParseProperty(JSContextRef ctx, JSObjectRef propertyObject) {
static JSStringRef nameString = JSStringCreateWithUTF8CString("name");
static inline Property RJSParseProperty(JSContextRef ctx, JSValueRef propertyAttributes, std::string propertyName, ObjectDefaults &objectDefaults) {
static JSStringRef defaultString = JSStringCreateWithUTF8CString("default");
static JSStringRef typeString = JSStringCreateWithUTF8CString("type");
static JSStringRef objectTypeString = JSStringCreateWithUTF8CString("objectType");
static JSStringRef optionalString = JSStringCreateWithUTF8CString("optional");
Property prop;
prop.name = RJSValidatedStringProperty(ctx, propertyObject, nameString);
prop.name = propertyName;
prop.is_nullable = false;
JSValueRef optionalValue = JSObjectGetProperty(ctx, propertyObject, optionalString, NULL);
if (!JSValueIsUndefined(ctx, optionalValue)) {
if (!JSValueIsBoolean(ctx, optionalValue)) {
throw std::runtime_error("'optional' designation expected to be of type boolean");
JSObjectRef propertyObject = NULL;
std::string type;
if (JSValueIsObject(ctx, propertyAttributes)) {
propertyObject = RJSValidatedValueToObject(ctx, propertyAttributes);
type = RJSValidatedStringProperty(ctx, propertyObject, typeString);
JSValueRef optionalValue = JSObjectGetProperty(ctx, propertyObject, optionalString, NULL);
if (!JSValueIsUndefined(ctx, optionalValue)) {
if (!JSValueIsBoolean(ctx, optionalValue)) {
throw std::runtime_error("'optional' designation expected to be of type boolean");
}
prop.is_nullable = JSValueToBoolean(ctx, optionalValue);
}
prop.is_nullable = JSValueToBoolean(ctx, optionalValue);
}
else {
type = RJSValidatedStringForValue(ctx, propertyAttributes);
}
std::string type = RJSValidatedStringProperty(ctx, propertyObject, typeString);
if (type == "bool") {
prop.type = PropertyTypeBool;
}
@ -72,14 +81,36 @@ static inline Property RJSParseProperty(JSContextRef ctx, JSObjectRef propertyOb
prop.type = PropertyTypeData;
}
else if (type == "list") {
if (!propertyObject) {
throw std::runtime_error("List property must specify 'objectType'");
}
prop.type = PropertyTypeArray;
prop.object_type = RJSValidatedStringProperty(ctx, propertyObject, objectTypeString);
}
else {
prop.type = PropertyTypeObject;
prop.is_nullable = true;
prop.object_type = type == "object" ? RJSValidatedStringProperty(ctx, propertyObject, objectTypeString) : type;
// The type could either be 'object' or the name of another object type in the same schema.
if (type == "object") {
if (!propertyObject) {
throw std::runtime_error("Object property must specify 'objectType'");
}
prop.object_type = RJSValidatedStringProperty(ctx, propertyObject, objectTypeString);
}
else {
prop.object_type = type;
}
}
if (propertyObject) {
JSValueRef defaultValue = RJSValidatedPropertyValue(ctx, propertyObject, defaultString);
if (!JSValueIsUndefined(ctx, defaultValue)) {
JSValueProtect(ctx, defaultValue);
objectDefaults.emplace(prop.name, defaultValue);
}
}
return prop;
}
@ -88,6 +119,7 @@ static inline ObjectSchema RJSParseObjectSchema(JSContextRef ctx, JSObjectRef ob
static JSStringRef prototypeString = JSStringCreateWithUTF8CString("prototype");
JSObjectRef prototypeObject = NULL;
JSValueRef prototypeValue = RJSValidatedPropertyValue(ctx, objectSchemaObject, prototypeString);
if (!JSValueIsUndefined(ctx, prototypeValue)) {
prototypeObject = RJSValidatedValueToObject(ctx, prototypeValue);
objectSchemaObject = RJSValidatedObjectProperty(ctx, prototypeObject, schemaString, "Realm object prototype must have a 'schema' property.");
@ -99,27 +131,46 @@ static inline ObjectSchema RJSParseObjectSchema(JSContextRef ctx, JSObjectRef ob
}
}
static JSStringRef propertiesString = JSStringCreateWithUTF8CString("properties");
JSObjectRef propertiesObject = RJSValidatedObjectProperty(ctx, objectSchemaObject, propertiesString, "ObjectSchema object must have a 'properties' array.");
ObjectSchema objectSchema;
ObjectDefaults objectDefaults;
static JSStringRef nameString = JSStringCreateWithUTF8CString("name");
static JSStringRef propertiesString = JSStringCreateWithUTF8CString("properties");
JSObjectRef propertiesObject = RJSValidatedObjectProperty(ctx, objectSchemaObject, propertiesString, "ObjectSchema must have a 'properties' object.");
ObjectDefaults objectDefaults;
ObjectSchema objectSchema;
objectSchema.name = RJSValidatedStringProperty(ctx, objectSchemaObject, nameString);
size_t numProperties = RJSValidatedListLength(ctx, propertiesObject);
for (unsigned int p = 0; p < numProperties; p++) {
JSObjectRef property = RJSValidatedObjectAtIndex(ctx, propertiesObject, p);
objectSchema.properties.emplace_back(RJSParseProperty(ctx, property));
JSPropertyNameArrayRef propertyNames = NULL;
size_t propertyCount;
static JSStringRef defaultString = JSStringCreateWithUTF8CString("default");
JSValueRef defaultValue = JSObjectGetProperty(ctx, property, defaultString, NULL);
if (!JSValueIsUndefined(ctx, defaultValue)) {
JSValueProtect(ctx, defaultValue);
objectDefaults.emplace(objectSchema.properties.back().name, defaultValue);
}
if (RJSIsValueArray(ctx, propertiesObject)) {
propertyCount = RJSValidatedListLength(ctx, propertiesObject);
}
else {
propertyNames = JSObjectCopyPropertyNames(ctx, propertiesObject);
propertyCount = JSPropertyNameArrayGetCount(propertyNames);
}
for (size_t i = 0; i < propertyCount; i++) {
Property property;
// Check if the properties were provided as an object.
if (propertyNames) {
JSStringRef propertyName = JSPropertyNameArrayGetNameAtIndex(propertyNames, i);
JSValueRef propertyValue = RJSValidatedPropertyValue(ctx, propertiesObject, propertyName);
property = RJSParseProperty(ctx, propertyValue, RJSStringForJSString(propertyName), objectDefaults);
}
else {
JSObjectRef propertyObject = RJSValidatedObjectAtIndex(ctx, propertiesObject, (unsigned int)i);
std::string propertyName = RJSValidatedStringProperty(ctx, propertyObject, nameString);
property = RJSParseProperty(ctx, propertyObject, propertyName, objectDefaults);
}
objectSchema.properties.emplace_back(property);
}
if (propertyNames) {
JSPropertyNameArrayRelease(propertyNames);
}
defaults.emplace(objectSchema.name, std::move(objectDefaults));
static JSStringRef primaryString = JSStringCreateWithUTF8CString("primaryKey");
JSValueRef primaryValue = RJSValidatedPropertyValue(ctx, objectSchemaObject, primaryString);
@ -138,6 +189,8 @@ static inline ObjectSchema RJSParseObjectSchema(JSContextRef ctx, JSObjectRef ob
prototypes[objectSchema.name] = std::move(prototypeObject);
}
defaults.emplace(objectSchema.name, std::move(objectDefaults));
return objectSchema;
}

View File

@ -163,15 +163,15 @@ module.exports = BaseTest.extend({
realm.write(function() {
obj = realm.create('NullableBasicTypesObject', basicTypesValues);
obj1 = realm.create('NullableBasicTypesObject', basicTypesValues);
for (var prop of schemas.NullableBasicTypes.properties) {
obj[prop.name] = null;
obj1[prop.name] = undefined;
for (var name in schemas.NullableBasicTypes.properties) {
obj[name] = null;
obj1[name] = undefined;
}
});
for (var prop of schemas.NullableBasicTypes.properties) {
TestCase.assertEqual(obj[prop.name], null);
TestCase.assertEqual(obj1[prop.name], null);
for (var name in schemas.NullableBasicTypes.properties) {
TestCase.assertEqual(obj[name], null);
TestCase.assertEqual(obj1[name], null);
}
realm.write(function() {
@ -262,7 +262,7 @@ module.exports = BaseTest.extend({
object = realm.create('BasicTypesObject', basicTypesValues);
});
var propNames = schemas.BasicTypes.properties.map(function(prop) { return prop.name; });
var propNames = Object.keys(schemas.BasicTypes.properties);
TestCase.assertArraysEqual(Object.keys(object), propNames, 'Object.keys');
for (var key in object) {

View File

@ -162,17 +162,19 @@ module.exports = BaseTest.extend({
var realm = new Realm({schema: [schemas.DefaultValues, schemas.TestObject]});
realm.write(function() {
var obj = realm.create('DefaultValuesObject', {});
TestCase.assertEqual(obj.boolCol, schemas.DefaultValues.properties[0].default);
TestCase.assertEqual(obj.intCol, schemas.DefaultValues.properties[1].default);
TestCase.assertEqualWithTolerance(obj.floatCol, schemas.DefaultValues.properties[2].default, 0.000001);
TestCase.assertEqual(obj.doubleCol, schemas.DefaultValues.properties[3].default);
TestCase.assertEqual(obj.stringCol, schemas.DefaultValues.properties[4].default);
TestCase.assertEqual(obj.dateCol.getTime(), schemas.DefaultValues.properties[5].default.getTime());
TestCase.assertEqual(obj.dataCol.byteLength, schemas.DefaultValues.properties[6].default.byteLength);
TestCase.assertEqual(obj.objectCol.doubleCol, schemas.DefaultValues.properties[7].default[0]);
var properties = schemas.DefaultValues.properties;
TestCase.assertEqual(obj.boolCol, properties.boolCol.default);
TestCase.assertEqual(obj.intCol, properties.intCol.default);
TestCase.assertEqualWithTolerance(obj.floatCol, properties.floatCol.default, 0.000001);
TestCase.assertEqual(obj.doubleCol, properties.doubleCol.default);
TestCase.assertEqual(obj.stringCol, properties.stringCol.default);
TestCase.assertEqual(obj.dateCol.getTime(), properties.dateCol.default.getTime());
TestCase.assertEqual(obj.dataCol.byteLength, properties.dataCol.default.byteLength);
TestCase.assertEqual(obj.objectCol.doubleCol, properties.objectCol.default[0]);
TestCase.assertEqual(obj.nullObjectCol, null);
TestCase.assertEqual(obj.arrayCol.length, schemas.DefaultValues.properties[9].default.length);
TestCase.assertEqual(obj.arrayCol[0].doubleCol, schemas.DefaultValues.properties[9].default[0][0]);
TestCase.assertEqual(obj.arrayCol.length, properties.arrayCol.default.length);
TestCase.assertEqual(obj.arrayCol[0].doubleCol, properties.arrayCol.default[0][0]);
});
},

View File

@ -7,20 +7,20 @@
var Realm = require('realm');
exports.TestObject = {
name: 'TestObject',
properties: [
{name: 'doubleCol', type: Realm.Types.DOUBLE},
]
name: 'TestObject',
properties: {
doubleCol: Realm.Types.DOUBLE,
}
};
function PersonObject() {}
PersonObject.prototype.schema = {
name: 'PersonObject',
properties: [
{name: 'name', type: Realm.Types.STRING},
{name: 'age', type: Realm.Types.DOUBLE},
{name: 'married', type: Realm.Types.BOOL, default: false}
]
name: 'PersonObject',
properties: {
name: Realm.Types.STRING,
age: Realm.Types.DOUBLE,
married: {type: Realm.Types.BOOL, default: false},
}
};
PersonObject.prototype.description = function() {
return this.name + ' ' + this.age;
@ -29,79 +29,79 @@ exports.PersonObject = PersonObject;
exports.BasicTypes = {
name: 'BasicTypesObject',
properties: [
{name: 'boolCol', type: Realm.Types.BOOL},
{name: 'intCol', type: Realm.Types.INT},
{name: 'floatCol', type: Realm.Types.FLOAT},
{name: 'doubleCol', type: Realm.Types.DOUBLE},
{name: 'stringCol', type: Realm.Types.STRING},
{name: 'dateCol', type: Realm.Types.DATE},
{name: 'dataCol', type: Realm.Types.DATA},
]
properties: {
boolCol: Realm.Types.BOOL,
intCol: Realm.Types.INT,
floatCol: Realm.Types.FLOAT,
doubleCol: Realm.Types.DOUBLE,
stringCol: Realm.Types.STRING,
dateCol: Realm.Types.DATE,
dataCol: Realm.Types.DATA,
}
};
exports.NullableBasicTypes = {
name: 'NullableBasicTypesObject',
properties: [
{name: 'boolCol', type: Realm.Types.BOOL, optional: true},
{name: 'intCol', type: Realm.Types.INT, optional: true},
{name: 'floatCol', type: Realm.Types.FLOAT, optional: true},
{name: 'doubleCol', type: Realm.Types.DOUBLE, optional: true},
{name: 'stringCol', type: Realm.Types.STRING, optional: true},
{name: 'dateCol', type: Realm.Types.DATE, optional: true},
{name: 'dataCol', type: Realm.Types.DATA, optional: true},
]
properties: {
boolCol: {type: Realm.Types.BOOL, optional: true},
intCol: {type: Realm.Types.INT, optional: true},
floatCol: {type: Realm.Types.FLOAT, optional: true},
doubleCol: {type: Realm.Types.DOUBLE, optional: true},
stringCol: {type: Realm.Types.STRING, optional: true},
dateCol: {type: Realm.Types.DATE, optional: true},
dataCol: {type: Realm.Types.DATA, optional: true},
}
};
exports.LinkTypes = {
name: 'LinkTypesObject',
properties: [
{name: 'objectCol', type: 'TestObject'},
{name: 'objectCol1', type: Realm.Types.OBJECT, objectType: 'TestObject'},
{name: 'arrayCol', type: Realm.Types.LIST, objectType: 'TestObject'},
]
properties: {
objectCol: 'TestObject',
objectCol1: {type: Realm.Types.OBJECT, objectType: 'TestObject'},
arrayCol: {type: Realm.Types.LIST, objectType: 'TestObject'},
}
};
exports.IntPrimary = {
name: 'IntPrimaryObject',
primaryKey: 'primaryCol',
properties: [
{name: 'primaryCol', type: Realm.Types.INT},
{name: 'valueCol', type: Realm.Types.STRING},
]
name: 'IntPrimaryObject',
primaryKey: 'primaryCol',
properties: {
primaryCol: Realm.Types.INT,
valueCol: Realm.Types.STRING,
}
};
exports.AllTypes = {
name: 'AllTypesObject',
primaryKey: 'primaryCol',
properties: [
{name: 'primaryCol',type: Realm.Types.STRING},
{name: 'boolCol', type: Realm.Types.BOOL},
{name: 'intCol', type: Realm.Types.INT},
{name: 'floatCol', type: Realm.Types.FLOAT},
{name: 'doubleCol', type: Realm.Types.DOUBLE},
{name: 'stringCol', type: Realm.Types.STRING},
{name: 'dateCol', type: Realm.Types.DATE},
{name: 'dataCol', type: Realm.Types.DATA},
{name: 'objectCol', type: 'TestObject'},
{name: 'arrayCol', type: Realm.Types.LIST, objectType: 'TestObject'},
]
name: 'AllTypesObject',
primaryKey: 'primaryCol',
properties: {
primaryCol: Realm.Types.STRING,
boolCol: Realm.Types.BOOL,
intCol: Realm.Types.INT,
floatCol: Realm.Types.FLOAT,
doubleCol: Realm.Types.DOUBLE,
stringCol: Realm.Types.STRING,
dateCol: Realm.Types.DATE,
dataCol: Realm.Types.DATA,
objectCol: 'TestObject',
arrayCol: {type: Realm.Types.LIST, objectType: 'TestObject'},
}
};
exports.DefaultValues = {
name: 'DefaultValuesObject',
properties: [
{name: 'boolCol', type: Realm.Types.BOOL, default: true},
{name: 'intCol', type: Realm.Types.INT, default: -1},
{name: 'floatCol', type: Realm.Types.FLOAT, default: -1.1},
{name: 'doubleCol', type: Realm.Types.DOUBLE, default: -1.11},
{name: 'stringCol', type: Realm.Types.STRING, default: 'defaultString'},
{name: 'dateCol', type: Realm.Types.DATE, default: new Date(1.111)},
{name: 'dataCol', type: Realm.Types.DATA, default: new ArrayBuffer(1)},
{name: 'objectCol', type: 'TestObject', default: [1]},
{name: 'nullObjectCol', type: 'TestObject', default: null},
{name: 'arrayCol', type: Realm.Types.LIST, objectType: 'TestObject', default: [[2]]},
]
name: 'DefaultValuesObject',
properties: {
boolCol: {type: Realm.Types.BOOL, default: true},
intCol: {type: Realm.Types.INT, default: -1},
floatCol: {type: Realm.Types.FLOAT, default: -1.1},
doubleCol: {type: Realm.Types.DOUBLE, default: -1.11},
stringCol: {type: Realm.Types.STRING, default: 'defaultString'},
dateCol: {type: Realm.Types.DATE, default: new Date(1.111)},
dataCol: {type: Realm.Types.DATA, default: new ArrayBuffer(1)},
objectCol: {type: 'TestObject', default: [1]},
nullObjectCol: {type: 'TestObject', default: null},
arrayCol: {type: Realm.Types.LIST, objectType: 'TestObject', default: [[2]]},
}
};
exports.QueryObject = {