dotEnv: Renamed WithStrategy -> UseStrategy, Added SkipDefaultEnv

This commit is contained in:
Daniele Teti 2023-11-01 23:10:39 +01:00
parent 9afaa6cd68
commit afafecf8cb
7 changed files with 85 additions and 42 deletions

View File

@ -107,7 +107,7 @@ resourcestring
' function: IMVCDotEnv' + sLineBreak +
' begin' + sLineBreak +
' Result := NewDotEnv' + sLineBreak +
' .WithStrategy(TMVCDotEnvPriority.FileThenEnv)' + sLineBreak +
' .UseStrategy(TMVCDotEnvPriority.FileThenEnv)' + sLineBreak +
' //if available, by default, loads default environment (.env)' + sLineBreak +
' .UseProfile(''test'') //if available loads the test environment (.env.test)' + sLineBreak +
' .UseProfile(''prod'') //if available loads the prod environment (.env.prod)' + sLineBreak +

View File

@ -2,8 +2,8 @@ object MainForm: TMainForm
Left = 0
Top = 0
Caption = 'dotEnv :: ShowCase'
ClientHeight = 644
ClientWidth = 1028
ClientHeight = 634
ClientWidth = 1178
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
@ -16,21 +16,21 @@ object MainForm: TMainForm
AlignWithMargins = True
Left = 3
Top = 3
Width = 1022
Height = 638
ActivePage = TabSheet2
Width = 1172
Height = 628
ActivePage = TabSheet1
Align = alClient
TabOrder = 0
ExplicitWidth = 1018
ExplicitHeight = 637
ExplicitWidth = 1168
ExplicitHeight = 627
object TabSheet1: TTabSheet
Caption = 'dotEnv Samples'
DesignSize = (
1014
608)
1164
598)
object btnSingleEnv: TButton
Left = 8
Top = 191
Top = 236
Width = 169
Height = 49
Caption = 'Single ENV without inheritance (only prod)'
@ -86,8 +86,8 @@ object MainForm: TMainForm
object mmVars: TMemo
Left = 183
Top = 18
Width = 825
Height = 587
Width = 982
Height = 588
Anchors = [akLeft, akTop, akRight, akBottom]
Font.Charset = ANSI_CHARSET
Font.Color = clWindowText
@ -96,6 +96,18 @@ object MainForm: TMainForm
Font.Style = []
ParentFont = False
TabOrder = 6
ExplicitWidth = 978
ExplicitHeight = 587
end
object btnSkipDefaultFile: TButton
Left = 8
Top = 181
Width = 169
Height = 49
Caption = 'Skip Default .env file (load only .env.prod profile)'
TabOrder = 7
WordWrap = True
OnClick = btnSkipDefaultFileClick
end
end
object TabSheet2: TTabSheet
@ -104,19 +116,18 @@ object MainForm: TMainForm
object Splitter1: TSplitter
Left = 625
Top = 0
Height = 608
ExplicitLeft = 408
ExplicitTop = 168
ExplicitHeight = 100
Height = 598
ExplicitHeight = 605
end
object Panel1: TPanel
Left = 0
Top = 0
Width = 625
Height = 608
Height = 598
Align = alLeft
Caption = 'Panel1'
TabOrder = 0
ExplicitHeight = 597
object Label1: TLabel
AlignWithMargins = True
Left = 4
@ -138,13 +149,13 @@ object MainForm: TMainForm
Left = 4
Top = 35
Width = 617
Height = 569
Height = 559
Align = alClient
Ctl3D = True
EditMargins.Auto = True
Font.Charset = ANSI_CHARSET
Font.Color = clWindowText
Font.Height = -15
Font.Height = -19
Font.Name = 'Consolas'
Font.Style = []
Lines.Strings = (
@ -224,16 +235,18 @@ object MainForm: TMainForm
object Panel2: TPanel
Left = 628
Top = 0
Width = 386
Height = 608
Width = 536
Height = 598
Align = alClient
Caption = 'Panel2'
TabOrder = 1
ExplicitWidth = 532
ExplicitHeight = 597
object Label2: TLabel
AlignWithMargins = True
Left = 4
Top = 4
Width = 378
Width = 211
Height = 25
Align = alTop
Caption = 'What'#39's application "sees"'
@ -243,20 +256,19 @@ object MainForm: TMainForm
Font.Name = 'Segoe UI'
Font.Style = []
ParentFont = False
ExplicitWidth = 211
end
object memDst: TMemo
AlignWithMargins = True
Left = 4
Top = 35
Width = 378
Height = 569
Width = 528
Height = 559
Align = alClient
Ctl3D = True
EditMargins.Auto = True
Font.Charset = ANSI_CHARSET
Font.Color = clWindowText
Font.Height = -15
Font.Height = -19
Font.Name = 'Consolas'
Font.Style = []
ParentCtl3D = False
@ -264,6 +276,8 @@ object MainForm: TMainForm
ReadOnly = True
TabOrder = 0
WordWrap = False
ExplicitWidth = 524
ExplicitHeight = 558
end
end
end

View File

@ -26,6 +26,7 @@ type
Panel2: TPanel;
Label1: TLabel;
Label2: TLabel;
btnSkipDefaultFile: TButton;
procedure btnSimpleClick(Sender: TObject);
procedure btnTestEnvClick(Sender: TObject);
procedure btnProdEnvClick(Sender: TObject);
@ -34,6 +35,7 @@ type
procedure btnRequireKeys2Click(Sender: TObject);
procedure memSrcChange(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure btnSkipDefaultFileClick(Sender: TObject);
private
procedure UpdatePlayGround;
procedure UpdateUI(dotEnv: IMVCDotEnv);
@ -48,10 +50,23 @@ implementation
{$R *.dfm}
procedure TMainForm.btnSkipDefaultFileClick(Sender: TObject);
begin
{ with this configuration, only .env.prod is loaded, because .env, which is the default, is skipped }
var dotEnv := NewDotEnv
.SkipDefaultEnv
.UseStrategy(TMVCDotEnvPriority.FileThenEnv)
.UseProfile('prod')
.Build();
mmVars.Clear;
mmVars.Lines.AddStrings(dotEnv.ToArray);
UpdateUI(dotEnv);
end;
procedure TMainForm.btnProdEnvClick(Sender: TObject);
begin
var dotEnv := NewDotEnv
.WithStrategy(TMVCDotEnvPriority.EnvThenFile)
.UseStrategy(TMVCDotEnvPriority.EnvThenFile)
.UseProfile('prod')
.Build();
mmVars.Clear;
@ -61,20 +76,20 @@ end;
procedure TMainForm.btnRequireKeys2Click(Sender: TObject);
begin
var dotEnv := NewDotEnv.WithStrategy(TMVCDotEnvPriority.EnvThenFile).Build();
var dotEnv := NewDotEnv.UseStrategy(TMVCDotEnvPriority.EnvThenFile).Build();
dotEnv.RequireKeys(['mode','dbuser','blablabla','dbhostname','unknown']);
end;
procedure TMainForm.btnRequireKeysClick(Sender: TObject);
begin
var dotEnv := NewDotEnv.WithStrategy(TMVCDotEnvPriority.EnvThenFile).Build();
var dotEnv := NewDotEnv.UseStrategy(TMVCDotEnvPriority.EnvThenFile).Build();
dotEnv.RequireKeys(['mode','dbuser','dbpassword','dbhostname']);
ShowMessage('Required Keys FOUND!');
end;
procedure TMainForm.btnSimpleClick(Sender: TObject);
begin
var dotEnv := NewDotEnv.WithStrategy(TMVCDotEnvPriority.EnvThenFile).Build();
var dotEnv := NewDotEnv.UseStrategy(TMVCDotEnvPriority.EnvThenFile).Build();
mmVars.Clear;
mmVars.Lines.AddStrings(dotEnv.ToArray);
UpdateUI(dotEnv);
@ -83,7 +98,7 @@ end;
procedure TMainForm.btnSingleEnvClick(Sender: TObject);
begin
var dotEnv := NewDotEnv
.WithStrategy(TMVCDotEnvPriority.EnvThenFile)
.UseStrategy(TMVCDotEnvPriority.EnvThenFile)
.UseProfile('prod')
.Build('env1');
mmVars.Clear;
@ -94,7 +109,7 @@ end;
procedure TMainForm.btnTestEnvClick(Sender: TObject);
begin
var dotEnv := NewDotEnv
.WithStrategy(TMVCDotEnvPriority.EnvThenFile)
.UseStrategy(TMVCDotEnvPriority.EnvThenFile)
.UseProfile('test')
.Build();
mmVars.Clear;
@ -107,7 +122,7 @@ begin
if DebugHook<>0 then
begin
ShowMessage('Please, run this sample without debugging!' + sLineBreak +
'It will raise exceptions manhy times while using the playground');
'It can raise exceptions many times while using the playground');
end;
UpdatePlayGround;
@ -124,7 +139,7 @@ begin
memSrc.Lines.SaveToFile(lFileName);
try
var dotEnv := NewDotEnv
.WithStrategy(TMVCDotEnvPriority.OnlyFile)
.UseStrategy(TMVCDotEnvPriority.OnlyFile)
.UseProfile('playground')
.Build(TPath.GetHomePath);
memDst.Clear;

View File

@ -5,7 +5,7 @@
mode=dev
#DB Name
dbhostname=my_product_db_dev
dbname=my_product_db_dev
#The DB username
dbuser=my_user

View File

@ -5,7 +5,7 @@
mode=prod
#DB Name
dbhostname=my_product_db
dbname=my_product_db
#The DB username
dbuser=${USERNAME}

View File

@ -5,7 +5,7 @@
mode=test
#DB Name
dbhostname=my_product_db_test
dbname=my_product_db_test
#DB Hostname
dbhostname=192.168.1.100

View File

@ -52,7 +52,8 @@ type
IMVCDotEnvBuilder = interface
['{1A5EDD44-7226-40BC-A8EE-789E27522392}']
function WithStrategy(const Strategy: TMVCDotEnvPriority = TMVCDotEnvPriority.EnvThenFile): IMVCDotEnvBuilder;
function UseStrategy(const Strategy: TMVCDotEnvPriority = TMVCDotEnvPriority.EnvThenFile): IMVCDotEnvBuilder;
function SkipDefaultEnv: IMVCDotEnvBuilder;
function UseLogger( const Logger: TProc<String>): IMVCDotEnvBuilder;
function UseProfile(const ProfileName: String): IMVCDotEnvBuilder; overload;
function UseProfile(const ProfileDelegate: TFunc<String>): IMVCDotEnvBuilder; overload;
@ -86,6 +87,7 @@ type
fEnvDict: TMVCDotEnvDictionary;
fLoggerProc: TProc<String>;
fProfiles: TList<String>;
fSkipDefaultEnv: Boolean;
procedure DoLog(const Value: String);
procedure ReadEnvFile;
function GetDotEnvVar(const key: string): string;
@ -95,7 +97,8 @@ type
procedure CheckAlreadyBuilt;
procedure ExplodeReferences;
strict protected
function WithStrategy(const Priority: TMVCDotEnvPriority = TMVCDotEnvPriority.EnvThenFile): IMVCDotEnvBuilder; overload;
function UseStrategy(const Priority: TMVCDotEnvPriority = TMVCDotEnvPriority.EnvThenFile): IMVCDotEnvBuilder; overload;
function SkipDefaultEnv: IMVCDotEnvBuilder;
function UseProfile(const ProfileName: String): IMVCDotEnvBuilder; overload;
function UseProfile(const ProfileDelegate: TFunc<String>): IMVCDotEnvBuilder; overload;
function UseLogger(const LoggerProc: TProc<String>): IMVCDotEnvBuilder;
@ -226,7 +229,7 @@ begin
Result := Self;
end;
function TMVCDotEnv.WithStrategy(const Priority: TMVCDotEnvPriority): IMVCDotEnvBuilder;
function TMVCDotEnv.UseStrategy(const Priority: TMVCDotEnvPriority): IMVCDotEnvBuilder;
begin
CheckAlreadyBuilt;
Result := Self;
@ -250,6 +253,7 @@ begin
end;
DoLog('Path = ' + fEnvPath);
fEnvDict.Clear;
lAllProfiles := ['default'] + fProfiles.ToArray();
DoLog('Active profile/s priority = [' + String.Join(',', lAllProfiles) + ']');
ReadEnvFile;
@ -280,6 +284,7 @@ begin
fEnvDict := TMVCDotEnvDictionary.Create;
fEnvPath := IncludeTrailingPathDelimiter(ExtractFilePath(ParamStr(0)));
fPriority := TMVCDotEnvPriority.EnvThenFile;
fSkipDefaultEnv := False;
end;
destructor TMVCDotEnv.Destroy;
@ -406,6 +411,12 @@ begin
Result := Self;
end;
function TMVCDotEnv.SkipDefaultEnv: IMVCDotEnvBuilder;
begin
fSkipDefaultEnv := True;
Result := Self;
end;
function TMVCDotEnv.ToArray: TArray<String>;
var
lKeys: TArray<String>;
@ -449,7 +460,10 @@ var
lProfileEnvPath: string;
I: Integer;
begin
PopulateDictionary(fEnvDict, IncludeTrailingPathDelimiter(fEnvPath) + '.env');
if not fSkipDefaultEnv then
begin
PopulateDictionary(fEnvDict, IncludeTrailingPathDelimiter(fEnvPath) + '.env');
end;
for I := 0 to fProfiles.Count - 1 do
begin
lProfileEnvPath := TPath.Combine(fEnvPath, '.env') + '.' + fProfiles[I];