GithubHelp home page GithubHelp logo

exilon / quicklib Goto Github PK

View Code? Open in Web Editor NEW
595.0 75.0 178.0 54.77 MB

Quick development library (AutoMapper, LinQ, IOC Dependency Injection, MemoryCache, Scheduled tasks, Json and Yml Config and Options pattern, Serializers, etc) with crossplatform support for Delphi/Firemonkey (Windows,Linux,OSX/IOS/Android) and freepascal (Windows/Linux).

License: Apache License 2.0

Pascal 100.00%
delphi azure config json monitor console chronometer service freepascal thread

quicklib's Introduction

alt text

QuickLib is a delphi/Firemonkey(Windows, Linux, Android, OSX & IOS) and fpc(Windows & Linux) library containing interesting and quick to implement functions, created to simplify application development and crossplatform support and improve productivity. Delphi XE8 - Delphi 12 Athens supported.

Give it a star

Please "star" this project in GitHub! It costs nothing but helps to reference the code. alt text

Star History

Star History Chart

Support

If you find this project useful, please consider making a donation.

paypal

Areas of functionality:

  • Mapping: Map fields from a class to other class, copy objects, etc..
  • Config: Use your config as an object and load/save from/to file (Json/Yaml) or Windows Registry.
  • Serialization: Serialize objects to/from json/Yaml.
  • Scheduling: Schedule tasks launching as independent threads with retry policies.
  • Threading: Simplify run and control of multithread background tasks, Thread-safe Lists, queues, etc
  • Data: Flexible data interchange and storage, allowing several input-output types.
  • Cloud: Simplify cloud Azure/Amazon file management, send emails and more.
  • Querying: Indexed Lists, Searchable Lists and Linq query system for generic lists and arrays.
  • Benchmark: Time elapsed control and benchmark functions.
  • Filesystem: Process and Services control, file modify monitors and helpers, etc...
  • FailControl: Fail and Retry policies.
  • Caching:: Cache string or objects to retrieve fast later.
  • Templating: Simple string templating with dictionaries.
  • Debuging: Utils to debug your code.
  • Parameters: Work with commandline parameters.

Main units description:

  • Quick.Commons: Functions frequently needed in the day to day of a developer.
  • Quick.AppService: Allow a console app to run as console mode or service mode with same code simplifying debug tasks.
  • Quick.Azure/Amazon: Simplifies blob iteraction with Azure and Amazon Cloud Storage.
  • Quick.Network: CIDR and IP Range functions.
  • Quick.Chrono: Chronometer and Benchmark a piece of code is simple.
  • Quick.Console: Write log messages to console with colors and more...
  • Quick.Log: Log to disk or memory with verbose levels and daily or max space rotation.
  • Quick.Config: Load/Save a config as Json or Yaml file or Windows Registry keys and manage it as an object.
  • Quick.FileMonitor: Monitorizes a file for changes and throws events.
  • Quick.JsonUtils: Utils for working with json objects.
  • Quick.SMTP: Send email with two code lines.
  • Quick.Threads: Thread safe classes, scheduling and backgrounds tasks with retry policies.
  • Quick.Process: Manages windows processes.
  • Quick.Services: Manages windows services.
  • Quick.Format: String format.
  • Quick.RTTI.Utils: Simplifies working with RTTI.
  • Quick.JsonSerializer: Serializes an object from/to json text. You can define if public or published will be processed (only Delphi, fpc rtti only supports published properties)
  • Quick.AutoMapper: Map fields from one class to another class. Allows custom mappings to match different fields and custom mapping procedure to cast/convert fields manually.
  • Quick.JsonRecord: Used as a DTO class, with json serialize and mapping functions included.
  • Quick.Lists: Improved lists with indexing or search features.
  • Quick.Value FlexValue stores any data type and allow pass to other class with integrated operators and autofrees.
  • Quick.Arrays: Improved arrays.
  • Quick.YAML: Yaml object structure.
  • Quick.YAML.Serializer: Serialize/Deserialize object from/to Yaml.
  • Quick.Expression: Evaluate object properties using expressions.
  • Quick.Linq: Makes Linq queries to any TObjectList, TList, TArray and TXArray, performing Select by complex Where like SQL syntax, update and order over your list.
  • Quick.MemoryCache: Caches objects/info with an expiration time, to avoid generate this info everytime is needed (database queries, hard to calculate info, etc).
  • Quick.Collections: Collections improvements like IList and IObjectList with Linq inherited.
  • Quick.Pooling: Creation of object pool to avoid external resource consum exhausts and overheads.
  • Quick.Template: String template replacing with dictionary or delegate.
  • Quick.Debug.Utils: Simple debugging and code benchmark utils.
  • Quick.Parameters: Work with commandline parameters like a class.
  • Quick.Url.Utils: Simple url manipulation
  • Quick.RegEx.Utils: Commonly used RegEx comparison (email verification, password complexity, etc)
  • Quick.Conditions: Pre and postcondition validations in fluent style.

Updates:

  • NEW: RAD Studio 12 supported
  • NEW: RAD Studio 11 supported
  • NEW: Condition checks
  • NEW: Commonly used RegEx validations
  • NEW: Url manipulation utils
  • NEW: QuickParameters to work with commandline arguments like a class.
  • NEW: HttpServer custom and dynamic error pages.
  • NEW: Debug utils
  • NEW: String Template
  • NEW: RAD Studio 10.4 supported
  • NEW: Collections: IList and IObjectList with linQ support.
  • NEW: Pooling: ObjectPool.
  • NEW: Options file settings with sections.
  • NEW: MemoryCache with expiration & object compression.
  • NEW: Now included on RAD Studio GetIt package manager.
  • NEW: Background & Scheduled task with retry policies
  • NEW: RunTask, FaultControl
  • NEW: Linq over generic lists and arrays.
  • NEW: QuickConfig YAML provider.
  • NEW: YAML Object and Serializer
  • NEW: AutoMapper customapping path namespaces style.
  • NEW: FlexArray, FlexPair & FlexPairArray.
  • NEW: AutoMapper mapping procedures (see documentation below)
  • NEW: JsonSerializer improved
  • NEW: TXArray: array like TList
  • NEW: Delphi Linux compatibility
  • NEW: QuickConfigJson reload if config file changed
  • NEW: First version with OSX/IOS partial support
  • NEW: Delphinus-Support

Installation:

  • From package managers:
  1. Search "QuickLib" on Delphinus or GetIt package managers and click Install
  • From Github:
  1. Clone Github repository or download zip file and extract it.
  2. Add QuickLib folder to your path libraries on Delphi IDE.

Documentation:

Quick.AppService:

Allow a console app to run as console mode or service mode with same code simplifying debug tasks.

if not AppService.IsRunningAsService then
begin
    ...your code running as console
end
else
begin
    AppService.ServiceName := 'MyService';
    AppService.DisplayName := 'MyServicesvc';
    //you can pass an anonymous method to events
    AppService.OnStart := procedure
                          begin
                            ...your start code
	                      end;
    AppService.OnExecute := YourExecuteFunction;
    AppService.OnStop := YourStopFunction;
    AppService.CheckParams;
end;

Quick.Azure/Amazon:

Simplifies blob iteraction with Azure and Amazon Cloud Storage.

//connect to a Azure blobstorage
QuickAzure := TQuickAzure.Create(AzureAccountName,AzureAccountKey);

//download a blob file to a stream
done := QuickAzure.GetBlob('MyContainer','MyFile.jpg',ResponseInfo,MyStream);
    
//check if exists a folder
found := ExistFolder('MyContainer','/Public/Documents/Personal');
    
//list blobs starting with a pattern (recursively or not)
for azBlob in ListBlobs('MyContainer','/Public/Documents',Recursive,ResponseInfo) do
begin
    if azBlob.Size > 1000 then Showmessage(azBlob.Name);
end;

Quick.Network:

CIDR and IP Range functions.

//convert ip string to integer
IPv4ToInt('192.168.1.10');

//get first and last ip of a subnet scope
GetIpRange('192.168.100.0','255.255.255.0',LowIp,HighIP);

Quick.Commons:

Functions frequently needed in the everyday of a developer.

//coverts UTC time TDateTime to Local date time
UTCToLocalTime(MyUTCTime);
    
//generate a 10 char length random password with alfanumeric and signs.
RandomPassword(10,[pfIncludeNumbers,pfIncludeSigns]);

//Capitalize every word of a phrase
CapitalizeAll('the grey fox'); //returns "The Grey Fox"

//Simple TCounter and TTimeCounter for loops
counter := TCounter;
counter.Init(200);
timecounter : TTimeCounter;
timecounter.Init(10000);
while true do
begin
    Inc(n);
    {your procedural process here}
    //every 200 steps writes to console
    if counter.Check then writeln(Format('Processed %d entries',[n]));
    //every 10 seconds writes to console
    if timecounter.Check then writeln('Im working...'); 
end;

Quick.Chrono:

Chronometer and Benchmark a piece of code is simple.

//get elapsed time execution of a code part
Chrono := TChronometer.Create(False);
Chrono.Start;
...code you need benchmark
Chrono.Stop;

//shows elapsed time in LongTime format (2 hour(s) and 10 minute(s))
Showmessage(Chrono.TimeElapsed(True));

//shows elapsed time in ShortTime format (02:10:00)
Showmessage(Chrono.TimeElapsed(False));
//get benchmak info of a process
Chrono := TChronoBenchMark.Create;
Chrono.TotalProcess := 100000;
for i := 1 to 10000 do
begin
    {your process here}
    Chrono.CurrentProcess := i;
    //shows estimated time your process will take in x hour(s), x minute(s) x second(s) format
    writeln(Chrono.EstimatedTime(True));
    //shows speed: num items per second processed of your process
    writeln(Format('Items processed %d/sec',[Chrono.Speed]));
