mirror of
https://github.com/danieleteti/delphimvcframework.git
synced 2024-11-15 07:45:54 +01:00
- dotEnv parser allows empty values
- dotEnv names adhere to [a-z,A-Z,_,\.][0-9,a-z,A-Z,_,\.]* - better dotEnv error reporting
This commit is contained in:
parent
83cf604f30
commit
72fd459537
@ -317,6 +317,7 @@ resourcestring
|
||||
// 3 - controller class name
|
||||
// 4 - middlewares
|
||||
// 5 - jsonrpc registration code
|
||||
// 6 - jsonrpc class unit
|
||||
sWebModuleUnit =
|
||||
'unit %0:s;' + sLineBreak +
|
||||
'' + sLineBreak +
|
||||
|
@ -2,94 +2,270 @@ object MainForm: TMainForm
|
||||
Left = 0
|
||||
Top = 0
|
||||
Caption = 'dotEnv :: ShowCase'
|
||||
ClientHeight = 442
|
||||
ClientWidth = 824
|
||||
ClientHeight = 644
|
||||
ClientWidth = 1028
|
||||
Color = clBtnFace
|
||||
Font.Charset = DEFAULT_CHARSET
|
||||
Font.Color = clWindowText
|
||||
Font.Height = -12
|
||||
Font.Name = 'Segoe UI'
|
||||
Font.Style = []
|
||||
DesignSize = (
|
||||
824
|
||||
442)
|
||||
OnShow = FormShow
|
||||
TextHeight = 15
|
||||
object Shape1: TShape
|
||||
Left = 8
|
||||
Top = 181
|
||||
Width = 169
|
||||
Height = 4
|
||||
Brush.Color = clGray
|
||||
end
|
||||
object btnSimple: TButton
|
||||
Left = 8
|
||||
Top = 16
|
||||
Width = 169
|
||||
Height = 49
|
||||
Caption = 'default ENV'
|
||||
object PageControl1: TPageControl
|
||||
AlignWithMargins = True
|
||||
Left = 3
|
||||
Top = 3
|
||||
Width = 1022
|
||||
Height = 638
|
||||
ActivePage = TabSheet2
|
||||
Align = alClient
|
||||
TabOrder = 0
|
||||
OnClick = btnSimpleClick
|
||||
end
|
||||
object mmVars: TMemo
|
||||
Left = 183
|
||||
Top = 16
|
||||
Width = 633
|
||||
Height = 418
|
||||
Anchors = [akLeft, akTop, akRight, akBottom]
|
||||
Font.Charset = ANSI_CHARSET
|
||||
Font.Color = clWindowText
|
||||
Font.Height = -15
|
||||
Font.Name = 'Consolas'
|
||||
Font.Style = []
|
||||
ParentFont = False
|
||||
TabOrder = 1
|
||||
ExplicitWidth = 629
|
||||
ExplicitHeight = 417
|
||||
end
|
||||
object btnTestEnv: TButton
|
||||
Left = 8
|
||||
Top = 71
|
||||
Width = 169
|
||||
Height = 49
|
||||
Caption = 'Test ENV (default + test)'
|
||||
TabOrder = 2
|
||||
OnClick = btnTestEnvClick
|
||||
end
|
||||
object btnProdEnv: TButton
|
||||
Left = 8
|
||||
Top = 126
|
||||
Width = 169
|
||||
Height = 49
|
||||
Caption = 'Prod ENV (default + prod)'
|
||||
TabOrder = 3
|
||||
OnClick = btnProdEnvClick
|
||||
end
|
||||
object btnSingleEnv: TButton
|
||||
Left = 8
|
||||
Top = 191
|
||||
Width = 169
|
||||
Height = 49
|
||||
Caption = 'Single ENV without inheritance (only prod)'
|
||||
TabOrder = 4
|
||||
WordWrap = True
|
||||
OnClick = btnSingleEnvClick
|
||||
end
|
||||
object btnRequireKeys: TButton
|
||||
Left = 8
|
||||
Top = 264
|
||||
Width = 169
|
||||
Height = 49
|
||||
Caption = 'Require Keys (OK)'
|
||||
TabOrder = 5
|
||||
OnClick = btnRequireKeysClick
|
||||
end
|
||||
object btnRequireKeys2: TButton
|
||||
Left = 8
|
||||
Top = 319
|
||||
Width = 169
|
||||
Height = 49
|
||||
Caption = 'Require Keys (FAIL)'
|
||||
TabOrder = 6
|
||||
OnClick = btnRequireKeys2Click
|
||||
ExplicitWidth = 1018
|
||||
ExplicitHeight = 637
|
||||
object TabSheet1: TTabSheet
|
||||
Caption = 'dotEnv Samples'
|
||||
DesignSize = (
|
||||
1014
|
||||
608)
|
||||
object btnSingleEnv: TButton
|
||||
Left = 8
|
||||
Top = 191
|
||||
Width = 169
|
||||
Height = 49
|
||||
Caption = 'Single ENV without inheritance (only prod)'
|
||||
TabOrder = 0
|
||||
WordWrap = True
|
||||
OnClick = btnSingleEnvClick
|
||||
end
|
||||
object btnRequireKeys: TButton
|
||||
Left = 8
|
||||
Top = 328
|
||||
Width = 169
|
||||
Height = 49
|
||||
Caption = 'Require Keys (OK)'
|
||||
TabOrder = 1
|
||||
OnClick = btnRequireKeysClick
|
||||
end
|
||||
object btnRequireKeys2: TButton
|
||||
Left = 8
|
||||
Top = 383
|
||||
Width = 169
|
||||
Height = 49
|
||||
Caption = 'Require Keys (FAIL)'
|
||||
TabOrder = 2
|
||||
OnClick = btnRequireKeys2Click
|
||||
end
|
||||
object btnProdEnv: TButton
|
||||
Left = 8
|
||||
Top = 126
|
||||
Width = 169
|
||||
Height = 49
|
||||
Caption = 'Prod ENV (default + prod)'
|
||||
TabOrder = 3
|
||||
OnClick = btnProdEnvClick
|
||||
end
|
||||
object btnTestEnv: TButton
|
||||
Left = 8
|
||||
Top = 71
|
||||
Width = 169
|
||||
Height = 49
|
||||
Caption = 'Test ENV (default + test)'
|
||||
TabOrder = 4
|
||||
OnClick = btnTestEnvClick
|
||||
end
|
||||
object btnSimple: TButton
|
||||
Left = 8
|
||||
Top = 16
|
||||
Width = 169
|
||||
Height = 49
|
||||
Caption = 'default ENV'
|
||||
TabOrder = 5
|
||||
OnClick = btnSimpleClick
|
||||
end
|
||||
object mmVars: TMemo
|
||||
Left = 183
|
||||
Top = 18
|
||||
Width = 825
|
||||
Height = 587
|
||||
Anchors = [akLeft, akTop, akRight, akBottom]
|
||||
Font.Charset = ANSI_CHARSET
|
||||
Font.Color = clWindowText
|
||||
Font.Height = -15
|
||||
Font.Name = 'Consolas'
|
||||
Font.Style = []
|
||||
ParentFont = False
|
||||
TabOrder = 6
|
||||
end
|
||||
end
|
||||
object TabSheet2: TTabSheet
|
||||
Caption = 'dotEnv Playground'
|
||||
ImageIndex = 1
|
||||
object Splitter1: TSplitter
|
||||
Left = 625
|
||||
Top = 0
|
||||
Height = 608
|
||||
ExplicitLeft = 408
|
||||
ExplicitTop = 168
|
||||
ExplicitHeight = 100
|
||||
end
|
||||
object Panel1: TPanel
|
||||
Left = 0
|
||||
Top = 0
|
||||
Width = 625
|
||||
Height = 608
|
||||
Align = alLeft
|
||||
Caption = 'Panel1'
|
||||
TabOrder = 0
|
||||
object Label1: TLabel
|
||||
AlignWithMargins = True
|
||||
Left = 4
|
||||
Top = 4
|
||||
Width = 617
|
||||
Height = 25
|
||||
Align = alTop
|
||||
Caption = '.env file contents'
|
||||
Font.Charset = DEFAULT_CHARSET
|
||||
Font.Color = clWindowText
|
||||
Font.Height = -19
|
||||
Font.Name = 'Segoe UI'
|
||||
Font.Style = []
|
||||
ParentFont = False
|
||||
ExplicitWidth = 142
|
||||
end
|
||||
object memSrc: TMemo
|
||||
AlignWithMargins = True
|
||||
Left = 4
|
||||
Top = 35
|
||||
Width = 617
|
||||
Height = 569
|
||||
Align = alClient
|
||||
Ctl3D = True
|
||||
EditMargins.Auto = True
|
||||
Font.Charset = ANSI_CHARSET
|
||||
Font.Color = clWindowText
|
||||
Font.Height = -15
|
||||
Font.Name = 'Consolas'
|
||||
Font.Style = []
|
||||
Lines.Strings = (
|
||||
'#############################################'
|
||||
'# Sample .env file'
|
||||
|
||||
'# This is a sample file just to play around with the .env syntax' +
|
||||
'.'
|
||||
|
||||
'# Feel free to change it and see how the variables are interpola' +
|
||||
'ted'
|
||||
'# and provided to your app at run-time'
|
||||
'#############################################'
|
||||
''
|
||||
'# This is a comment'
|
||||
''
|
||||
'# Let'#39's configure a database connection'
|
||||
'db_host = myserver'
|
||||
'db_port = 5432'
|
||||
'db_url = pg://${db_host}:${db_port}'
|
||||
''
|
||||
|
||||
'## variable names can start with ". _ a-z" and can contains "0-9' +
|
||||
' . _ a-z"'
|
||||
''
|
||||
'valid.varname = OK # this is valid'
|
||||
'.also.valid = OK # this is valid'
|
||||
'__also__valid__ = OK # this is valid'
|
||||
'.123 = OK # this is valid'
|
||||
'_.123 = OK # this is valid'
|
||||
|
||||
'# 3notvalid = KO # this is not valid (try to uncomment and see' +
|
||||
' the error)'
|
||||
''
|
||||
'#############################################'
|
||||
'# Logging configuration'
|
||||
'#############################################'
|
||||
''
|
||||
|
||||
'# loglevel can be: debug, normal (default), warning, error, exce' +
|
||||
'ption'
|
||||
'loglevel = error'
|
||||
''
|
||||
'dmvc.profiler.enabled = false'
|
||||
'dmvc.profiler.warning_threshold = 3000'
|
||||
''
|
||||
'#############################################'
|
||||
'# EMail configuration'
|
||||
'#############################################'
|
||||
''
|
||||
'email.sender = peter.parker@bittime.com'
|
||||
'email.smtpserver = mail.bittime.com'
|
||||
'email.smtpserverport = 25'
|
||||
''
|
||||
'#############################################'
|
||||
'# FireDAC configuration'
|
||||
'#############################################'
|
||||
''
|
||||
'# enable or disable tracing'
|
||||
'firedac.trace = true'
|
||||
''
|
||||
'# trace file name '
|
||||
'firedac.trace_filename = firedaclog.log'
|
||||
''
|
||||
'# append or replace the trace file contents at each execution?'
|
||||
'firedac.trace_file_append = false '
|
||||
''
|
||||
''
|
||||
'')
|
||||
ParentCtl3D = False
|
||||
ParentFont = False
|
||||
TabOrder = 0
|
||||
WordWrap = False
|
||||
OnChange = memSrcChange
|
||||
end
|
||||
end
|
||||
object Panel2: TPanel
|
||||
Left = 628
|
||||
Top = 0
|
||||
Width = 386
|
||||
Height = 608
|
||||
Align = alClient
|
||||
Caption = 'Panel2'
|
||||
TabOrder = 1
|
||||
object Label2: TLabel
|
||||
AlignWithMargins = True
|
||||
Left = 4
|
||||
Top = 4
|
||||
Width = 378
|
||||
Height = 25
|
||||
Align = alTop
|
||||
Caption = 'What'#39's application "sees"'
|
||||
Font.Charset = DEFAULT_CHARSET
|
||||
Font.Color = clWindowText
|
||||
Font.Height = -19
|
||||
Font.Name = 'Segoe UI'
|
||||
Font.Style = []
|
||||
ParentFont = False
|
||||
ExplicitWidth = 211
|
||||
end
|
||||
object memDst: TMemo
|
||||
AlignWithMargins = True
|
||||
Left = 4
|
||||
Top = 35
|
||||
Width = 378
|
||||
Height = 569
|
||||
Align = alClient
|
||||
Ctl3D = True
|
||||
EditMargins.Auto = True
|
||||
Font.Charset = ANSI_CHARSET
|
||||
Font.Color = clWindowText
|
||||
Font.Height = -15
|
||||
Font.Name = 'Consolas'
|
||||
Font.Style = []
|
||||
ParentCtl3D = False
|
||||
ParentFont = False
|
||||
ReadOnly = True
|
||||
TabOrder = 0
|
||||
WordWrap = False
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -5,25 +5,37 @@ interface
|
||||
uses
|
||||
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
|
||||
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls,
|
||||
MVCFramework.DotEnv;
|
||||
MVCFramework.DotEnv, Vcl.ComCtrls, System.IOUtils;
|
||||
|
||||
type
|
||||
TMainForm = class(TForm)
|
||||
btnSimple: TButton;
|
||||
mmVars: TMemo;
|
||||
btnTestEnv: TButton;
|
||||
btnProdEnv: TButton;
|
||||
Shape1: TShape;
|
||||
PageControl1: TPageControl;
|
||||
TabSheet1: TTabSheet;
|
||||
TabSheet2: TTabSheet;
|
||||
btnSingleEnv: TButton;
|
||||
btnRequireKeys: TButton;
|
||||
btnRequireKeys2: TButton;
|
||||
btnProdEnv: TButton;
|
||||
btnTestEnv: TButton;
|
||||
btnSimple: TButton;
|
||||
mmVars: TMemo;
|
||||
memDst: TMemo;
|
||||
Splitter1: TSplitter;
|
||||
memSrc: TMemo;
|
||||
Panel1: TPanel;
|
||||
Panel2: TPanel;
|
||||
Label1: TLabel;
|
||||
Label2: TLabel;
|
||||
procedure btnSimpleClick(Sender: TObject);
|
||||
procedure btnTestEnvClick(Sender: TObject);
|
||||
procedure btnProdEnvClick(Sender: TObject);
|
||||
procedure btnSingleEnvClick(Sender: TObject);
|
||||
procedure btnRequireKeysClick(Sender: TObject);
|
||||
procedure btnRequireKeys2Click(Sender: TObject);
|
||||
procedure memSrcChange(Sender: TObject);
|
||||
procedure FormShow(Sender: TObject);
|
||||
private
|
||||
procedure UpdatePlayGround;
|
||||
procedure UpdateUI(dotEnv: IMVCDotEnv);
|
||||
public
|
||||
{ Public declarations }
|
||||
@ -90,6 +102,43 @@ begin
|
||||
UpdateUI(dotEnv);
|
||||
end;
|
||||
|
||||
procedure TMainForm.FormShow(Sender: TObject);
|
||||
begin
|
||||
if DebugHook<>0 then
|
||||
begin
|
||||
ShowMessage('Please, run this sample without debugging!' + sLineBreak +
|
||||
'It will raise exceptions manhy times while using the playground');
|
||||
end;
|
||||
|
||||
UpdatePlayGround;
|
||||
end;
|
||||
|
||||
procedure TMainForm.memSrcChange(Sender: TObject);
|
||||
begin
|
||||
UpdatePlayGround;
|
||||
end;
|
||||
|
||||
procedure TMainForm.UpdatePlayGround;
|
||||
begin
|
||||
var lFileName := TPath.Combine(TPath.GetHomePath, '.env.playground');
|
||||
memSrc.Lines.SaveToFile(lFileName);
|
||||
try
|
||||
var dotEnv := NewDotEnv
|
||||
.WithStrategy(TMVCDotEnvPriority.OnlyFile)
|
||||
.UseProfile('playground')
|
||||
.Build(TPath.GetHomePath);
|
||||
memDst.Clear;
|
||||
memDst.Lines.AddStrings(dotEnv.ToArray);
|
||||
memDst.Color := clWindow;
|
||||
except
|
||||
on E: Exception do
|
||||
begin
|
||||
memDst.Lines.Text := E.Message;
|
||||
memDst.Color := clRed;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TMainForm.UpdateUI(dotEnv: IMVCDotEnv);
|
||||
begin
|
||||
Caption := 'dotEnv ShowCase :: MODE = ' + dotEnv.Env('mode');
|
||||
|
@ -101,8 +101,8 @@ 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]);
|
||||
raise EMVCDotEnvParser.CreateFmt('Error: %s - got "%s" at line: %d',
|
||||
[Error, fCurrChar, fCurLine + 1]);
|
||||
end;
|
||||
end;
|
||||
|
||||
@ -189,7 +189,7 @@ begin
|
||||
EatSpaces;
|
||||
Check(MatchSymbol('='), 'Expected "="');
|
||||
EatSpaces;
|
||||
Check(MatchValue(lValue), 'Expected "Value"');
|
||||
MatchValue(lValue);
|
||||
EnvDictionay.AddOrSetValue(lKey, lValue);
|
||||
EatSpaces;
|
||||
MatchInLineComment;
|
||||
@ -202,10 +202,11 @@ begin
|
||||
begin
|
||||
EatUpToLineBreak;
|
||||
EatLineBreaks;
|
||||
Inc(fCurLine);
|
||||
end
|
||||
else
|
||||
begin
|
||||
raise EMVCDotEnvParser.CreateFmt('Unexpected char "%s" at %d', [fCurrChar, fIndex - fSavedIndex]);
|
||||
raise EMVCDotEnvParser.CreateFmt('Unexpected char "%s" at line %d', [fCurrChar, fCurLine + 1]);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
@ -237,9 +238,22 @@ begin
|
||||
end;
|
||||
|
||||
function TMVCDotEnvParser.MatchIdentifier(out Value: String): Boolean;
|
||||
const
|
||||
FirstCharSet = ['a' .. 'z', 'A' .. 'Z', '_', '.'];
|
||||
CharSet = ['0' .. '9'] + FirstCharSet;
|
||||
begin
|
||||
Value := '';
|
||||
while CharInSet(fCode.Chars[fIndex], ['0' .. '9', 'a' .. 'z', 'A' .. 'Z', '_', '.', ':', '$', '%']) do
|
||||
if CharInSet(fCode.Chars[fIndex], FirstCharSet) then
|
||||
begin
|
||||
Value := fCode.Chars[fIndex];
|
||||
NextChar;
|
||||
end
|
||||
else
|
||||
begin
|
||||
Exit(False);
|
||||
end;
|
||||
|
||||
while CharInSet(fCode.Chars[fIndex], CharSet) do
|
||||
begin
|
||||
Value := Value + fCode.Chars[fIndex];
|
||||
NextChar;
|
||||
|
@ -271,6 +271,10 @@ type
|
||||
[Test]
|
||||
procedure TestKeyValue;
|
||||
[Test]
|
||||
procedure TestWithBadNames;
|
||||
[Test]
|
||||
procedure TestWithEmptyValue;
|
||||
[Test]
|
||||
procedure TestKeyValueWithQuotedValues;
|
||||
[Test]
|
||||
procedure TestValueWithMultiline;
|
||||
@ -2322,6 +2326,48 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TTestDotEnvParser.TestWithBadNames;
|
||||
const
|
||||
DOTENVCODE = 'key1=value1' + sLineBreak + '3key2 = 12';
|
||||
begin
|
||||
var lParser := TMVCDotEnvParser.Create;
|
||||
try
|
||||
var lDict := TMVCDotEnvDictionary.Create();
|
||||
try
|
||||
Assert.WillRaise(
|
||||
procedure
|
||||
begin
|
||||
lParser.Parse(lDict, DOTENVCODE);
|
||||
end,
|
||||
EMVCDotEnvParser);
|
||||
finally
|
||||
lDict.Free;
|
||||
end;
|
||||
finally
|
||||
lParser.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TTestDotEnvParser.TestWithEmptyValue;
|
||||
const
|
||||
DOTENVCODE = 'key1=value1' + sLineBreak + 'key2 = ' + sLineBreak + 'key3 = xyz ' + sLineBreak;
|
||||
begin
|
||||
var lParser := TMVCDotEnvParser.Create;
|
||||
try
|
||||
var lDict := TMVCDotEnvDictionary.Create();
|
||||
try
|
||||
lParser.Parse(lDict, DOTENVCODE);
|
||||
Assert.AreEqual('value1', lDict['key1']);
|
||||
Assert.AreEqual('', lDict['key2']);
|
||||
Assert.AreEqual('xyz', lDict['key3']);
|
||||
finally
|
||||
lDict.Free;
|
||||
end;
|
||||
finally
|
||||
lParser.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
initialization
|
||||
|
||||
TDUnitX.RegisterTestFixture(TTestRouting);
|
||||
|
@ -11,4 +11,4 @@ key2.4 = '${key2.1}|${key2.2}|${key2.3}|${key7}'
|
||||
key5=${key6}
|
||||
key6=${key3}
|
||||
key7=${key6}
|
||||
|
||||
key8=
|
Loading…
Reference in New Issue
Block a user