Browse Source

[options] small refactory to allow implement custom serializers as db, registry etc

Exilon 3 years ago
parent
commit
70a4fa5d9e

+ 28 - 21
Quick.Options.Serializer.Json.pas

@@ -45,25 +45,32 @@ uses
 
 
 type
 type
 
 
-  TJsonOptionsSerializer = class(TOptionsSerializer)
+  TJsonOptionsSerializer = class(TOptionsFileSerializer)
   private
   private
     fSerializer : TRTTIJson;
     fSerializer : TRTTIJson;
-    function ParseFile(const aFilename : string; out aJsonObj : TJsonObject) : Boolean;
+    function ParseFile(out aJsonObj : TJsonObject) : Boolean;
   public
   public
-    constructor Create;
+    constructor Create(const aFilename : string);
     destructor Destroy; override;
     destructor Destroy; override;
-    function Load(const aFilename : string; aSections : TSectionList; aFailOnSectionNotExists : Boolean) : Boolean; override;
-    function LoadSection(const aFilename : string; aSections : TSectionList; aOptions: TOptions) : Boolean; override;
-    procedure Save(const aFilename : string; aSections : TSectionList); override;
-    function GetFileSectionNames(const aFilename : string; out oSections : TArray<string>) : Boolean; override;
+    function Load(aSections : TSectionList; aFailOnSectionNotExists : Boolean) : Boolean; override;
+    function LoadSection(aSections : TSectionList; aOptions: TOptions) : Boolean; override;
+    procedure Save(aSections : TSectionList); override;
+    function GetFileSectionNames(out oSections : TArray<string>) : Boolean; override;
+    function ConfigExists : Boolean; override;
   end;
   end;
 
 
 implementation
 implementation
 
 
 { TJsonOptionsSerializer }
 { TJsonOptionsSerializer }
 
 
-constructor TJsonOptionsSerializer.Create;
+function TJsonOptionsSerializer.ConfigExists: Boolean;
 begin
 begin
+  Result := FileExists(Filename);
+end;
+
+constructor TJsonOptionsSerializer.Create(const aFilename : string);
+begin
+  Filename := aFilename;
   fSerializer := TRTTIJson.Create(TSerializeLevel.slPublishedProperty,True);
   fSerializer := TRTTIJson.Create(TSerializeLevel.slPublishedProperty,True);
 end;
 end;
 
 
@@ -73,14 +80,14 @@ begin
   inherited;
   inherited;
 end;
 end;
 
 
-function TJsonOptionsSerializer.GetFileSectionNames(const aFilename: string; out oSections: TArray<string>): Boolean;
+function TJsonOptionsSerializer.GetFileSectionNames(out oSections: TArray<string>): Boolean;
 var
 var
   json : TJsonObject;
   json : TJsonObject;
   i : Integer;
   i : Integer;
 begin
 begin
   Result := False;
   Result := False;
   json := nil;
   json := nil;
-  if ParseFile(aFilename,json) then
+  if ParseFile(json) then
   begin
   begin
     try
     try
       for i := 0 to json.Count - 1 do
       for i := 0 to json.Count - 1 do
@@ -94,25 +101,25 @@ begin
   end;
   end;
 end;
 end;
 
 
-function TJsonOptionsSerializer.ParseFile(const aFilename : string; out aJsonObj : TJsonObject) : Boolean;
+function TJsonOptionsSerializer.ParseFile(out aJsonObj : TJsonObject) : Boolean;
 var
 var
   fileoptions : string;
   fileoptions : string;
 begin
 begin
   aJsonObj := nil;
   aJsonObj := nil;
-  if FileExists(aFilename) then
+  if FileExists(Filename) then
   begin
   begin
     {$IFDEF DELPHIRX102_UP}
     {$IFDEF DELPHIRX102_UP}
-    fileoptions := TFile.ReadAllText(aFilename,TEncoding.UTF8);
+    fileoptions := TFile.ReadAllText(Filename,TEncoding.UTF8);
     {$ELSE}
     {$ELSE}
-    fileoptions := TFile.ReadAllText(aFilename);
+    fileoptions := TFile.ReadAllText(fFilename);
     {$ENDIF}
     {$ENDIF}
     aJsonObj := TJsonObject.ParseJSONValue(fileoptions) as TJsonObject;
     aJsonObj := TJsonObject.ParseJSONValue(fileoptions) as TJsonObject;