end;
writeln(Chrono.ElapsedTime(False)); //shows total time elapsed in 00:00:00 format

Quick.Console:

Write log messages to console with colors and more...

//define which level of output needed
Console.Verbose := LOG_DEBUG;

//writes line to console in red color
cout('Error x',etError); 

//writes formatted line in green color
coutFmt('Proccess %s finished',[ProccesName],etSuccess);

//writes integer
cout(12348);

//Connect a QuickLog and write to disk and screen with one line of code (with independent verbose levels)
MyQuickLog := TQuickLog.Create;
MyQuickLog.Verbose := LOG_ALL;
Console.Verbose := LOG_ONLYERRORS;
Console.Log := MyQuickLog;

Quick.Log:

Log to disk or memory with verbose levels and daily or max space rotation.

//write a header on start with info as running path, appname, debugmode, user, etc...
Log.ShowHeader := True;

//sets log with rotation at 20MB
Log.SetLog('.\mylog.log',False,20);

//write an error message
Log.Add('Error x',etError);

//write formatted error message
Log.Add('Error is %s',[ErrorStr],etError);

Quick.Config:

Load/Save a config as Json or Yaml file or Windows Registry keys. Create a descend class from TAppConfigJson, TAppConfigYaml or TAppConfigRegistry and added published properties will be loaded/saved. Files configs can be reloaded on detect files changes.

//create a class heritage
TMyConfig = class(TAppConfigJson)
private
    fName : string;
    fSurname : string;
    fStatus : Integer;
published
    property Name : string read fName write fName;
    property SurName : string read fSurname write fSurname;
    property Status : Integer read fStatus write fStatus;
end;

//create your config to json file
//Add Quick.Config.Json to your uses
MyConfig := TMyConfig.Create('Config.json');
MyConfig.Provider.CreateIfNotExists := True;
MyConfig.Provider.ReloadIfFileModified := True;
MyConfig.Name := 'John';
MyConfig.Surname := 'Smith';
//load
MyConfig.Load;
//save
MyConfig.Save;
  
//create your config to Windows Registry
//Add Quick.Config.Registry to your uses
MyConfig := TMyConfig.Create;
//Define Registry as HKEY_CURRENT_USER\Software\MyApp
MyConfig.HRoot := HKEY_CURRENT_USER; 
MyConfig.MainKey := 'MyApp';
MyConfig.Name := 'John';
MyConfig.Surname := 'Smith';
//load
MyConfig.Load;
//save
MyConfig.Save;

//Create a custom Config with no default provider
TMyConfig = class(TAppConfig)
...your properties
end;

MyConfig := TMyConfig.Create(TAppConfigJsonProvider.Create('.\config.json');

Quick.FileMonitor:

Monitorizes a file for changes and throws events.

FileMonitor.Filename := '.\myfile.txt';
//check file changes every 2 seconds
FileMonitor.Interval := 2000;
//watch for deleted or modified file events
FileMonitor.Notifies := [mnFileModified, mnFileDeleted)];
FileMonitor.OnFileChange := MyFileChangeFunction;
FileMonitor.Enabled := True;

Quick.JsonUtils:

Utils for working with json objects.

//When unit declared in uses, a TObject Helper allows all your objects to be loaded or saved to/from json string
MyObject.FromJson := jsonstring;
MyString := MyObject.ToJson;

//You can clone simple objects with clone function
MyObject1.Clone(MyObject2);

Quick.SMTP:

Send email with two code lines.

//Send email
SMTP := TSMTP.Create('mail.domain.com',25,False);
SMTP.SendMail('[email protected]','[email protected]','Email subject','My message body');

//You can define more advanced options
SMTP.SenderName := 'John';
SMTP.From := '[email protected]';
SMTP.Recipient := '[email protected],[email protected]';
SMTP.Subject := 'Email subject';
SMTP.AddBodyFromFile := '.\body.html';
SMTP.CC := '[email protected]';
SMTP.BCC := '[email protected]';
SMTP.Attachments.Add('.\notes.txt');
SMTP.SendMail;

Quick.Threads:

Thread safe classes.

TThreadedQueueCS: Version of TThreadedQueue with Critical Section.

TThreadObjectList: Thread safe Object List.

TThreadedQueueList: Thread safe Queue List. Autogrow and with Critical Section.

TAnonymousThread: Creates anonymous thread defining unchained Execute and OnTerminate methods. Use Execute_Sync and OnTerminate_Sync methods if code needs to update UI.

  • Execute: Specify code to execute on start.
  • Execute_Sync: Like Execute but runs code with syncronized thread method (avoids problems if your code updates UI).
  • OnTerminate: Specify code to execute when task finishes.
  • OnTerminate_Sync: Like OnTerminate but runs code with syncronized thread method (avoids problems if your code updates UI).
  • Start: Starts thread execution.
//simple anonymousthread
TAnonymousThread.Execute(
      procedure
      var
        i : Integer;
      begin
        for i := 0 to 10 do cout('Working %d',[i],etTrace);
        cout('executed thread',etSuccess);
      end)
    .OnTerminate(
      procedure
      begin
        cout('terminated thread',etSuccess);
        cout('PRESS <ENTER> TO EXIT',etInfo);
      end)
    .Start;

TRunTask: Launch an autofree single task thread with fault & retry control policies. Params can be passed and created into code.

  • Define code to execute:
    • Execute: Specify Task name, parameters to pass to anonymous method(If OwnedParams=true, task will free params on termination task) and method than will be executed.
    • Execute_Sync: Like Execute but runs code with synchronize thread method (avoids problems if your code updates UI).
    • SetParameter: Defines values or objects needed by your task.
  • Define events to control:
    • OnInitialize: Specify code to run before main execute task (this code only runs one time, OnExecute can be retried more than one time)
    • OnRetry: Specify code to run when execution fails and decide if needs to retry or cancel next retries.
    • OnTerminate: Specify code to execute when task finishes.
    • OnTerminate_Sync: Like OnTerminate but runs code with syncronized thread method (avoids problems if your code updates UI).
    • OnException: Specify code to execute when task generates an exception.
  • Define fail/retry policies:
    • RetryForever: If execution fails, code will be retry forever until task executes ok.
    • Retry: If execution fails, code will be retry x times.
    • WaitAndRetry: If execution fails, code will be retry x times, and wait x millisecons before each retry. You can specify number of retries and wait time between retries.
    • Run: Starts task execution.
  TRunTask.Execute(
      procedure(task : ITask)
      var
        stream : TStringStream;
        response : IHttpRequestResponse;
      begin
        stream := TStringStream.Create;
        try
          response := TJsonHttpClient(task['httpclient'].AsObject).Get(task['url']);
          task.Result := response.StatusCode;
          if response.StatusCode <> 200 then raise Exception.Create(response.StatusText);
        finally
          stream.Free;
        end;
      end)
    .SetParameter('httpclient',(TJsonHttpClient.Create),True)
    .SetParameter('url','https://mydomain.com/testfile')
    .WaitAndRetry(5,250,2)
    .OnRetry(
      procedure(task : ITask; aException : Exception; var vStopRetries : Boolean)
      begin
        //if error 404 don't try to retry request
        if task.Result = 404 then vStopRetries := True;
      end)
    .OnException(
      procedure(task : ITask; aException : Exception)
      begin
        coutFmt('Exception downloading (Error: %s / StatusCode: %d)...',[aException.Message,task.Result.AsInteger],etError);
      end)
    .OnTerminated(
      procedure(task : ITask)
      begin
        if task.Done then coutFmt('Download "%s" finished ok',[task['url'].AsString],etSuccess)
          else coutFmt('Download "%s" failed after %d retries',[task['url'].AsString,task.NumRetries],etError);
      end)
    .Run;

TBackgroundsTasks: Launch tasks in background allowing number of concurrent workers with fault and retry control policies. Use AddTask_Sync and OnTerminate_Sync methods if code needs to update UI.

  • Add a task to execute:
    • AddTask: Specify Task name, parameters to pass to anonymous method(If OwnedParams=true, task will free params on expiration task) and method than will be executed.
    • AddTask_Sync: Like AddTask but runs code with synchronize thread method (avoids problems if your code updates UI).
    • SetParameter: Defines values or objects needed by your task. Every parameter will be accesible into anomymous methods defines as task[] or task.[index]
  • Define events to control:
    • OnInitialize: Specify code to run before main execute task (this code only runs one time, OnExecute can be retried more than one time)
    • OnRetry: Specify code to run when execution fails and decide if needs to retry or cancel next retries.
    • OnTerminate: Specify code to execute when task finishes.
    • *OnTerminate_Sync: Like OnTerminate but runs code with syncronized thread method (avoids problems if your code updates UI).
    • OnException: Specify code to execute when task generates an exception.
  • Define fail/retry policies:
    • RetryForever: If execution fails, code will be retry forever until task executes ok.
    • Retry: If execution fails, code will be retry x times. Allow define array of milliseconds as wait time.
    • WaitAndRetry: If execution fails, code will be retry x times, and wait x millisecons before each retry. You can specify number of retries and wait time between retries.
  • Begin execution:
    • Start: Starts tasks execution.
    backgroundtasks := TBackgroundTasks.Create(10);
    for i := 1 to 100 do
    begin
      mytask := TMyTask.Create;
      mytask.Id := i;
      mytask.Name := 'Task' + i.ToString;
      backgroundtasks.AddTask([mytask],False,
                              procedure(task : ITask)
                              begin
                                cout('task %d started',[TMyTask(task.Param[0].AsObject).Id],etDebug);
                                TMyTask(task.Param[0].AsObject).DoJob;
                              end
							).WaitAndRetry([250,2000,10000])
                            ).OnException(
                              procedure(task : ITask; aException : Exception)
                              begin
                                cout('task %d failed (%s)',[TMyTask(task.Param[0].AsObject).Id,aException.Message],etError);
                              end
                            ).OnTerminated(
                              procedure(task : ITask)
                              begin
                                cout('task %d finished',[TMyTask(task.Param[0].AsObject).Id],etDebug);
                                TMyTask(task.Param[0].AsObject).Free;
                              end
                            ).Run;
    end;
    backgroundtasks.Start;

