Merge branch 'master' into al-android-example

* master:
  Fix typo in README
  Improve clarity of schema parsing and serialization
  Add test that checks schema validation
  Update README with new Schema API
  Cleanup some code and comments to make it consistent
  Convert tests to create objects with property objects
  Update example apps with new schema API
  Remove confusion between propTypes and objectTypes
  Simplify object schema info returned from RPC
  RPC now keeps object keys in the same order
  Change schema API to take properties as an object
  Add method to clear mutation listeners in RPC client
  Plug small leak from not releasing a JSStringRef
  Make RJSStringForValue use consistent exception style
  Improve error messages inside RJSUtil
This commit is contained in:
Scott Kyle 2016-01-14 15:39:45 -08:00
commit 2f1eca4a9c
20 changed files with 648 additions and 344 deletions

View File

@ -28,12 +28,12 @@ const Realm = require('realm');
const personSchema = {
name: 'Person',
primaryKey: 'name',
properties: [
{name: 'name', type: Realm.Types.STRING},
{name: 'birthday', type: Realm.Types.DATE},
{name: 'friends', type: Realm.Types.LIST, objectType: 'Person'},
{name: 'points', type: Realm.Types.INT, default: 0},
],
properties: {
name: 'string',
birthday: 'date',
friends: {type: 'list', objectType: 'Person'},
points: {type: 'int', default: 0},
},
};
const realm = new Realm({schema: [personSchema]});
@ -101,27 +101,28 @@ The `realmConfig` passed to the constructor can contain the following:
### ObjectSchema
- `name` string used to refer to this object type
- `properties` - array of property defitions (see below)
- `properties` - object with property definitions (see below)
- `primaryKey` optional - name of `STRING` or `INT` property that should be unique
### Property Types
When definining object `properties` in a `schema`, each should have a unique `name`, and the `type` of each property must be defined as either the name of an object type in the same schema **or** as one of the following:
When defining object `properties` in a `schema`, the value for each property must either be the `type` **OR** an object with a `type` key along with other options detailed below. The `type` of each property must be defined as either the name of an object type in the same schema **or** as one of the following:
- `Realm.Types.BOOL`
- `Realm.Types.INT`
- `Realm.Types.FLOAT`
- `Realm.Types.DOUBLE`
- `Realm.Types.STRING`
- `Realm.Types.DATE`
- `Realm.Types.DATA`
- `Realm.Types.OBJECT` (requires `objectType`, is always `optional`)
- `Realm.Types.LIST` (requires `objectType`, is never `optional`)
- `Realm.Types.BOOL` (`"bool"`)
- `Realm.Types.INT` (`"int"`)
- `Realm.Types.FLOAT` (`"float"`)
- `Realm.Types.DOUBLE` (`"double"`)
- `Realm.Types.STRING` (`"string"`)
- `Realm.Types.DATE` (`"date"`)
- `Realm.Types.DATA` (`"data"`)
- `Realm.Types.LIST` (`"list"` requires `objectType` and cannot be `optional`)
You _may_ specify these property options as well:
- `default` default value when property was not specified on creation
- `optional` boolean indicating if this property may be assigned `null` or `undefined`
**Note:** When the `type` of a property is that of an object type in the same schema, it _always_ will be `optional`.
### `Realm` Instance Methods
#### `create(type, props [, update])`
- `type` string matching object `name` in the `schema` definition

View File

@ -10,17 +10,17 @@ module.exports = new Realm({
schema: [
{
name: 'Todo',
properties: [
{name: 'done', type: Realm.Types.BOOL, default: false},
{name: 'text', type: Realm.Types.STRING, default: ''},
]
properties: {
done: {type: Realm.Types.BOOL, default: false},
text: Realm.Types.STRING,
},
},
{
name: 'TodoList',
properties: [
{name: 'name', type: Realm.Types.STRING},
{name: 'items', type: Realm.Types.LIST, objectType: 'Todo'},
]
properties: {
name: Realm.Types.STRING,
items: {type: Realm.Types.LIST, objectType: 'Todo', default: []},
},
},
],
});

View File

@ -23,7 +23,7 @@ class TodoApp extends React.Component {
let todoLists = realm.objects('TodoList');
if (todoLists.length < 1) {
realm.write(() => {
realm.create('TodoList', {name: 'Todo List', items: []});
realm.create('TodoList', {name: 'Todo List'});
});
}
@ -103,7 +103,7 @@ class TodoApp extends React.Component {
}
realm.write(() => {
realm.create('TodoList', {name: '', items: []});
realm.create('TodoList', {name: ''});
});
this._setEditingRow(items.length - 1);

View File

@ -17,12 +17,18 @@ let propTypes = {};
});
[
'DATA',
'DATE',
'DICT',
'FUNCTION',
'LIST',
'OBJECT',
'REALM',
'RESULTS',
'UNDEFINED',
].forEach(function(type) {
Object.defineProperty(objectTypes, type, {
value: 'ObjectTypes' + type,
value: type.toLowerCase(),
});
});

View File