-    if aJsonObj = nil then raise EOptionLoadError.CreateFmt('Config file "%s" is damaged or not well-formed Json format!',[ExtractFileName(aFilename)]);
+    if aJsonObj = nil then raise EOptionLoadError.CreateFmt('Config file "%s" is damaged or not well-formed Json format!',[ExtractFileName(Filename)]);
   end;
   end;
   Result := aJsonObj <> nil;
   Result := aJsonObj <> nil;
 end;
 end;
 
 
-function TJsonOptionsSerializer.Load(const aFilename : string; aSections : TSectionList; aFailOnSectionNotExists : Boolean) : Boolean;
+function TJsonOptionsSerializer.Load(aSections : TSectionList; aFailOnSectionNotExists : Boolean) : Boolean;
 var
 var
   option : TOptions;
   option : TOptions;
   json : TJsonObject;
   json : TJsonObject;
@@ -120,7 +127,7 @@ var
   found : Integer;
   found : Integer;
 begin
 begin
   Result := False;
   Result := False;
-  if ParseFile(aFilename,json) then
+  if ParseFile(json) then
   begin
   begin
     found := 0;
     found := 0;
     try
     try
@@ -154,14 +161,14 @@ begin
   end;
   end;
 end;
 end;
 
 
-function TJsonOptionsSerializer.LoadSection(const aFilename : string; aSections : TSectionList; aOptions: TOptions) : Boolean;
+function TJsonOptionsSerializer.LoadSection(aSections : TSectionList; aOptions: TOptions) : Boolean;
 var
 var
   json : TJsonObject;
   json : TJsonObject;
   jpair : TJSONPair;
   jpair : TJSONPair;
 begin
 begin
   Result := False;
   Result := False;
   //read option file
   //read option file
-  if ParseFile(aFilename,json) then
+  if ParseFile(json) then
   begin
   begin
     try
     try
       jpair := fSerializer.GetJsonPairByName(json,aOptions.Name);
       jpair := fSerializer.GetJsonPairByName(json,aOptions.Name);
@@ -179,7 +186,7 @@ begin
   end;
   end;
 end;
 end;
 
 
-procedure TJsonOptionsSerializer.Save(const aFilename : string; aSections : TSectionList);
+procedure TJsonOptionsSerializer.Save(aSections : TSectionList);
 var
 var
   option : TOptions;
   option : TOptions;
   fileoptions : string;
   fileoptions : string;
@@ -200,7 +207,7 @@ begin
       end;
       end;
     end;
     end;
     fileoptions := TJsonUtils.JsonFormat(json.ToJSON);
     fileoptions := TJsonUtils.JsonFormat(json.ToJSON);
-    if not fileoptions.IsEmpty then TFile.WriteAllText(aFilename,fileoptions);
+    if not fileoptions.IsEmpty then TFile.WriteAllText(Filename,fileoptions);
   finally
   finally
     json.Free;
     json.Free;
   end;
   end;

+ 28 - 21
Quick.Options.Serializer.Yaml.pas

@@ -44,25 +44,32 @@ uses
 
 
 type
 type
 
 
-  TYamlOptionsSerializer = class(TOptionsSerializer)
+  TYamlOptionsSerializer = class(TOptionsFileSerializer)
   private
   private
     fSerializer : TRTTIYaml;
     fSerializer : TRTTIYaml;
-    function ParseFile(const aFilename : string; out aYamlObj : TYamlObject) : Boolean;
+    function ParseFile(out aYamlObj : TYamlObject) : Boolean;
   public
   public
-    constructor Create;
+    constructor Create(const aFilename : string);
     destructor Destroy; override;
     destructor Destroy; override;
-    function Load(const aFilename : string; aSections : TSectionList; aFailOnSectionNotExists : Boolean) : Boolean; override;
-    function LoadSection(const aFilename : string; aSections : TSectionList; aOptions: TOptions) : Boolean; override;
-    procedure Save(const aFilename : string; aSections : TSectionList); override;
-    function GetFileSectionNames(const aFilename : string; out oSections : TArray<string>) : Boolean; override;
+    function Load(aSections : TSectionList; aFailOnSectionNotExists : Boolean) : Boolean; override;
+    function LoadSection(aSections : TSectionList; aOptions: TOptions) : Boolean; override;
+    procedure Save(aSections : TSectionList); override;
+    function GetFileSectionNames(out oSections : TArray<string>) : Boolean; override;
+    function ConfigExists : Boolean; override;
   end;
   end;
 
 
 implementation
 implementation
 
 
 { TYamlOptionsSerializer }
 { TYamlOptionsSerializer }
 
 