TScheduledTasks: Alternative to Timer. You can assign tasks with start time, repeat options and expiration date and fail and retry control policies. Use AddTask_Sync, OnTerminate_Sync and OnExpired_Sync if code needs to update UI. You can assign anonymous methods to execute, exception, terminate and expiration events.

  • Add a task to execute:
    • AddTask: Specify Task name, parameters to pass to anonymous method(If OwnedParams=true, task will free params on expiration task) and method than will be executed.
    • AddTask_Sync: Like AddTask but runs code with synchronize thread method (avoids problems if your code updates UI).
    • SetParameter: Defines values or objects needed by your task. Every parameter will be accesible into anomymous methods defines as task[] or task.[index]
  • Define events to control:
    • OnInitialize: Specify code to run before main execute task (this code only runs one time, OnExecute can be retried more than one time)
    • OnRetry: Specify code to run when execution fails and decide if needs to retry or cancel next retries.
    • OnTerminate: Specify code to execute when task finishes.
    • OnTerminate_Sync: Like OnTerminate but runs code with syncronized thread method (avoids problems if your code updates UI).
    • OnExpire: Specify code to execute when task expiration reached or task was cancelled.
    • OnExpire_Sync: Like OnExpire but runs code with synchronized thread method (avoids problems if your code updates UI).
    • OnException: Specify code to execute when task generates an exception.
  • Define when to start task:
    • StartNow: Start task immediately.
    • StartAt: Date and time to start task.
    • StartTodayAt: Start task today at defined time.
    • StartTomorrowAt: Start task tomorrow at defined time.
    • StartOnDayChange: Start task when day changes.
    • StartInMinutes: Start task after x minutes.
    • StartInSeconds: Start task after x seconds.
  • Define if needs to repeat or not (if not defined a previous StartAt, StartOn, etc, task will be executed immediately):
    • RunOnce: Task will be executed one time only.
    • RepeatEvery: Can indicate repeat step over time and expiration date.
    • RepeatEveryDay: Repeat task every day at same hour.
    • RepeatEveryWeek: Repeat task every week at same hour.
  • Define fail/retry policies:
    • RetryForever: If execution fails, code will be retry forever until task executes ok.
    • Retry: If execution fails, code will be retry x times.
    • WaitAndRetry: If execution fails, code will be retry x times, and wait x millisecons before each retry. You can specify number of retries and wait time between retries.
  • Start/Stop scheduler:
    • Start: Starts scheduler.
    • Stop: Stops scheduler.
myjob := TMyJob.Create;
myjob.Name := Format('Run at %s and repeat every 1 second until %s',[DateTimeToStr(ScheduledDate),DateTimeToStr(ExpirationDate)]);
scheduledtasks.AddTask('Task1',[myjob],True,
                            procedure(task : ITask)
                            begin
                              cout('task "%s" started',[TMyTask(task.Param[0]).Name],etDebug);
                              TMyJob(task.Param[0]).DoJob;
                            end
                          ).OnException(
                            procedure(task : ITask; aException : Exception)
                            begin
                              cout('task "%s" failed (%s)',[TMyJob(task.Param[0]).Name,aException.Message],etError);
                            end
                          ).OnTerminated(
                            procedure(task : ITask)
                            begin
                              cout('task "%s" finished',[TMyJob(task.Param[0]).Name],etDebug);
                            end
                          ).OnExpired(
                            procedure(task : ITask)
                            begin
                              cout('task "%s" expired',[TMyJob(task.Param[0]).Name],etWarning);
                            end
                          ).StartAt(ScheduledDate
                          ).RepeatEvery(1,TTimeMeasure.tmSeconds,ExpirationDate);
scheduledtasks.Start;
  • ITask: Interface passed to every task event of TRunTask, TBackgroundTasks and TScheduledTasks.
    • NumWorker: Return number of worker assigned to execute task.
    • Result: Can store any value type (TFlexValue is like variant type)
    • Param[name]: Can store parameters passed to task or created dynamically into every anonymous methods passed to each event.
    • Param[index]: Can store parameters passed to task or created dynamically into every anonymous methods passed to each event.
    • Done: Return true is task is executed without errors.
    • Failed: Return true is task has failed.
    • IdTask: Task id defined.
    • NumRetries: Number of retries done.
    • MaxRetries: Number of maximum retries allowed before mark task as failed.
    • LastException: Return last exception of a failed task.
    • CircuitBreaked: Return true if max retries has been reached or user cancelled into OnRetry event.
    • IsEnabled: Return status of task.

Quick.FaultControl:

Manages fail and retry policies, defining max retries, wait time beetween retries and circuit break mecanism.

Quick.Process:

Manages windows processes.

