Merge pull request #395 from realm/al-is-valid
Add `realm.isValid()` to support checking for deleted objects
This commit is contained in:
commit
49fa4884ef
|
@ -5,9 +5,11 @@ x.x.x Release notes (yyyy-MM-dd)
|
|||
|
||||
### Enhancements
|
||||
* Support for queries comparing optional properties to `null`
|
||||
* `object.isValid()` has been added to enable checking if an object has been deleted
|
||||
|
||||
### Bugfixes
|
||||
* None
|
||||
* When accessing an empty Results `undefined` is returned rather than throwing an exception
|
||||
* Accessing a deleted object throws a javascript exception rather than crashing
|
||||
|
||||
0.11.1 Release notes (2016-3-29)
|
||||
=============================================================
|
||||
|
|
|
@ -23,7 +23,7 @@ import { keys, propTypes, objectTypes } from './constants';
|
|||
import Collection, * as collections from './collections';
|
||||
import List, { createList } from './lists';
|
||||
import Results, { createResults } from './results';
|
||||
import * as objects from './objects';
|
||||
import RealmObject, { createObject, registerConstructors, typeForConstructor } from './objects';
|
||||
import * as rpc from './rpc';
|
||||
import * as util from './util';
|
||||
|
||||
|
@ -32,7 +32,7 @@ const listenersKey = Symbol();
|
|||
|
||||
rpc.registerTypeConverter(objectTypes.LIST, createList);
|
||||
rpc.registerTypeConverter(objectTypes.RESULTS, createResults);
|
||||
rpc.registerTypeConverter(objectTypes.OBJECT, objects.create);
|
||||
rpc.registerTypeConverter(objectTypes.OBJECT, createObject);
|
||||
|
||||
export default class Realm {
|
||||
constructor(config) {
|
||||
|
@ -62,7 +62,7 @@ export default class Realm {
|
|||
|
||||
let realmId = rpc.createRealm(Array.from(arguments));
|
||||
|
||||
objects.registerConstructors(realmId, constructors);
|
||||
registerConstructors(realmId, constructors);
|
||||
|
||||
this[keys.id] = realmId;
|
||||
this[keys.realm] = realmId;
|
||||
|
@ -79,7 +79,7 @@ export default class Realm {
|
|||
|
||||
create(type, ...args) {
|
||||
if (typeof type == 'function') {
|
||||
type = objects.typeForConstructor(this[keys.realm], type);
|
||||
type = typeForConstructor(this[keys.realm], type);
|
||||
}
|
||||
|
||||
let method = util.createMethod(objectTypes.REALM, 'create', true);
|
||||
|
@ -88,7 +88,7 @@ export default class Realm {
|
|||
|
||||
objects(type, ...args) {
|
||||
if (typeof type == 'function') {
|
||||
type = objects.typeForConstructor(this[keys.realm], type);
|
||||
type = typeForConstructor(this[keys.realm], type);
|
||||
}
|
||||
|
||||
let method = util.createMethod(objectTypes.REALM, 'objects');
|
||||
|
@ -169,6 +169,9 @@ Object.defineProperties(Realm, {
|
|||
Results: {
|
||||
value: Results,
|
||||
},
|
||||
Object: {
|
||||
value: RealmObject,
|
||||
},
|
||||
defaultPath: {
|
||||
get: util.getterForProperty('defaultPath'),
|
||||
set: util.setterForProperty('defaultPath'),
|
||||
|
|
|
@ -18,15 +18,23 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
import { keys } from './constants';
|
||||
import { getterForProperty, setterForProperty } from './util';
|
||||
import { keys, objectTypes } from './constants';
|
||||
import { getterForProperty, setterForProperty, createMethods } from './util';
|
||||
|
||||
const registeredConstructors = {};
|
||||
|
||||
export function create(realmId, info) {
|
||||
export default class RealmObject {
|
||||
}
|
||||
|
||||
// Non-mutating methods:
|
||||
createMethods(RealmObject.prototype, objectTypes.OBJECT, [
|
||||
'isValid',
|
||||
]);
|
||||
|
||||
export function createObject(realmId, info) {
|
||||
let schema = info.schema;
|
||||
let constructor = (registeredConstructors[realmId] || {})[schema.name];
|
||||
let object = constructor ? Object.create(constructor.prototype) : {};
|
||||
let object = Object.create(constructor ? constructor.prototype : RealmObject.prototype);
|
||||
|
||||
object[keys.realm] = realmId;
|
||||
object[keys.id] = info.id;
|
||||
|
|
|
@ -231,11 +231,13 @@ inline typename T::Function Realm<T>::create_constructor(ContextType ctx) {
|
|||
FunctionType collection_constructor = ObjectWrap<T, CollectionClass<T>>::create_constructor(ctx);
|
||||
FunctionType list_constructor = ObjectWrap<T, ListClass<T>>::create_constructor(ctx);
|
||||
FunctionType results_constructor = ObjectWrap<T, ResultsClass<T>>::create_constructor(ctx);
|
||||
FunctionType realm_object_constructor = ObjectWrap<T, RealmObjectClass<T>>::create_constructor(ctx);
|
||||
|
||||
PropertyAttributes attributes = PropertyAttributes(ReadOnly | DontEnum | DontDelete);
|
||||
Object::set_property(ctx, realm_constructor, "Collection", collection_constructor, attributes);
|
||||
Object::set_property(ctx, realm_constructor, "List", list_constructor, attributes);
|
||||
Object::set_property(ctx, realm_constructor, "Results", results_constructor, attributes);
|
||||
Object::set_property(ctx, realm_constructor, "Object", realm_object_constructor, attributes);
|
||||
|
||||
return realm_constructor;
|
||||
}
|
||||
|
@ -469,7 +471,7 @@ void Realm<T>::write(ContextType ctx, ObjectType this_object, size_t argc, const
|
|||
}
|
||||
catch (std::exception &e) {
|
||||
realm->cancel_transaction();
|
||||
throw e;
|
||||
throw;
|
||||
}
|
||||
|
||||
realm->commit_transaction();
|
||||
|
|
|
@ -46,6 +46,8 @@ class RealmObject {
|
|||
static void get_property(ContextType, ObjectType, const String &, ReturnValue &);
|
||||
static bool set_property(ContextType, ObjectType, const String &, ValueType);
|
||||
static std::vector<String> get_property_names(ContextType, ObjectType);
|
||||
|
||||
static void is_valid(ContextType, ObjectType, size_t, const ValueType [], ReturnValue &);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
|
@ -59,8 +61,17 @@ struct RealmObjectClass : ClassDefinition<T, realm::Object> {
|
|||
wrap<RealmObject::set_property>,
|
||||
wrap<RealmObject::get_property_names>,
|
||||
};
|
||||
|
||||
MethodMap<T> const methods = {
|
||||
{"isValid", wrap<RealmObject::is_valid>},
|
||||
};
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
void RealmObject<T>::is_valid(ContextType ctx, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) {
|
||||
return_value.set(get_internal<T, RealmObjectClass<T>>(this_object)->is_valid());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename T::Object RealmObject<T>::create_instance(ContextType ctx, realm::Object &realm_object) {
|
||||
static String prototype_string = "prototype";
|
||||
|
|
|
@ -104,7 +104,7 @@ struct Value {
|
|||
static return_t validated_to_##type(ContextType ctx, const ValueType &value, const char *name = nullptr) { \
|
||||
if (!is_##type(ctx, value)) { \
|
||||
std::string prefix = name ? std::string("'") + name + "'" : "JS value"; \
|
||||
throw std::invalid_argument(prefix + " must be: " #type); \
|
||||
throw std::invalid_argument(prefix + " must be of type: " #type); \
|
||||
} \
|
||||
return to_##type(ctx, value); \
|
||||
}
|
||||
|
|
|
@ -48,6 +48,8 @@ namespace realm {
|
|||
const ObjectSchema &get_object_schema() { return *m_object_schema; }
|
||||
Row row() { return m_row; }
|
||||
|
||||
bool is_valid() const { return m_row.is_attached(); }
|
||||
|
||||
private:
|
||||
SharedRealm m_realm;
|
||||
const ObjectSchema *m_object_schema;
|
||||
|
@ -57,6 +59,8 @@ namespace realm {
|
|||
inline void set_property_value_impl(ContextType ctx, const Property &property, ValueType value, bool try_update);
|
||||
template<typename ValueType, typename ContextType>
|
||||
inline ValueType get_property_value_impl(ContextType ctx, const Property &property);
|
||||
|
||||
inline void verify_attached();
|
||||
};
|
||||
|
||||
//
|
||||
|
@ -109,6 +113,13 @@ namespace realm {
|
|||
static Mixed to_mixed(ContextType ctx, ValueType &val) { throw std::runtime_error("'Any' type is unsupported"); }
|
||||
};
|
||||
|
||||
class InvalidatedObjectException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
InvalidatedObjectException(const std::string object_type, const std::string message) : std::runtime_error(message), object_type(object_type) {}
|
||||
const std::string object_type;
|
||||
};
|
||||
|
||||
class InvalidPropertyException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
|
@ -161,6 +172,8 @@ namespace realm {
|
|||
{
|
||||
using Accessor = NativeAccessor<ValueType, ContextType>;
|
||||
|
||||
verify_attached();
|
||||
|
||||
if (!m_realm->is_in_transaction()) {
|
||||
throw MutationOutsideTransactionException("Can only set property values within a transaction.");
|
||||
}
|
||||
|
@ -225,6 +238,8 @@ namespace realm {
|
|||
{
|
||||
using Accessor = NativeAccessor<ValueType, ContextType>;
|
||||
|
||||
verify_attached();
|
||||
|
||||
size_t column = property.table_column;
|
||||
if (property.is_nullable && m_row.is_null(column)) {
|
||||
return Accessor::null_value(ctx);
|
||||
|
@ -324,6 +339,14 @@ namespace realm {
|
|||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
inline void Object::verify_attached() {
|
||||
if (!m_row.is_attached()) {
|
||||
throw InvalidatedObjectException(m_object_schema->name,
|
||||
"Accessing object of type " + m_object_schema->name + " which has been deleted"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// List implementation
|
||||
|
|
|
@ -431,5 +431,29 @@ module.exports = BaseTest.extend({
|
|||
object.dataCol = [1];
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
testObjectConstructor: function() {
|
||||
var realm = new Realm({schema: [schemas.TestObject]});
|
||||
realm.write(function() {
|
||||
var obj = realm.create('TestObject', {doubleCol: 1});
|
||||
TestCase.assertTrue(obj instanceof Realm.Object);
|
||||
});
|
||||
},
|
||||
|
||||
testIsValid: function() {
|
||||
var realm = new Realm({schema: [schemas.TestObject]});
|
||||
var obj;
|
||||
realm.write(function() {
|
||||
obj = realm.create('TestObject', {doubleCol: 1});
|
||||
TestCase.assertEqual(obj.isValid(), true);
|
||||
realm.delete(obj);
|
||||
TestCase.assertEqual(obj.isValid(), false);
|
||||
});
|
||||
|
||||
TestCase.assertEqual(obj.isValid(), false);
|
||||
TestCase.assertThrows(function() {
|
||||
obj.doubleCol;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
|
|
@ -50,6 +50,8 @@ module.exports = BaseTest.extend({
|
|||
|
||||
testResultsSubscript: function() {
|
||||
var realm = new Realm({schema: [schemas.PersonObject]});
|
||||
TestCase.assertEqual(realm.objects('PersonObject')[0], undefined);
|
||||
|
||||
realm.write(function() {
|
||||
realm.create('PersonObject', {name: 'name1', age: 1});
|
||||
realm.create('PersonObject', {name: 'name2', age: 2});
|
||||
|
|
Loading…
Reference in New Issue