-constructor TYamlOptionsSerializer.Create;
+function TYamlOptionsSerializer.ConfigExists: Boolean;
 begin
 begin
+  Result := FileExists(Filename);
+end;
+
+constructor TYamlOptionsSerializer.Create(const aFilename : string);
+begin
+  Filename := aFilename;
   fSerializer := TRTTIYaml.Create(TSerializeLevel.slPublishedProperty,True);
   fSerializer := TRTTIYaml.Create(TSerializeLevel.slPublishedProperty,True);
 end;
 end;
 
 
@@ -72,14 +79,14 @@ begin
   inherited;
   inherited;
 end;
 end;
 
 
-function TYamlOptionsSerializer.GetFileSectionNames(const aFilename : string; out oSections : TArray<string>) : Boolean;
+function TYamlOptionsSerializer.GetFileSectionNames(out oSections : TArray<string>) : Boolean;
 var
 var
   yaml : TYamlObject;
   yaml : TYamlObject;
   i : Integer;
   i : Integer;
 begin
 begin
   Result := False;
   Result := False;
   yaml := nil;
   yaml := nil;
-  if ParseFile(aFilename,yaml) then
+  if ParseFile(yaml) then
   begin
   begin
     try
     try
       for i := 0 to yaml.Count - 1 do
       for i := 0 to yaml.Count - 1 do
@@ -93,23 +100,23 @@ begin
   end;
   end;
 end;
 end;
 
 
-function TYamlOptionsSerializer.ParseFile(const aFilename : string; out aYamlObj : TYamlObject) : Boolean;
+function TYamlOptionsSerializer.ParseFile(out aYamlObj : TYamlObject) : Boolean;
 var
 var
   fileoptions : string;
   fileoptions : string;
 begin
 begin
   aYamlObj := nil;
   aYamlObj := nil;
-  if FileExists(aFilename) then
+  if FileExists(Filename) then
   begin
   begin
-    fileoptions := TFile.ReadAllText(aFilename,TEncoding.UTF8);
-    if fileoptions.IsEmpty then EOptionLoadError.CreateFmt('Config file "%s" is empty!',[ExtractFileName(aFilename)]);
+    fileoptions := TFile.ReadAllText(Filename,TEncoding.UTF8);
+    if fileoptions.IsEmpty then EOptionLoadError.CreateFmt('Config file "%s" is empty!',[ExtractFileName(Filename)]);
 
 
     aYamlObj := TYamlObject.ParseYAMLValue(fileoptions) as TYamlObject;
     aYamlObj := TYamlObject.ParseYAMLValue(fileoptions) as TYamlObject;
-    if aYamlObj = nil then raise EOptionLoadError.CreateFmt('Config file "%s" is damaged or not well-formed Yaml format!',[ExtractFileName(aFilename)]);
+    if aYamlObj = nil then raise EOptionLoadError.CreateFmt('Config file "%s" is damaged or not well-formed Yaml format!',[ExtractFileName(Filename)]);
   end;
   end;
   Result := aYamlObj <> nil;
   Result := aYamlObj <> nil;
 end;
 end;
 
 
-function TYamlOptionsSerializer.Load(const aFilename : string; aSections : TSectionList; aFailOnSectionNotExists : Boolean) : Boolean;
+function TYamlOptionsSerializer.Load(aSections : TSectionList; aFailOnSectionNotExists : Boolean) : Boolean;
 var
 var
   option : TOptions;
   option : TOptions;
   yaml : TYamlObject;
   yaml : TYamlObject;
@@ -118,7 +125,7 @@ var
 begin
 begin
   Result := False;
   Result := False;
   //read option file
   //read option file
-  if ParseFile(aFilename,yaml) then
+  if ParseFile(yaml) then
   begin
   begin
     found := 0;
     found := 0;
     try
     try
@@ -152,14 +159,14 @@ begin
   end;
   end;
 end;
 end;
 
 
-function TYamlOptionsSerializer.LoadSection(const aFilename : string; aSections : TSectionList; aOptions: TOptions) : Boolean;
+function TYamlOptionsSerializer.LoadSection(aSections : TSectionList; aOptions: TOptions) : Boolean;
 var
 var
   yaml : TYamlObject;
   yaml : TYamlObject;
   ypair : TYamlPair;
   ypair : TYamlPair;
 begin
 begin
   Result := False;
   Result := False;
   //read option file
   //read option file
