Accept constructor in create() and objects() methods

This will help the linter prevent spelling mistakes and make some code more easily readable.

Resolves #233
This commit is contained in:
Scott Kyle 2016-02-24 17:34:20 -08:00
parent 578e6b9742
commit 4a81b091cb
4 changed files with 86 additions and 11 deletions

View File

@ -74,6 +74,24 @@ export default class Realm {
});
}
create(type, ...args) {
if (typeof type == 'function') {
type = objects.typeForConstructor(this[keys.realm], type);
}
let method = util.createMethod(objectTypes.REALM, 'create', true);
return method.apply(this, [type, ...args]);
}
objects(type, ...args) {
if (typeof type == 'function') {
type = objects.typeForConstructor(this[keys.realm], type);
}
let method = util.createMethod(objectTypes.REALM, 'objects');
return method.apply(this, [type, ...args]);
}
addListener(name, callback) {
if (typeof callback != 'function') {
throw new Error('Realm.addListener must be passed a function!');
@ -130,12 +148,10 @@ export default class Realm {
// Non-mutating methods:
util.createMethods(Realm.prototype, objectTypes.REALM, [
'close',
'objects',
]);
// Mutating methods:
util.createMethods(Realm.prototype, objectTypes.REALM, [
'create',
'delete',
'deleteAll',
], true);

View File

@ -53,3 +53,15 @@ export function create(realmId, info) {
export function registerConstructors(realmId, constructors) {
registeredConstructors[realmId] = constructors;
}
export function typeForConstructor(realmId, constructor) {
let constructors = registeredConstructors[realmId];
for (let name in constructors) {
if (constructors[name] == constructor) {
return name;
}
}
return null;
}

View File

@ -249,11 +249,28 @@ JSValueRef RealmGetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef pr
return NULL;
}
std::string RealmObjectTypeForValue(SharedRealm &realm, JSContextRef ctx, JSValueRef value) {
if (JSValueIsObject(ctx, value) && JSObjectIsConstructor(ctx, (JSObjectRef)value)) {
JSObjectRef constructor = (JSObjectRef)value;
for (auto pair : RJSConstructors(realm.get())) {
if (pair.second == constructor) {
return pair.first;
}
}
throw std::runtime_error("Constructor was not registered in the schema for this Realm");
}
return RJSValidatedStringForValue(ctx, value, "objectType");
}
JSValueRef RealmObjects(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* jsException) {
try {
RJSValidateArgumentCount(1, argumentCount);
std::string className = RJSValidatedStringForValue(ctx, arguments[0], "objectType");
RJSValidateArgumentCount(argumentCount, 1);
SharedRealm sharedRealm = *RJSGetInternal<SharedRealm *>(thisObject);
std::string className = RealmObjectTypeForValue(sharedRealm, ctx, arguments[0]);
return RJSResultsCreate(ctx, sharedRealm, className);
}
catch (std::exception &exp) {
@ -291,10 +308,12 @@ JSValueRef RealmCreateObject(JSContextRef ctx, JSObjectRef function, JSObjectRef
try {
RJSValidateArgumentRange(argumentCount, 2, 3);
std::string className = RJSValidatedStringForValue(ctx, arguments[0], "objectType");
SharedRealm sharedRealm = *RJSGetInternal<SharedRealm *>(thisObject);
auto object_schema = sharedRealm->config().schema->find(className);
if (object_schema == sharedRealm->config().schema->end()) {
std::string className = RealmObjectTypeForValue(sharedRealm, ctx, arguments[0]);
auto &schema = sharedRealm->config().schema;
auto object_schema = schema->find(className);
if (object_schema == schema->end()) {
*jsException = RJSMakeError(ctx, "Object type '" + className + "' not found in schema.");
return NULL;
}

View File

@ -318,6 +318,13 @@ module.exports = BaseTest.extend({
// Should have been multiplied by 100 in the constructor.
TestCase.assertEqual(object.intCol, 100);
// Should be able to create object by passing in constructor.
object = realm.create(CustomObject, {intCol: 2});
TestCase.assertTrue(object instanceof CustomObject);
TestCase.assertTrue(Object.getPrototypeOf(object) == CustomObject.prototype);
TestCase.assertEqual(customCreated, 2);
TestCase.assertEqual(object.intCol, 200);
});
TestCase.assertThrows(function() {
@ -325,6 +332,16 @@ module.exports = BaseTest.extend({
realm.create('InvalidObject', {intCol: 1});
});
});
// Only the original constructor should be valid.
function WrongCustomObject() {}
WrongCustomObject.schema = CustomObject.schema;
TestCase.assertThrows(function() {
realm.write(function() {
realm.create(WrongCustomObject, {intCol: 1});
});
});
},
testRealmDelete: function() {
@ -390,6 +407,7 @@ module.exports = BaseTest.extend({
testRealmObjects: function() {
var realm = new Realm({schema: [schemas.PersonObject, schemas.DefaultValues, schemas.TestObject]});
realm.write(function() {
realm.create('PersonObject', {name: 'Ari', age: 10});
realm.create('PersonObject', {name: 'Tim', age: 11});
@ -397,18 +415,28 @@ module.exports = BaseTest.extend({
realm.create('PersonObject', {name: 'Alex', age: 12, married: true});
});
TestCase.assertThrows(function() {
// Should be able to pass constructor for getting objects.
var objects = realm.objects(schemas.PersonObject);
TestCase.assertTrue(objects[0] instanceof schemas.PersonObject);
function WrongPerson() {}
WrongPerson.schema = schemas.PersonObject.schema;
TestCase.assertThrows(function() {
realm.objects();
});
TestCase.assertThrows(function() {
TestCase.assertThrows(function() {
realm.objects([]);
});
TestCase.assertThrows(function() {
TestCase.assertThrows(function() {
realm.objects('InvalidClass');
});
TestCase.assertThrows(function() {
TestCase.assertThrows(function() {
realm.objects('PersonObject', 'truepredicate');
});
TestCase.assertThrows(function() {
realm.objects(WrongPerson);
});
},
testNotifications: function() {