diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cde8d66..dab2431f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,13 +4,14 @@ X.Y.Z Release notes * None ### Enhancements +* Added methods `Realm.beginTransaction()`, `Realm.commitTransaction()`, `Realm.cancelTransaction()` to manually control write transactions. +* Added property `Realm.isInTransaction` which indicates if write transaction is in progress. * Added `shouldCompactOnLaunch` to configuration (#507). * Added `Realm.compact()` for manually compacting Realm files. ### Bug fixes * None - 1.10.3 Release notes (2017-8-16) ============================================================= ### Breaking changes @@ -20,7 +21,7 @@ X.Y.Z Release notes * None ### Bug fixes -* none +* None 1.10.2 Release notes (2017-8-16) diff --git a/docs/realm.js b/docs/realm.js index 3f5fbd99..a4d667aa 100644 --- a/docs/realm.js +++ b/docs/realm.js @@ -64,6 +64,14 @@ class Realm { */ get schemaVersion() {} + /** + * Indicates if this Realm is in a write transaction. + * @type {boolean} + * @readonly + * @since 1.10.3 + */ + get isInTransaction() {} + /** * Gets the sync session if this is a synced Realm * @type {Session} @@ -184,6 +192,22 @@ class Realm { write(callback) {} /** + * Initiate a write transaction. + * @throws {Error} When already in write transaction + */ + beginTransaction() {} + + /** + * Commit a write transaction. + */ + commitTransaction() {} + + /** + * Cancel a write transaction. + */ + cancelTransaction() {} + + /* * Replaces all string columns in this Realm with a string enumeration column and compacts the * database file. * diff --git a/lib/browser/index.js b/lib/browser/index.js index 8f6ef684..ccecf45e 100644 --- a/lib/browser/index.js +++ b/lib/browser/index.js @@ -58,6 +58,7 @@ function setupRealm(realm, realmId) { 'schema', 'schemaVersion', 'syncSession', + 'isInTransaction', ].forEach((name) => { Object.defineProperty(realm, name, {get: util.getterForProperty(name)}); }); @@ -133,6 +134,9 @@ util.createMethods(Realm.prototype, objectTypes.REALM, [ 'deleteAll', 'write', 'compact', + 'beginTransaction', + 'commitTransaction', + 'cancelTransaction', ], true); const Sync = { diff --git a/lib/index.d.ts b/lib/index.d.ts index 663fe621..227f82d5 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -358,6 +358,7 @@ declare class Realm { readonly readOnly: boolean; readonly schema: Realm.ObjectSchema[]; readonly schemaVersion: number; + readonly isInTransaction: boolean; readonly syncSession: Realm.Sync.Session | null; @@ -454,6 +455,21 @@ declare class Realm { */ write(callback: () => void): void; + /** + * @returns void + */ + beginTransaction(): void; + + /** + * @returns void + */ + commitTransaction(): void; + + /** + * @returns void + */ + cancelTransaction(): void; + /** * @returns boolean */ diff --git a/src/js_realm.hpp b/src/js_realm.hpp index a54dfb7e..c9702575 100644 --- a/src/js_realm.hpp +++ b/src/js_realm.hpp @@ -171,6 +171,9 @@ public: static void delete_one(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); static void delete_all(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); static void write(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); + static void begin_transaction(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue&); + static void commit_transaction(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue&); + static void cancel_transaction(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue&); static void add_listener(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); static void wait_for_download_completion(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); static void remove_listener(ContextType, FunctionType, ObjectType, size_t, const ValueType[], ReturnValue &); @@ -185,6 +188,7 @@ public: static void get_schema_version(ContextType, ObjectType, ReturnValue &); static void get_schema(ContextType, ObjectType, ReturnValue &); static void get_read_only(ContextType, ObjectType, ReturnValue &); + static void get_is_in_transaction(ContextType, ObjectType, ReturnValue &); #if REALM_ENABLE_SYNC static void get_sync_session(ContextType, ObjectType, ReturnValue &); #endif @@ -221,6 +225,9 @@ public: {"delete", wrap}, {"deleteAll", wrap}, {"write", wrap}, + {"beginTransaction", wrap}, + {"commitTransaction", wrap}, + {"cancelTransaction", wrap}, {"addListener", wrap}, {"removeListener", wrap}, {"removeAllListeners", wrap}, @@ -234,6 +241,7 @@ public: {"schemaVersion", {wrap, nullptr}}, {"schema", {wrap, nullptr}}, {"readOnly", {wrap, nullptr}}, + {"isInTransaction", {wrap, nullptr}}, #if REALM_ENABLE_SYNC {"syncSession", {wrap, nullptr}}, #endif @@ -558,6 +566,11 @@ void RealmClass::get_read_only(ContextType ctx, ObjectType object, ReturnValu return_value.set(get_internal>(object)->get()->config().read_only()); } +template +void RealmClass::get_is_in_transaction(ContextType ctx, ObjectType object, ReturnValue &return_value) { + return_value.set(get_internal>(object)->get()->is_in_transaction()); +} + #if REALM_ENABLE_SYNC template void RealmClass::get_sync_session(ContextType ctx, ObjectType object, ReturnValue &return_value) { @@ -811,6 +824,30 @@ void RealmClass::write(ContextType ctx, FunctionType, ObjectType this_object, realm->commit_transaction(); } +template +void RealmClass::begin_transaction(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { + validate_argument_count(argc, 0); + + SharedRealm realm = *get_internal>(this_object); + realm->begin_transaction(); +} + +template +void RealmClass::commit_transaction(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { + validate_argument_count(argc, 0); + + SharedRealm realm = *get_internal>(this_object); + realm->commit_transaction(); +} + +template +void RealmClass::cancel_transaction(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { + validate_argument_count(argc, 0); + + SharedRealm realm = *get_internal>(this_object); + realm->cancel_transaction(); +} + template void RealmClass::add_listener(ContextType ctx, FunctionType, ObjectType this_object, size_t argc, const ValueType arguments[], ReturnValue &return_value) { validate_argument_count(argc, 2); diff --git a/tests/js/realm-tests.js b/tests/js/realm-tests.js index 67749dbd..4a66bc5c 100644 --- a/tests/js/realm-tests.js +++ b/tests/js/realm-tests.js @@ -945,6 +945,37 @@ module.exports = { TestCase.assertTrue(realm.empty); }, + testManualTransaction: function() { + const realm = new Realm({schema: [schemas.TestObject]}); + TestCase.assertTrue(realm.empty); + + realm.beginTransaction(); + realm.create('TestObject', {doubleCol: 3.1415}); + realm.commitTransaction(); + + TestCase.assertEqual(realm.objects('TestObject').length, 1); + }, + + testCancelTransaction: function() { + const realm = new Realm({schema: [schemas.TestObject]}); + TestCase.assertTrue(realm.empty); + + realm.beginTransaction(); + realm.create('TestObject', {doubleCol: 3.1415}); + realm.cancelTransaction(); + + TestCase.assertTrue(realm.empty); + }, + + testIsInTransaction: function() { + const realm = new Realm({schema: [schemas.TestObject]}); + TestCase.assertTrue(!realm.isInTransaction); + realm.beginTransaction(); + TestCase.assertTrue(realm.isInTransaction); + realm.cancelTransaction(); + TestCase.assertTrue(!realm.isInTransaction); + }, + testCompact: function() { var wasCalled = false; const count = 1000;