-  if ParseFile(aFilename,yaml) then
+  if ParseFile(yaml) then
   begin
   begin
     try
     try
       ypair := fSerializer.GetYamlPairByName(yaml,aOptions.Name);
       ypair := fSerializer.GetYamlPairByName(yaml,aOptions.Name);
@@ -177,7 +184,7 @@ begin
   end;
   end;
 end;
 end;
 
 
-procedure TYamlOptionsSerializer.Save(const aFilename : string; aSections : TSectionList);
+procedure TYamlOptionsSerializer.Save(aSections : TSectionList);
 var
 var
   option : TOptions;
   option : TOptions;
   fileoptions : string;
   fileoptions : string;
@@ -198,7 +205,7 @@ begin
       end;
       end;
     end;
     end;
     fileoptions := yaml.ToYaml;
     fileoptions := yaml.ToYaml;
-    if not fileoptions.IsEmpty then TFile.WriteAllText(aFilename,fileoptions);
+    if not fileoptions.IsEmpty then TFile.WriteAllText(Filename,fileoptions);
   finally
   finally
     yaml.Free;
     yaml.Free;
   end;
   end;

+ 139 - 79
Quick.Options.pas

@@ -149,18 +149,36 @@ type
 
 
   IOptionsSerializer = interface
   IOptionsSerializer = interface
   ['{7DECE203-4AAE-4C9D-86C8-B3D583DF7C8B}']
   ['{7DECE203-4AAE-4C9D-86C8-B3D583DF7C8B}']
-    function Load(const aFilename : string; aSections : TSectionList; aFailOnSectionNotExists : Boolean) : Boolean;
-    function LoadSection(const aFilename : string; aSections : TSectionList; aOptions: TOptions) : Boolean;
-    procedure Save(const aFilename : string; aSections : TSectionList);
-    function GetFileSectionNames(const aFilename : string; out oSections : TArray<string>) : Boolean;
+    function Load(aSections : TSectionList; aFailOnSectionNotExists : Boolean) : Boolean;
+    function LoadSection(aSections : TSectionList; aOptions: TOptions) : Boolean;
+    procedure Save(aSections : TSectionList);
+    function GetFileSectionNames(out oSections : TArray<string>) : Boolean;
+    function ConfigExists : Boolean;
+  end;
+
+  IFileOptionsSerializer = interface(IOptionsSerializer)
+  ['{3417B142-2879-4DA6-86CA-19F0F427A92C}']
+    function GetFileName : string;
+    procedure SetFileName(const aFilename : string);
+    property Filename : string read GetFilename write SetFilename;
   end;
   end;
 
 
   TOptionsSerializer = class(TInterfacedObject,IOptionsSerializer)
   TOptionsSerializer = class(TInterfacedObject,IOptionsSerializer)
   public
   public
-    function Load(const aFilename : string; aSections : TSectionList; aFailOnSectionNotExists : Boolean) : Boolean; virtual; abstract;
-    function LoadSection(const aFilename : string; aSections : TSectionList; aOptions: TOptions) : Boolean; virtual; abstract;
-    procedure Save(const aFilename : string; aSections : TSectionList); virtual; abstract;
-    function GetFileSectionNames(const aFilename : string; out oSections : TArray<string>) : Boolean; virtual; abstract;
+    function Load(aSections : TSectionList; aFailOnSectionNotExists : Boolean) : Boolean; virtual; abstract;
+    function LoadSection(aSections : TSectionList; aOptions: TOptions) : Boolean; virtual; abstract;
+    procedure Save(aSections : TSectionList); virtual; abstract;
+    function GetFileSectionNames(out oSections : TArray<string>) : Boolean; virtual; abstract;
+    function ConfigExists : Boolean; virtual; abstract;
+  end;
+
+  TOptionsFileSerializer = class(TOptionsSerializer,IFileOptionsSerializer)
+  private
+    fFilename : string;
+    function GetFileName : string;
+    procedure SetFileName(const aFilename : string);
+  public
+    property Filename : string read GetFilename write SetFilename;
   end;
   end;
 
 
   TFileModifiedEvent = reference to procedure;
   TFileModifiedEvent = reference to procedure;
