Browse Source

Change how local databases are loaded

This patch addresses issues with inability to define and create a new
database from scratch from configs, as well as invalid definition of
database links that required them to contain "/" character.
pull/8/head
Anton Tarasenko 3 years ago
parent
commit
ca21077fe1
  1. 51
      sources/Data/Database/DBAPI.uc
  2. 9
      sources/Data/Database/Local/LocalDatabase.uc
  3. 13
      sources/Data/Database/Tests/TEST_DatabaseCommon.uc

51
sources/Data/Database/DBAPI.uc

@ -37,10 +37,12 @@ private final function CreateLocalDBMapIfMissing()
/** /**
* Loads database based on the link. * Loads database based on the link.
* *
* Links have the form of "<type>:<db_name>", followed by the JSON pointer * Links have the form of "<db_name>:" (or, optionally, "[<type>]<db_name>:"),
* (possibly empty one) to the object inside it. "<type>" can be either "local" * followed by the JSON pointer (possibly empty one) to the object inside it.
* or "remote" and "<db_name>" refers to the database that we are expected * "<type>" can be either "local" or "remote" and is necessary only when both
* to load. This name has to consist of numbers and latin letters only. * local and remote database have the same name (which should be avoided).
* "<db_name>" 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. * @param databaseLink Link from which to extract database's name.
* @return Database named "<db_name>" of type "<type>" from the `databaseLink`. * @return Database named "<db_name>" of type "<type>" from the `databaseLink`.
@ -55,14 +57,16 @@ public final function Database Load(Text databaseLink)
} }
parser = _.text.Parse(databaseLink); parser = _.text.Parse(databaseLink);
// Only local DBs are supported for now! // 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()) if (!parser.Ok())
{ {
parser.FreeSelf(); parser.FreeSelf();
return none; return none;
} }
parser.MUntil(databaseName, _.text.GetCharacter("/"));
result = LoadLocal(databaseName); result = LoadLocal(databaseName);
parser.FreeSelf();
databaseName.FreeSelf(); databaseName.FreeSelf();
return result; return result;
} }
@ -70,14 +74,18 @@ public final function Database Load(Text databaseLink)
/** /**
* Extracts `JSONPointer` from the database path, given by `databaseLink`. * Extracts `JSONPointer` from the database path, given by `databaseLink`.
* *
* Links have the form of "<type>:<db_name>", followed by the JSON pointer * Links have the form of "<db_name>:" (or, optionally, "[<type>]<db_name>:"),
* (possibly empty one) to the object inside it. "<type>" can be either "local" * followed by the JSON pointer (possibly empty one) to the object inside it.
* or "remote" and "<db_name>" refers to the database that we are to use to * "<type>" can be either "local" or "remote" and is necessary only when both
* get the data, specified in the link. * local and remote database have the same name (which should be avoided).
* "<db_name>" 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. * This method returns `JSONPointer` that comes after type-name pair.
* *
* @param Link from which to extract `JSONPointer`. * @param Link from which to extract `JSONPointer`.
* @return `JSONPointer` from the database link. * @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) public final function JSONPointer GetPointer(Text databaseLink)
{ {
@ -87,11 +95,11 @@ public final function JSONPointer GetPointer(Text databaseLink)
if (databaseLink == none) { if (databaseLink == none) {
return none; return none;
} }
slashIndex = databaseLink.IndexOf(P("/")); slashIndex = databaseLink.IndexOf(P(":"));
if (slashIndex < 0) { if (slashIndex < 0) {
return JSONPointer(_.memory.Allocate(class'JSONPointer')); return JSONPointer(_.memory.Allocate(class'JSONPointer'));
} }
textPointer = databaseLink.Copy(slashIndex); textPointer = databaseLink.Copy(slashIndex + 1);
result = _.json.Pointer(textPointer); result = _.json.Pointer(textPointer);
textPointer.FreeSelf(); textPointer.FreeSelf();
return result; return result;
@ -157,14 +165,27 @@ public final function LocalDatabaseInstance LoadLocal(Text databaseName)
// No need to check `databaseName` for being valid, // No need to check `databaseName` for being valid,
// since `Load()` will just return `none` if it is not. // since `Load()` will just return `none` if it is not.
newConfig = class'LocalDatabase'.static.Load(databaseName); newConfig = class'LocalDatabase'.static.Load(databaseName);
if (newConfig == none) return none; if (newConfig == none) {
if (!newConfig.HasDefinedRoot()) return none; return none;
}
if (!newConfig.HasDefinedRoot() && !newConfig.ShouldCreateIfMissing()) {
return none;
}
newLocalDBInstance = LocalDatabaseInstance(_.memory.Allocate(localDBClass)); newLocalDBInstance = LocalDatabaseInstance(_.memory.Allocate(localDBClass));
loadedLocalDatabases.SetItem(databaseName.Copy(), newLocalDBInstance); loadedLocalDatabases.SetItem(databaseName.Copy(), newLocalDBInstance);
if (newConfig.HasDefinedRoot())
{
rootRecordName = newConfig.GetRootName(); rootRecordName = newConfig.GetRootName();
rootRecord = class'DBRecord'.static rootRecord = class'DBRecord'.static
.LoadRecord(rootRecordName, databaseName); .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); newLocalDBInstance.Initialize(newConfig, rootRecord);
_.memory.Free(rootRecordName); _.memory.Free(rootRecordName);
return newLocalDBInstance; return newLocalDBInstance;

9
sources/Data/Database/Local/LocalDatabase.uc

@ -26,6 +26,7 @@ class LocalDatabase extends AcediaObject
config(AcediaDB); config(AcediaDB);
var config private string root; var config private string root;
var config private bool createIfMissing;
var config private bool enforceAcediaStructure; var config private bool enforceAcediaStructure;
public final function Text GetPackageName() 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 * Updates `LocalDatabase` record inside it's config file. If caller
* `LocalDatabase` does not have defined root `HasDefinedRoot() == none`, * `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() 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 * Forgets all information stored in the caller `LocalDatabase` and erases it
* from the config files. After this call, creating `LocalDatabase` object * from the config files. After this call, creating `LocalDatabase` object
@ -105,4 +111,5 @@ public final function DeleteSelf()
defaultproperties defaultproperties
{ {
enforceAcediaStructure = false enforceAcediaStructure = false
createIfMissing = false
} }

13
sources/Data/Database/Tests/TEST_DatabaseCommon.uc

@ -25,17 +25,20 @@ protected static function TESTS()
local JSONPointer pointer; local JSONPointer pointer;
Context("Testing extracting `JSONPointer` from database link."); Context("Testing extracting `JSONPointer` from database link.");
Issue("`JSONPointer` is incorrectly extracted."); Issue("`JSONPointer` is incorrectly extracted.");
pointer = pointer = __().db.GetPointer(
__().db.GetPointer(__().text.FromString("local:default/huh/what/is/")); __().text.FromString("[local]default:/huh/what/is/"));
TEST_ExpectNotNone(pointer); TEST_ExpectNotNone(pointer);
TEST_ExpectTrue(pointer.ToText().ToString() == "/huh/what/is/"); 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_ExpectNotNone(pointer);
TEST_ExpectTrue(pointer.ToText().ToString() == ""); TEST_ExpectTrue(pointer.ToText().ToString() == "");
pointer = __().db.GetPointer(__().text.FromString("remote:")); pointer = __().db.GetPointer(__().text.FromString("[remote]:"));
TEST_ExpectNotNone(pointer); TEST_ExpectNotNone(pointer);
TEST_ExpectTrue(pointer.ToText().ToString() == ""); 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_ExpectNotNone(pointer);
TEST_ExpectTrue(pointer.ToText().ToString() == "/just/a/pointer"); TEST_ExpectTrue(pointer.ToText().ToString() == "/just/a/pointer");
} }

Loading…
Cancel
Save