mirror of
https://github.com/status-im/realm-js.git
synced 2025-02-18 17:47:32 +00:00
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
|
### Enhancements
|
||||||
* Support for queries comparing optional properties to `null`
|
* Support for queries comparing optional properties to `null`
|
||||||
|
* `object.isValid()` has been added to enable checking if an object has been deleted
|
||||||
|
|
||||||
### Bugfixes
|
### 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)
|
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 Collection, * as collections from './collections';
|
||||||
import List, { createList } from './lists';
|
import List, { createList } from './lists';
|
||||||
import Results, { createResults } from './results';
|
import Results, { createResults } from './results';
|
||||||
import * as objects from './objects';
|
import RealmObject, { createObject, registerConstructors, typeForConstructor } from './objects';
|
||||||
import * as rpc from './rpc';
|
import * as rpc from './rpc';
|
||||||
import * as util from './util';
|
import * as util from './util';
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ const listenersKey = Symbol();
|
|||||||
|
|
||||||
rpc.registerTypeConverter(objectTypes.LIST, createList);
|
rpc.registerTypeConverter(objectTypes.LIST, createList);
|
||||||
rpc.registerTypeConverter(objectTypes.RESULTS, createResults);
|
rpc.registerTypeConverter(objectTypes.RESULTS, createResults);
|
||||||
rpc.registerTypeConverter(objectTypes.OBJECT, objects.create);
|
rpc.registerTypeConverter(objectTypes.OBJECT, createObject);
|
||||||
|
|
||||||
export default class Realm {
|
export default class Realm {
|
||||||
constructor(config) {
|
constructor(config) {
|
||||||
@ -62,7 +62,7 @@ export default class Realm {
|
|||||||
|
|
||||||
let realmId = rpc.createRealm(Array.from(arguments));
|
let realmId = rpc.createRealm(Array.from(arguments));
|
||||||
|
|
||||||
objects.registerConstructors(realmId, constructors);
|
registerConstructors(realmId, constructors);
|
||||||
|
|
||||||
this[keys.id] = realmId;
|
this[keys.id] = realmId;
|
||||||
this[keys.realm] = realmId;
|
this[keys.realm] = realmId;
|
||||||
@ -79,7 +79,7 @@ export default class Realm {
|
|||||||
|
|
||||||
create(type, ...args) {
|
create(type, ...args) {
|
||||||
if (typeof type == 'function') {
|
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);
|
let method = util.createMethod(objectTypes.REALM, 'create', true);
|
||||||
@ -88,7 +88,7 @@ export default class Realm {
|
|||||||
|
|
||||||
objects(type, ...args) {
|
objects(type, ...args) {
|
||||||
if (typeof type == 'function') {
|
if (typeof type == 'function') {
|
||||||
type = objects.typeForConstructor(this[keys.realm], type);
|
type = typeForConstructor(this[keys.realm], type);
|
||||||
}
|
}
|
||||||
|
|
||||||
let method = util.createMethod(objectTypes.REALM, 'objects');
|
let method = util.createMethod(objectTypes.REALM, 'objects');
|
||||||
@ -169,6 +169,9 @@ Object.defineProperties(Realm, {
|
|||||||
Results: {
|
Results: {
|
||||||
value: Results,
|
value: Results,
|
||||||
},
|
},
|
||||||
|
Object: {
|
||||||
|
value: RealmObject,
|
||||||
|
},
|
||||||
defaultPath: {
|
defaultPath: {
|
||||||
get: util.getterForProperty('defaultPath'),
|
get: util.getterForProperty('defaultPath'),
|
||||||
set: util.setterForProperty('defaultPath'),
|
set: util.setterForProperty('defaultPath'),
|
||||||
|
@ -18,15 +18,23 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { keys } from './constants';
|
import { keys, objectTypes } from './constants';
|
||||||
import { getterForProperty, setterForProperty } from './util';
|
import { getterForProperty, setterForProperty, createMethods } from './util';
|
||||||
|
|
||||||
const registeredConstructors = {};
|
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 schema = info.schema;
|
||||||
let constructor = (registeredConstructors[realmId] || {})[schema.name];
|
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.realm] = realmId;
|
||||||
object[keys.id] = info.id;
|
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 collection_constructor = ObjectWrap<T, CollectionClass<T>>::create_constructor(ctx);
|
||||||
FunctionType list_constructor = ObjectWrap<T, ListClass<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 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);
|
PropertyAttributes attributes = PropertyAttributes(ReadOnly | DontEnum | DontDelete);
|
||||||
Object::set_property(ctx, realm_constructor, "Collection", collection_constructor, attributes);
|
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, "List", list_constructor, attributes);
|
||||||
Object::set_property(ctx, realm_constructor, "Results", results_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;
|
return realm_constructor;
|
||||||
}
|
}
|
||||||
@ -469,7 +471,7 @@ void Realm<T>::write(ContextType ctx, ObjectType this_object, size_t argc, const
|
|||||||
}
|
}
|
||||||
catch (std::exception &e) {
|
catch (std::exception &e) {
|
||||||
realm->cancel_transaction();
|
realm->cancel_transaction();
|
||||||
throw e;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
realm->commit_transaction();
|
realm->commit_transaction();
|
||||||
|
@ -46,6 +46,8 @@ class RealmObject {
|
|||||||
static void get_property(ContextType, ObjectType, const String &, ReturnValue &);
|
static void get_property(ContextType, ObjectType, const String &, ReturnValue &);
|
||||||
static bool set_property(ContextType, ObjectType, const String &, ValueType);
|
static bool set_property(ContextType, ObjectType, const String &, ValueType);
|
||||||
static std::vector<String> get_property_names(ContextType, ObjectType);
|
static std::vector<String> get_property_names(ContextType, ObjectType);
|
||||||
|
|
||||||
|
static void is_valid(ContextType, ObjectType, size_t, const ValueType [], ReturnValue &);
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
@ -59,8 +61,17 @@ struct RealmObjectClass : ClassDefinition<T, realm::Object> {
|
|||||||
wrap<RealmObject::set_property>,
|
wrap<RealmObject::set_property>,
|
||||||
wrap<RealmObject::get_property_names>,
|
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>
|
template<typename T>
|
||||||
typename T::Object RealmObject<T>::create_instance(ContextType ctx, realm::Object &realm_object) {
|
typename T::Object RealmObject<T>::create_instance(ContextType ctx, realm::Object &realm_object) {
|
||||||
static String prototype_string = "prototype";
|
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) { \
|
static return_t validated_to_##type(ContextType ctx, const ValueType &value, const char *name = nullptr) { \
|
||||||
if (!is_##type(ctx, value)) { \
|
if (!is_##type(ctx, value)) { \
|
||||||
std::string prefix = name ? std::string("'") + name + "'" : "JS 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); \
|
return to_##type(ctx, value); \
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,8 @@ namespace realm {
|
|||||||
const ObjectSchema &get_object_schema() { return *m_object_schema; }
|
const ObjectSchema &get_object_schema() { return *m_object_schema; }
|
||||||
Row row() { return m_row; }
|
Row row() { return m_row; }
|
||||||
|
|
||||||
|
bool is_valid() const { return m_row.is_attached(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SharedRealm m_realm;
|
SharedRealm m_realm;
|
||||||
const ObjectSchema *m_object_schema;
|
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);
|
inline void set_property_value_impl(ContextType ctx, const Property &property, ValueType value, bool try_update);
|
||||||
template<typename ValueType, typename ContextType>
|
template<typename ValueType, typename ContextType>
|
||||||
inline ValueType get_property_value_impl(ContextType ctx, const Property &property);
|
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"); }
|
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
|
class InvalidPropertyException : public std::runtime_error
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -161,6 +172,8 @@ namespace realm {
|
|||||||
{
|
{
|
||||||
using Accessor = NativeAccessor<ValueType, ContextType>;
|
using Accessor = NativeAccessor<ValueType, ContextType>;
|
||||||
|
|
||||||
|
verify_attached();
|
||||||
|
|
||||||
if (!m_realm->is_in_transaction()) {
|
if (!m_realm->is_in_transaction()) {
|
||||||
throw MutationOutsideTransactionException("Can only set property values within a transaction.");
|
throw MutationOutsideTransactionException("Can only set property values within a transaction.");
|
||||||
}
|
}
|
||||||
@ -225,6 +238,8 @@ namespace realm {
|
|||||||
{
|
{
|
||||||
using Accessor = NativeAccessor<ValueType, ContextType>;
|
using Accessor = NativeAccessor<ValueType, ContextType>;
|
||||||
|
|
||||||
|
verify_attached();
|
||||||
|
|
||||||
size_t column = property.table_column;
|
size_t column = property.table_column;
|
||||||
if (property.is_nullable && m_row.is_null(column)) {
|
if (property.is_nullable && m_row.is_null(column)) {
|
||||||
return Accessor::null_value(ctx);
|
return Accessor::null_value(ctx);
|
||||||
@ -325,6 +340,14 @@ namespace realm {
|
|||||||
return object;
|
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
|
// List implementation
|
||||||
//
|
//
|
||||||
|
@ -431,5 +431,29 @@ module.exports = BaseTest.extend({
|
|||||||
object.dataCol = [1];
|
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() {
|
testResultsSubscript: function() {
|
||||||
var realm = new Realm({schema: [schemas.PersonObject]});
|
var realm = new Realm({schema: [schemas.PersonObject]});
|
||||||
|
TestCase.assertEqual(realm.objects('PersonObject')[0], undefined);
|
||||||
|
|
||||||
realm.write(function() {
|
realm.write(function() {
|
||||||
realm.create('PersonObject', {name: 'name1', age: 1});
|
realm.create('PersonObject', {name: 'name1', age: 1});
|
||||||
realm.create('PersonObject', {name: 'name2', age: 2});
|
realm.create('PersonObject', {name: 'name2', age: 2});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user