@@ -177,34 +195,24 @@ type
 
 
   TOptionsContainer = class(TInterfacedObject,IOptionsContainer)
   TOptionsContainer = class(TInterfacedObject,IOptionsContainer)
   private
   private
-    fFilename : string;
     fSerializer : IOptionsSerializer;
     fSerializer : IOptionsSerializer;
     fSections : TSectionList;
     fSections : TSectionList;
-    fFileMonitor : TFileMonitor;
-    fOnFileModified : TFileModifiedEvent;
     fLoaded : Boolean;
     fLoaded : Boolean;
-    fReloadIfFileChanged : Boolean;
     fOnConfigLoaded : TLoadConfigEvent;
     fOnConfigLoaded : TLoadConfigEvent;
     fOnConfigReloaded : TLoadConfigEvent;
     fOnConfigReloaded : TLoadConfigEvent;
-    procedure CreateFileMonitor;
-    procedure FileModifiedNotify(MonitorNotify : TMonitorNotify);
-    procedure SetReloadIfFileChanged(const Value: Boolean);
     function GetOptions(aOptionClass : TOptionsClass): TOptions; overload;
     function GetOptions(aOptionClass : TOptionsClass): TOptions; overload;
     function GetOptions(aIndex : Integer) : TOptions; overload;
     function GetOptions(aIndex : Integer) : TOptions; overload;
     function GetSection(aOptionsSection : TOptionsClass; var vOptions : TOptions) : Boolean; overload;
     function GetSection(aOptionsSection : TOptionsClass; var vOptions : TOptions) : Boolean; overload;
   public
   public
-    constructor Create(const aFilename : string; aOptionsSerializer : IOptionsSerializer; aReloadIfFileChanged : Boolean = False);
+    constructor Create(aOptionsSerializer : IOptionsSerializer);
     destructor Destroy; override;
     destructor Destroy; override;
-    property FileName : string read fFilename write fFilename;
-    property ReloadIfFileChanged : Boolean read fReloadIfFileChanged write SetReloadIfFileChanged;
     property IsLoaded : Boolean read fLoaded;
     property IsLoaded : Boolean read fLoaded;
     function ExistsSection(aOption : TOptionsClass; const aSectionName : string = '') : Boolean; overload;
     function ExistsSection(aOption : TOptionsClass; const aSectionName : string = '') : Boolean; overload;
     function ExistsSection<T : TOptions>(const aSectionName : string = '') : Boolean; overload;
     function ExistsSection<T : TOptions>(const aSectionName : string = '') : Boolean; overload;
-    property OnFileModified : TFileModifiedEvent read fOnFileModified write fOnFileModified;
-    property OnConfigLoaded : TLoadConfigEvent read fOnConfigLoaded write fOnConfigLoaded;
-    property OnConfigReloaded : TLoadConfigEvent read fOnConfigReloaded write fOnConfigReloaded;
     property Items[aOptionClass : TOptionsClass] : TOptions read GetOptions; default;
     property Items[aOptionClass : TOptionsClass] : TOptions read GetOptions; default;
     property Items[aIndex : Integer] : TOptions read GetOptions; default;
     property Items[aIndex : Integer] : TOptions read GetOptions; default;
+    property OnConfigLoaded : TLoadConfigEvent read fOnConfigLoaded write fOnConfigLoaded;
+    property OnConfigReloaded : TLoadConfigEvent read fOnConfigReloaded write fOnConfigReloaded;
     function AddSection(aOption : TOptionsClass; const aSectionName : string = '') : TOptions; overload;
     function AddSection(aOption : TOptionsClass; const aSectionName : string = '') : TOptions; overload;
     function AddSection<T : TOptions>(const aSectionName : string = '') : TOptions<T>; overload;
     function AddSection<T : TOptions>(const aSectionName : string = '') : TOptions<T>; overload;
     procedure AddOption(aOption : TOptions);
     procedure AddOption(aOption : TOptions);
@@ -212,9 +220,28 @@ type
     function GetSection<T : TOptions>(const aSectionName : string = '') : T; overload;
     function GetSection<T : TOptions>(const aSectionName : string = '') : T; overload;
     function GetFileSectionNames(out oSections : TArray<string>) : Boolean;
     function GetFileSectionNames(out oSections : TArray<string>) : Boolean;
     function Count : Integer;
     function Count : Integer;