//kill explorer process
KillProcess('explorer.exe');
//determine if an application is running
if IsProcessRunning('explorer.exe') then Showmessage('Explorer is running!');
//get username who is running an exe
writeln('Explorer.exe open by: ' + GetProcessUser('explorer.exe');
//gets handle of a window with a 20 seconds timeout
if FindWindowTimeout('MainWindow',20) then writeln('Window detected');

Quick.Services:

Manages windows services.

//detect if a service is installed
if not ServiceIsPresent('localhost','MySvc') then raise Exception.Create('Service not installed!');
//Start a service
ServiceStart('localhost','MySvc');
//Uninstall a service
ServiceUninstall('MySvc');

Quick.Format:

String format.

//Format bytes to MB, GB, TB...
FormatBytes(50000) //shows 50KB
FormatBytes(90000000) //shows 90MB

Quick.JsonSerializer:

Serializes an object from/to json text. You can define if public or published will be processed (only Delphi, fpc rtti only supports published properties)

json := '{"name":"Peter","age":30}';
serializer := TJsonSerializer.Create(TSerializeLevel.slPublishedProperty);
try
   serializer.JsonToObject(user,json);
finally
   serializer.Free;
end;

Quick.AutoMapper:

Map fields from one class to another class. Allows custom mappings to match different fields and custom mapping procedure to cast/convert fields manually.

//Map values from User1 to User2
TMapper<TUser2>.Map(User);

//Map custom mappings
AutoMapper := TAutoMapper<TUser,TUser2>.Create;

//option1: you can define auto map different named properties
AutoMapper.CustomMapping.AddMap('Cash','Money');
AutoMapper.CustomMapping.AddMap('Id','IdUser');

//option2: you can decide to modify each property manually or allow to auto someones
AutoMapper.OnDoMapping := procedure(const aSrcObj : TUser; const aTargetName : string; out Value : TFlexValue)
                          begin
                            if aTargetName = 'Money' then Value := aSrcObj.Cash * 2
                              else if aTargetName = 'IdUser' then Value := aSrcObj.Id;
                          end;

//option3: you can modify some properties after automapping done
AutoMapper.OnAfterMapping := procedure(const aSrcObj : TUser; aTgtObj : TUser2)
                             begin
                               aTgtObj.Money := aSrcObj.Cash * 2;
                               aTgtObj.IdUser := aSrcObj.Id;
                             end;

User2 := AutoMapper.Map(User);

Quick.JsonRecord:

Used as a DTO class, with json serialize and mapping functions included.

type
   TUser = class(TJsonRecord)
   private
      fName : string;
      fAge : Integer;
   published
      property Name : string read fName write fName;
      property Age : Integer read fAge write fAge;
   end;
var
   user, user2 : TUser;
begin
   user := TUser.Create;
   //show as json string
   Writeln(user.ToJson);
   //mapping to other class
   user.Mapto(User2);
   Writeln(user2.ToJson);
   //load from file
   user.LoadFromFile('.\user.json');
   //save to file
   user2.SaveToFile('.\user2.json');
end;

Quick.Lists:

Improved lists with indexing or search features.

  • TIndexedObjectList: Allows fast hashed searches by object properties or fields.
  • TSearchObjectList: Allows iteration search by object properties or fields.
var
   users : TIndexedObjectList<TUser>;
begin
   users := TIndexedObjectList<TUser>.Create(True);
   //create index by property "Name"
   users.Indexes.Add('Name','Name',TClassField.cfProperty);
   //create index by private field "Id"
   users.Indexes.Add('Id','fId',TClassField.cfField);
   //get user by "Name" index
   writeln(users.Get('Name','Peter').SurName);
end;

Quick.Value

FlexValue stores any data type and allow pass to other class with integrated operators and autofrees.

var
  value : TFlexValue;
  str : string;
  num : Integer; 
begin
  value := 'hello';
  str := value;
  value := 123;
  num := value;
end;

Quick.Arrays:

Improved arrays.

TXArray: Array with methods like TList.

var
   users : TXArray<TUser>;
begin
   users.Add(User);
   if users.Count:= TIndexedObjectList<TUser>.Create(True);
   //create index by property "Name"
   users.Indexes.Add('Name','Name',TClassField.cfProperty);
   //create index by private field "Id"
   users.Indexes.Add('Id','fId',TClassField.cfField);
   //get user by "Name" index
   writeln(users.Get('Name','Peter').SurName);
end;

TFlexArray: Array with methods like TList than can storage different value types into same array.

var
  flexarray : TFlexArray;
begin
    flexarray.Add(10);
    flexarray.Add('Hello');
    user := TUser.Create;
    try
      user.Name := 'Joe';
      flexarray.Add(user);

      cout('Integer Item = %d',[flexarray[0].AsInteger],etInfo);
      cout('String Item = %s',[flexarray[1].AsString],etInfo);
      cout('Record Item = %s',[TUser(flexarray[2]).Name],etInfo);
    finally
      user.Free;
    end;
end;

TFlexPairArray: Array with methods like TList than can store different value types into same array, and search by item name.

var
  flexarray : TFlexPairArray;
begin
    flexarray.Add('onenumber',10);
    flexarray.Add('other','Hello boy!');
    user := TUser.Create;
    try
      user.Name := 'Joe';
      flexarray.Add('myuser',user);

      cout('Integer Item = %d',[flexarray.GetValue('onenumber').AsInteger],etInfo);
      cout('String Item = %s',[flexarray.GetValue('other').AsString],etInfo);
      cout('Record Item = %s',[TUser(flexarray.GetValue('myuser')).Name],etInfo);
    finally
      user.Free;
    end;
end;

Quick.YAML:

Yaml object structure.

TYamlObject: A Yaml object is and array of YamlValue pairs.

  //create Yaml object from yaml text
  yamlobj.ParseYamlValue(aYaml)
  //add a pair
  yamlobj.AddPair('Name','Mike');
  //display as yaml structure
  Writeln(yamlobj.ToYaml);

TYamlArray: Array of objects or scalars.

  yamlarray.AddElement(TYamlPair.Create('Age',30));
  yamlobj.AddPair('myarray',yamlarray);

TYamlPair: Name-Value pair. Value can be object, array or scalar.

  n := yamlobj.GetPair('Name').Value as TYamlInteger;

Quick.YAML.Serializer:

Serialize/Deserialize object from/to Yaml.

  //Serialize
  text := YamlSerializer.ObjectToYaml(obj);
  //Deserialize
  YamlSerializer.YamlToObject(obj,yamltext);

Quick.Expression:

Evaluate object properties or single values using expressions.

  if TExpressionParser.Validate(user,('(Age > 30) AND (Dept.Name = "Financial")') then
  begin
    //do something
  end;

  if TExpressionParser.Validate(user,('(20 > 30) OR (5 > 3)') then
  begin
    //do something
  end;

Quick.Linq:

Makes Linq queries to any TObjectList, TList, TArray and TXArray, performing Select by complex Where like SQL syntax, update and order over your list. Where clauses uses namespaces to determine nested properties. LinQ can search for a element into a property array. Now includes and TArray helper to add, remove and search with regular expressions into array.

  • From: Array, XArray or TObjectList to use.
  • Where: Expression to search. You can use a dots to define property path.
  • SelectAll: Returns an array of objects matching where clause
  • SelectTop: Returns top x objects matching where clause.
  • SelectFirst: Returns first object matching where clause.
  • SelectLast: Returns last object matching where clause.
  • OrderBy: Define order of returned list.
  • Update: Update fields of matching where clause.
  • Delete: Delete objectes matching where clause.
  • Count: Return number of elements matching where clause.
  //Select multi conditional
  for user in TLinq<TUser>.From(userslist).Where('(Name = ?) OR (SurName = ?) OR (SurName = ?)',['Peter','Smith','Huan']).Select do
  begin
    //do something
  end;
  
  //Select like and update field
  TLinq<TUser>.From(userlist).Where('SurName Like ?',['%son']).SelectFirst.Name := 'Robert';
  
  //Select top and Order by field
  for user in TLinq<TUser>.From(userlist).Where('Age > ?',[18]).SelectTop(10).OrderBy('Name') do
  begin
    //do something
  end;
  
  //update fields by conditional
  TLinq<TUser>.From(userlist).Where('Name = ?',['Peter']).Update(['Name'],['Joe']);
  
  //count results
  numusers := TLinq<TUser>.From(userlist).Where('(Age > ?) AND (Age < ?)',[30,40]).Count;

Quick.HTTPServer:

TCustomHttpServer is a simple interfaced HttpServer with own HttpRequest and HttpResponse implementations to allow easy httpserver engine changes. You can enable custom error pages to return customized pages and dynamic error pages. THttpServer is the IndyHttpServer implementation, but you can define your own.

TMyHttpServer = class(THttpServer)
  public
    procedure ProcessRequest(aRequest: IHttpRequest; aResponse: IHttpResponse); override;
  end;

  procedure TMyHttpServer.ProcessRequest(aRequest: IHttpRequest; aResponse: IHttpResponse);
  begin
    aResponse.ContentText := 'Hello world!';
  end;

Quick.MemoryCache:

Caches objects or strings with an expiration time, to avoid generate this info everytime is needed (database queries, hard to calculate info, etc). TMemoryCache allows to cache objects and strings. Generic version TMemoryCache allows to cache a defined type only.

  • Create: Could be defined Purge interval and Serialization and Compression engines. By default serializes as Json and compress with gzip.
 //create MemoryCache with 10 seconds purge interval
 cache := TMemoryCache.Create(10);

 //create MemoryCache for a type
 cache := TMemoryCache<TMyObj>.Create;
  • Compression: Enables/Disables cache data compression.
  • CachedObjects: Returns number of objects currently in cache.
  • CacheSize: Returns size in bytes of all objects currently in cache. Real memory used depends of memory managers or architecture. This value is the real size of data bytes.
  • PurgeInterval: Interval purge job tries to find expired objects to remove from cache (Default value 20 seconds).
  • OnCacheFlushed: When cache is flushed.
  • OnBeginPurgerJob: When PurgerJob starts.
  • OnEndPurgerJob: When PurgerJob ends.
  • Flush: Removes all cache objects.
  • SetValue: Adds an object to cache. You can indicate expiration date or number of milliseconds to expire. If not defined cache will be infinity. MemoryCache can store objects or strings.
//set string to cache without expiration
cache.SetValue('mystring','hello world');

//set string to cache with expiration to 10 seconds
cache.SetValue('mystring','this cache will expire in 10 seconds';

//set object to cache
cache.SetValue('Obj1',valueobj);
  • TryGetValue: Tries to get and object from cache. Returns false if object doesn't exists or it's expired.
//get string query result
cache.GetValue('Query12');

//get integer
cache.TryGetValue<Integer>('number',valueint);

//get object
cache.TryGetValue('Obj1',valueobj);
  • RemoveValue: Removes an object from cache.

  • Cache Engine providers:

  • TCacheSerializerJSON: Uses JSON to serialize cache data.

  • TCacheCompressorGzip: Uses Gzip to compress cache data.

  • TCacheCompressorLZO: Uses LZO to compress cache data.

 //create MemoryCache with 20 seconds purge interval and compression with LZO engine
 cache := TMemoryCache.Create(10,nil,TCacheCompressorLZO.Create);

Quick.IOC:

Inversion of Control manager allows autocreate interfaced o instanced object or autoinject them in constructor classes, to avoid dependency.

Create a container to manage dependency injection.

iocContainer := TIocContainer.Create;

Register Types:

You need to register types before you can inject them. A Type can be registered as Singleton, Transient. Singleton: Life cycle will be one single instance for all injections, similar to a Global variable. Transient: Life cycle will be one instance per each injection. Register an interface type into container as transient:

iocContainer.RegisterType<IMultService,TMultService>.AsTransient;

Register an interface type as singleton, delegating construction:

iocContainer.RegisterType<ISumService,TSumService>.AsSingleTon.DelegateTo(
  function : TSumService
  begin
    Result := TSumService.Create;
  end
);

Register Instances:

Register a named instance object as transient, delegating construction:

iocContainer.RegisterInstance<TDivideService>('one').AsTransient.DelegateTo(
  function : TDivideService
  begin
    Result := TDivideService.Create(True);
  end
);

Register Options:

Register IOptions (only singleton):

 iocContainer.RegisterOptions<TMyOptions>(MyOptions);

Resolve Types:

AbtractFactory: Creates a class trying to resolve all creation method parameter with dependency injection.

MyClass := iocContainer.AbstractFactory<TMyBaseClass>(TMyClass);

Resolve an interface dependency:

multservice := iocContainer.Resolve<IMultService>;
result := multservice.Mult(2,4);

Resolve Instances:

Resolve a named instance dependency:

divideservice := iocContainer.Resolve<TDivideService>('other');
result := divideservice.Divide(100,2);

Interface instances will be freed automatically, but instance dependencies only will be freed if defined as singleton, transient instances will be destroyed by code.

Quick.Options:

You define sections as classes and saves as single file settings. Works similar to dotnet Options. Options file can be in JSON or YAML format.

Define your option class inherited from TOptions and all published properties will be load/save. Create options container, with JsonSerializer and reloading on change:

Options := TOptionsContainer.Create('.\options.conf',TJsonOptionsSerializer.Create,True);

Add a section to your container options:

Options.AddSection<TLoggingOptions>('Logging')

Configure Options:

You can define section name to save into file and delegate configuration default settings and validating values:

Options.AddSection<TLoggingOptions>('Logging').ConfigureOptions(
  procedure(aOptions : TLoggingOptions)
  begin
    aOptions.Path := 'C:\';
  end
).ValidateOptions;

Validate Options:

Validate options allows verify if option settings are setted between defined ranges. This validation needs previously assigned custom attributes to properties in your TOptions class.

  • StringLength(max,messagestr): Allows define max length in string properties, returning messagestr if length greater than max.
  • Range(min,max,messagestr): Allows define a range of min and max values permitted, returning messagestr if value length outbounds margins.
TLoggingOptions = class(TOptions)
  private
    fPath : string;
  published
    [Required, StringLength(255,'Path too long')]
    property Path : string read fPath write fPath;
    [Range(0.0,5.2)]
    property Level : Double read fLevel write fLevel;
  end;

Use Options: To retrieve option section:

LoggingOptions := Options.GetSection<TLoggingOptions>;
LoggginOptions.Path := 'C:\Path';

Use IOptions: IOptions is a dependency injectable interface to TOptions. You can register with IocContainer.RegisterOptions to make injectable into constructor methods.

UIOptions := Options.GetSectionInterface<TUIOptions>.Value;
UIOptions.WindowColor := clBlue;

Load/Save Options:

Load options from file settings:

options.Load;

Save options to file settings:

options.Save;

If you defined container creation with ReloadOnChanged parameter to true, every time file settings is changed, configuration will be reloaded. If you need to control when to reload, you can listen to the event:

Options.OnFileModified := procedure
  begin
    cout('Detected config file modification!',etWarning);
  end;

Quick.Pooling:

Define pool of connection, threads or any object you want to control to avoid resource consum like database connections, http clients, etc...

Create http client pool:

 pool := TObjectPool<THTTPClient>.Create(5,5000,procedure(var aInstance : THTTPClient)
        begin
          aInstance := THTTPClient.Create;
          aInstante.UserAgent := 'MyAgent';
        end);

Get object from pool:

httpcli := pool.Get.Item;
statuscode := httpcli.Get('https://www.mydomain.com').StatusCode;

Quick.Collections:

Defines interfaced List and Objectlist with linQ support inherited.

  • TXList / IList: Interfaced List allowing LinQ regEx search/remove/update.
myarray := ['Joe','Mat','Lee'];
//search for regex match
cout('Search for regex match',ccYellow);
for name in myarray.Where('e$',True).Select do
begin
  cout('User %s ends with "e"',[name],etInfo);
end;
  • TXObjectList / IObjectList: Interfaced ObjectList allowing LinQ predicate or expression search/remove/update. Expression search:
user := ListObj.Where('Profile.Name = ?',['Lee']).SelectFirst;

Expression search for item array:

users := ListObj.Where('Roles CONTAINS ?',['SuperAdmin']).Select;

Predicate search:

user := ListObj.Where(function(aUser : TUser) : Boolean
      begin
        Result := aUser.Name.StartsWith('J');
      end).SelectFirst;
    if user <> nil then cout('%s starts with J letter',[user.Name],etInfo);

See Quick.Linq section to view more functions allowed.

Quick.Template:

String template replacing using a dictionary or delegate function. You can specify quoted token chars.

Replace passing a dictionary:

dict := TDictionary<string,string>.Create;
dict.Add('User','John');
dict.Add('Age','20');
dict.Add('SurName','Peterson');
mytemplate := 'User {{User}} {{SurName}} are {{Age}} years old.';
template := TStringTemplate.Create('{{','}}',dict);
Result := template.Replace(mytemplate);

Replace with delegate function:

mytemplate := 'User {{User}} {{SurName}} are {{Age}} years old.';
template := TStringTemplate.Create('{{','}}',function(const aToken : string) : string
  begin
    if token = 'User' then Result := 'John'
    else if token = 'Surname' then Result := 'Peterson'
    else if token = 'Age' then Result := '20';
  end);
Result := template.Replace(mytemplate);

Quick.Debug.Utils:

Debug utils to check performance and get enter and exit method checkpoint.Define with a Debug a compiler directive to only be active when your app is compiled in debug mode. On console apps uses console out by default. You can pass a logger to output in:

TDebugUtils.SetLogger(ilogger);

Trace a part of your code:

function TCalculator.Subs(a, b: Int64): Int64;
begin
  {$IFDEF DEBUG}
  TDebugger.Trace(Self,Format('Substract %d - %d',[a,b]));
  {$ENDIF}
  Result := a - b;
  //simulate working for 200ms
  Sleep(200);
end;
//Returns:
//29-06-2020 23:31:41.391  [TRACE] TCalculator -> Substract 30 - 12

Calculate time to process from point to exit function:

function TCalculator.Sum(a, b: Int64): Int64;
begin
  {$IFDEF DEBUG}
  TDebugger.TimeIt(Self,'Sum',Format('Sum %d + %d',[a,b]));
  {$ENDIF}
  Result := a + b;
  //simulate working for 1 seconds
  Sleep(1000);
end;
//Returns:
//29-06-2020 22:58:45.808  [CHRONO] TCalculator.Sum -> Sum 100 + 50 = 1,00s

Calculate time to process from point to point and exit function:

function TCalculator.Divide(a, b: Int64): Double;
begin
  {$IFDEF DEBUG}
  var crono := TDebugger.TimeIt(Self,'Divide',Format('Divide %d / %d',[a,b]));
  {$ENDIF}
  Result := a / b;
  //simulate working for 500ms
  Sleep(500);
  {$IFDEF DEBUG}
  crono.BreakPoint('Only divide');
  {$ENDIF}
  //simulate working for 1 second
  Sleep(1000);
  {$IFDEF DEBUG}
  crono.BreakPoint('Only Sleep');
  {$ENDIF}
end;
//Returns:
//29-06-2020 23:25:46.223  [CHRONO] TCalculator.Divide -> First point = 500,18ms
//29-06-2020 23:25:47.224  [CHRONO] TCalculator.Divide -> Second point = 1,00s
//29-06-2020 23:25:47.225  [CHRONO] TCalculator.Divide -> Divide 10 / 2 = 1,50s

Get notification when enter and exit function, and times it:

function TCalculator.Mult(a, b: Int64): Int64;
begin
  {$IFDEF DEBUG}
  TDebugger.Enter(Self,'Mult').TimeIt;
  {$ENDIF}
  Result := a * b;
  //simulate working for 2 seconds
  Sleep(2000);
end;
//Returns:
//29-06-2020 22:58:45.808  [ENTER] >> TCalculator.Mult
//29-06-2020 22:58:47.810  [EXIT] >> TCalculator.Mult in 2,00s

Quick.Parameters:

Working with commandline parameters will be easy using commandline extension. Define a class inherited from TParameters or TServiceParameters (if working with QuickAppServices) with your possible arguments as published properties:

uses
  Quick.Parameters;
type
  TCommand = (Copy, Move, Remove);
  TMyMode = (mdAdd, mdSelect, mdRemove);

  [CommandDescription('Simple console application example with Quick.Parameters')]
  TMyParameter = class(TParameters)
  private
    fCommand : TCommand;
    fHost : string;
    fPort : Integer;
    fRetries : Integer;
    fUseTCP : Boolean;
    fConfigFile: string;
    fSilent: Boolean;
    fMyMode: TMyMode;
    fLogErrorsConsole: Boolean;
    fLogErrors: Boolean;
    fShowReport: Boolean;
  published
    [ParamCommand(1)]
    [ParamRequired]
    [ParamHelp('Command action.','command-action')]
    property Command : TCommand read fCommand write fCommand;

    [ParamName('HostName'),ParamRequired]
    [ParamHelp('Define host to connect.','host')]
    property Host : string read fHost write fHost;

    [ParamName('Port','p')]
    [ParamValueIsNextParam]
    [ParamHelp('Define Port to connect (default 80)','port')]
    property Port : Integer read fPort write fPort;

    [ParamHelp('Number of max retries.')]
    property Retries : Integer read fRetries write fRetries;

    [ParamHelp('Path to config.','path')]
    [ParamName('Config-file')]
    property ConfigFile : String read fConfigFile write fConfigFile;

    [ParamHelp('Silent mode.')]
    property Silent : Boolean read fSilent write fSilent;

    [ParamHelp('Modes (mdAdd, mdSelect, mdRemove)')]
    property Mode : TMyMode read fMyMode write fMyMode;
  end;

Use param:

params := TMyParameter.Create;

When you call your exe with --help you get documentation. If you need to check for a switch or value, you can do like this:

if params.Port = 0 then ...
if params.Silent then ...

QuickParameters uses custom attributes to define special parameter conditions:

  • CommandDescription: Defines text to describe your application in help documentation.

  • ParamCommand(number): Defines static position into commandline for single parameters.

  • ParamName(name,alias): Define a diferent name for parameter. Allows to use special characters not allowed for class properties (like file-name or config.file). Optional Alias argument defines an alternative (normally short name) parameter name.

  • ParamHelp(helptext,valuename): Defines a commandline help text and value name in usage section.

  • ParamSwitchChar(sign): Defines string or char to indicate switch or parameter. If not defined, a double dash (--) will be used by default.

  • ParamValueSeparator(sign): Defines string or char to separate parameter name from value (filename=config.json). If not defined, equal sign (=) will be used by default.

  • ParamValueIsNextParam: Defines a parameter with a value without value separator (filename c:\config.ini)

  • ParamRequired: Defines a parameter as required. If param not found, an exception will be raised.

QuickParameter automatically checks for value types. If you define a parameter value as Integer, and pass an alfanumeric, an exception will be raised.

Help customization: You can define your own color customization with ColorizeHelp. Enabled property will use custom colors, otherwise b/w will be used.

Parameters.ColorizeHelp.Enabled := True;
Parameters.ColorizeHelp.CommandName := ccCyan;
Parameters.ColorizeHelp.CommandUsage := ccBlue;

When parameters detects help parameter, help documentation will be showed.

Parameters.ShowHelp: Shows help documentation, generated automatically:

Parameters v.1.0
Usage: Parameters <command-action> <--HostName=<host>> [--Port <port>] [--Retries=<value>]
                  [--Config-file=<path>] [--UseTCP] [--Silent] [--Mode=<value>]
                  [--ShowReport] [--Help]

Simple console application example with Quick.Parameters

Arguments:

    Command                  Command action.
  --HostName                 Define host to connect.
  --Port, -p                 Define Port to connect (default 80)
  --Retries                  Number of max retries.
  --Config-file              Path to config.
  --UseTCP                   Use TCP connection if present.
  --Silent                   Silent mode.
  --Mode                     Modes (mdAdd, mdSelect, mdRemove)
  --Help, -h                 Show this documentation

Quick.Url.Utils:

  • GetProtocol: Get protocol from an url.
  • GetHost: Get hostname from an url.
  • GetPath: Get path from an url.
  • GetQuery: Get Query part from an url.
  • RemoveProtocol: Remove protocol from an url.
  • RemoveQuery: Remove query part from an url.
  • EncodeUrl: Encode path and query from and url.

Quick.RegEx.Utils:

Commonly used validations.

Quick.Conditions:

Pre and postcondition validations in fluent style. Condition.Requires evaluates a variable for conditions before do some operations. Condition.Ensures evaluates a variable result for conditions after do some operations.

    Condition.Requires(num, "num")
        .IsInRange(1,10,'value for num is out of range');   // throws custom error if not in range
        .IsNotGreater(50);   // throws ArgumentException if not equal to 128

    Condition.Requires(myobj, "myobj")
        .WithExceptionOnFailure(EMyException) //throws specific exception on failure
        .IsNotNull()          // throws ArgumentNullException if null
        .Evaluate(myobj.id > 10); // myobj.id must be greater than 10

    Condition.Requires(text, "text")
        .IsNotEmpty()          // throws ArgumentNullException if empty
        .StartsWith("<html>") // throws ArgumentException if not starts with <html>
        .EndsWith("</html>") // throws ArgumentException if not ends with </html>
        .IsNotLowerCase // thows ArgumentException if not lowercase
        .Evaluate(text.Contains("sometxt") or test.Contains('othertxt')); // throws ArgumentException if not evaluates

Do you want to learn delphi or improve your skills? learndelphi.org

quicklib's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

quicklib's Issues

ScheduledTasks RemoveTaskAfterExpiration not working...

First of all many thanks for this great library.
I'm using ScheduledTasks with RemoveTaskAfterExpiration option true.
After expiration ScheduledTasks dont remove task from its task list.

When I check the code I see that TScheduler.Execute cannot remove task from list because TScheduledTask.DoExpire method sets task's enabled property to false.

QuickLib.Expression have bug and Some advice

First, it was found that priority processing was not supported, causing some partial errors:
Assert(true, TExpressionParser.Validate(5, '(0.3 < 8) or (5 < 4) and (2 = 1)')); //false is err
Assert(true, TExpressionParser.Validate(5, '(8 > 3) or (5 > 4) and (2 = 1)')); //false is err

Some statements cannot be executed
// PRN(TExpressionParser.Validate(5,'8<1'));
// PRN(TExpressionParser.Validate(5,'8 < 1 or 5 > 4 '));

Unable to support addition, subtraction, multiplication and division. Hope to support common functions as well as custom functions

TQuickAzure.PutBlob Filesize Issue

Hi,

When you upload a file to Azure using PutBlob function then the filesize is wrong on Azure. When you download this file again, tested with an excel file, it will be damaged (it will ask for a repair).
The reason is the way the local var Content is handled. I found a fix in the Embarcadero 10.3.3 Demos on GitHub, filename CloudPopulator.pas (Trunk\Object Pascal\Database\CloudAPI\CloudExplorer).

`function TQuickAzure.PutBlob(const azContainer : string; cStream : TStream; const azBlobName : string; out azResponseInfo : TAzureResponseInfo) : Boolean;
var
BlobService : TAzureBlobService;
//Content : TArray;
Content: TBytes;
CloudResponseInfo : TCloudResponseInfo;
container : string;
blobname : string;
begin
azResponseInfo.StatusCode := 500;
if cStream.Size = 0 then
begin
azResponseInfo.StatusMsg := 'Stream is empty';
Exit;
end;

container := CheckContainer(azContainer);
blobname := RemoveFirstSlash(azBlobName);
try
BlobService := TAzureBlobService.Create(fconAzure);
try
BlobService.Timeout := fTimeout;
CloudResponseInfo := TCloudResponseInfo.Create;
try
// Content := StreamToArray(cStream);
Content := ByteContent(cStream);
Result := BlobService.PutBlockBlob(container,blobname,Content,EmptyStr,nil,nil,CloudResponseInfo);
azResponseInfo := GetResponseInfo(CloudResponseInfo);
finally
CloudResponseInfo.Free;
end;
finally
BlobService.Free;
end;
except
on E : Exception do
begin
azResponseInfo.StatusCode := 500;
azResponseInfo.StatusMsg := e.message;
Result := False;
end;
end;
end;`

and added (copied from the Embarcadero Demos on GitHub, CloudPopulator.pas)

`function TQuickAzure.ByteContent(DataStream: TStream): TBytes;
var
Buffer: TBytes;
begin
if not Assigned(DataStream) then
exit(nil);

SetLength(Buffer, DataStream.Size);
// the content may have been read
DataStream.Position := 0;
if DataStream.Size > 0 then
DataStream.Read(Buffer[0], DataStream.Size);

Result := Buffer;
end;`

Regards,
Pascal

Syntax error in DebugUtils (D10.0)

function TCalculator.Divide(a, b: Int64): Double;
begin
{$IFDEF DEBUG}
var crono := TDebugger.TimeIt(Self,'Divide',Format('Divide %d / %d',[a,b]));
...

Quick.Console

Feature :
CoutXY() -> Event type is not supported. Would be fantastic to add this feature.

Quick.Config

It cannot be compiled in FreePascal. FPC version 3.0.4, Lazarus 2.0.2.

1st error: QuickFiles line#412
Result := Result + [rec.Name]

I have replaced it with SetLength(Result, length(Result) + 1); Result[High(Result)] := rec.Name;

then 2nd error:
Quick.Config.Json
line#43
Rtti
I have replaced it with rttiutils

then 3rd error:
Quick.Commons
line#816
loUserLocale - identificator not found

QuickLib can not compiler with directive:'5023'

uses Quicklib units,and complie project,then debug message outpu message is:
[dcc32 Error] quicklib.pas(7): E1030 Invalid compiler directive: '5023' [dcc32 Fatal Error] Quick.Console.pas(204): F2063 Could not compile used unit 'Quick.Log.pas'
delphi version is 10.2 25.0.29899.2631

Improvement request: Scheduled Task start time based on cron

I know it might not be easy to implement now because the way it was created, but it would be interesting if the TScheduledTasks could start and repeat at specific time similar to cron expression
Examples:
5 4 * * * : every day at 4:05am
0 22 * * 1-5 : At 22:00 on every day-of-week from Monday through Friday
*/5 * * * * : At every 5th minute
The not so good part from traditional cron is that it start in minute interval and ideally it would be good having second intervals (adding one additional number/expression).
0 0 * * * * : every day and hour when min = 0 and second = 0
*/5 * * * * * : every 5th second (e.g. 0, 5, 10, 15, ...)

Memory leak in Quick.Process.GetProcessList

Quick.Process.GetProcessList creates a memory leak.

To avoid a memory-leak, implement a var parameter for an existing StringList, for example:

procedure GetProcessList (var MyStringList: TStringList);

This would allow freeing the String-List manually after use.

FireMonkey Fix Suggestion

Hi,
I see you fixed the code in Quick.Commons to compile in other platforms.

I was thinking that this code is more suitable:

{$ELSE}
var
{$IFDEF NEXTGEN}
plogin : MarshaledAString;
{$ELSE}
plogin : PAnsiChar;
{$ENDIF}
begin
{$IFDEF POSIX}

rather than string in NEXTGEN

This is what is suggested in the docs too.

What do you think?

could Quick.Process add a function that can redirect output?

thanks for your awsome delphi lib.
i use it in my work.
now , i wanna to write a function that could redirect the program ouput to my program.
i wrote a function like my words long before,but it works bad.
so , could quicklib provide this function that can wait for terminate and get output with callback.
thans again.
and sorry for my pool english

Compile errors on Linux

I'm testing QuickLib on Linux, and i get many compile errors on units Quick.Commons, it seems that it want to compile some parts that belongs to IOS.

I'm using Delphi 10.4 Sydney with 2 patches applied

[DCC Error] Quick.Commons.pas(946): E2003 Undeclared identifier: 'GetDeviceModel'
[DCC Error] Quick.Commons.pas(1144): E2003 Undeclared identifier: 'NSBundle'
[DCC Error] Quick.Commons.pas(1145): E2003 Undeclared identifier: 'NSString'
[DCC Error] Quick.Commons.pas(1148): E2003 Undeclared identifier: 'StrToNSStr'
[DCC Error] Quick.Commons.pas(1148): E2003 Undeclared identifier: 'ILocalObject'
[DCC Error] Quick.Commons.pas(1148): E2015 Operator not applicable to this operand type
[DCC Error] Quick.Commons.pas(1149): E2003 Undeclared identifier: 'TNSBundle'
[DCC Error] Quick.Commons.pas(1149): E2066 Missing operator or semicolon
[DCC Error] Quick.Commons.pas(1149): E2125 EXCEPT or FINALLY expected
[DCC Error] Quick.Commons.pas(1155): E2029 '.' expected but ';' found
[DCC Error] Quick.Commons.pas(1248): E2003 Undeclared identifier: 'NSBundle'
[DCC Error] Quick.Commons.pas(1249): E2003 Undeclared identifier: 'NSString'
[DCC Error] Quick.Commons.pas(1251): E2003 Undeclared identifier: 'StrToNSStr'
[DCC Error] Quick.Commons.pas(1251): E2003 Undeclared identifier: 'ILocalObject'
[DCC Error] Quick.Commons.pas(1251): E2015 Operator not applicable to this operand type
[DCC Error] Quick.Commons.pas(1252): E2003 Undeclared identifier: 'TNSBundle'
[DCC Error] Quick.Commons.pas(1252): E2066 Missing operator or semicolon
[DCC Error] Quick.Commons.pas(1252): E2029 'END' expected but ')' found
[DCC Fatal Error] Quick.SysInfo.pas(226): F2063 Could not compile used unit 'Quick.Commons.pas'

Improvement request: Serialization Dataset to Json / Json to Dataset

The great QuickLib already contain nearly everything to serialize from dataset to json (and back) which would be useful for transferring a dataset from server to client in JSON format. The tricky part is from JSON to DATASET as ideally it should create the field from a memory table. I had a quick look and Rest Dataware does have it (https://sourceforge.net/projects/rest-dataware-componentes/) but it is part of their component set and therefore it is linked to a lot of units and not straightforward to use it without their specific framework

loadFromJSON error?

Hi,
it sounds like the properties of any object are unchanged after loadFromJSON. I'm using Delphi 10.3.2 (not yet tested with FPC). Here is may program:

program QuickCloneTest;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  Quick.Commons,
  Quick.Console,
  Quick.JSON.Helper;

type
  TMyObject = class
  public
    caption: string;
  end;

var
  jsonOutput: string;
  myObject, myOtherObject: TMyObject;

begin
  try
    myObject := TMyObject.Create;
    try
      myOtherObject := TMyObject.Create;
      try
        myOtherObject.caption := 'Second object''s caption';
        myObject.caption := 'First object''s caption';
        jsonOutput := myObject.ToJson;
        cout('Json is: ', ccYellow);
        cout(jsonOutput, etInfo);
        cout('', ccWhite);

        myOtherObject.FromJson(jsonOutput);
        cout('MyOtherObject''s caption is: ', ccYellow);
        cout(myOtherObject.caption, etInfo);
        cout('', ccWhite);

        myOtherObject.Clone(myObject);
        cout('After cloning, myOtherObject''s caption is: ', ccYellow);
        cout(myOtherObject.caption, etInfo);
        cout('', ccWhite);

        myObject.caption := 'Changed';
        myObject.FromJson(jsonOutput);
        cout('MyObject''s caption is: ', ccYellow);
        cout(myObject.caption, etInfo);

      finally
        myOtherObject.Free;
      end;
    finally
      myObject.Free;
    end;
    ConsoleWaitForEnterKey;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

and here is its output

Json is:
{
    "caption": "First object's caption"
}

MyOtherObject's caption is:
Second object's caption

After cloning, myOtherObject's caption is:
Second object's caption

MyObject's caption is:
Changed

where I expected always "First object's caption".

SAML AzureAD Single Sign-on Support - Feature Request

https://docs.microsoft.com/en-us/azure/active-directory/develop/single-sign-on-saml-protocol

https://docs.microsoft.com/en-us/azure/active-directory/saas-apps/saml-toolkit-tutorial

The link below has a BASIC implementation of SAML for ASP.NET. (323 lines of c# code):

https://github.com/jitbit/AspNetSaml

How SAML works?
SAML workflow has 2 steps:

User is redirected to the SAML provider (where he authenticates)
User is redirected back to your app, where you validate the payload
Here's how you do it (this example is for ASP.NET MVC:

  1. Redirecting the user to the saml provider:
    //this example is an ASP.NET MVC action method
    public ActionResult Login()
    {
    //TODO: specify the SAML provider url here, aka "Endpoint"
    var samlEndpoint = "http://saml-provider-that-we-use.com/login/";

    var request = new AuthRequest(
    "http://www.myapp.com", //TODO: put your app's "entity ID" here
    "http://www.myapp.com/SamlConsume" //TODO: put Assertion Consumer URL (where the provider should redirect users after authenticating)
    );

    //redirect the user to the SAML provider
    return Redirect(request.GetRedirectUrl(samlEndpoint));
    }

  2. User has been redirected back
    User is sent back to your app - you need to validate the SAML response ("assertion") that you recieved via POST.

Here's an example of how you do it in ASP.NET MVC

//ASP.NET MVC action method... But you can easily modify the code for Web-forms etc.
public ActionResult SamlConsume()
{
// 1. TODO: specify the certificate that your SAML provider gave you
string samlCertificate = @"-----BEGIN CERTIFICATE-----
BLAHBLAHBLAHBLAHBLAHBLAHBLAHBLAHBLAHBLAHBLAHBLAH123543==
-----END CERTIFICATE-----";

// 2. Let's read the data - SAML providers usually POST it into the "SAMLResponse" var
Saml.Response samlResponse = new Response(samlCertificate, Request.Form["SAMLResponse"]);

// 3. We're done!
if (samlResponse.IsValid())
	username = samlResponse.GetNameID();

}
Reading more attributes from the provider
SAML providers usually send more data with their response: username, first/last names etc. Here's how to get it:

if (samlResponse.IsValid())
{
//WOOHOO!!! user is logged in

//Some more optional stuff for you
//let's extract username/firstname etc
string username, email, firstname, lastname;
try
{
	username = samlResponse.GetNameID();
	email = samlResponse.GetEmail();
	firstname = samlResponse.GetFirstName();
	lastname = samlResponse.GetLastName();
}
catch(Exception ex)
{
	//insert error handling code
	//no, really, please do
	return null;
}

//user has been authenticated, put your code here, like set a cookie or something...
//or call FormsAuthentication.SetAuthCookie() or something

}

Problem in class TQuickAmazon.PutObject ( StreamToArray )

Problem in class TQuickAmazon.PutObject

The "StreamToArray" function is not converting the STREAM to ARRAY correctly. At the end of the file, null characters (<0x00>) are remaining.
To solve the problem I had to replace the function with the code below:

function ByteContent(DataStream: TStream): TBytes;
var
  Buffer: TBytes;
begin
  if not Assigned(DataStream) then
    exit(nil);

  SetLength(Buffer, DataStream.Size);
  // the content may have been read
  DataStream.Position := 0;
  if DataStream.Size > 0 then
    DataStream.Read(Buffer[0], DataStream.Size);

  Result := Buffer;
end;

image (118)

Quick Config Return for Load or new Event when file is corrupted

I know that Quick Config does not generate any error if the configuration file is corrupted or not well formatted.
As an improvement suggestion, it would be great to know (after executing the load procedure) if the config file was read without error or if it contained errors.

How to use Factory and inject value in constructor in IOC?

Hi,
I am looking at the IOC and I want to do the following:

Say I have TBase class and TClassA and TClassB inheriting from TBase.

Now I want a factory to produce either classA or classB. Can I use Abstract Factory for this?

Thanks

suggestion in FaultControl

Hi, exilon.

in TRunTask and TScheduledTasks with WaitAndRetry,
parameter is 'aMaxRetries', 'aWaitTimeBetweenRetriesMs' and 'aWaitTimeMultiplierFactor'

for example --
aMaxRetires is -1 or INFINITE, aWaitTimeBetweenRetriesMs is 5000
and aWaitTimeMultiplierFactor is 0.
I think this is every 5 seconds retry. but it's not.
i think that cases MultiplierFactor is 0 means just follow aWaitTimeBetweenRetriesMS interval.

next is original code

if IsEmptyArray(fWaitTimeArray) then waitretryMS := fWaitTimeBetweenRetries *   Round(fNumRetries * fWaitTimeMultiplierFactor)
else waitretryMS := fWaitTimeArray[fNumRetries - 1];
if waitretryMS > 0 then WaitBeforeRetry(waitretryMS);

how about this...

          if fWaitTimeMultiplierFactor = 0 then
          begin
            if IsEmptyArray(fWaitTimeArray) then waitretryMS := fWaitTimeBetweenRetries
              else waitretryMS := fWaitTimeArray[fNumRetries - 1];
            if waitretryMS > 0 then WaitBeforeRetry(waitretryMS);
          end
          else
          begin
            if IsEmptyArray(fWaitTimeArray) then waitretryMS := fWaitTimeBetweenRetries * Round(fNumRetries * fWaitTimeMultiplierFactor)
              else waitretryMS := fWaitTimeArray[fNumRetries - 1];
            if waitretryMS > 0 then WaitBeforeRetry(waitretryMS);
          end;

Quick.Azure TQuickAzure.ListContainers

Line 797

if Assigned(AzContainer) then begin //frees ContainerList objects for AzContainer in AzContainers do AzContainer.Free; AzContainers.Free; end;

Should be

if Assigned(AzContainers) then begin //frees ContainerList objects for AzContainer in AzContainers do AzContainer.Free; AzContainers.Free; end;

any plan TDateTime convert mongodb $date type in JsonSerializer??

Hi, exilon.

thanks for QuickLib.
always i used quicklib with mongodb and IJsonSerializer.
but, TDateTime is changed to string type into mongodb.
TJson.ObjectToJsonString(Object, [joDateFormatMongo]) it can make mongo datetime type json.

however, do you have any plan support convert tdatetime to $date options?

BUG in Quick.Commons

//checks if need include signs // signs!
if pfIncludeNumbers in Complexity then // pfIncludeNumbers!

// QuickLib 2.1

Here is the bug-fix:

image

TAppConfig.JsonIndent property is ignored

The Config json provider calls its serializer with a constant aIndent parameter (for TJsonSerializer.ObjectToJson) instead of using TAppConfig's JsonIndent property.

Quick.Config.Json, line 220:

json.Text := serializer.ObjectToJson(cConfig,True);

Should be:

json.Text := serializer.ObjectToJson(cConfig,cConfig.JsonIndent);

I was trying to set this property to overcome the range check error reported in #63.

Memory leak in Quick.Commons.GetSpecialFolderPath

Using Delphi 10.3 in Windows 10, setting ReportMemoryLeaksOnShutdown to True detects a memory leak in GetSpecialFolderPath function.
As stated in https://www.experts-exchange.com/questions/10206340/Locating-special-folder.html, maybe you could consider a solution like:

implementation
uses
Winapi.ActiveX;
...
function GetSpecialFolderPath(folderID : Integer) : string;
var
malloc : IMalloc;
ppidl: PItemIdList;
begin
if (SHGetMalloc(malloc)=NOERROR) then
begin
SHGetSpecialFolderLocation(0, folderID, ppidl);
SetLength(Result, MAX_PATH);
if not SHGetPathFromIDList(ppidl,{$IFDEF FPC}PAnsiChar(Result){$ELSE}PChar(Result){$ENDIF}) then
begin
malloc.Free(ppidl);
raise EShellError.create(Format('GetSpecialFolderPath: Invalid PIPL (%d)',[folderID]));
end;
SetLength(Result, lStrLen({$IFDEF FPC}PAnsiChar(Result){$ELSE}PChar(Result){$ENDIF}));
malloc.Free(ppidl);
end;
end;

In my test, that solved the problem.

Regards,

Paolo

From not set

Mail.From := fMail.From;

Mail.From := fMail.From; // Not set if calling this method directly ? Exception / check now ? Or just allow exception on sending ?

Quick.Azure

function ListContainers(const azContainersStartWith : string; azResponseInfo : TAzureResponseInfo) : TStrings;

Should be

function ListContainers(const azContainersStartWith : string; out azResponseInfo : TAzureResponseInfo) : TStrings;

Very nice Lib BTW

Regards,
Pascal

Quick.SMTP - TLS (GMail/Office365)

     Self.UseTLS := utUseExplicitTLS;   // REQUIRED for GMAIL / Office365

Line 236 (above) needs to be uncommented for GMAIL and Office365 to send email.

Not sure why this is commented out in the code ???

Improvement request: Database Connection Pool

If I am not wrong, according to description, it might be easy to use the Quick.Pooling to creste a database connection pool. An example to accomplish this (e.g. with ZeosDBO) would be great

Quick.AppService Starting with Param

Hi,

I install the Application with a parameter.
Example: MyApplicaiton MyParam1 /install

After the installation, When i try to start the application ParamStr(1) gives me empty value.

When i make a normal service application without using Quick.AppService, ParamStr(1) gives me the parameter which i give on installation.

How can i get params on start?

Best Regards,
Serhan.

Little issue - the .pas source files should be encoded in utf8

Thanks again for this great, feature-rich library! I'm checking out the pooling feature and the thread pool feature, after getting QuickLogger compiled.

One little, not-critical suggestion - please consider save the .pas source file in utf8 instead of the default ANSI encoding, otherwise when they are opened in other locals the unicode characters won't be displayed correctly, as shown in the attached screenshot.

The issue is minor when the character displaying issue happens inside the comments, but it will cause it unable to be compiled if it happens inside string constants.
QuickLibPasFileShouldBeUtf8

Compiler Error in Quick.Json.Serializer

Delphi 10 Seattle

[dcc32 Error] Quick.Json.Serializer.pas(390): E2003 Undeclared identifier: 'JsonString'

Line:
if rField.FieldType.TypeKind in [tkString, tkLString, tkWString, tkUString] then
{$IFDEF DELPHIRX103_UP}
rValue := DeserializeType(aObject,rField.FieldType.TypeKind,rField.FieldType.Handle,TJsonValue(member).value)
{$ELSE}
rValue := DeserializeType(aObject,rField.FieldType.TypeKind,rField.FieldType.Handle,member.JsonString.ToString)
{$ENDIF}
else rValue := DeserializeType(aObject,rField.FieldType.TypeKind,rField.FieldType.Handle,member.ToJSON);

TJsonUtils.JsonFormat range error beyond end of string

I believe I found a bug with the JsonFormat command. The for loop iterates through the supplied json string, but when the last character is a '}', the case statement checks beyond the end of the string json[i+1].

  for i := 1 to json.Length do
  begin
    c := json[i];
    ...
      case c of
        ...
        '}',']' :
          begin
            Delete(LIndent, 1, Length(INDENT));
            if not isEOL then Result := Result + EOL;
            if json[i+1] = ',' then Result := Result + LIndent + c
              else if json[i-1] in ['{','['] then Result := Result + c + EOL
                else Result := Result + LIndent + c + EOL;
            isEOL := True;
          end;

if json[i+1] (Quick.JSON.Utils.pas line 112) causes an index out of range (unless there is a space or newline at the end of the json string).

My input looked like this:

{"Config":{"foo":"bar"}}

Adding a length check corrects the problem:

 if (i<json.Length) and (json[i+1] = ',') then Result := Result + LIndent + c

Thank you for this wonderful project!

install with delphinus error!

hi,i am install QuickLib in delphinus, but it report an error!
Downloading QuickLib
Version: HEAD
Error: Delphinus.Install.json seems to be invalid json
deleting tempfiles
Error: Installation failed

can you help me?

Memory Leak on ConfigToJson

Hi,

Just run sample and click Load From file and close app, you will see memory leak!


Unexpected Memory Leak

An unexpected memory leak has occurred. The unexpected small block leaks are:

25 - 40 bytes: TJSONObject x 1

73 - 88 bytes: TList<System.JSON.TJSONPair> x 1

Delphi 10.4
Path: \QuickLib\samples\delphi\QuickConfig\ConfigToJSON

error in linux

Hello,

actually, i test QuikcLogger in Ubuntu 18.04 with QuickLib. (latest version 2.0 and 1.43)
just include Quick.Logger, Quick.Logger.Provider.Files.
(few months ago, i reported this error. but it's not fixed)
i try execute in linux machine after than result is show this error.

realloc(): invalid next size
중지됨 (core dumped)

i patch source code in Quick.Commons.pas like this.
(in GetComputerName procedure .. line 948)
{$IFDEF DELPHILINUX}
var
phost : PAnsiChar;
begin
try
phost := AllocMem(256);
if gethostname(phost,_SC_HOST_NAME_MAX) = 0 then
begin
{$IFDEF DEBUG}
Result := Copy(Trim(phost),1,Length(Trim(phost)));
{$ELSE}
Result := Copy(phost,1,Length(phost));
{$ENDIF}
end
else Result := 'N/A.';
FreeMem(phost);
except
Result := 'N/A';
end;
end;

gethostname need allocated buffer. but phost not allocation error.
i hope it helps.

What's the lowest supported Delphi compiler?

Thanks for this excellent, versatile library!

A quick question - Delphi XE4 emits the following error when it is compiling Quick.Commons.TArrayOfStringHelper.Remove:
[dcc32 Error] Quick.Commons.pas(1617): E2008 Incompatible types

It seems that System.Delete doesn't support TArray in this version of Delphi.

ScheduledJobs synchronization

First of all, good job with developing and publishing those sets of useful classes. They are a big time-saver and there are several very useful ones.

I was interested in the scheduled tasks and made a test, which worked nicely. However, I didn't see any way to wait for the tasks or to stop them. If it exists and I have missed it, sorry.

Here is what I mean: Let's say that there is a scheduled task, which has a long execution time and it starts. If I want to close the application, I will have to signal the task to end and to wait for it to do it gracefully.
It would be handy to have a set of methods like scheduledtasks.Terminate(and a flag that can be checked by the task) to signal any running task to interrupt, and a scheduledtasks.Wait to wait for any running one to finish.
Of course this can be achieved by doing the sync manually, but it will be useful to have it integrated.

Btw, some of your interface like ITask are named the same as the ones in System.Threading. These are the best names, but it creates an inconvenience when having a lot of code using the System ITask, and trying to integrate your classes gradually :)
I have to go over the code and add prefixes or reorder the units under uses...

Bug in Quick.AppService.

Hi, exilon.

i'll try AppService in console application.
in help text contain 'console' option.
but, in 'CheckParams' procedure not working with 'console' option.
only show 'Unknown parameter specified'.

i think should be
-->

if (ParamFindSwitch('h')) or (ParamFindSwitch('help')) then Self.Help
    else if ParamFindSwitch('install') then
    begin
      if (fCanInstallWithOtherName) and (ParamGetSwitch('instance',svcname)) then
      begin
        fServiceName := svcname;
        fDisplayName := svcname;
      end;
      Self.Install;
    end
    else if ParamFindSwitch('remove') then
    begin
      if (fCanInstallWithOtherName) and (ParamGetSwitch('instance',svcname)) then
      begin
        fServiceName := svcname;
        fDisplayName := svcname;
      end;
      Self.Remove;
    end
    else if ParamFindSwitch('console') then
    begin
    end
    else
      Writeln('Unknow parameter specified!');

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.