diff --git a/sources/Data/Database/DBAPI.uc b/sources/Data/Database/DBAPI.uc index 9e428ce..71923e1 100644 --- a/sources/Data/Database/DBAPI.uc +++ b/sources/Data/Database/DBAPI.uc @@ -37,10 +37,12 @@ private final function CreateLocalDBMapIfMissing() /** * Loads database based on the link. * - * Links have the form of ":", followed by the JSON pointer - * (possibly empty one) to the object inside it. "" can be either "local" - * or "remote" and "" refers to the database that we are expected - * to load. This name has to consist of numbers and latin letters only. + * Links have the form of ":" (or, optionally, "[]:"), + * followed by the JSON pointer (possibly empty one) to the object inside it. + * "" can be either "local" or "remote" and is necessary only when both + * local and remote database have the same name (which should be avoided). + * "" refers to the database that we are expected + * to load, it has to consist of numbers and latin letters only. * * @param databaseLink Link from which to extract database's name. * @return Database named "" of type "" from the `databaseLink`. @@ -55,14 +57,16 @@ public final function Database Load(Text databaseLink) } parser = _.text.Parse(databaseLink); // Only local DBs are supported for now! - parser.Match(P("local:")); + // So just consume this prefix, if it's present. + parser.Match(P("[local]")).Confirm(); + parser.R().MUntil(databaseName, _.text.GetCharacter(":")).MatchS(":"); if (!parser.Ok()) { parser.FreeSelf(); return none; } - parser.MUntil(databaseName, _.text.GetCharacter("/")); result = LoadLocal(databaseName); + parser.FreeSelf(); databaseName.FreeSelf(); return result; } @@ -70,14 +74,18 @@ public final function Database Load(Text databaseLink) /** * Extracts `JSONPointer` from the database path, given by `databaseLink`. * - * Links have the form of ":", followed by the JSON pointer - * (possibly empty one) to the object inside it. "" can be either "local" - * or "remote" and "" refers to the database that we are to use to - * get the data, specified in the link. + * Links have the form of ":" (or, optionally, "[]:"), + * followed by the JSON pointer (possibly empty one) to the object inside it. + * "" can be either "local" or "remote" and is necessary only when both + * local and remote database have the same name (which should be avoided). + * "" refers to the database that we are expected + * to load, it has to consist of numbers and latin letters only. * This method returns `JSONPointer` that comes after type-name pair. * * @param Link from which to extract `JSONPointer`. * @return `JSONPointer` from the database link. + * Guaranteed to not be `none` if provided argument `databaseLink` + * is not `none`. */ public final function JSONPointer GetPointer(Text databaseLink) { @@ -87,11 +95,11 @@ public final function JSONPointer GetPointer(Text databaseLink) if (databaseLink == none) { return none; } - slashIndex = databaseLink.IndexOf(P("/")); + slashIndex = databaseLink.IndexOf(P(":")); if (slashIndex < 0) { return JSONPointer(_.memory.Allocate(class'JSONPointer')); } - textPointer = databaseLink.Copy(slashIndex); + textPointer = databaseLink.Copy(slashIndex + 1); result = _.json.Pointer(textPointer); textPointer.FreeSelf(); return result; @@ -157,14 +165,27 @@ public final function LocalDatabaseInstance LoadLocal(Text databaseName) // No need to check `databaseName` for being valid, // since `Load()` will just return `none` if it is not. newConfig = class'LocalDatabase'.static.Load(databaseName); - if (newConfig == none) return none; - if (!newConfig.HasDefinedRoot()) return none; - + if (newConfig == none) { + return none; + } + if (!newConfig.HasDefinedRoot() && !newConfig.ShouldCreateIfMissing()) { + return none; + } newLocalDBInstance = LocalDatabaseInstance(_.memory.Allocate(localDBClass)); loadedLocalDatabases.SetItem(databaseName.Copy(), newLocalDBInstance); - rootRecordName = newConfig.GetRootName(); - rootRecord = class'DBRecord'.static - .LoadRecord(rootRecordName, databaseName); + if (newConfig.HasDefinedRoot()) + { + rootRecordName = newConfig.GetRootName(); + rootRecord = class'DBRecord'.static + .LoadRecord(rootRecordName, databaseName); + } + else + { + rootRecord = class'DBRecord'.static.NewRecord(databaseName); + rootRecordName = _.text.FromString(string(rootRecord.name)); + newConfig.SetRootName(rootRecordName); + newConfig.Save(); + } newLocalDBInstance.Initialize(newConfig, rootRecord); _.memory.Free(rootRecordName); return newLocalDBInstance; diff --git a/sources/Data/Database/Local/LocalDatabase.uc b/sources/Data/Database/Local/LocalDatabase.uc index 868a716..b39ecd5 100644 --- a/sources/Data/Database/Local/LocalDatabase.uc +++ b/sources/Data/Database/Local/LocalDatabase.uc @@ -26,6 +26,7 @@ class LocalDatabase extends AcediaObject config(AcediaDB); var config private string root; +var config private bool createIfMissing; var config private bool enforceAcediaStructure; public final function Text GetPackageName() @@ -78,7 +79,7 @@ public final static function LocalDatabase Load(Text databaseName) /** * Updates `LocalDatabase` record inside it's config file. If caller * `LocalDatabase` does not have defined root `HasDefinedRoot() == none`, - * then this method will erase it's record from the config. + * then this method will erase its record from the config. */ public final function Save() { @@ -90,6 +91,11 @@ public final function Save() } } +public final function bool ShouldCreateIfMissing() +{ + return createIfMissing; +} + /** * Forgets all information stored in the caller `LocalDatabase` and erases it * from the config files. After this call, creating `LocalDatabase` object @@ -104,5 +110,6 @@ public final function DeleteSelf() defaultproperties { - enforceAcediaStructure = false + enforceAcediaStructure = false + createIfMissing = false } \ No newline at end of file diff --git a/sources/Data/Database/Tests/TEST_DatabaseCommon.uc b/sources/Data/Database/Tests/TEST_DatabaseCommon.uc index 1049e4e..63b7992 100644 --- a/sources/Data/Database/Tests/TEST_DatabaseCommon.uc +++ b/sources/Data/Database/Tests/TEST_DatabaseCommon.uc @@ -25,17 +25,20 @@ protected static function TESTS() local JSONPointer pointer; Context("Testing extracting `JSONPointer` from database link."); Issue("`JSONPointer` is incorrectly extracted."); - pointer = - __().db.GetPointer(__().text.FromString("local:default/huh/what/is/")); + pointer = __().db.GetPointer( + __().text.FromString("[local]default:/huh/what/is/")); TEST_ExpectNotNone(pointer); TEST_ExpectTrue(pointer.ToText().ToString() == "/huh/what/is/"); - pointer = __().db.GetPointer(__().text.FromString("remote:db")); + pointer = __().db.GetPointer(__().text.FromString("[remote]db:")); TEST_ExpectNotNone(pointer); TEST_ExpectTrue(pointer.ToText().ToString() == ""); - pointer = __().db.GetPointer(__().text.FromString("remote:")); + pointer = __().db.GetPointer(__().text.FromString("[remote]:")); TEST_ExpectNotNone(pointer); TEST_ExpectTrue(pointer.ToText().ToString() == ""); - pointer = __().db.GetPointer(__().text.FromString("/just/a/pointer")); + pointer = __().db.GetPointer(__().text.FromString("db:/just/a/pointer")); + TEST_ExpectNotNone(pointer); + TEST_ExpectTrue(pointer.ToText().ToString() == "/just/a/pointer"); + pointer = __().db.GetPointer(__().text.FromString(":/just/a/pointer")); TEST_ExpectNotNone(pointer); TEST_ExpectTrue(pointer.ToText().ToString() == "/just/a/pointer"); }