-    procedure Load(aFailOnSectionNotExists : Boolean = False);
+    procedure Load(aFailOnSectionNotExists : Boolean = False); virtual;
     procedure LoadSection(aOptions : TOptions);
     procedure LoadSection(aOptions : TOptions);
-    procedure Save;
+    procedure Save; virtual;
+  end;
+
+  TFileOptionsContainer = class(TOptionsContainer)
+  private
+    fFilename : string;
+    fFileMonitor : TFileMonitor;
+    fOnFileModified : TFileModifiedEvent;
+    fReloadIfFileChanged : Boolean;
+    procedure CreateFileMonitor;
+    procedure FileModifiedNotify(MonitorNotify : TMonitorNotify);
+    procedure SetReloadIfFileChanged(const Value: Boolean);
+    function GetFileSectionNames(out oSections: TArray<string>): Boolean;
+  public
+    constructor Create(aOptionsSerializer : IFileOptionsSerializer; aReloadIfFileChanged : Boolean = False);
+    destructor Destroy; override;
+    property FileName : string read fFilename;
+    property ReloadIfFileChanged : Boolean read fReloadIfFileChanged write SetReloadIfFileChanged;
+    property OnFileModified : TFileModifiedEvent read fOnFileModified write fOnFileModified;
+    procedure Save; override;
   end;
   end;
 
 
   IOptionsBuilder<T : TOptions> = interface
   IOptionsBuilder<T : TOptions> = interface
@@ -237,41 +264,7 @@ type
 
 
 implementation
 implementation
 
 
-{ TOptionsContainer }
-
-constructor TOptionsContainer.Create(const aFilename : string; aOptionsSerializer : IOptionsSerializer; aReloadIfFileChanged : Boolean = False);
-begin
-  fSerializer := aOptionsSerializer;
-  fSections := TSectionList.Create(False);
-  fFilename := aFilename;
-  fLoaded := False;
-  fReloadIfFileChanged := aReloadIfFileChanged;
-  if aReloadIfFileChanged then CreateFileMonitor;
-end;
-
-procedure TOptionsContainer.CreateFileMonitor;
-begin
-  fFileMonitor := TQuickFileMonitor.Create;
-  fFileMonitor.FileName := fFilename;
-  fFileMonitor.Interval := 2000;
-  fFileMonitor.Notifies := [TMonitorNotify.mnFileModified];
-  fFileMonitor.OnFileChange := FileModifiedNotify;
-  fFileMonitor.Enabled := True;
-end;
-
-destructor TOptionsContainer.Destroy;
-var
-  option : TOptions;
-begin
-  if Assigned(fFileMonitor) then fFileMonitor.Free;
-  fSerializer := nil;
-  for option in fSections do
-  begin
-    if option.RefCount = 0 then option.Free;
-  end;
-  fSections.Free;
-  inherited;
-end;
+{ TCustomOptionsContainer}
 
 
 function TOptionsContainer.ExistsSection(aOption: TOptionsClass;const aSectionName: string): Boolean;
 function TOptionsContainer.ExistsSection(aOption: TOptionsClass;const aSectionName: string): Boolean;
 var
 var
@@ -292,18 +285,6 @@ begin
   Result := GetSection<T>(aSectionName) <> nil;
   Result := GetSection<T>(aSectionName) <> nil;
 end;
 end;
 
 
-procedure TOptionsContainer.FileModifiedNotify(MonitorNotify: TMonitorNotify);
-begin
-  if MonitorNotify = TMonitorNotify.mnFileModified then
-  begin
-    if Assigned(fOnFileModified) then fOnFileModified;
-    if fReloadIfFileChanged then
-    begin
-      Load(False);
-    end;
-  end;
-end;
-
 procedure TOptionsContainer.AddOption(aOption: TOptions);
 procedure TOptionsContainer.AddOption(aOption: TOptions);
 begin
 begin
   if aOption.Name.IsEmpty then aOption.Name := Copy(aOption.ClassName,2,aOption.ClassName.Length);
   if aOption.Name.IsEmpty then aOption.Name := Copy(aOption.ClassName,2,aOption.ClassName.Length);
@@ -344,9 +325,29 @@ begin
   Result := fSections.Count;
   Result := fSections.Count;
 end;
 end;
 
 