@ -7,6 +7,8 @@
const constants = require('./constants');
const util = require('./util');
const {objectTypes} = constants;
module.exports = {
create,
};
@ -14,12 +16,12 @@ module.exports = {
class List {}
// Non-mutating methods:
util.createMethods(List.prototype, constants.propTypes.LIST, [
util.createMethods(List.prototype, objectTypes.LIST, [
'snapshot',
]);
// Mutating methods:
util.createMethods(List.prototype, constants.propTypes.LIST, [
util.createMethods(List.prototype, objectTypes.LIST, [
'pop',
'shift',
'push',

View File

@ -24,9 +24,7 @@ function create(realmId, info) {
object[keys.id] = info.id;
object[keys.type] = info.type;
schema.properties.forEach((prop) => {
let name = prop.name;
schema.properties.forEach((name) => {
Object.defineProperty(object, name, {
enumerable: true,
get: util.getterForProperty(name),

View File

@ -14,8 +14,8 @@ const util = require('./util');
const {keys, propTypes, objectTypes} = constants;
const listenersKey = Symbol();
rpc.registerTypeConverter(propTypes.LIST, lists.create);
rpc.registerTypeConverter(propTypes.OBJECT, objects.create);
rpc.registerTypeConverter(objectTypes.LIST, lists.create);
rpc.registerTypeConverter(objectTypes.OBJECT, objects.create);
rpc.registerTypeConverter(objectTypes.RESULTS, results.create);
class Realm {
@ -125,7 +125,10 @@ Object.defineProperties(Realm, {
set: util.setterForProperty('defaultPath'),
},
clearTestState: {
value: rpc.clearTestState,
value: function() {
util.clearMutationListeners();
rpc.clearTestState();
},
},
});

View File

@ -9,7 +9,8 @@ const constants = require('./constants');
const DEVICE_HOST = 'localhost:8082';
const {keys, objectTypes, propTypes} = constants;
const {keys, objectTypes} = constants;
const {id: idKey, realm: realmKey} = keys;
const typeConverters = {};
let XMLHttpRequest = global.originalXMLHttpRequest || global.XMLHttpRequest;
@ -38,8 +39,9 @@ module.exports = {
clearTestState,
};
registerTypeConverter(propTypes.DATA, (_, {value}) => base64.decode(value));
registerTypeConverter(propTypes.DATE, (_, {value}) => new Date(value));
registerTypeConverter(objectTypes.DATA, (_, {value}) => base64.decode(value));
registerTypeConverter(objectTypes.DATE, (_, {value}) => new Date(value));
registerTypeConverter(objectTypes.DICT, deserializeDict);
function registerTypeConverter(type, handler) {
typeConverters[type] = handler;
@ -94,21 +96,19 @@ function clearTestState() {
}
function serialize(realmId, value) {
if (typeof value == 'undefined') {
return {type: objectTypes.UNDEFINED};
}
if (typeof value == 'function') {
return {type: objectTypes.FUNCTION};
}
if (typeof value === 'undefined') {
return {type: 'undefined'};
}
if (!value || typeof value != 'object') {
return {value: value};
}
let id = value[keys.id];
let id = value[idKey];
if (id) {
if (value[keys.realm] != realmId) {
if (value[realmKey] != realmId) {
throw new Error('Unable to serialize value from another Realm');
}
@ -116,7 +116,7 @@ function serialize(realmId, value) {
}
if (value instanceof Date) {
return {type: propTypes.DATE, value: value.getTime()};
return {type: objectTypes.DATE, value: value.getTime()};
}
if (Array.isArray(value)) {
@ -125,14 +125,12 @@ function serialize(realmId, value) {
}
if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) {
return {type: propTypes.DATA, value: base64.encode(value)};
return {type: objectTypes.DATA, value: base64.encode(value)};
}
let object = {};
for (let key in value) {
object[key] = serialize(realmId, value[key]);
}
return {value: object};
let keys = Object.keys(value);
let values = keys.map((key) => serialize(realmId, value[key]));
return {type: objectTypes.DICT, keys, values};
}
function deserialize(realmId, info) {
@ -150,6 +148,17 @@ function deserialize(realmId, info) {
return value;
}
function deserializeDict(realmId, info) {
let {keys, values} = info;
let object = {};
for (let i = 0, len = keys.length; i < len; i++) {
object[keys[i]] = values[i];
}
return object;
}
function sendRequest(command, data) {
data = Object.assign({}, data, sessionId ? {sessionId} : null);

View File

@ -8,9 +8,10 @@ const constants = require('./constants');
const rpc = require('./rpc');
const {keys} = constants;
const mutationListeners = {};
let mutationListeners = {};
module.exports = {
clearMutationListeners,
fireMutationListeners,
createList,
createMethods,
@ -31,6 +32,10 @@ function removeMutationListener(realmId, callback) {
}
}
function clearMutationListeners() {
mutationListeners = {};
}
function fireMutationListeners(realmId) {
let listeners = mutationListeners[realmId];
if (listeners) {

View File

@ -33,7 +33,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

@ -69,13 +69,16 @@ namespace realm {
template<> bool RJSAccessor::dict_has_value_for_key(JSContextRef ctx, JSValueRef dict, const std::string &prop_name) {
JSObjectRef object = RJSValidatedValueToObject(ctx, dict);
JSStringRef propStr =JSStringCreateWithUTF8CString(prop_name.c_str());
return JSObjectHasProperty(ctx, object, propStr);
JSStringRef propStr = RJSStringForString(prop_name);
bool ret = JSObjectHasProperty(ctx, object, propStr);
JSStringRelease(propStr);
return ret;
}
template<> JSValueRef RJSAccessor::dict_value_for_key(JSContextRef ctx, JSValueRef dict, const std::string &prop_name) {
JSObjectRef object = RJSValidatedValueToObject(ctx, dict);
JSStringRef propStr =JSStringCreateWithUTF8CString(prop_name.c_str());
JSStringRef propStr = RJSStringForString(prop_name);
JSValueRef ex = NULL;
JSValueRef ret = JSObjectGetProperty(ctx, object, propStr, &ex);
if (ex) {

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,22 +81,49 @@ 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;
}
static inline ObjectSchema RJSParseObjectSchema(JSContextRef ctx, JSObjectRef objectSchemaObject, std::map<std::string, realm::ObjectDefaults> &defaults, std::map<std::string, JSValueRef> &prototypes) {
static JSStringRef schemaString = JSStringCreateWithUTF8CString("schema");
static JSStringRef nameString = JSStringCreateWithUTF8CString("name");
static JSStringRef primaryString = JSStringCreateWithUTF8CString("primaryKey");
static JSStringRef prototypeString = JSStringCreateWithUTF8CString("prototype");
static JSStringRef propertiesString = JSStringCreateWithUTF8CString("properties");
static JSStringRef schemaString = JSStringCreateWithUTF8CString("schema");
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,29 +135,30 @@ 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");
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));
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);
JSObjectRef propertiesObject = RJSValidatedObjectProperty(ctx, objectSchemaObject, propertiesString, "ObjectSchema must have a 'properties' object.");
if (RJSIsValueArray(ctx, propertiesObject)) {
size_t propertyCount = RJSValidatedListLength(ctx, propertiesObject);
for (size_t i = 0; i < propertyCount; i++) {
JSObjectRef propertyObject = RJSValidatedObjectAtIndex(ctx, propertiesObject, (unsigned int)i);
std::string propertyName = RJSValidatedStringProperty(ctx, propertyObject, nameString);
objectSchema.properties.emplace_back(RJSParseProperty(ctx, propertyObject, propertyName, objectDefaults));
}
}
defaults.emplace(objectSchema.name, std::move(objectDefaults));
else {
JSPropertyNameArrayRef propertyNames = JSObjectCopyPropertyNames(ctx, propertiesObject);
size_t propertyCount = JSPropertyNameArrayGetCount(propertyNames);
for (size_t i = 0; i < propertyCount; i++) {
JSStringRef propertyName = JSPropertyNameArrayGetNameAtIndex(propertyNames, i);
JSValueRef propertyValue = RJSValidatedPropertyValue(ctx, propertiesObject, propertyName);
objectSchema.properties.emplace_back(RJSParseProperty(ctx, propertyValue, RJSStringForJSString(propertyName), objectDefaults));
}
JSPropertyNameArrayRelease(propertyNames);
}
static JSStringRef primaryString = JSStringCreateWithUTF8CString("primaryKey");
JSValueRef primaryValue = RJSValidatedPropertyValue(ctx, objectSchemaObject, primaryString);
if (!JSValueIsUndefined(ctx, primaryValue)) {
objectSchema.primary_key = RJSValidatedStringForValue(ctx, primaryValue);
@ -132,12 +169,14 @@ static inline ObjectSchema RJSParseObjectSchema(JSContextRef ctx, JSObjectRef ob
property->is_primary = true;
}
// store prototype
// Store prototype so that objects of this type will have their prototype set to this prototype object.
if (prototypeObject) {
JSValueProtect(ctx, prototypeObject);
prototypes[objectSchema.name] = std::move(prototypeObject);
}
defaults.emplace(objectSchema.name, std::move(objectDefaults));
return objectSchema;
}

View File

@ -24,21 +24,6 @@ JSValueRef RJSMakeError(JSContextRef ctx, const std::string &message) {
return JSObjectMakeError(ctx, 1, &value, NULL);
}
std::string RJSTypeGet(PropertyType propertyType) {
switch (propertyType) {
case PropertyTypeBool: return "bool";
case PropertyTypeInt: return "int";
case PropertyTypeFloat: return "float";
case PropertyTypeDouble:return "double";
case PropertyTypeString:return "string";
case PropertyTypeDate: return "date";
case PropertyTypeData: return "data";
case PropertyTypeObject:return "object";
case PropertyTypeArray: return "list";
default: return nullptr;
}
}
std::string RJSStringForJSString(JSStringRef jsString) {
std::string str;
size_t maxSize = JSStringGetMaximumUTF8CStringSize(jsString);
@ -48,10 +33,10 @@ std::string RJSStringForJSString(JSStringRef jsString) {
}
std::string RJSStringForValue(JSContextRef ctx, JSValueRef value) {
JSValueRef *exception = nullptr;
JSStringRef jsString = JSValueToStringCopy(ctx, value, exception);
JSValueRef exception = nullptr;
JSStringRef jsString = JSValueToStringCopy(ctx, value, &exception);
if (!jsString) {
throw RJSException(ctx, *exception);
throw RJSException(ctx, exception);
}
std::string string = RJSStringForJSString(jsString);
@ -98,4 +83,3 @@ bool RJSIsValueDate(JSContextRef ctx, JSValueRef value) {
static JSStringRef dateString = JSStringCreateWithUTF8CString("Date");
return RJSIsValueObjectOfType(ctx, value, dateString);
}

View File

@ -51,8 +51,6 @@ JSClassRef RJSCreateWrapperClass(const char * name, JSObjectGetPropertyCallback
return JSClassCreate(&classDefinition);
}
std::string RJSTypeGet(realm::PropertyType propertyType);
std::string RJSStringForJSString(JSStringRef jsString);
std::string RJSStringForValue(JSContextRef ctx, JSValueRef value);
std::string RJSValidatedStringForValue(JSContextRef ctx, JSValueRef value, const char * name = nullptr);
@ -148,7 +146,7 @@ static inline JSValueRef RJSValidatedPropertyValue(JSContextRef ctx, JSObjectRef
static inline JSObjectRef RJSValidatedObjectProperty(JSContextRef ctx, JSObjectRef object, JSStringRef property, const char *err = NULL) {
JSValueRef propertyValue = RJSValidatedPropertyValue(ctx, object, property);
if (JSValueIsUndefined(ctx, propertyValue)) {
throw std::runtime_error(err ?: "Object property is undefined");
throw std::runtime_error(err ?: "Object property '" + RJSStringForJSString(property) + "' is undefined");
}
return RJSValidatedValueToObject(ctx, propertyValue, err);
}
@ -168,7 +166,7 @@ static inline std::string RJSValidatedStringProperty(JSContextRef ctx, JSObjectR
if (exception) {
throw RJSException(ctx, exception);
}
return RJSValidatedStringForValue(ctx, propertyValue);
return RJSValidatedStringForValue(ctx, propertyValue, RJSStringForJSString(property).c_str());
}
static inline size_t RJSValidatedListLength(JSContextRef ctx, JSObjectRef object) {

View File

@ -23,8 +23,14 @@
using RJSAccessor = realm::NativeAccessor<JSValueRef, JSContextRef>;
using namespace realm_js;
static const char * const RealmObjectTypesFunction = "ObjectTypesFUNCTION";
static const char * const RealmObjectTypesResults = "ObjectTypesRESULTS";
static const char * const RealmObjectTypesData = "data";
static const char * const RealmObjectTypesDate = "date";
static const char * const RealmObjectTypesDictionary = "dict";
static const char * const RealmObjectTypesFunction = "function";
static const char * const RealmObjectTypesList = "list";
static const char * const RealmObjectTypesObject = "object";
static const char * const RealmObjectTypesResults = "results";
static const char * const RealmObjectTypesUndefined = "undefined";
RPCServer::RPCServer() {
m_context = JSGlobalContextCreate(NULL);
@ -219,7 +225,7 @@ json RPCServer::serialize_json_value(JSValueRef value) {
if (JSValueIsObjectOfClass(m_context, value, RJSObjectClass())) {
realm::Object *object = RJSGetInternal<realm::Object *>(js_object);
return {
{"type", RJSTypeGet(realm::PropertyTypeObject)},
{"type", RealmObjectTypesObject},
{"id", store_object(js_object)},
{"schema", serialize_object_schema(object->get_object_schema())}
};
@ -227,7 +233,7 @@ json RPCServer::serialize_json_value(JSValueRef value) {
else if (JSValueIsObjectOfClass(m_context, value, RJSListClass())) {
realm::List *list = RJSGetInternal<realm::List *>(js_object);
return {
{"type", RJSTypeGet(realm::PropertyTypeArray)},
{"type", RealmObjectTypesList},
{"id", store_object(js_object)},
{"size", list->size()},
{"schema", serialize_object_schema(list->get_object_schema())}
@ -253,26 +259,47 @@ json RPCServer::serialize_json_value(JSValueRef value) {
else if (RJSIsValueArrayBuffer(m_context, value)) {
std::string data = RJSAccessor::to_binary(m_context, value);
return {
{"type", RJSTypeGet(realm::PropertyTypeData)},
{"type", RealmObjectTypesData},
{"value", base64_encode((unsigned char *)data.data(), data.size())},
};
}
else if (RJSIsValueDate(m_context, value)) {
return {
{"type", RJSTypeGet(realm::PropertyTypeDate)},
{"type", RealmObjectTypesDate},
{"value", RJSValidatedValueToNumber(m_context, value)},
};
}
else {
// Serialize this JS object as a plain object since it doesn't match any known types above.
JSPropertyNameArrayRef js_keys = JSObjectCopyPropertyNames(m_context, js_object);
size_t count = JSPropertyNameArrayGetCount(js_keys);
std::vector<std::string> keys;
std::vector<json> values;
for (size_t i = 0; i < count; i++) {
JSStringRef js_key = JSPropertyNameArrayGetNameAtIndex(js_keys, i);
JSValueRef js_value = RJSValidatedPropertyValue(m_context, js_object, js_key);
keys.push_back(RJSStringForJSString(js_key));
values.push_back(serialize_json_value(js_value));
}
JSPropertyNameArrayRelease(js_keys);
return {
{"type", RealmObjectTypesDictionary},
{"keys", keys},
{"values", values},
};
}
assert(0);
}
json RPCServer::serialize_object_schema(const realm::ObjectSchema &object_schema) {
json properties = json::array();
std::vector<std::string> properties;
for (realm::Property prop : object_schema.properties) {
properties.push_back({
{"name", prop.name},
{"type", RJSTypeGet(prop.type)},
});
properties.push_back(prop.name);
}
return {
@ -300,14 +327,30 @@ JSValueRef RPCServer::deserialize_json_value(const json dict)
return js_function;
}
else if (type_string == RJSTypeGet(realm::PropertyTypeData)) {
else if (type_string == RealmObjectTypesDictionary) {
JSObjectRef js_object = JSObjectMake(m_context, NULL, NULL);
json keys = dict["keys"];
json values = dict["values"];
size_t count = keys.size();
for (size_t i = 0; i < count; i++) {
JSStringRef js_key = RJSStringForString(keys.at(i));
JSValueRef js_value = deserialize_json_value(values.at(i));
JSObjectSetProperty(m_context, js_object, js_key, js_value, 0, NULL);
JSStringRelease(js_key);
}
return js_object;
}
else if (type_string == RealmObjectTypesData) {
std::string bytes;
if (!base64_decode(value.get<std::string>(), &bytes)) {
throw std::runtime_error("Failed to decode base64 encoded data");
}
return RJSAccessor::from_binary(m_context, realm::BinaryData(bytes));
}
else if (type_string == RJSTypeGet(realm::PropertyTypeDate)) {
else if (type_string == RealmObjectTypesDate) {
JSValueRef exception = NULL;
JSValueRef time = JSValueMakeNumber(m_context, value.get<double>());
JSObjectRef date = JSObjectMakeDate(m_context, 1, &time, &exception);
@ -317,7 +360,7 @@ JSValueRef RPCServer::deserialize_json_value(const json dict)
}
return date;
}
else if (type_string == "undefined") {
else if (type_string == RealmObjectTypesUndefined) {
return JSValueMakeUndefined(m_context);
}
assert(0);
@ -345,19 +388,5 @@ JSValueRef RPCServer::deserialize_json_value(const json dict)
return JSObjectMakeArray(m_context, count, js_values, NULL);
}
else if (value.is_object()) {
JSObjectRef js_object = JSObjectMake(m_context, NULL, NULL);
for (json::iterator it = value.begin(); it != value.end(); ++it) {
JSValueRef js_value = deserialize_json_value(it.value());
JSStringRef js_key = JSStringCreateWithUTF8CString(it.key().c_str());
JSObjectSetProperty(m_context, js_object, js_key, js_value, 0, NULL);
JSStringRelease(js_key);
}
return js_object;
}
assert(0);
}

View File

@ -12,20 +12,30 @@ var schemas = require('./schemas');
module.exports = BaseTest.extend({
testArrayLength: function() {
var realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]});
var array;
realm.write(function() {
var obj = realm.create('LinkTypesObject', [[1], [2], [[3]]]);
TestCase.assertEqual(obj.arrayCol.length, 1);
var obj = realm.create('LinkTypesObject', {
objectCol: {doubleCol: 1},
objectCol1: {doubleCol: 2},
arrayCol: [{doubleCol: 3}],
});
array = obj.arrayCol;
TestCase.assertEqual(array.length, 1);
obj.arrayCol = [];
TestCase.assertEqual(obj.arrayCol.length, 0);
TestCase.assertEqual(array.length, 0);
obj.arrayCol = [[1], [2]];
TestCase.assertEqual(obj.arrayCol.length, 2);
obj.arrayCol = [{doubleCol: 1}, {doubleCol: 2}];
TestCase.assertEqual(array.length, 2);
TestCase.assertThrows(function() {
obj.arrayCol.length = 0;
array.length = 0;
}, 'cannot set length property on lists');
});
TestCase.assertEqual(array.length, 2);
},
testArraySubscriptGetters: function() {
@ -33,7 +43,12 @@ module.exports = BaseTest.extend({
var array;
realm.write(function() {
var obj = realm.create('LinkTypesObject', [[1], [2], [[3], [4]]]);
var obj = realm.create('LinkTypesObject', {
objectCol: {doubleCol: 1},
objectCol1: {doubleCol: 2},
arrayCol: [{doubleCol: 3}, {doubleCol: 4}],
});
array = obj.arrayCol;
});
@ -48,26 +63,36 @@ module.exports = BaseTest.extend({
var array;
realm.write(function() {
var obj = realm.create('LinkTypesObject', [[1], [2], [[3], [4]]]);
array = obj.arrayCol;
var obj = realm.create('LinkTypesObject', {
objectCol: {doubleCol: 1},
objectCol1: {doubleCol: 2},
arrayCol: [{doubleCol: 3}, {doubleCol: 4}],
});
array[0] = [5];
array[1] = [6];
array = obj.arrayCol;
array[0] = {doubleCol: 5};
array[1] = {doubleCol: 6};
TestCase.assertEqual(array[0].doubleCol, 5);
TestCase.assertEqual(array[1].doubleCol, 6);
array[0] = obj.objectCol;
array[1] = obj.objectCol1;
TestCase.assertEqual(array[0].doubleCol, 1);
TestCase.assertEqual(array[1].doubleCol, 2);
TestCase.assertThrows(function() {
array[2] = [1];
array[2] = {doubleCol: 1};
}, 'cannot set list item beyond its bounds');
TestCase.assertThrows(function() {
array[-1] = [1];
array[-1] = {doubleCol: 1};
}, 'cannot set list item with negative index');
});
TestCase.assertThrows(function() {
array[0] = [3];
array[0] = {doubleCol: 1};
}, 'cannot set list item outside write transaction');
},
@ -76,7 +101,12 @@ module.exports = BaseTest.extend({
var array;
realm.write(function() {
var obj = realm.create('LinkTypesObject', [[1], [2], [[3], [4]]]);
var obj = realm.create('LinkTypesObject', {
objectCol: {doubleCol: 1},
objectCol1: {doubleCol: 2},
arrayCol: [{doubleCol: 3}, {doubleCol: 4}],
});
array = obj.arrayCol;
});
@ -88,21 +118,26 @@ module.exports = BaseTest.extend({
var obj;
realm.write(function() {
obj = realm.create('LinkTypesObject', [[1], [2], []]);
obj = realm.create('LinkTypesObject', {
objectCol: {doubleCol: 1},
objectCol1: {doubleCol: 2},
arrayCol: [],
});
});
for (var index in obj.arrayCol) {
var index;
for (index in obj.arrayCol) {
TestCase.assertTrue(false, "No objects should have been enumerated: " + index);
}
realm.write(function() {
obj.arrayCol = [[0], [1]];
obj.arrayCol = [{doubleCol: 0}, {doubleCol: 1}];
TestCase.assertEqual(obj.arrayCol.length, 2);
});
var count = 0;
var keys = Object.keys(obj.arrayCol);
for (var index in obj.arrayCol) {
for (index in obj.arrayCol) {
TestCase.assertEqual(count++, +index);
TestCase.assertEqual(keys[index], index);
}
@ -116,11 +151,16 @@ module.exports = BaseTest.extend({
var array;
realm.write(function() {
var obj = realm.create('LinkTypesObject', [[1], [2], [[3]]]);
TestCase.assertEqual(obj.arrayCol.length, 1);
var obj = realm.create('LinkTypesObject', {
objectCol: {doubleCol: 1},
objectCol1: {doubleCol: 2},
arrayCol: [{doubleCol: 3}],
});
array = obj.arrayCol;
TestCase.assertEqual(array.push([4]), 2);
TestCase.assertEqual(array.length, 1);
TestCase.assertEqual(array.push({doubleCol: 4}), 2);
TestCase.assertEqual(array.length, 2);
TestCase.assertEqual(array[1].doubleCol, 4);
@ -145,9 +185,13 @@ module.exports = BaseTest.extend({
var array;
realm.write(function() {
var obj = realm.create('LinkTypesObject', [[1], [2], [[3], [4]]]);
array = obj.arrayCol;
var obj = realm.create('LinkTypesObject', {
objectCol: {doubleCol: 1},
objectCol1: {doubleCol: 2},
arrayCol: [{doubleCol: 3}, {doubleCol: 4}],
});
array = obj.arrayCol;
TestCase.assertEqual(array.pop().doubleCol, 4);
TestCase.assertEqual(array.pop().doubleCol, 3);
TestCase.assertEqual(array.length, 0);
@ -169,11 +213,16 @@ module.exports = BaseTest.extend({
var array;
realm.write(function() {
var obj = realm.create('LinkTypesObject', [[1], [2], [[3]]]);
TestCase.assertEqual(obj.arrayCol.length, 1);
var obj = realm.create('LinkTypesObject', {
objectCol: {doubleCol: 1},
objectCol1: {doubleCol: 2},
arrayCol: [{doubleCol: 3}],
});
array = obj.arrayCol;
TestCase.assertEqual(array.unshift([5]), 2);
TestCase.assertEqual(array.length, 1);
TestCase.assertEqual(array.unshift({doubleCol: 5}), 2);
TestCase.assertEqual(array.length, 2);
TestCase.assertEqual(array[0].doubleCol, 5);
@ -185,7 +234,7 @@ module.exports = BaseTest.extend({
TestCase.assertEqual(array.length, 4);
TestCase.assertThrows(function() {
array.unshift([1]);
array.unshift({doubleCol: 1});
}, 'can only unshift in a write transaction');
},
@ -194,9 +243,13 @@ module.exports = BaseTest.extend({
var array;
realm.write(function() {
var obj = realm.create('LinkTypesObject', [[1], [2], [[3], [4]]]);
array = obj.arrayCol;
var obj = realm.create('LinkTypesObject', {
objectCol: {doubleCol: 1},
objectCol1: {doubleCol: 2},
arrayCol: [{doubleCol: 3}, {doubleCol: 4}],
});
array = obj.arrayCol;
TestCase.assertEqual(array.shift().doubleCol, 3);
TestCase.assertEqual(array.shift().doubleCol, 4);
TestCase.assertEqual(array.length, 0);
@ -218,10 +271,14 @@ module.exports = BaseTest.extend({
var array;
realm.write(function() {
var obj = realm.create('LinkTypesObject', [[1], [2], [[3], [4]]]);
var removed;
var obj = realm.create('LinkTypesObject', {
objectCol: {doubleCol: 1},
objectCol1: {doubleCol: 2},
arrayCol: [{doubleCol: 3}, {doubleCol: 4}],
});
array = obj.arrayCol;
var removed;
removed = array.splice(0, 0, obj.objectCol, obj.objectCol1);
TestCase.assertEqual(removed.length, 0);
@ -229,7 +286,7 @@ module.exports = BaseTest.extend({
TestCase.assertEqual(array[0].doubleCol, 1);
TestCase.assertEqual(array[1].doubleCol, 2);
removed = array.splice(2, 2, [5], [6]);
removed = array.splice(2, 2, {doubleCol: 5}, {doubleCol: 6});
TestCase.assertEqual(removed.length, 2);
TestCase.assertEqual(removed[0].doubleCol, 3);
TestCase.assertEqual(removed[1].doubleCol, 4);
@ -270,7 +327,7 @@ module.exports = BaseTest.extend({
});
TestCase.assertThrows(function() {
array.splice(0, 0, [1]);
array.splice(0, 0, {doubleCol: 1});
}, 'can only splice in a write transaction');
},
@ -280,7 +337,12 @@ module.exports = BaseTest.extend({
var array;
realm.write(function() {
object = realm.create('LinkTypesObject', [[1], [2], [[3], [4]]]);
object = realm.create('LinkTypesObject', {
objectCol: {doubleCol: 1},
objectCol1: {doubleCol: 2},
arrayCol: [{doubleCol: 3}, {doubleCol: 4}],
});
array = object.arrayCol;
});
@ -313,21 +375,27 @@ module.exports = BaseTest.extend({
var array;
realm.write(function() {
var obj = realm.create('LinkTypesObject', [[1], [2], [[3], [4]]]);
var obj = realm.create('LinkTypesObject', {
objectCol: {doubleCol: 1},
objectCol1: {doubleCol: 2},
arrayCol: [{doubleCol: 3}, {doubleCol: 4}],
});
array = obj.arrayCol;
});
TestCase.assertEqual(array.length, 2);
TestCase.assertEqual(objects.length, 4);
try {
realm.write(function() {
array.push([5]);
array.push({doubleCol: 5});
TestCase.assertEqual(objects.length, 5);
array.unshift([2]);
array.unshift({doubleCol: 2});
TestCase.assertEqual(objects.length, 6);
array.splice(0, 0, [1]);
array.splice(0, 0, {doubleCol: 1});
TestCase.assertEqual(objects.length, 7);
array.push(objects[0], objects[1]);

View File

@ -16,67 +16,105 @@ var RANDOM_DATA = new Uint8Array([
module.exports = BaseTest.extend({
testBasicTypesPropertyGetters: function() {
var basicTypesValues = [true, 1, 1.1, 1.11, 'string', new Date(1), RANDOM_DATA];
var realm = new Realm({schema: [schemas.BasicTypes]});
var object;
var basicTypesValues = {
boolCol: true,
intCol: 1,
floatCol: 1.1,
doubleCol: 1.11,
stringCol: 'string',
dateCol: new Date(1),
dataCol: RANDOM_DATA,
};
realm.write(function() {
object = realm.create('BasicTypesObject', basicTypesValues);
});
for (var i = 0; i < schemas.BasicTypes.properties.length; i++) {
var prop = schemas.BasicTypes.properties[i];
if (prop.type == Realm.Types.FLOAT) {
TestCase.assertEqualWithTolerance(object[prop.name], basicTypesValues[i], 0.000001);
for (var name in schemas.BasicTypes.properties) {
var prop = schemas.BasicTypes.properties[name];
var type = typeof prop == 'object' ? prop.type : prop;
if (type == Realm.Types.FLOAT || type == Realm.Types.DOUBLE) {
TestCase.assertEqualWithTolerance(object[name], basicTypesValues[name], 0.000001);
}
else if (prop.type == Realm.Types.DATA) {
TestCase.assertArraysEqual(new Uint8Array(object[prop.name]), RANDOM_DATA);
else if (type == Realm.Types.DATA) {
TestCase.assertArraysEqual(new Uint8Array(object[name]), RANDOM_DATA);
}
else if (prop.type == Realm.Types.DATE) {
TestCase.assertEqual(object[prop.name].getTime(), basicTypesValues[i].getTime());
else if (type == Realm.Types.DATE) {
TestCase.assertEqual(object[name].getTime(), basicTypesValues[name].getTime());
}
else {
TestCase.assertEqual(object[prop.name], basicTypesValues[i]);
TestCase.assertEqual(object[name], basicTypesValues[name]);
}
}
TestCase.assertEqual(object.nonexistent, undefined);
},
testNullableBasicTypesPropertyGetters: function() {
var nullValues = [null, null, null, null, null, null, null];
var basicTypesValues = [true, 1, 1.1, 1.11, 'string', new Date(1), RANDOM_DATA];
var realm = new Realm({schema: [schemas.NullableBasicTypes]});
var nullObject = null;
var object = null;
var object, nullObject;
var basicTypesValues = {
boolCol: true,
intCol: 1,
floatCol: 1.1,
doubleCol: 1.11,
stringCol: 'string',
dateCol: new Date(1),
dataCol: RANDOM_DATA,
};
realm.write(function() {
nullObject = realm.create('NullableBasicTypesObject', nullValues);
object = realm.create('NullableBasicTypesObject', basicTypesValues);
nullObject = realm.create('NullableBasicTypesObject', {
boolCol: null,
intCol: null,
floatCol: null,
doubleCol: null,
stringCol: null,
dateCol: null,
dataCol: null,
});
});
for (var i = 0; i < schemas.BasicTypes.properties.length; i++) {
var prop = schemas.BasicTypes.properties[i];
TestCase.assertEqual(nullObject[prop.name], null);
for (var name in schemas.BasicTypes.properties) {
var prop = schemas.BasicTypes.properties[name];
var type = typeof prop == 'object' ? prop.type : prop;
if (prop.type == Realm.Types.FLOAT) {
TestCase.assertEqualWithTolerance(object[prop.name], basicTypesValues[i], 0.000001);
TestCase.assertEqual(nullObject[name], null);
if (type == Realm.Types.FLOAT || type == Realm.Types.DOUBLE) {
TestCase.assertEqualWithTolerance(object[name], basicTypesValues[name], 0.000001);
}
else if (prop.type == Realm.Types.DATA) {
TestCase.assertArraysEqual(new Uint8Array(object[prop.name]), RANDOM_DATA);
else if (type == Realm.Types.DATA) {
TestCase.assertArraysEqual(new Uint8Array(object[name]), RANDOM_DATA);
}
else if (prop.type == Realm.Types.DATE) {
TestCase.assertEqual(object[prop.name].getTime(), basicTypesValues[i].getTime());
else if (type == Realm.Types.DATE) {
TestCase.assertEqual(object[name].getTime(), basicTypesValues[name].getTime());
}
else {
TestCase.assertEqual(object[prop.name], basicTypesValues[i]);
TestCase.assertEqual(object[name], basicTypesValues[name]);
}
}
},
testBasicTypesPropertySetters: function() {
var basicTypesValues = [true, 1, 1.1, 1.11, 'string', new Date(1), new ArrayBuffer()];
var realm = new Realm({schema: [schemas.BasicTypes]});
var obj = null;
var obj;
var basicTypesValues = {
boolCol: true,
intCol: 1,
floatCol: 1.1,
doubleCol: 1.11,
stringCol: 'string',
dateCol: new Date(1),
dataCol: new ArrayBuffer(),
};
realm.write(function() {
obj = realm.create('BasicTypesObject', basicTypesValues);
@ -92,7 +130,7 @@ module.exports = BaseTest.extend({
TestCase.assertEqual(obj.boolCol, false, 'wrong bool value');
TestCase.assertEqual(obj.intCol, 2, 'wrong int value');
TestCase.assertEqualWithTolerance(obj.floatCol, 2.2, 0.000001, 'wrong float value');
TestCase.assertEqual(obj.doubleCol, 2.22, 'wrong double value');
TestCase.assertEqualWithTolerance(obj.doubleCol, 2.22, 0.000001, 'wrong double value');
TestCase.assertEqual(obj.stringCol, 'STRING', 'wrong string value');
TestCase.assertEqual(obj.dateCol.getTime(), 2, 'wrong date value');
TestCase.assertArraysEqual(new Uint8Array(obj.dataCol), RANDOM_DATA, 'wrong data value');
@ -156,24 +194,32 @@ module.exports = BaseTest.extend({
TestCase.assertEqual(obj.boolCol, false, 'bool value changed outside transaction');
},
testNullableBasicTypesPropertySetters: function() {
var basicTypesValues = [true, 1, 1.1, 1.11, 'string', new Date(1), RANDOM_DATA];
var realm = new Realm({schema: [schemas.NullableBasicTypes]});
var obj, obj1;
var basicTypesValues = {
boolCol: true,
intCol: 1,
floatCol: 1.1,
doubleCol: 1.11,
stringCol: 'string',
dateCol: new Date(1),
dataCol: RANDOM_DATA,
};
realm.write(function() {
obj = realm.create('NullableBasicTypesObject', basicTypesValues);
obj1 = realm.create('NullableBasicTypesObject', basicTypesValues);
for (var index in schemas.NullableBasicTypes.properties) {
var prop = schemas.NullableBasicTypes.properties[index];
obj[prop.name] = null;
obj1[prop.name] = undefined;
for (var name in schemas.NullableBasicTypes.properties) {
obj[name] = null;
obj1[name] = undefined;
}
});
for (var index in schemas.NullableBasicTypes.properties) {
var prop = schemas.NullableBasicTypes.properties[index];
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() {
@ -192,8 +238,13 @@ module.exports = BaseTest.extend({
testLinkTypesPropertyGetters: function() {
var realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]});
var obj = null;
realm.write(function() {
obj = realm.create('LinkTypesObject', [[1], null, [[3]]]);
obj = realm.create('LinkTypesObject', {
objectCol: {doubleCol: 1},
objectCol1: null,
arrayCol: [{doubleCol: 3}],
});
});
var objVal = obj.objectCol;
@ -212,11 +263,16 @@ module.exports = BaseTest.extend({
testLinkTypesPropertySetters: function() {
var realm = new Realm({schema: [schemas.LinkTypes, schemas.TestObject]});
var objects = realm.objects('TestObject');
var obj = null;
var obj;
realm.write(function() {
obj = realm.create('LinkTypesObject', [[1], null, [[3]]]);
obj = realm.create('LinkTypesObject', {
objectCol: {doubleCol: 1},
objectCol1: null,
arrayCol: [{doubleCol: 3}],
});
});
TestCase.assertEqual(objects.length, 2);
TestCase.assertThrows(function() {
@ -240,15 +296,20 @@ module.exports = BaseTest.extend({
// set object as JSON
realm.write(function() {
obj.objectCol = { doubleCol: 1 };
obj.objectCol = {doubleCol: 1};
});
TestCase.assertEqual(obj.objectCol.doubleCol, 1);
TestCase.assertEqual(objects.length, 3);
// set array property
realm.write(function() {
obj.arrayCol = [obj.arrayCol[0], obj.objectCol, realm.create('TestObject', [2])];
obj.arrayCol = [
obj.arrayCol[0],
obj.objectCol,
realm.create('TestObject', {doubleCol: 2}),
];
});
TestCase.assertEqual(objects.length, 4);
TestCase.assertEqual(obj.arrayCol.length, 3);
TestCase.assertEqual(obj.arrayCol[0].doubleCol, 3);
@ -256,15 +317,22 @@ module.exports = BaseTest.extend({
TestCase.assertEqual(obj.arrayCol[2].doubleCol, 2);
},
testEnumerablePropertyNames: function() {
var basicTypesValues = [true, 1, 1.1, 1.11, 'string', new Date(1), new ArrayBuffer()];
var realm = new Realm({schema: [schemas.BasicTypes]});
var object;
realm.write(function() {
object = realm.create('BasicTypesObject', basicTypesValues);
object = realm.create('BasicTypesObject', {
boolCol: true,
intCol: 1,
floatCol: 1.1,
doubleCol: 1.11,
stringCol: 'string',
dateCol: new Date(1),
dataCol: RANDOM_DATA,
});
});
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

@ -12,8 +12,12 @@ var util = require('./util');
module.exports = BaseTest.extend({
testRealmConstructorPath: function() {
TestCase.assertThrows(function() { new Realm('/invalidpath'); });
TestCase.assertThrows(function() { new Realm(util.realmPathForFile('test1.realm'), 'invalidArgument'); });
TestCase.assertThrows(function() {
new Realm('/invalidpath');
}, 'Realm cannot be created with an invalid path');
TestCase.assertThrows(function() {
new Realm(util.realmPathForFile('test1.realm'), 'invalidArgument');
}, 'Realm constructor can only have 0 or 1 argument(s)');
var defaultRealm = new Realm({schema: []});
TestCase.assertEqual(defaultRealm.path, Realm.defaultPath);
@ -57,11 +61,33 @@ module.exports = BaseTest.extend({
realm = new Realm({path: testPath, schema: [schemas.TestObject], schemaVersion: 2});
realm.write(function() {
realm.create('TestObject', [1]);
realm.create('TestObject', {doubleCol: 1});
});
TestCase.assertEqual(realm.objects('TestObject')[0].doubleCol, 1)
},
testRealmConstructorSchemaValidation: function() {
TestCase.assertThrows(function() {
new Realm({schema: schemas.AllTypes});
}, 'The schema should be an array');
TestCase.assertThrows(function() {
new Realm({schema: ['SomeType']});
}, 'The schema should be an array of objects');
TestCase.assertThrows(function() {
new Realm({schema: [{}]});
}, 'The schema should be an array of ObjectSchema objects');
TestCase.assertThrows(function() {
new Realm({schema: [{name: 'SomeObject'}]});
}, 'The schema should be an array of ObjectSchema objects');
TestCase.assertThrows(function() {
new Realm({schema: [{properties: {intCol: Realm.Types.INT}}]});
}, 'The schema should be an array of ObjectSchema objects');
},
testDefaultPath: function() {
var defaultRealm = new Realm({schema: []});
TestCase.assertEqual(defaultRealm.path, Realm.defaultPath);
@ -77,12 +103,12 @@ module.exports = BaseTest.extend({
var realm = new Realm({schema: [schemas.IntPrimary, schemas.AllTypes, schemas.TestObject]});
TestCase.assertThrows(function() {
realm.create('TestObject', [1]);
realm.create('TestObject', {doubleCol: 1});
}, 'can only create inside a write transaction');
realm.write(function() {
realm.create('TestObject', [1]);
realm.create('TestObject', {'doubleCol': 2});
realm.create('TestObject', {doubleCol: 1});
realm.create('TestObject', {doubleCol: 2});
});
var objects = realm.objects('TestObject');
@ -92,16 +118,31 @@ module.exports = BaseTest.extend({
// test int primary object
realm.write(function() {
var obj0 = realm.create('IntPrimaryObject', [0, 'val0']);
var obj0 = realm.create('IntPrimaryObject', {
primaryCol: 0,
valueCol: 'val0',
});
TestCase.assertThrows(function() {
realm.create('IntPrimaryObject', [0, 'val0']);
});
realm.create('IntPrimaryObject', [1, 'val1'], true);
realm.create('IntPrimaryObject', {
primaryCol: 0,
valueCol: 'val0',
});
}, 'cannot create object with conflicting primary key');
realm.create('IntPrimaryObject', {
primaryCol: 1,
valueCol: 'val1',
}, true);
var objects = realm.objects('IntPrimaryObject');
TestCase.assertEqual(objects.length, 2);
realm.create('IntPrimaryObject', [0, 'newVal0'], true);
realm.create('IntPrimaryObject', {
primaryCol: 0,
valueCol: 'newVal0',
}, true);
TestCase.assertEqual(obj0.valueCol, 'newVal0');
TestCase.assertEqual(objects.length, 2);
@ -111,47 +152,89 @@ module.exports = BaseTest.extend({
// test upsert with all type and string primary object
realm.write(function() {
var values = ['0', true, 1, 1.1, 1.11, '1', new Date(1), new ArrayBuffer(1), [1], []];
var values = {
primaryCol: '0',
boolCol: true,
intCol: 1,
floatCol: 1.1,
doubleCol: 1.11,
stringCol: '1',
dateCol: new Date(1),
dataCol: new ArrayBuffer(1),
objectCol: {doubleCol: 1},
arrayCol: [],
};
var obj0 = realm.create('AllTypesObject', values);
TestCase.assertThrows(function() {
realm.create('AllTypesObject', values);
});
var obj1 = realm.create('AllTypesObject', ['1', false, 2, 2.2, 2.11, '2', new Date(2), new ArrayBuffer(2), [0], [[2]]], true);
}, 'cannot create object with conflicting primary key');
var obj1 = realm.create('AllTypesObject', {
primaryCol: '1',
boolCol: false,
intCol: 2,
floatCol: 2.2,
doubleCol: 2.22,
stringCol: '2',
dateCol: new Date(2),
dataCol: new ArrayBuffer(2),
objectCol: {doubleCol: 0},
arrayCol: [{doubleCol: 2}],
}, true);
var objects = realm.objects('AllTypesObject');
TestCase.assertEqual(objects.length, 2);
realm.create('AllTypesObject', ['0', false, 2, 2.2, 2.22, '2', new Date(2), new ArrayBuffer(2), null, [[2]]], true);
realm.create('AllTypesObject', {
primaryCol: '0',
boolCol: false,
intCol: 2,
floatCol: 2.2,
doubleCol: 2.22,
stringCol: '2',
dateCol: new Date(2),
dataCol: new ArrayBuffer(2),
objectCol: null,
arrayCol: [{doubleCol: 2}],
}, true);
TestCase.assertEqual(objects.length, 2);
TestCase.assertEqual(obj0.stringCol, '2');
TestCase.assertEqual(obj0.boolCol, false);
TestCase.assertEqual(obj0.intCol, 2);
TestCase.assertEqualWithTolerance(obj0.floatCol, 2.2, 0.000001);
TestCase.assertEqual(obj0.doubleCol, 2.22);
TestCase.assertEqualWithTolerance(obj0.doubleCol, 2.22, 0.000001);
TestCase.assertEqual(obj0.dateCol.getTime(), 2);
TestCase.assertEqual(obj0.dataCol.byteLength, 2);
TestCase.assertEqual(obj0.objectCol, null);
TestCase.assertEqual(obj0.arrayCol.length, 1);
realm.create('AllTypesObject', { primaryCol: '0' }, true);
realm.create('AllTypesObject', { primaryCol: '1' }, true);
realm.create('AllTypesObject', {primaryCol: '0'}, true);
realm.create('AllTypesObject', {primaryCol: '1'}, true);
TestCase.assertEqual(obj0.stringCol, '2');
TestCase.assertEqual(obj0.objectCol, null);
TestCase.assertEqual(obj1.objectCol.doubleCol, 0);
realm.create('AllTypesObject', { primaryCol: '0', stringCol: '3', objectCol: [0] }, true);
realm.create('AllTypesObject', {
primaryCol: '0',
stringCol: '3',
objectCol: {doubleCol: 0},
}, true);
TestCase.assertEqual(obj0.stringCol, '3');
TestCase.assertEqual(obj0.boolCol, false);
TestCase.assertEqual(obj0.intCol, 2);
TestCase.assertEqualWithTolerance(obj0.floatCol, 2.2, 0.000001);
TestCase.assertEqual(obj0.doubleCol, 2.22);
TestCase.assertEqualWithTolerance(obj0.doubleCol, 2.22, 0.000001);
TestCase.assertEqual(obj0.dateCol.getTime(), 2);
TestCase.assertEqual(obj0.dataCol.byteLength, 2);
TestCase.assertEqual(obj0.objectCol.doubleCol, 0);
TestCase.assertEqual(obj0.arrayCol.length, 1);
realm.create('AllTypesObject', { primaryCol: '0', objectCol: undefined }, true);
realm.create('AllTypesObject', { primaryCol: '1', objectCol: null }, true);
realm.create('AllTypesObject', {primaryCol: '0', objectCol: undefined}, true);
realm.create('AllTypesObject', {primaryCol: '1', objectCol: null}, true);
TestCase.assertEqual(obj0.objectCol, null);
TestCase.assertEqual(obj1.objectCol, null);
});
@ -159,27 +242,31 @@ module.exports = BaseTest.extend({
testRealmCreateWithDefaults: function() {
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.assertEqualWithTolerance(obj.doubleCol, properties.doubleCol.default, 0.000001);
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.doubleCol);
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].doubleCol);
});
},
testRealmDelete: function() {
var realm = new Realm({schema: [schemas.TestObject]});
realm.write(function() {
for (var i = 0; i < 10; i++) {
realm.create('TestObject', [i]);
realm.create('TestObject', {doubleCol: i});
}
});
@ -213,11 +300,13 @@ module.exports = BaseTest.extend({
testDeleteAll: function() {
var realm = new Realm({schema: [schemas.TestObject, schemas.IntPrimary]});
realm.write(function() {
realm.create('TestObject', [1]);
realm.create('TestObject', [2]);
realm.create('IntPrimaryObject', [2, 'value']);
realm.create('TestObject', {doubleCol: 1});
realm.create('TestObject', {doubleCol: 2});
realm.create('IntPrimaryObject', {primaryCol: 2, valueCol: 'value'});
});
TestCase.assertEqual(realm.objects('TestObject').length, 2);
TestCase.assertEqual(realm.objects('IntPrimaryObject').length, 1);
@ -236,10 +325,10 @@ module.exports = BaseTest.extend({
testRealmObjects: function() {
var realm = new Realm({schema: [schemas.PersonObject, schemas.DefaultValues, schemas.TestObject]});
realm.write(function() {
realm.create('PersonObject', ['Ari', 10, false]);
realm.create('PersonObject', ['Tim', 11, false]);
realm.create('PersonObject', {'name': 'Bjarne', 'age': 12});
realm.create('PersonObject', {'name': 'Alex', 'age': 12, 'married': true});
realm.create('PersonObject', {name: 'Ari', age: 10});
realm.create('PersonObject', {name: 'Tim', age: 11});
realm.create('PersonObject', {name: 'Bjarne', age: 12});
realm.create('PersonObject', {name: 'Alex', age: 12, married: true});
});
TestCase.assertThrows(function() {

View File

@ -16,7 +16,7 @@ module.exports = BaseTest.extend({
TestCase.assertEqual(objects.length, 0);
realm.write(function() {
realm.create('TestObject', [1]);
realm.create('TestObject', {doubleCol: 1});
TestCase.assertEqual(objects.length, 1);
});
TestCase.assertEqual(objects.length, 1);
@ -24,8 +24,8 @@ module.exports = BaseTest.extend({
testResultsSubscript: function() {
var realm = new Realm({schema: [schemas.PersonObject]});
realm.write(function() {
realm.create('PersonObject', ['name1', 1, false]);
realm.create('PersonObject', ['name2', 2, false]);
realm.create('PersonObject', {name: 'name1', age: 1});
realm.create('PersonObject', {name: 'name2', age: 2});
});
var people = realm.objects('PersonObject');
@ -41,17 +41,17 @@ module.exports = BaseTest.extend({
var objects = realm.objects('TestObject');
realm.write(function() {
realm.create('TestObject', [1]);
realm.create('TestObject', {doubleCol: 1});
});
TestCase.assertThrows(function() {
objects[-1] = [0];
objects[-1] = {doubleCol: 0};
});
TestCase.assertThrows(function() {
objects[0] = [0];
objects[0] = {doubleCol: 0};
});
TestCase.assertThrows(function() {
objects[1] = [0];
objects[1] = {doubleCol: 0};
});
TestCase.assertThrows(function() {
objects.length = 0;
@ -71,12 +71,13 @@ module.exports = BaseTest.extend({
testResultsEnumerate: function() {
var realm = new Realm({schema: [schemas.TestObject]});
var objects = realm.objects('TestObject');
for (var object in objects) {
for (var index in objects) {
TestCase.assertTrue(false, "No objects should have been enumerated");
}
realm.write(function() {
realm.create('TestObject', [1]);
realm.create('TestObject', {doubleCol: 1});
TestCase.assertEqual(objects.length, 1);
});
@ -93,12 +94,13 @@ module.exports = BaseTest.extend({
testSort: function() {
var realm = new Realm({schema: [schemas.TestObject]});
var objects = realm.objects('TestObject');
realm.write(function() {
realm.create('TestObject', [2]);
realm.create('TestObject', [3]);
realm.create('TestObject', [1]);
realm.create('TestObject', [4]);
realm.create('TestObject', [0]);
realm.create('TestObject', {doubleCol: 2});
realm.create('TestObject', {doubleCol: 3});
realm.create('TestObject', {doubleCol: 1});
realm.create('TestObject', {doubleCol: 4});
realm.create('TestObject', {doubleCol: 0});
});
objects.sortByProperty('doubleCol');

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: {doubleCol: 1}},
nullObjectCol: {type: 'TestObject', default: null},
arrayCol: {type: Realm.Types.LIST, objectType: 'TestObject', default: [{doubleCol: 2}]},
}
};
exports.QueryObject = {