mirror of
https://github.com/danieleteti/delphimvcframework.git
synced 2024-11-15 15:55:54 +01:00
Support for .env (WIP)
This commit is contained in:
parent
bb30db152d
commit
d892c21cc4
308
sources/MVCFramework.DotEnv.Parser.pas
Normal file
308
sources/MVCFramework.DotEnv.Parser.pas
Normal file
@ -0,0 +1,308 @@
|
||||
// *************************************************************************** }
|
||||
//
|
||||
// Delphi MVC Framework
|
||||
//
|
||||
// Copyright (c) 2010-2023 Daniele Teti and the DMVCFramework Team
|
||||
//
|
||||
// https://github.com/danieleteti/delphimvcframework
|
||||
//
|
||||
// ***************************************************************************
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// ***************************************************************************
|
||||
|
||||
unit MVCFramework.DotEnv.Parser;
|
||||
|
||||
interface
|
||||
|
||||
uses System.Generics.Collections, System.SysUtils;
|
||||
|
||||
type
|
||||
{$SCOPEDENUMS ON}
|
||||
TMVCDotEnvParserState = (FileThenEnv, EnvThenFile, OnlyFile, OnlyEnv);
|
||||
|
||||
TMVCDotEnvDictionary = class(TDictionary<String, String>)
|
||||
public
|
||||
constructor Create; virtual;
|
||||
end;
|
||||
|
||||
EMVCDotEnvParser = class(Exception)
|
||||
|
||||
end;
|
||||
|
||||
TLineBreakStyle = (MSWindows, Linux { MacOS too } );
|
||||
TStringQuotedStyle = (SingleQuoted, DoublyQuoted, UnQuoted);
|
||||
|
||||
{
|
||||
For Windows, it is CRLF
|
||||
For UNIX, it is LF
|
||||
For MAC (up through version 9) it was CR
|
||||
For MAC OS X, it is LF
|
||||
|
||||
https://en.wikipedia.org/wiki/Newline
|
||||
}
|
||||
|
||||
{
|
||||
https://pypi.org/project/python-dotenv/
|
||||
}
|
||||
|
||||
TMVCDotEnvParser = class
|
||||
private
|
||||
fCode: string;
|
||||
fCurrChar: Char;
|
||||
fIndex: Integer;
|
||||
fCurLine: Integer;
|
||||
fLineBreakStyle: TLineBreakStyle;
|
||||
fLineBreaksStyle: TLineBreakStyle;
|
||||
fSavedIndex: Integer;
|
||||
fCodeLength: Integer;
|
||||
function MatchIdentifier(out Value: String): Boolean;
|
||||
function MatchKey(out Token: String): Boolean;
|
||||
function MatchValue(out Token: String): Boolean;
|
||||
function MatchSymbol(const Symbol: Char): Boolean;
|
||||
procedure Check(Value: Boolean; Error: String = '');
|
||||
function MatchString(out Value: String): Boolean;
|
||||
procedure EatLineBreaks;
|
||||
procedure EatUpToLineBreak;
|
||||
function NextChar: Char;
|
||||
procedure EatSpaces;
|
||||
function DetectLineBreakStyle(Code: String): TLineBreakStyle;
|
||||
procedure MatchInLineComment;
|
||||
public
|
||||
constructor Create; virtual;
|
||||
destructor Destroy; override;
|
||||
procedure Parse(const EnvDictionay: TMVCDotEnvDictionary; const DotEnvCode: String);
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
System.IOUtils,
|
||||
System.TypInfo,
|
||||
System.Classes;
|
||||
|
||||
const
|
||||
LINE_BREAKS: array [TLineBreakStyle.MSWindows .. TLineBreakStyle.Linux] of AnsiString = (#13#10, #10);
|
||||
|
||||
{ TMVCDotEnvParser }
|
||||
|
||||
procedure TMVCDotEnvParser.Check(Value: Boolean; Error: String);
|
||||
begin
|
||||
if not Value then
|
||||
begin
|
||||
raise EMVCDotEnvParser.CreateFmt('Error: %s - got "%s" at line: %d - index: %d',
|
||||
[Error, fCurrChar, fCurLine, fIndex - fSavedIndex]);
|
||||
end;
|
||||
end;
|
||||
|
||||
constructor TMVCDotEnvParser.Create;
|
||||
begin
|
||||
inherited;
|
||||
end;
|
||||
|
||||
destructor TMVCDotEnvParser.Destroy;
|
||||
begin
|
||||
inherited;
|
||||
end;
|
||||
|
||||
function TMVCDotEnvParser.DetectLineBreakStyle(Code: String): TLineBreakStyle;
|
||||
begin
|
||||
if Code.Contains(String(LINE_BREAKS[TLineBreakStyle.MSWindows])) then
|
||||
Exit(TLineBreakStyle.MSWindows);
|
||||
if Code.Contains(String(LINE_BREAKS[TLineBreakStyle.Linux])) then
|
||||
Exit(TLineBreakStyle.Linux);
|
||||
Result := TLineBreakStyle.MSWindows; // just one line or empty file
|
||||
end;
|
||||
|
||||
procedure TMVCDotEnvParser.EatLineBreaks;
|
||||
begin
|
||||
while CharInSet(fCode.Chars[fIndex], [#13, #10]) do
|
||||
begin
|
||||
NextChar;
|
||||
if (fCurrChar = String(LINE_BREAKS[fLineBreakStyle])[1]) then
|
||||
begin
|
||||
Inc(fCurLine);
|
||||
fSavedIndex := fIndex;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TMVCDotEnvParser.EatSpaces;
|
||||
begin
|
||||
while CharInSet(fCode.Chars[fIndex], [#32, #9]) do
|
||||
begin
|
||||
NextChar;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TMVCDotEnvParser.EatUpToLineBreak;
|
||||
begin
|
||||
while not CharInSet(fCode.Chars[fIndex], [#13, #10]) do
|
||||
begin
|
||||
NextChar;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TMVCDotEnvParser.MatchInLineComment;
|
||||
begin
|
||||
EatSpaces;
|
||||
if MatchSymbol('#') then
|
||||
begin
|
||||
EatUpToLineBreak;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TMVCDotEnvParser.Parse(const EnvDictionay: TMVCDotEnvDictionary; const DotEnvCode: String);
|
||||
var
|
||||
lKey: string;
|
||||
lValue: string;
|
||||
begin
|
||||
fCode := DotEnvCode;
|
||||
fCodeLength := Length(fCode);
|
||||
fLineBreaksStyle := DetectLineBreakStyle(fCode);
|
||||
fIndex := -1;
|
||||
fCurLine := 0;
|
||||
fSavedIndex := 0;
|
||||
NextChar;
|
||||
while fIndex < Length(DotEnvCode) do
|
||||
begin
|
||||
EatLineBreaks;
|
||||
EatSpaces;
|
||||
if MatchKey(lKey) then
|
||||
begin
|
||||
EatSpaces;
|
||||
Check(MatchSymbol('='), 'Expected "="');
|
||||
EatSpaces;
|
||||
Check(MatchValue(lValue), 'Expected "Value"');
|
||||
EnvDictionay.AddOrSetValue(lKey, lValue);
|
||||
end
|
||||
else if fCurrChar = #0 then
|
||||
begin
|
||||
Break;
|
||||
end
|
||||
else if CharInSet(fCurrChar, [';', '#']) then
|
||||
begin
|
||||
EatUpToLineBreak;
|
||||
EatLineBreaks;
|
||||
end
|
||||
else
|
||||
begin
|
||||
raise EMVCDotEnvParser.CreateFmt('Unexpected char "%s" at %d', [fCurrChar, fIndex - fSavedIndex]);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TMVCDotEnvParser.MatchKey(out Token: String): Boolean;
|
||||
var
|
||||
lTmp: String;
|
||||
begin
|
||||
lTmp := '';
|
||||
if MatchSymbol('''') then
|
||||
begin
|
||||
Check(MatchIdentifier(Token));
|
||||
Check(MatchSymbol(''''));
|
||||
Result := True;
|
||||
end
|
||||
else
|
||||
begin
|
||||
Result := MatchIdentifier(Token);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TMVCDotEnvParser.MatchSymbol(const Symbol: Char): Boolean;
|
||||
begin
|
||||
Result := fCode.Chars[fIndex] = Symbol;
|
||||
if Result then
|
||||
begin
|
||||
NextChar;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TMVCDotEnvParser.MatchIdentifier(out Value: String): Boolean;
|
||||
begin
|
||||
Value := '';
|
||||
while CharInSet(fCode.Chars[fIndex], ['0' .. '9', 'a' .. 'z', 'A' .. 'Z', '_', '.', ':', '$', '%']) do
|
||||
begin
|
||||
Value := Value + fCode.Chars[fIndex];
|
||||
NextChar;
|
||||
end;
|
||||
Result := not Value.IsEmpty;
|
||||
end;
|
||||
|
||||
function TMVCDotEnvParser.MatchString(out Value: String): Boolean;
|
||||
procedure MatchUpToCharacterSingleLine(out Value: String; const Delimiter1: Char);
|
||||
begin
|
||||
while (fIndex < fCodeLength) and (fCode.Chars[fIndex] <> Delimiter1) and
|
||||
(not CharInSet(fCode.Chars[fIndex], [#13, #10])) do
|
||||
begin
|
||||
Value := Value + fCode.Chars[fIndex];
|
||||
NextChar;
|
||||
end;
|
||||
end;
|
||||
procedure MatchUpToCharacterMultiLine(out Value: String; const Delimiter1: Char);
|
||||
begin
|
||||
while (fIndex < fCodeLength) and (fCode.Chars[fIndex] <> Delimiter1) and
|
||||
(not CharInSet(fCode.Chars[fIndex], [#13, #10])) do
|
||||
begin
|
||||
Value := Value + fCode.Chars[fIndex];
|
||||
NextChar;
|
||||
end;
|
||||
end;
|
||||
|
||||
begin
|
||||
Value := '';
|
||||
EatSpaces;
|
||||
if MatchSymbol('"') then
|
||||
begin
|
||||
MatchUpToCharacterMultiLine(Value, '"');
|
||||
Check(MatchSymbol('"'), 'Expected ''"''');
|
||||
EatSpaces;
|
||||
MatchInLineComment;
|
||||
end
|
||||
else if MatchSymbol('''') then
|
||||
begin
|
||||
MatchUpToCharacterMultiLine(Value, '''');
|
||||
Check(MatchSymbol(''''), 'Expected ''''');
|
||||
EatSpaces;
|
||||
MatchInLineComment;
|
||||
end
|
||||
else
|
||||
begin
|
||||
MatchUpToCharacterSingleLine(Value, '#');
|
||||
Value := Value.Trim;
|
||||
end;
|
||||
Result := not Value.IsEmpty;
|
||||
end;
|
||||
|
||||
function TMVCDotEnvParser.MatchValue(out Token: String): Boolean;
|
||||
begin
|
||||
Result := MatchString(Token);
|
||||
end;
|
||||
|
||||
function TMVCDotEnvParser.NextChar: Char;
|
||||
begin
|
||||
Inc(fIndex);
|
||||
Result := fCode.Chars[fIndex];
|
||||
fCurrChar := Result;
|
||||
end;
|
||||
|
||||
{ TMVCDotEnvDictionary }
|
||||
|
||||
constructor TMVCDotEnvDictionary.Create;
|
||||
begin
|
||||
inherited Create;
|
||||
end;
|
||||
|
||||
end.
|
309
sources/MVCFramework.DotEnv.pas
Normal file
309
sources/MVCFramework.DotEnv.pas
Normal file
@ -0,0 +1,309 @@
|
||||
// *************************************************************************** }
|
||||
//
|
||||
// Delphi MVC Framework
|
||||
//
|
||||
// Copyright (c) 2010-2023 Daniele Teti and the DMVCFramework Team
|
||||
//
|
||||
// https://github.com/danieleteti/delphimvcframework
|
||||
//
|
||||
// ***************************************************************************
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// ***************************************************************************
|
||||
|
||||
unit MVCFramework.DotEnv;
|
||||
|
||||
interface
|
||||
|
||||
uses System.SysUtils, System.Generics.Collections, MVCFramework.DotEnv.Parser;
|
||||
|
||||
type
|
||||
{$SCOPEDENUMS ON}
|
||||
TMVCDotEnvPriority = (FileThenEnv, EnvThenFile, OnlyFile, OnlyEnv);
|
||||
|
||||
EMVCDotEnv = class(Exception)
|
||||
|
||||
end;
|
||||
|
||||
IMVCDotEnv = interface
|
||||
['{5FD2C3CB-0895-4CCD-985F-27394798E4A8}']
|
||||
function WithStrategy(const Strategy: TMVCDotEnvPriority = TMVCDotEnvPriority.EnvThenFile): IMVCDotEnv;
|
||||
function UseProfile(const ProfileName: String): IMVCDotEnv;
|
||||
function ClearProfiles: IMVCDotEnv;
|
||||
function Build(const DotEnvPath: string = ''): IMVCDotEnv; overload;
|
||||
function Env(const Name: string): string; overload;
|
||||
function SaveToFile(const FileName: String): IMVCDotEnv;
|
||||
function IsFrozen: Boolean;
|
||||
end;
|
||||
|
||||
|
||||
function GlobalDotEnv: IMVCDotEnv;
|
||||
function NewDotEnv: IMVCDotEnv;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
System.IOUtils,
|
||||
System.TypInfo,
|
||||
System.Classes;
|
||||
|
||||
var
|
||||
gDotEnv: IMVCDotEnv = nil;
|
||||
|
||||
{ TDotEnv }
|
||||
|
||||
type
|
||||
TMVCDotEnv = class(TInterfacedObject, IMVCDotEnv)
|
||||
strict private
|
||||
fFrozen: Boolean;
|
||||
fPriority: TMVCDotEnvPriority;
|
||||
fEnvPath: string;
|
||||
fEnvDict: TMVCDotEnvDictionary;
|
||||
fProfiles: TList<String>;
|
||||
procedure ReadEnvFile;
|
||||
function GetDotEnvVar(const key: string): string;
|
||||
function ExplodePlaceholders(const Value: string): string;
|
||||
procedure PopulateDictionary(const EnvDict: TDictionary<string, string>; const EnvFilePath: String);
|
||||
procedure CheckAlreadyBuilt;
|
||||
procedure CheckNotBuilt;
|
||||
procedure ExplodeReferences;
|
||||
strict protected
|
||||
function WithStrategy(const Priority: TMVCDotEnvPriority = TMVCDotEnvPriority.EnvThenFile): IMVCDotEnv; overload;
|
||||
function UseProfile(const ProfileName: String): IMVCDotEnv;
|
||||
function ClearProfiles: IMVCDotEnv;
|
||||
function Build(const DotEnvDirectory: string = ''): IMVCDotEnv; overload;
|
||||
function IsFrozen: Boolean;
|
||||
function Env(const Name: string): string; overload;
|
||||
function SaveToFile(const FileName: String): IMVCDotEnv;
|
||||
public
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
end;
|
||||
|
||||
|
||||
function TMVCDotEnv.GetDotEnvVar(const key: string): string;
|
||||
begin
|
||||
fEnvDict.TryGetValue(key, Result);
|
||||
end;
|
||||
|
||||
function TMVCDotEnv.Env(const Name: string): string;
|
||||
begin
|
||||
CheckNotBuilt;
|
||||
if fPriority in [TMVCDotEnvPriority.FileThenEnv, TMVCDotEnvPriority.OnlyFile] then
|
||||
begin
|
||||
Result := GetDotEnvVar(name);
|
||||
if fPriority = TMVCDotEnvPriority.OnlyFile then
|
||||
begin
|
||||
// OnlyFile
|
||||
Exit;
|
||||
end;
|
||||
// FileThenEnv
|
||||
if Result.IsEmpty then
|
||||
begin
|
||||
Exit(ExplodePlaceholders(GetEnvironmentVariable(Name)));
|
||||
end;
|
||||
end
|
||||
else if fPriority in [TMVCDotEnvPriority.EnvThenFile, TMVCDotEnvPriority.OnlyEnv] then
|
||||
begin
|
||||
Result := ExplodePlaceholders(GetEnvironmentVariable(Name));
|
||||
if fPriority = TMVCDotEnvPriority.OnlyEnv then
|
||||
begin
|
||||
// OnlyEnv
|
||||
Exit;
|
||||
end;
|
||||
// EnvThenFile
|
||||
if Result.IsEmpty then
|
||||
begin
|
||||
Exit(GetDotEnvVar(Name));
|
||||
end;
|
||||
end
|
||||
else
|
||||
begin
|
||||
raise Exception.Create('Unknown Strategy');
|
||||
end;
|
||||
end;
|
||||
|
||||
function TMVCDotEnv.UseProfile(const ProfileName: String): IMVCDotEnv;
|
||||
begin
|
||||
CheckAlreadyBuilt;
|
||||
fProfiles.Add(ProfileName);
|
||||
Result := Self;
|
||||
end;
|
||||
|
||||
function TMVCDotEnv.WithStrategy(const Priority: TMVCDotEnvPriority): IMVCDotEnv;
|
||||
begin
|
||||
CheckAlreadyBuilt;
|
||||
Result := Self;
|
||||
fPriority := Priority;
|
||||
end;
|
||||
|
||||
function TMVCDotEnv.Build(const DotEnvDirectory: string): IMVCDotEnv;
|
||||
begin
|
||||
CheckNotBuilt;
|
||||
Result := Self;
|
||||
fEnvPath := TDirectory.GetParent(GetModuleName(HInstance));
|
||||
if not DotEnvDirectory.IsEmpty then
|
||||
begin
|
||||
fEnvPath := TPath.Combine(fEnvPath, DotEnvDirectory);
|
||||
end;
|
||||
fEnvDict.Clear;
|
||||
ReadEnvFile;
|
||||
ExplodeReferences;
|
||||
fFrozen := True;
|
||||
end;
|
||||
|
||||
procedure TMVCDotEnv.CheckAlreadyBuilt;
|
||||
begin
|
||||
if fFrozen then
|
||||
begin
|
||||
raise Exception.Create('DotEnv Engine Already Built');
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TMVCDotEnv.CheckNotBuilt;
|
||||
begin
|
||||
if fFrozen then
|
||||
begin
|
||||
raise EMVCDotEnv.Create('Build must be called before use the engine');
|
||||
end;
|
||||
end;
|
||||
|
||||
function TMVCDotEnv.ClearProfiles: IMVCDotEnv;
|
||||
begin
|
||||
CheckAlreadyBuilt;
|
||||
fProfiles.Clear;
|
||||
Result := Self;
|
||||
end;
|
||||
|
||||
constructor TMVCDotEnv.Create;
|
||||
begin
|
||||
inherited;
|
||||
fFrozen := False;
|
||||
fProfiles := TList<String>.Create;
|
||||
fEnvDict := TMVCDotEnvDictionary.Create;
|
||||
fEnvPath := IncludeTrailingPathDelimiter(ExtractFilePath(ParamStr(0)));
|
||||
fPriority := TMVCDotEnvPriority.EnvThenFile;
|
||||
end;
|
||||
|
||||
destructor TMVCDotEnv.Destroy;
|
||||
begin
|
||||
FreeAndNil(fEnvDict);
|
||||
fProfiles.Free;
|
||||
inherited;
|
||||
end;
|
||||
|
||||
function TMVCDotEnv.IsFrozen: Boolean;
|
||||
begin
|
||||
Result := fFrozen;
|
||||
end;
|
||||
|
||||
function TMVCDotEnv.ExplodePlaceholders(const Value: string): string;
|
||||
var
|
||||
lStartPos, lEndPos: Integer;
|
||||
lKey, lValue: string;
|
||||
begin
|
||||
Result := Value;
|
||||
while Result.IndexOf('${') > -1 do
|
||||
begin
|
||||
lStartPos := Result.IndexOf('${');
|
||||
lEndPos := Result.IndexOf('}');
|
||||
if (lEndPos = -1) or (lEndPos < lStartPos) then
|
||||
begin
|
||||
raise EMVCDotEnv.Create('Unclosed expansion (${...}) at: ' + Value);
|
||||
end;
|
||||
lKey := Result.Substring(lStartPos + 2, lEndPos - (lStartPos + 2));
|
||||
lValue := Env(lKey);
|
||||
Result := StringReplace(Result, '${' + lKey + '}', lValue, [rfReplaceAll]);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TMVCDotEnv.ExplodeReferences;
|
||||
var
|
||||
lKey: String;
|
||||
begin
|
||||
for lKey in fEnvDict.Keys do
|
||||
begin
|
||||
fEnvDict.AddOrSetValue(lKey, ExplodePlaceholders(fEnvDict[lKey]));
|
||||
end;
|
||||
end;
|
||||
|
||||
function TMVCDotEnv.SaveToFile(const FileName: String): IMVCDotEnv;
|
||||
var
|
||||
lKeys: TArray<String>;
|
||||
lKey: String;
|
||||
lSL: TStringList;
|
||||
begin
|
||||
lKeys := fEnvDict.Keys.ToArray;
|
||||
TArray.Sort<String>(lKeys);
|
||||
lSL := TStringList.Create;
|
||||
try
|
||||
for lKey in lKeys do
|
||||
begin
|
||||
lSL.Values[lKey] := GetDotEnvVar(lKey);
|
||||
end;
|
||||
lSL.SaveToFile(FileName);
|
||||
finally
|
||||
lSL.Free;
|
||||
end;
|
||||
Result := Self;
|
||||
end;
|
||||
|
||||
procedure TMVCDotEnv.PopulateDictionary(const EnvDict: TDictionary<string, string>; const EnvFilePath: String);
|
||||
var
|
||||
lDotEnvCode: string;
|
||||
lParser: TMVCDotEnvParser;
|
||||
begin
|
||||
if not TFile.Exists(EnvFilePath) then
|
||||
begin
|
||||
Exit;
|
||||
end;
|
||||
|
||||
lDotEnvCode := TFile.ReadAllText(EnvFilePath);
|
||||
lParser := TMVCDotEnvParser.Create;
|
||||
try
|
||||
lParser.Parse(fEnvDict, lDotEnvCode);
|
||||
finally
|
||||
lParser.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TMVCDotEnv.ReadEnvFile;
|
||||
var
|
||||
lProfileEnvPath: string;
|
||||
I: Integer;
|
||||
begin
|
||||
PopulateDictionary(fEnvDict, IncludeTrailingPathDelimiter(fEnvPath) + '.env');
|
||||
for I := 0 to fProfiles.Count - 1 do
|
||||
begin
|
||||
lProfileEnvPath := TPath.Combine(fEnvPath, '.env') + '.' + fProfiles[I];
|
||||
PopulateDictionary(fEnvDict, lProfileEnvPath);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
function GlobalDotEnv: IMVCDotEnv;
|
||||
begin
|
||||
Result := gDotEnv;
|
||||
end;
|
||||
|
||||
function NewDotEnv: IMVCDotEnv;
|
||||
begin
|
||||
Result := TMVCDotEnv.Create;
|
||||
end;
|
||||
|
||||
initialization
|
||||
|
||||
gDotEnv := NewDotEnv;
|
||||
|
||||
end.
|
@ -12,7 +12,6 @@ uses
|
||||
DUnitX.TestFramework,
|
||||
{$IFDEF CONSOLE_TESTRUNNER}
|
||||
DUnitX.Loggers.Console,
|
||||
DUnitX.Loggers.XML.NUnit,
|
||||
{$ENDIF }
|
||||
{$IFDEF TESTINSIGHT}
|
||||
TestInsight.DUnitX,
|
||||
@ -73,7 +72,9 @@ uses
|
||||
EntitiesProcessors in 'EntitiesProcessors.pas',
|
||||
MVCFramework.Nullables in '..\..\..\sources\MVCFramework.Nullables.pas',
|
||||
IntfObjectPoolTestU in 'IntfObjectPoolTestU.pas',
|
||||
ObjectPoolTestU in 'ObjectPoolTestU.pas';
|
||||
ObjectPoolTestU in 'ObjectPoolTestU.pas',
|
||||
MVCFramework.DotEnv.Parser in '..\..\..\sources\MVCFramework.DotEnv.Parser.pas',
|
||||
MVCFramework.DotEnv in '..\..\..\sources\MVCFramework.DotEnv.pas';
|
||||
|
||||
{$R *.RES}
|
||||
|
||||
|
@ -183,8 +183,7 @@
|
||||
<PropertyGroup Condition="'$(Cfg_2_Win64)'!=''">
|
||||
<VerInfo_Locale>1033</VerInfo_Locale>
|
||||
<VerInfo_Keys>CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=</VerInfo_Keys>
|
||||
<Manifest_File>(None)</Manifest_File>
|
||||
<AppDPIAwarenessMode>none</AppDPIAwarenessMode>
|
||||
<DCC_DebugDCUs>false</DCC_DebugDCUs>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<DelphiCompile Include="$(MainSource)">
|
||||
@ -259,6 +258,8 @@
|
||||
<DCCReference Include="..\..\..\sources\MVCFramework.Nullables.pas"/>
|
||||
<DCCReference Include="IntfObjectPoolTestU.pas"/>
|
||||
<DCCReference Include="ObjectPoolTestU.pas"/>
|
||||
<DCCReference Include="..\..\..\sources\MVCFramework.DotEnv.Parser.pas"/>
|
||||
<DCCReference Include="..\..\..\sources\MVCFramework.DotEnv.pas"/>
|
||||
<BuildConfiguration Include="Base">
|
||||
<Key>Base</Key>
|
||||
</BuildConfiguration>
|
||||
@ -342,6 +343,8 @@
|
||||
<Source Name="MainSource">DMVCFrameworkTests.dpr</Source>
|
||||
</Source>
|
||||
<Excluded_Packages>
|
||||
<Excluded_Packages Name="$(BDSBIN)\bcboffice2k280.bpl">Embarcadero C++Builder Office 2000 Servers Package</Excluded_Packages>
|
||||
<Excluded_Packages Name="$(BDSBIN)\bcbofficexp280.bpl">Embarcadero C++Builder Office XP Servers Package</Excluded_Packages>
|
||||
<Excluded_Packages Name="$(BDSBIN)\dcloffice2k280.bpl">Microsoft Office 2000 Sample Automation Server Wrapper Components</Excluded_Packages>
|
||||
<Excluded_Packages Name="$(BDSBIN)\dclofficexp280.bpl">Microsoft Office XP Sample Automation Server Wrapper Components</Excluded_Packages>
|
||||
</Excluded_Packages>
|
||||
|
@ -290,6 +290,32 @@ type
|
||||
procedure TestPutGet_Check_No_AV;
|
||||
end;
|
||||
|
||||
|
||||
[TestFixture]
|
||||
TTestDotEnv = class(TObject)
|
||||
public
|
||||
[Test]
|
||||
procedure TestWithoutProfiles;
|
||||
[Test]
|
||||
procedure TestWithDevProfile;
|
||||
[Test]
|
||||
procedure TestWithDevAndTestProfile;
|
||||
end;
|
||||
|
||||
[TestFixture]
|
||||
TTestDotEnvParser = class(TObject)
|
||||
public
|
||||
[Test]
|
||||
procedure TestKeyValue;
|
||||
[Test]
|
||||
procedure TestKeyValueWithQuotedValues;
|
||||
[Test]
|
||||
procedure TestVarPlaceHolders;
|
||||
[Test]
|
||||
procedure TestInLineComments;
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
|
||||
{$WARN SYMBOL_DEPRECATED OFF}
|
||||
@ -312,7 +338,8 @@ uses
|
||||
{$ENDIF}
|
||||
TestServerControllerU, System.Classes,
|
||||
MVCFramework.DuckTyping, System.IOUtils, MVCFramework.SystemJSONUtils,
|
||||
IdGlobal, System.TypInfo, System.Types, Winapi.Windows;
|
||||
IdGlobal, System.TypInfo, System.Types, Winapi.Windows, MVCFramework.DotEnv,
|
||||
MVCFramework.DotEnv.Parser;
|
||||
|
||||
var
|
||||
JWT_SECRET_KEY_TEST: string = 'myk3y';
|
||||
@ -2150,6 +2177,166 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
{ TTestDotEnv }
|
||||
|
||||
function Are2FilesEqual(const File1, File2: TFileName): Boolean;
|
||||
var
|
||||
ms1, ms2: TMemoryStream;
|
||||
begin
|
||||
Result := False;
|
||||
ms1 := TMemoryStream.Create;
|
||||
try
|
||||
ms1.LoadFromFile(File1);
|
||||
ms2 := TMemoryStream.Create;
|
||||
try
|
||||
ms2.LoadFromFile(File2);
|
||||
if ms1.Size = ms2.Size then
|
||||
begin
|
||||
Result := CompareMem(ms1.Memory, ms2.memory, ms1.Size);
|
||||
end;
|
||||
finally
|
||||
ms2.Free;
|
||||
end;
|
||||
finally
|
||||
ms1.Free;
|
||||
end
|
||||
end;
|
||||
|
||||
procedure TTestDotEnv.TestWithDevAndTestProfile;
|
||||
var
|
||||
lDotEnv: IMVCDotEnv;
|
||||
begin
|
||||
lDotEnv := NewDotEnv.UseProfile('dev').UseProfile('test').Build('..\dotEnv');
|
||||
lDotEnv.SaveToFile('..\dotEnv\dotEnvDump-profile-dev-and-test.test.txt');
|
||||
Assert.IsTrue(Are2FilesEqual('..\dotEnv\dotEnvDump-profile-dev-and-test.correct.txt','..\dotEnv\dotEnvDump-profile-dev-and-test.test.txt'), 'Files are different');
|
||||
end;
|
||||
|
||||
procedure TTestDotEnv.TestWithDevProfile;
|
||||
var
|
||||
lDotEnv: IMVCDotEnv;
|
||||
begin
|
||||
lDotEnv := NewDotEnv.UseProfile('dev').Build('..\dotEnv');
|
||||
lDotEnv.SaveToFile('..\dotEnv\dotEnvDump-profile-dev.test.txt');
|
||||
Assert.IsTrue(Are2FilesEqual('..\dotEnv\dotEnvDump-profile-dev.correct.txt','..\dotEnv\dotEnvDump-profile-dev.test.txt'), 'Files are different');
|
||||
end;
|
||||
|
||||
procedure TTestDotEnv.TestWithoutProfiles;
|
||||
var
|
||||
lDotEnv: IMVCDotEnv;
|
||||
begin
|
||||
lDotEnv := NewDotEnv.Build('..\dotEnv');
|
||||
lDotEnv.SaveToFile('..\dotEnv\dotEnvDump-noprofile.test.txt');
|
||||
Assert.IsTrue(Are2FilesEqual('..\dotEnv\dotEnvDump-noprofile.correct.txt','..\dotEnv\dotEnvDump-noprofile.test.txt'), 'Files are different');
|
||||
end;
|
||||
|
||||
{ TTestDotEnvParser }
|
||||
|
||||
procedure TTestDotEnvParser.TestInLineComments;
|
||||
const
|
||||
DOTENVCODE =
|
||||
'#comment1' + sLineBreak +
|
||||
'#comment2' + sLineBreak +
|
||||
'key1= "value1" #inline comment' + sLineBreak +
|
||||
';comment3' + sLineBreak +
|
||||
'key2 = ''value2'' #inline comment' + sLineBreak +
|
||||
';comment' + sLineBreak +
|
||||
'key3 = value3 #inline comment' + sLineBreak +
|
||||
'key4 = " value4 " #inline comment' + sLineBreak +
|
||||
';commentX';
|
||||
|
||||
begin
|
||||
var lParser := TMVCDotEnvParser.Create;
|
||||
try
|
||||
var lDict := TMVCDotEnvDictionary.Create();
|
||||
try
|
||||
lParser.Parse(lDict, DOTENVCODE);
|
||||
Assert.AreEqual('value1', lDict['key1']);
|
||||
Assert.AreEqual('value2', lDict['key2']);
|
||||
Assert.AreEqual('value3', lDict['key3']);
|
||||
Assert.AreEqual(' value4 ', lDict['key4']);
|
||||
finally
|
||||
lDict.Free;
|
||||
end;
|
||||
finally
|
||||
lParser.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TTestDotEnvParser.TestKeyValue;
|
||||
const
|
||||
DOTENVCODE = 'key1=value1' + sLineBreak + 'key2 = value2 with another value' + sLineBreak;
|
||||
begin
|
||||
var lParser := TMVCDotEnvParser.Create;
|
||||
try
|
||||
var lDict := TMVCDotEnvDictionary.Create();
|
||||
try
|
||||
lParser.Parse(lDict, DOTENVCODE);
|
||||
Assert.AreEqual('value1', lDict['key1']);
|
||||
Assert.AreEqual('value2 with another value', lDict['key2']);
|
||||
finally
|
||||
lDict.Free;
|
||||
end;
|
||||
finally
|
||||
lParser.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TTestDotEnvParser.TestKeyValueWithQuotedValues;
|
||||
const
|
||||
DOTENVCODE =
|
||||
'key1= "value1"' + sLineBreak +
|
||||
'key2 = ''value2''' + sLineBreak +
|
||||
'key3 = "uno''due''"' + sLineBreak +
|
||||
'key4 = ''uno"due"''' + sLineBreak;
|
||||
begin
|
||||
var lParser := TMVCDotEnvParser.Create;
|
||||
try
|
||||
var lDict := TMVCDotEnvDictionary.Create();
|
||||
try
|
||||
lParser.Parse(lDict, DOTENVCODE);
|
||||
Assert.AreEqual('value1', lDict['key1']);
|
||||
Assert.AreEqual('value2', lDict['key2']);
|
||||
Assert.AreEqual('uno''due''', lDict['key3']);
|
||||
Assert.AreEqual('uno"due"', lDict['key4']);
|
||||
finally
|
||||
lDict.Free;
|
||||
end;
|
||||
finally
|
||||
lParser.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TTestDotEnvParser.TestVarPlaceHolders;
|
||||
const
|
||||
DOTENVCODE =
|
||||
'#comment1' + sLineBreak +
|
||||
'#comment2' + sLineBreak +
|
||||
'key1= "value1"' + sLineBreak +
|
||||
';comment3' + sLineBreak +
|
||||
'key2 = ''value2''' + sLineBreak +
|
||||
';comment' + sLineBreak +
|
||||
'key3 = |${key1}|${key2}|' + sLineBreak +
|
||||
'key4 = value4' + sLineBreak +
|
||||
';commentX';
|
||||
|
||||
begin
|
||||
var lParser := TMVCDotEnvParser.Create;
|
||||
try
|
||||
var lDict := TMVCDotEnvDictionary.Create();
|
||||
try
|
||||
lParser.Parse(lDict, DOTENVCODE);
|
||||
Assert.AreEqual('value1', lDict['key1']);
|
||||
Assert.AreEqual('value2', lDict['key2']);
|
||||
Assert.AreEqual('|${key1}|${key2}|', lDict['key3']);
|
||||
Assert.AreEqual('value4', lDict['key4']);
|
||||
finally
|
||||
lDict.Free;
|
||||
end;
|
||||
finally
|
||||
lParser.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
initialization
|
||||
|
||||
TDUnitX.RegisterTestFixture(TTestRouting);
|
||||
@ -2159,6 +2346,8 @@ TDUnitX.RegisterTestFixture(TTestMultiMap);
|
||||
TDUnitX.RegisterTestFixture(TTestNameCase);
|
||||
TDUnitX.RegisterTestFixture(TTestCryptUtils);
|
||||
TDUnitX.RegisterTestFixture(TTestLRUCache);
|
||||
TDUnitX.RegisterTestFixture(TTestDotEnv);
|
||||
TDUnitX.RegisterTestFixture(TTestDotEnvParser);
|
||||
|
||||
finalization
|
||||
|
||||
|
14
unittests/general/Several/dotEnv/.env
Normal file
14
unittests/general/Several/dotEnv/.env
Normal file
@ -0,0 +1,14 @@
|
||||
key1=value1
|
||||
key2= value2
|
||||
key3 = value3 #incline comment
|
||||
key4 = value4
|
||||
;comment1
|
||||
key2.1= "value2.1"
|
||||
key2.2= 'value2.2'
|
||||
key2.3 = 'value2.3'
|
||||
key2.4 = '${key2.1}|${key2.2}|${key2.3}|${key7}'
|
||||
#comment2
|
||||
key5=${key6}
|
||||
key6=${key3}
|
||||
key7=${key6}
|
||||
|
5
unittests/general/Several/dotEnv/.env.dev
Normal file
5
unittests/general/Several/dotEnv/.env.dev
Normal file
@ -0,0 +1,5 @@
|
||||
key1=devvalue1
|
||||
key2= devvalue2
|
||||
key3 = devvalue3 #incline comment
|
||||
key7=${key2.1}
|
||||
mode=dev
|
2
unittests/general/Several/dotEnv/.env.test
Normal file
2
unittests/general/Several/dotEnv/.env.test
Normal file
@ -0,0 +1,2 @@
|
||||
key1=testvalue1
|
||||
mode=test
|
@ -0,0 +1,11 @@
|
||||
key1=value1
|
||||
key2=value2
|
||||
key2.1=value2.1
|
||||
key2.2=value2.2
|
||||
key2.3=value2.3
|
||||
key2.4=value2.1|value2.2|value2.3|value3
|
||||
key3=value3
|
||||
key4=value4
|
||||
key5=value3
|
||||
key6=value3
|
||||
key7=value3
|
@ -0,0 +1,12 @@
|
||||
key1=testvalue1
|
||||
key2=devvalue2
|
||||
key2.1=value2.1
|
||||
key2.2=value2.2
|
||||
key2.3=value2.3
|
||||
key2.4=value2.1|value2.2|value2.3|value2.1
|
||||
key3=devvalue3
|
||||
key4=value4
|
||||
key5=devvalue3
|
||||
key6=devvalue3
|
||||
key7=value2.1
|
||||
mode=test
|
@ -0,0 +1,12 @@
|
||||
key1=devvalue1
|
||||
key2=devvalue2
|
||||
key2.1=value2.1
|
||||
key2.2=value2.2
|
||||
key2.3=value2.3
|
||||
key2.4=value2.1|value2.2|value2.3|value2.1
|
||||
key3=devvalue3
|
||||
key4=value4
|
||||
key5=devvalue3
|
||||
key6=devvalue3
|
||||
key7=value2.1
|
||||
mode=dev
|
Loading…
Reference in New Issue
Block a user