-function TOptionsContainer.GetFileSectionNames(out oSections : TArray<string>) : Boolean;
+constructor TOptionsContainer.Create(aOptionsSerializer: IOptionsSerializer);
+begin
+  fSerializer := aOptionsSerializer;
+  fSections := TSectionList.Create(False);
+  fLoaded := False;
+end;
+
+destructor TOptionsContainer.Destroy;
+var
+  option : TOptions;
+begin
+  fSerializer := nil;
+  for option in fSections do
+  begin
+    if option.RefCount = 0 then option.Free;
+  end;
+  fSections.Free;
+  inherited;
+end;
+
+function TOptionsContainer.GetFileSectionNames(out oSections: TArray<string>): Boolean;
 begin
 begin
-  Result := fSerializer.GetFileSectionNames(fFilename,oSections);
+
 end;
 end;
 
 
 function TOptionsContainer.GetOptions(aIndex: Integer): TOptions;
 function TOptionsContainer.GetOptions(aIndex: Integer): TOptions;
@@ -407,9 +408,9 @@ procedure TOptionsContainer.Load(aFailOnSectionNotExists : Boolean = False);
 var
 var
   option : TOptions;
   option : TOptions;
 begin
 begin
-  if FileExists(fFilename) then
+  if fSerializer.ConfigExists then
   begin
   begin
-    if not fSerializer.Load(fFilename,fSections,aFailOnSectionNotExists) then Save;
+    if not fSerializer.Load(fSections,aFailOnSectionNotExists) then Save;
     if not fLoaded then
     if not fLoaded then
     begin
     begin
       fLoaded := True;
       fLoaded := True;
@@ -428,13 +429,27 @@ end;
 
 
 procedure TOptionsContainer.LoadSection(aOptions : TOptions);
 procedure TOptionsContainer.LoadSection(aOptions : TOptions);
 begin
 begin
-  if FileExists(fFilename) then
+  if fSerializer.ConfigExists then
   begin
   begin
-    if not fSerializer.LoadSection(fFilename,fSections,aOptions) then Save;
+    if not fSerializer.LoadSection(fSections,aOptions) then Save;
   end;
   end;
 end;
 end;
 
 
 procedure TOptionsContainer.Save;
 procedure TOptionsContainer.Save;
+begin
+  fSerializer.Save(fSections);
+end;
+
+{ TOptionsContainer }
+
+constructor TFileOptionsContainer.Create(aOptionsSerializer : IFileOptionsSerializer; aReloadIfFileChanged : Boolean = False);
+begin
+  inherited Create(aOptionsSerializer);
+  fFilename := aOptionsSerializer.Filename;
+  if aReloadIfFileChanged then CreateFileMonitor;
+end;
+
+procedure TFileOptionsContainer.Save;
 var
 var
   laststate : Boolean;
   laststate : Boolean;
 begin
 begin
@@ -445,17 +460,17 @@ begin
     fFileMonitor.Enabled := False;
     fFileMonitor.Enabled := False;
     try
     try
       //save config file
       //save config file
-      fSerializer.Save(fFilename,fSections);
+      inherited;
     finally
     finally
       //set last state
       //set last state
       Sleep(0);
       Sleep(0);
       fFileMonitor.Enabled := laststate;
       fFileMonitor.Enabled := laststate;
     end;
     end;
   end
   end
-  else fSerializer.Save(fFilename,fSections);
+  else inherited;
 end;
 end;
 
 
-procedure TOptionsContainer.SetReloadIfFileChanged(const Value: Boolean);
+procedure TFileOptionsContainer.SetReloadIfFileChanged(const Value: Boolean);
 begin
 begin
   if Value = fReloadIfFileChanged then Exit;
   if Value = fReloadIfFileChanged then Exit;
   fReloadIfFileChanged := Value;
   fReloadIfFileChanged := Value;
@@ -463,6 +478,39 @@ begin
   if fReloadIfFileChanged then CreateFileMonitor;
   if fReloadIfFileChanged then CreateFileMonitor;
 end;
 end;
 
 
