From afafecf8cb2651d6e8743b8d1cbb47f4916b122a Mon Sep 17 00:00:00 2001 From: Daniele Teti Date: Wed, 1 Nov 2023 23:10:39 +0100 Subject: [PATCH] dotEnv: Renamed WithStrategy -> UseStrategy, Added SkipDefaultEnv --- ideexpert/DMVC.Expert.CodeGen.Templates.pas | 2 +- samples/dotenv_showcase/MainFormU.dfm | 66 +++++++++++++-------- samples/dotenv_showcase/MainFormU.pas | 31 +++++++--- samples/dotenv_showcase/bin/.env | 2 +- samples/dotenv_showcase/bin/.env.prod | 2 +- samples/dotenv_showcase/bin/.env.test | 2 +- sources/MVCFramework.DotEnv.pas | 22 +++++-- 7 files changed, 85 insertions(+), 42 deletions(-) diff --git a/ideexpert/DMVC.Expert.CodeGen.Templates.pas b/ideexpert/DMVC.Expert.CodeGen.Templates.pas index cd2019ee..c1d58d7f 100644 --- a/ideexpert/DMVC.Expert.CodeGen.Templates.pas +++ b/ideexpert/DMVC.Expert.CodeGen.Templates.pas @@ -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 + diff --git a/samples/dotenv_showcase/MainFormU.dfm b/samples/dotenv_showcase/MainFormU.dfm index 349a07eb..983fba73 100644 --- a/samples/dotenv_showcase/MainFormU.dfm +++ b/samples/dotenv_showcase/MainFormU.dfm @@ -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 diff --git a/samples/dotenv_showcase/MainFormU.pas b/samples/dotenv_showcase/MainFormU.pas index 8f7ae90a..85f66bcd 100644 --- a/samples/dotenv_showcase/MainFormU.pas +++ b/samples/dotenv_showcase/MainFormU.pas @@ -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; diff --git a/samples/dotenv_showcase/bin/.env b/samples/dotenv_showcase/bin/.env index 58d9fb95..bdcf0ef8 100644 --- a/samples/dotenv_showcase/bin/.env +++ b/samples/dotenv_showcase/bin/.env @@ -5,7 +5,7 @@ mode=dev #DB Name -dbhostname=my_product_db_dev +dbname=my_product_db_dev #The DB username dbuser=my_user diff --git a/samples/dotenv_showcase/bin/.env.prod b/samples/dotenv_showcase/bin/.env.prod index 05188aac..dc885be1 100644 --- a/samples/dotenv_showcase/bin/.env.prod +++ b/samples/dotenv_showcase/bin/.env.prod @@ -5,7 +5,7 @@ mode=prod #DB Name -dbhostname=my_product_db +dbname=my_product_db #The DB username dbuser=${USERNAME} diff --git a/samples/dotenv_showcase/bin/.env.test b/samples/dotenv_showcase/bin/.env.test index d76ba4ee..9fef6dcb 100644 --- a/samples/dotenv_showcase/bin/.env.test +++ b/samples/dotenv_showcase/bin/.env.test @@ -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 diff --git a/sources/MVCFramework.DotEnv.pas b/sources/MVCFramework.DotEnv.pas index 83298183..aeccce55 100644 --- a/sources/MVCFramework.DotEnv.pas +++ b/sources/MVCFramework.DotEnv.pas @@ -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): IMVCDotEnvBuilder; function UseProfile(const ProfileName: String): IMVCDotEnvBuilder; overload; function UseProfile(const ProfileDelegate: TFunc): IMVCDotEnvBuilder; overload; @@ -86,6 +87,7 @@ type fEnvDict: TMVCDotEnvDictionary; fLoggerProc: TProc; fProfiles: TList; + 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): IMVCDotEnvBuilder; overload; function UseLogger(const LoggerProc: TProc): 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; var lKeys: TArray; @@ -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];