+procedure TFileOptionsContainer.CreateFileMonitor;
+begin
+  fFileMonitor := TQuickFileMonitor.Create;
+  fFileMonitor.FileName := fFilename;
+  fFileMonitor.Interval := 2000;
+  fFileMonitor.Notifies := [TMonitorNotify.mnFileModified];
+  fFileMonitor.OnFileChange := FileModifiedNotify;
+  fFileMonitor.Enabled := True;
+end;
+
+destructor TFileOptionsContainer.Destroy;
+begin
+  if Assigned(fFileMonitor) then fFileMonitor.Free;
+  inherited;
+end;
+
+procedure TFileOptionsContainer.FileModifiedNotify(MonitorNotify: TMonitorNotify);
+begin
+  if MonitorNotify = TMonitorNotify.mnFileModified then
+  begin
+    if Assigned(fOnFileModified) then fOnFileModified;
+    if fReloadIfFileChanged then
+    begin
+      Load(False);
+    end;
+  end;
+end;
+
+function TFileOptionsContainer.GetFileSectionNames(out oSections : TArray<string>) : Boolean;
+begin
+  Result := fSerializer.GetFileSectionNames(oSections);
+end;
+
 { TOptions }
 { TOptions }
 
 
 function TOptions.ConfigureOptions<T>(aOptionsFunc: TConfigureOptionsProc<T>): IOptionsValidator;
 function TOptions.ConfigureOptions<T>(aOptionsFunc: TConfigureOptionsProc<T>): IOptionsValidator;
@@ -687,4 +735,16 @@ begin
   Result := fOptions;
   Result := fOptions;
 end;
 end;
 
 
+{ TOptionsFileSerializer }
+
+function TOptionsFileSerializer.GetFileName: string;
+begin
+  Result := fFilename;
+end;
+
+procedure TOptionsFileSerializer.SetFileName(const aFilename: string);
+begin
+  fFilename := aFilename;
+end;
+
 end.
 end.

+ 2 - 14
Quick.YAML.Serializer.pas

@@ -1,30 +1,22 @@
-{ ***************************************************************************
-
+{ ***************************************************************************
   Copyright (c) 2015-2021 Kike P�rez
   Copyright (c) 2015-2021 Kike P�rez
-
   Unit        : Quick.YAML.Serializer
   Unit        : Quick.YAML.Serializer
   Description : YAML Serializer
   Description : YAML Serializer
   Author      : Kike P�rez
   Author      : Kike P�rez
   Version     : 1.0
   Version     : 1.0
   Created     : 12/04/2019
   Created     : 12/04/2019
   Modified    : 05/08/2021
   Modified    : 05/08/2021
-
   This file is part of QuickLib: https://github.com/exilon/QuickLib
   This file is part of QuickLib: https://github.com/exilon/QuickLib
-
  ***************************************************************************
  ***************************************************************************
-
   Licensed under the Apache License, Version 2.0 (the "License");
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at
   You may obtain a copy of the License at
-
   http://www.apache.org/licenses/LICENSE-2.0
   http://www.apache.org/licenses/LICENSE-2.0
-
   Unless required by applicable law or agreed to in writing, software
   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   See the License for the specific language governing permissions and
   limitations under the License.
   limitations under the License.
-
  *************************************************************************** }
  *************************************************************************** }
 
 
 unit Quick.YAML.Serializer;
 unit Quick.YAML.Serializer;
@@ -1622,8 +1614,4 @@ end;
 {$ENDIF}
 {$ENDIF}
 
 
 
 
-end.
-
-
-
-
+end.

+ 2 - 2
samples/delphi/QuickOptions/Optionsdemo.dpr

@@ -63,7 +63,7 @@ type
   end;
   end;
 
 
 var
 var
-  Options : TOptionsContainer;
+  Options : TFileOptionsContainer;
   LoggingOptions : TLoggingOptions;
   LoggingOptions : TLoggingOptions;
   GlobalOptions : TGlobalOptions;
   GlobalOptions : TGlobalOptions;
   UIOptions : TUIOptions;
   UIOptions : TUIOptions;
@@ -84,7 +84,7 @@ end;
 begin
 begin
   try
   try
     ReportMemoryLeaksOnShutdown := True;
     ReportMemoryLeaksOnShutdown := True;
-    Options := TOptionsContainer.Create('.\options.conf',TJsonOptionsSerializer.Create,True);
+    Options := TFileOptionsContainer.Create(TJsonOptionsSerializer.Create('.\options.conf'),True);
     Options.OnFileModified := procedure
     Options.OnFileModified := procedure
                               begin
                               begin
                                 cout('Detected config file modification!',etWarning);
                                 cout('Detected config file modification!',etWarning);

File diff suppressed because it is too large
+ 8 - 27
samples/delphi/QuickOptions/Optionsdemo.dproj


Some files were not shown because too many files changed in this diff