{"id":13804069,"url":"https://github.com/exilon/QuickLib","last_synced_at":"2025-05-13T17:31:14.838Z","repository":{"id":43002304,"uuid":"104770428","full_name":"exilon/QuickLib","owner":"exilon","description":"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).","archived":false,"fork":false,"pushed_at":"2024-10-15T19:44:06.000Z","size":56266,"stargazers_count":635,"open_issues_count":31,"forks_count":186,"subscribers_count":76,"default_branch":"master","last_synced_at":"2024-10-29T17:28:49.006Z","etag":null,"topics":["automapper","azure","chronometer","config","console","delphi","dependency-injection","firemonkey","freepascal","ioc","json","linq","linux","monitor","scheduled-tasks","serializer","service","thread","yaml"],"latest_commit_sha":null,"homepage":"","language":"Pascal","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/exilon.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-09-25T15:49:16.000Z","updated_at":"2024-10-28T08:50:13.000Z","dependencies_parsed_at":"2023-01-29T21:15:57.254Z","dependency_job_id":"9c7c2e91-bc43-4489-a0c8-a205fe9e5de9","html_url":"https://github.com/exilon/QuickLib","commit_stats":{"total_commits":666,"total_committers":9,"mean_commits":74.0,"dds":0.05555555555555558,"last_synced_commit":"d085aa28e5fd65bae766446f5355ec4dc80fae9e"},"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exilon%2FQuickLib","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exilon%2FQuickLib/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exilon%2FQuickLib/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exilon%2FQuickLib/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/exilon","download_url":"https://codeload.github.com/exilon/QuickLib/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225247826,"owners_count":17444122,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["automapper","azure","chronometer","config","console","delphi","dependency-injection","firemonkey","freepascal","ioc","json","linq","linux","monitor","scheduled-tasks","serializer","service","thread","yaml"],"created_at":"2024-08-04T01:00:41.172Z","updated_at":"2024-11-18T20:31:30.947Z","avatar_url":"https://github.com/exilon.png","language":"Pascal","readme":"![alt text](docs/QuickLib.png \"QuickLib\") \n\nQuickLib is a delphi/Firemonkey(Windows, Linux, Android, OSX \u0026 IOS) and fpc(Windows \u0026 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.\n\n## Give it a star\nPlease \"star\" this project in GitHub! It costs nothing but helps to reference the code.\n![alt text](docs/githubstartme.jpg \"Give it a star\")\n\n## Star History\n\n[![Star History Chart](https://api.star-history.com/svg?repos=exilon/quicklib\u0026type=Date)](https://star-history.com/#exilon/quicklib\u0026Date)\n\n## Support\nIf you find this project useful, please consider making a donation.\n\n[![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/donate/?hosted_button_id=BKLKPNEYKSBKL)\n\n**Areas of functionality:**\n  \n* **Mapping**: Map fields from a class to other class, copy objects, etc..\n* **Config**: Use your config as an object and load/save from/to file (Json/Yaml) or Windows Registry.\n* **Serialization**: Serialize objects to/from json/Yaml.\n* **Scheduling**: Schedule tasks launching as independent threads with retry policies.\n* **Threading**: Simplify run and control of multithread background tasks, Thread-safe Lists, queues, etc\n* **Data**: Flexible data interchange and storage, allowing several input-output types.\n* **Cloud**: Simplify cloud Azure/Amazon file management, send emails and more.\n* **Querying**: Indexed Lists, Searchable Lists and Linq query system for generic lists and arrays.\n* **Benchmark**: Time elapsed control and benchmark functions.\n* **Filesystem**: Process and Services control, file modify monitors and helpers, etc...\n* **FailControl**: Fail and Retry policies.\n* **Caching:**: Cache string or objects to retrieve fast later.\n* **Templating:** Simple string templating with dictionaries.\n* **Debuging:** Utils to debug your code.\n* **Parameters:** Work with commandline parameters.\n\n**Main units description:**\n\n* **Quick.Commons:** Functions frequently needed in the day to day of a developer. \n* **Quick.AppService:** Allow a console app to run as console mode or service mode with same code simplifying debug tasks.\n* **Quick.Azure/Amazon:** Simplifies blob iteraction with Azure and Amazon Cloud Storage.\n* **Quick.Network:** CIDR and IP Range functions.\n* **Quick.Chrono:** Chronometer and Benchmark a piece of code is simple.\n* **Quick.Console:** Write log messages to console with colors and more...\n* **Quick.Log:** Log to disk or memory with verbose levels and daily or max space rotation.\n* **Quick.Config:** Load/Save a config as Json or Yaml file or Windows Registry keys and manage it as an object.\n* **Quick.FileMonitor:** Monitorizes a file for changes and throws events.\n* **Quick.JsonUtils:** Utils for working with json objects.\n* **Quick.SMTP:** Send email with two code lines.\n* **Quick.Threads:** Thread safe classes, scheduling and backgrounds tasks with retry policies.\n* **Quick.Process:** Manages windows processes.\n* **Quick.Services:** Manages windows services.\n* **Quick.Format:** String format.\n* **Quick.RTTI.Utils:** Simplifies working with RTTI.\n* **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)\t\n* **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.\t\n* **Quick.JsonRecord:** Used as a DTO class, with json serialize and mapping functions included.\t\n* **Quick.Lists:** Improved lists with indexing or search features.\n* **Quick.Value** FlexValue stores any data type and allow pass to other class with integrated operators and autofrees.\n* **Quick.Arrays:** Improved arrays.\n* **Quick.YAML:** Yaml object structure.\n* **Quick.YAML.Serializer:** Serialize/Deserialize object from/to Yaml.\n* **Quick.Expression:** Evaluate object properties using expressions.\n* **Quick.Linq:** Makes Linq queries to any TObjectList\u003cT\u003e, TList\u003cT\u003e, TArray\u003cT\u003e and TXArray\u003cT\u003e, performing Select by complex Where like SQL syntax, update and order over your list.\n* **Quick.MemoryCache:** Caches objects/info with an expiration time, to avoid generate this info everytime is needed (database queries, hard to calculate info, etc).\n* **Quick.Collections:** Collections improvements like IList and IObjectList with Linq inherited.\n* **Quick.Pooling:** Creation of object pool to avoid external resource consum exhausts and overheads.\n* **Quick.Template:** String template replacing with dictionary or delegate.\n* **Quick.Debug.Utils:** Simple debugging and code benchmark utils.\n* **Quick.Parameters:** Work with commandline parameters like a class.\n* **Quick.Url.Utils:** Simple url manipulation\n* **Quick.RegEx.Utils:** Commonly used RegEx comparison (email verification, password complexity, etc)\n* **Quick.Conditions:** Pre and postcondition validations in fluent style.\n\n\n**Updates:**\n\n* NEW: RAD Studio 12 supported\n* NEW: RAD Studio 11 supported\n* NEW: Condition checks\n* NEW: Commonly used RegEx validations\n* NEW: Url manipulation utils\n* NEW: QuickParameters to work with commandline arguments like a class.\n* NEW: HttpServer custom and dynamic error pages.\n* NEW: Debug utils\n* NEW: String Template\n* NEW: RAD Studio 10.4 supported\n* NEW: Collections: IList and IObjectList with linQ support.\n* NEW: Pooling: ObjectPool.\n* NEW: Options file settings with sections.\n* NEW: MemoryCache with expiration \u0026 object compression.\n* NEW: Now included on RAD Studio GetIt package manager.\n* NEW: Background \u0026 Scheduled task with retry policies\n* NEW: RunTask, FaultControl\n* NEW: Linq over generic lists and arrays.\n* NEW: QuickConfig YAML provider.\n* NEW: YAML Object and Serializer\n* NEW: AutoMapper customapping path namespaces style.\n* NEW: FlexArray, FlexPair \u0026 FlexPairArray.\n* NEW: AutoMapper mapping procedures (see documentation below)\n* NEW: JsonSerializer improved\n* NEW: TXArray: array like TList\n* NEW: Delphi Linux compatibility\n* NEW: QuickConfigJson reload if config file changed\n* NEW: First version with OSX/IOS partial support\n* NEW: Delphinus-Support\n\n**Installation:**\n----------\n* **From package managers:**\n1. Search \"QuickLib\" on Delphinus or GetIt package managers and click *Install*\n* **From Github:**\n1. Clone Github repository or download zip file and extract it.\n2. Add QuickLib folder to your path libraries on Delphi IDE.\n\n**Documentation:**\n----------\n**Quick.AppService:**\n--\nAllow a console app to run as console mode or service mode with same code simplifying debug tasks.\n\n```delphi\nif not AppService.IsRunningAsService then\nbegin\n    ...your code running as console\nend\nelse\nbegin\n    AppService.ServiceName := 'MyService';\n    AppService.DisplayName := 'MyServicesvc';\n    //you can pass an anonymous method to events\n    AppService.OnStart := procedure\n                          begin\n                            ...your start code\n\t                      end;\n    AppService.OnExecute := YourExecuteFunction;\n    AppService.OnStop := YourStopFunction;\n    AppService.CheckParams;\nend;\n```\n\n**Quick.Azure/Amazon:**\n--\nSimplifies blob iteraction with Azure and Amazon Cloud Storage.\n\n```delphi\n//connect to a Azure blobstorage\nQuickAzure := TQuickAzure.Create(AzureAccountName,AzureAccountKey);\n\n//download a blob file to a stream\ndone := QuickAzure.GetBlob('MyContainer','MyFile.jpg',ResponseInfo,MyStream);\n    \n//check if exists a folder\nfound := ExistFolder('MyContainer','/Public/Documents/Personal');\n    \n//list blobs starting with a pattern (recursively or not)\nfor azBlob in ListBlobs('MyContainer','/Public/Documents',Recursive,ResponseInfo) do\nbegin\n    if azBlob.Size \u003e 1000 then Showmessage(azBlob.Name);\nend;\n```\n\n\n**Quick.Network:**\n--\nCIDR and IP Range functions.\n\n```delphi\n//convert ip string to integer\nIPv4ToInt('192.168.1.10');\n\n//get first and last ip of a subnet scope\nGetIpRange('192.168.100.0','255.255.255.0',LowIp,HighIP);\n```\n\n**Quick.Commons:**\n--\nFunctions frequently needed in the everyday of a developer.\n\n```delphi\n//coverts UTC time TDateTime to Local date time\nUTCToLocalTime(MyUTCTime);\n    \n//generate a 10 char length random password with alfanumeric and signs.\nRandomPassword(10,[pfIncludeNumbers,pfIncludeSigns]);\n\n//Capitalize every word of a phrase\nCapitalizeAll('the grey fox'); //returns \"The Grey Fox\"\n\n//Simple TCounter and TTimeCounter for loops\ncounter := TCounter;\ncounter.Init(200);\ntimecounter : TTimeCounter;\ntimecounter.Init(10000);\nwhile true do\nbegin\n    Inc(n);\n    {your procedural process here}\n    //every 200 steps writes to console\n    if counter.Check then writeln(Format('Processed %d entries',[n]));\n    //every 10 seconds writes to console\n    if timecounter.Check then writeln('Im working...'); \nend;\n```\n\n**Quick.Chrono:**\n--\nChronometer and Benchmark a piece of code is simple.\n\n```delphi\n//get elapsed time execution of a code part\nChrono := TChronometer.Create(False);\nChrono.Start;\n...code you need benchmark\nChrono.Stop;\n\n//shows elapsed time in LongTime format (2 hour(s) and 10 minute(s))\nShowmessage(Chrono.TimeElapsed(True));\n\n//shows elapsed time in ShortTime format (02:10:00)\nShowmessage(Chrono.TimeElapsed(False));\n//get benchmak info of a process\nChrono := TChronoBenchMark.Create;\nChrono.TotalProcess := 100000;\nfor i := 1 to 10000 do\nbegin\n    {your process here}\n    Chrono.CurrentProcess := i;\n    //shows estimated time your process will take in x hour(s), x minute(s) x second(s) format\n    writeln(Chrono.EstimatedTime(True));\n    //shows speed: num items per second processed of your process\n    writeln(Format('Items processed %d/sec',[Chrono.Speed]));\nend;\nwriteln(Chrono.ElapsedTime(False)); //shows total time elapsed in 00:00:00 format\n```\n\n**Quick.Console:**\n--\nWrite log messages to console with colors and more...\n\n```delphi\n//define which level of output needed\nConsole.Verbose := LOG_DEBUG;\n\n//writes line to console in red color\ncout('Error x',etError); \n\n//writes formatted line in green color\ncoutFmt('Proccess %s finished',[ProccesName],etSuccess);\n\n//writes integer\ncout(12348);\n\n//Connect a QuickLog and write to disk and screen with one line of code (with independent verbose levels)\nMyQuickLog := TQuickLog.Create;\nMyQuickLog.Verbose := LOG_ALL;\nConsole.Verbose := LOG_ONLYERRORS;\nConsole.Log := MyQuickLog;\n```\n\n**Quick.Log:**\n--\nLog to disk or memory with verbose levels and daily or max space rotation.\n\n```delphi\n//write a header on start with info as running path, appname, debugmode, user, etc...\nLog.ShowHeader := True;\n\n//sets log with rotation at 20MB\nLog.SetLog('.\\mylog.log',False,20);\n\n//write an error message\nLog.Add('Error x',etError);\n\n//write formatted error message\nLog.Add('Error is %s',[ErrorStr],etError);\n```\n\n**Quick.Config:**\n--\nLoad/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.\n\n```delphi\n//create a class heritage\nTMyConfig = class(TAppConfigJson)\nprivate\n    fName : string;\n    fSurname : string;\n    fStatus : Integer;\npublished\n    property Name : string read fName write fName;\n    property SurName : string read fSurname write fSurname;\n    property Status : Integer read fStatus write fStatus;\nend;\n\n//create your config to json file\n//Add Quick.Config.Json to your uses\nMyConfig := TMyConfig.Create('Config.json');\nMyConfig.Provider.CreateIfNotExists := True;\nMyConfig.Provider.ReloadIfFileModified := True;\nMyConfig.Name := 'John';\nMyConfig.Surname := 'Smith';\n//load\nMyConfig.Load;\n//save\nMyConfig.Save;\n  \n//create your config to Windows Registry\n//Add Quick.Config.Registry to your uses\nMyConfig := TMyConfig.Create;\n//Define Registry as HKEY_CURRENT_USER\\Software\\MyApp\nMyConfig.HRoot := HKEY_CURRENT_USER; \nMyConfig.MainKey := 'MyApp';\nMyConfig.Name := 'John';\nMyConfig.Surname := 'Smith';\n//load\nMyConfig.Load;\n//save\nMyConfig.Save;\n\n//Create a custom Config with no default provider\nTMyConfig = class(TAppConfig)\n...your properties\nend;\n\nMyConfig := TMyConfig.Create(TAppConfigJsonProvider.Create('.\\config.json');\n\n```\n\n**Quick.FileMonitor:**\n--\nMonitorizes a file for changes and throws events.\n\n```delphi\nFileMonitor.Filename := '.\\myfile.txt';\n//check file changes every 2 seconds\nFileMonitor.Interval := 2000;\n//watch for deleted or modified file events\nFileMonitor.Notifies := [mnFileModified, mnFileDeleted)];\nFileMonitor.OnFileChange := MyFileChangeFunction;\nFileMonitor.Enabled := True;\n```\n\t\n**Quick.JsonUtils:**\n--\nUtils for working with json objects.\n\n```delphi\n//When unit declared in uses, a TObject Helper allows all your objects to be loaded or saved to/from json string\nMyObject.FromJson := jsonstring;\nMyString := MyObject.ToJson;\n\n//You can clone simple objects with clone function\nMyObject1.Clone(MyObject2);\n```\n\n**Quick.SMTP:**\n--\nSend email with two code lines.\n\n```delphi\n//Send email\nSMTP := TSMTP.Create('mail.domain.com',25,False);\nSMTP.SendMail('my@email.com','to@email.com','Email subject','My message body');\n\n//You can define more advanced options\nSMTP.SenderName := 'John';\nSMTP.From := 'my@email.com';\nSMTP.Recipient := 'one@email.com,two@email.com';\nSMTP.Subject := 'Email subject';\nSMTP.AddBodyFromFile := '.\\body.html';\nSMTP.CC := 'other@email.com';\nSMTP.BCC := 'more@email.com';\nSMTP.Attachments.Add('.\\notes.txt');\nSMTP.SendMail;\n```\n\n**Quick.Threads:**\n--\nThread safe classes.\n\n**TThreadedQueueCS:** Version of TThreadedQueue with Critical Section.\n\n**TThreadObjectList:** Thread safe Object List.\n\n**TThreadedQueueList:** Thread safe Queue List. Autogrow and with Critical Section.\n\n**TAnonymousThread:** Creates anonymous thread defining unchained Execute and OnTerminate methods. Use Execute_Sync and OnTerminate_Sync methods if code needs to update UI.\n  - **Execute:** Specify code to execute on start.\n  - **Execute_Sync:** Like Execute but runs code with syncronized thread method (avoids problems if your code updates UI).\n  - **OnTerminate:** Specify code to execute when task finishes.\n  - **OnTerminate_Sync:** Like OnTerminate but runs code with syncronized thread method (avoids problems if your code updates UI).\n  - **Start:** Starts thread execution.\n```delphi\n//simple anonymousthread\nTAnonymousThread.Execute(\n      procedure\n      var\n        i : Integer;\n      begin\n        for i := 0 to 10 do cout('Working %d',[i],etTrace);\n        cout('executed thread',etSuccess);\n      end)\n    .OnTerminate(\n      procedure\n      begin\n        cout('terminated thread',etSuccess);\n        cout('PRESS \u003cENTER\u003e TO EXIT',etInfo);\n      end)\n    .Start;\n```\n\n**TRunTask:** Launch an autofree single task thread with fault \u0026 retry control policies. Params can be passed and created into code.\n- *Define code to execute:*\n  - **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. \n  - **Execute_Sync:** Like Execute but runs code with synchronize thread method (avoids problems if your code updates UI).\n  - **SetParameter:** Defines values or objects needed by your task.\n- *Define events to control:*\n  - **OnInitialize:** Specify code to run before main execute task (this code only runs one time, OnExecute can be retried more than one time)\n  - **OnRetry:** Specify code to run when execution fails and decide if needs to retry or cancel next retries.\n  - **OnTerminate:** Specify code to execute when task finishes.\n  - **OnTerminate_Sync:** Like OnTerminate but runs code with syncronized thread method (avoids problems if your code updates UI).\n  - **OnException:** Specify code to execute when task generates an exception.\n- *Define fail/retry policies:*\n  - **RetryForever:** If execution fails, code will be retry forever until task executes ok.\n  - **Retry:** If execution fails, code will be retry x times.\n  - **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.\n  - **Run:** Starts task execution.\n```delphi\n  TRunTask.Execute(\n      procedure(task : ITask)\n      var\n        stream : TStringStream;\n        response : IHttpRequestResponse;\n      begin\n        stream := TStringStream.Create;\n        try\n          response := TJsonHttpClient(task['httpclient'].AsObject).Get(task['url']);\n          task.Result := response.StatusCode;\n          if response.StatusCode \u003c\u003e 200 then raise Exception.Create(response.StatusText);\n        finally\n          stream.Free;\n        end;\n      end)\n    .SetParameter('httpclient',(TJsonHttpClient.Create),True)\n    .SetParameter('url','https://mydomain.com/testfile')\n    .WaitAndRetry(5,250,2)\n    .OnRetry(\n      procedure(task : ITask; aException : Exception; var vStopRetries : Boolean)\n      begin\n        //if error 404 don't try to retry request\n        if task.Result = 404 then vStopRetries := True;\n      end)\n    .OnException(\n      procedure(task : ITask; aException : Exception)\n      begin\n        coutFmt('Exception downloading (Error: %s / StatusCode: %d)...',[aException.Message,task.Result.AsInteger],etError);\n      end)\n    .OnTerminated(\n      procedure(task : ITask)\n      begin\n        if task.Done then coutFmt('Download \"%s\" finished ok',[task['url'].AsString],etSuccess)\n          else coutFmt('Download \"%s\" failed after %d retries',[task['url'].AsString,task.NumRetries],etError);\n      end)\n    .Run;\n```\n\n**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.\n- *Add a task to execute:*\n  - **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. \n  - **AddTask_Sync:** Like AddTask but runs code with synchronize thread method (avoids problems if your code updates UI).\n  - **SetParameter:** Defines values or objects needed by your task. Every parameter will be accesible into anomymous methods defines as task[\u003cname\u003e] or task.[index]\n- *Define events to control:*\n  - **OnInitialize:** Specify code to run before main execute task (this code only runs one time, OnExecute can be retried more than one time)\n  - **OnRetry:** Specify code to run when execution fails and decide if needs to retry or cancel next retries.\n  - **OnTerminate:** Specify code to execute when task finishes.\n  - **OnTerminate_Sync:* Like OnTerminate but runs code with syncronized thread method (avoids problems if your code updates UI).\n  - **OnException:** Specify code to execute when task generates an exception.\n- *Define fail/retry policies:*\n  - **RetryForever:** If execution fails, code will be retry forever until task executes ok.\n  - **Retry:** If execution fails, code will be retry x times. Allow define array of milliseconds as wait time.\n  - **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.\n- *Begin execution:*\n  - **Start:** Starts tasks execution.\n```delphi\n    backgroundtasks := TBackgroundTasks.Create(10);\n    for i := 1 to 100 do\n    begin\n      mytask := TMyTask.Create;\n      mytask.Id := i;\n      mytask.Name := 'Task' + i.ToString;\n      backgroundtasks.AddTask([mytask],False,\n                              procedure(task : ITask)\n                              begin\n                                cout('task %d started',[TMyTask(task.Param[0].AsObject).Id],etDebug);\n                                TMyTask(task.Param[0].AsObject).DoJob;\n                              end\n\t\t\t\t\t\t\t).WaitAndRetry([250,2000,10000])\n                            ).OnException(\n                              procedure(task : ITask; aException : Exception)\n                              begin\n                                cout('task %d failed (%s)',[TMyTask(task.Param[0].AsObject).Id,aException.Message],etError);\n                              end\n                            ).OnTerminated(\n                              procedure(task : ITask)\n                              begin\n                                cout('task %d finished',[TMyTask(task.Param[0].AsObject).Id],etDebug);\n                                TMyTask(task.Param[0].AsObject).Free;\n                              end\n                            ).Run;\n    end;\n    backgroundtasks.Start;\n```\n**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.\nYou can assign anonymous methods to execute, exception, terminate and expiration events.\n- *Add a task to execute:*\n  - **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. \n  - **AddTask_Sync:** Like AddTask but runs code with synchronize thread method (avoids problems if your code updates UI).\n  - **SetParameter:** Defines values or objects needed by your task. Every parameter will be accesible into anomymous methods defines as task[\u003cname\u003e] or task.[index]\n- *Define events to control:*\n  - **OnInitialize:** Specify code to run before main execute task (this code only runs one time, OnExecute can be retried more than one time)\n  - **OnRetry:** Specify code to run when execution fails and decide if needs to retry or cancel next retries.\n  - **OnTerminate:** Specify code to execute when task finishes.\n  - **OnTerminate_Sync:** Like OnTerminate but runs code with syncronized thread method (avoids problems if your code updates UI).\n  - **OnExpire:** Specify code to execute when task expiration reached or task was cancelled.\n  - **OnExpire_Sync:** Like OnExpire but runs code with synchronized thread method (avoids problems if your code updates UI).\n  - **OnException:** Specify code to execute when task generates an exception.\n- *Define when to start task:*\n  - **StartNow:** Start task immediately.\n  - **StartAt:** Date and time to start task. \n  - **StartTodayAt:** Start task today at defined time.\n  - **StartTomorrowAt:** Start task tomorrow at defined time.\n  - **StartOnDayChange:** Start task when day changes.\n  - **StartInMinutes:** Start task after x minutes.\n  - **StartInSeconds:** Start task after x seconds.\n- *Define if needs to repeat or not (if not defined a previous StartAt, StartOn, etc, task will be executed immediately):*\n  - **RunOnce:** Task will be executed one time only. \n  - **RepeatEvery:** Can indicate repeat step over time and expiration date.\n  - **RepeatEveryDay:** Repeat task every day at same hour.\n  - **RepeatEveryWeek:** Repeat task every week at same hour.\n- *Define fail/retry policies:*\n  - **RetryForever:** If execution fails, code will be retry forever until task executes ok.\n  - **Retry:** If execution fails, code will be retry x times.\n  - **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.\n- *Start/Stop scheduler:*\n  - **Start:** Starts scheduler.\n  - **Stop:** Stops scheduler.\n```delphi\nmyjob := TMyJob.Create;\nmyjob.Name := Format('Run at %s and repeat every 1 second until %s',[DateTimeToStr(ScheduledDate),DateTimeToStr(ExpirationDate)]);\nscheduledtasks.AddTask('Task1',[myjob],True,\n                            procedure(task : ITask)\n                            begin\n                              cout('task \"%s\" started',[TMyTask(task.Param[0]).Name],etDebug);\n                              TMyJob(task.Param[0]).DoJob;\n                            end\n                          ).OnException(\n                            procedure(task : ITask; aException : Exception)\n                            begin\n                              cout('task \"%s\" failed (%s)',[TMyJob(task.Param[0]).Name,aException.Message],etError);\n                            end\n                          ).OnTerminated(\n                            procedure(task : ITask)\n                            begin\n                              cout('task \"%s\" finished',[TMyJob(task.Param[0]).Name],etDebug);\n                            end\n                          ).OnExpired(\n                            procedure(task : ITask)\n                            begin\n                              cout('task \"%s\" expired',[TMyJob(task.Param[0]).Name],etWarning);\n                            end\n                          ).StartAt(ScheduledDate\n                          ).RepeatEvery(1,TTimeMeasure.tmSeconds,ExpirationDate);\nscheduledtasks.Start;\n```\n\n- **ITask:** Interface passed to every task event of TRunTask, TBackgroundTasks and TScheduledTasks.\n  - **NumWorker:** Return number of worker assigned to execute task.\n  - **Result:** Can store any value type (TFlexValue is like variant type)\n  - **Param[name]:** Can store parameters passed to task or created dynamically into every anonymous methods passed to each event.\n  - **Param[index]:** Can store parameters passed to task or created dynamically into every anonymous methods passed to each event.\n  - **Done:** Return true is task is executed without errors.\n  - **Failed:** Return true is task has failed.\n  - **IdTask:** Task id defined.\n  - **NumRetries:** Number of retries done.\n  - **MaxRetries:** Number of maximum retries allowed before mark task as failed.\n  - **LastException:** Return last exception of a failed task.\n  - **CircuitBreaked:** Return true if max retries has been reached or user cancelled into OnRetry event.\n  - **IsEnabled:** Return status of task.\n\n**Quick.FaultControl:**\n--\nManages fail and retry policies, defining max retries, wait time beetween retries and circuit break mecanism.\n\n**Quick.Process:**\n--\nManages windows processes.\n```delphi\n//kill explorer process\nKillProcess('explorer.exe');\n//determine if an application is running\nif IsProcessRunning('explorer.exe') then Showmessage('Explorer is running!');\n//get username who is running an exe\nwriteln('Explorer.exe open by: ' + GetProcessUser('explorer.exe');\n//gets handle of a window with a 20 seconds timeout\nif FindWindowTimeout('MainWindow',20) then writeln('Window detected');\n```\n\n**Quick.Services:**\n--\nManages windows services.\n```delphi\n//detect if a service is installed\nif not ServiceIsPresent('localhost','MySvc') then raise Exception.Create('Service not installed!');\n//Start a service\nServiceStart('localhost','MySvc');\n//Uninstall a service\nServiceUninstall('MySvc');\n```\n\n**Quick.Format:**\n--\nString format.\n\n```delphi\n//Format bytes to MB, GB, TB...\nFormatBytes(50000) //shows 50KB\nFormatBytes(90000000) //shows 90MB\n```\n\n**Quick.JsonSerializer:**\n--\nSerializes an object from/to json text. You can define if public or published will be processed (only Delphi, fpc rtti only supports published properties)\t\n\n```delphi\njson := '{\"name\":\"Peter\",\"age\":30}';\nserializer := TJsonSerializer.Create(TSerializeLevel.slPublishedProperty);\ntry\n   serializer.JsonToObject(user,json);\nfinally\n   serializer.Free;\nend;\n```\n\n**Quick.AutoMapper:**\n--\nMap fields from one class to another class. Allows custom mappings to match different fields and custom mapping procedure to cast/convert fields manually.\t\n\n```delphi\n//Map values from User1 to User2\nTMapper\u003cTUser2\u003e.Map(User);\n\n//Map custom mappings\nAutoMapper := TAutoMapper\u003cTUser,TUser2\u003e.Create;\n\n//option1: you can define auto map different named properties\nAutoMapper.CustomMapping.AddMap('Cash','Money');\nAutoMapper.CustomMapping.AddMap('Id','IdUser');\n\n//option2: you can decide to modify each property manually or allow to auto someones\nAutoMapper.OnDoMapping := procedure(const aSrcObj : TUser; const aTargetName : string; out Value : TFlexValue)\n                          begin\n                            if aTargetName = 'Money' then Value := aSrcObj.Cash * 2\n                              else if aTargetName = 'IdUser' then Value := aSrcObj.Id;\n                          end;\n\n//option3: you can modify some properties after automapping done\nAutoMapper.OnAfterMapping := procedure(const aSrcObj : TUser; aTgtObj : TUser2)\n                             begin\n                               aTgtObj.Money := aSrcObj.Cash * 2;\n                               aTgtObj.IdUser := aSrcObj.Id;\n                             end;\n\nUser2 := AutoMapper.Map(User);\n```\n\n**Quick.JsonRecord:**\n--\nUsed as a DTO class, with json serialize and mapping functions included.\t\n\n```delphi\ntype\n   TUser = class(TJsonRecord)\n   private\n      fName : string;\n      fAge : Integer;\n   published\n      property Name : string read fName write fName;\n      property Age : Integer read fAge write fAge;\n   end;\nvar\n   user, user2 : TUser;\nbegin\n   user := TUser.Create;\n   //show as json string\n   Writeln(user.ToJson);\n   //mapping to other class\n   user.Mapto(User2);\n   Writeln(user2.ToJson);\n   //load from file\n   user.LoadFromFile('.\\user.json');\n   //save to file\n   user2.SaveToFile('.\\user2.json');\nend;\n```\n\n**Quick.Lists:**\n--\nImproved lists with indexing or search features.\n- **TIndexedObjectList:** Allows fast hashed searches by object properties or fields.\n- **TSearchObjectList:** Allows iteration search by object properties or fields.\n```delphi\nvar\n   users : TIndexedObjectList\u003cTUser\u003e;\nbegin\n   users := TIndexedObjectList\u003cTUser\u003e.Create(True);\n   //create index by property \"Name\"\n   users.Indexes.Add('Name','Name',TClassField.cfProperty);\n   //create index by private field \"Id\"\n   users.Indexes.Add('Id','fId',TClassField.cfField);\n   //get user by \"Name\" index\n   writeln(users.Get('Name','Peter').SurName);\nend;\n```\n\n**Quick.Value**\n--\nFlexValue stores any data type and allow pass to other class with integrated operators and autofrees.\n```delphi\nvar\n  value : TFlexValue;\n  str : string;\n  num : Integer; \nbegin\n  value := 'hello';\n  str := value;\n  value := 123;\n  num := value;\nend;\n```\n\n**Quick.Arrays:**\n--\nImproved arrays.\n\n**TXArray:** Array with methods like TList.\n```delphi\nvar\n   users : TXArray\u003cTUser\u003e;\nbegin\n   users.Add(User);\n   if users.Count:= TIndexedObjectList\u003cTUser\u003e.Create(True);\n   //create index by property \"Name\"\n   users.Indexes.Add('Name','Name',TClassField.cfProperty);\n   //create index by private field \"Id\"\n   users.Indexes.Add('Id','fId',TClassField.cfField);\n   //get user by \"Name\" index\n   writeln(users.Get('Name','Peter').SurName);\nend;\n```\n\n**TFlexArray:** Array with methods like TList than can storage different value types into same array.\n```delphi\nvar\n  flexarray : TFlexArray;\nbegin\n    flexarray.Add(10);\n    flexarray.Add('Hello');\n    user := TUser.Create;\n    try\n      user.Name := 'Joe';\n      flexarray.Add(user);\n\n      cout('Integer Item = %d',[flexarray[0].AsInteger],etInfo);\n      cout('String Item = %s',[flexarray[1].AsString],etInfo);\n      cout('Record Item = %s',[TUser(flexarray[2]).Name],etInfo);\n    finally\n      user.Free;\n    end;\nend;\n```\n**TFlexPairArray:** Array with methods like TList than can store different value types into same array, and search by item name.\n```delphi\nvar\n  flexarray : TFlexPairArray;\nbegin\n    flexarray.Add('onenumber',10);\n    flexarray.Add('other','Hello boy!');\n    user := TUser.Create;\n    try\n      user.Name := 'Joe';\n      flexarray.Add('myuser',user);\n\n      cout('Integer Item = %d',[flexarray.GetValue('onenumber').AsInteger],etInfo);\n      cout('String Item = %s',[flexarray.GetValue('other').AsString],etInfo);\n      cout('Record Item = %s',[TUser(flexarray.GetValue('myuser')).Name],etInfo);\n    finally\n      user.Free;\n    end;\nend;\n```\n\n**Quick.YAML:**\n--\nYaml object structure.\n\n**TYamlObject:** A Yaml object is and array of YamlValue pairs.\n```delphi\n  //create Yaml object from yaml text\n  yamlobj.ParseYamlValue(aYaml)\n  //add a pair\n  yamlobj.AddPair('Name','Mike');\n  //display as yaml structure\n  Writeln(yamlobj.ToYaml);\n```\n**TYamlArray:** Array of objects or scalars.\n```delphi\n  yamlarray.AddElement(TYamlPair.Create('Age',30));\n  yamlobj.AddPair('myarray',yamlarray);\n```\n**TYamlPair:** Name-Value pair. Value can be object, array or scalar.\n```delphi\n  n := yamlobj.GetPair('Name').Value as TYamlInteger;\n```\n\n**Quick.YAML.Serializer:**\n--\nSerialize/Deserialize object from/to Yaml.\n```delphi\n  //Serialize\n  text := YamlSerializer.ObjectToYaml(obj);\n  //Deserialize\n  YamlSerializer.YamlToObject(obj,yamltext);\n```\n\n**Quick.Expression:**\n--\n Evaluate object properties or single values using expressions.\n```delphi\n  if TExpressionParser.Validate(user,('(Age \u003e 30) AND (Dept.Name = \"Financial\")') then\n  begin\n    //do something\n  end;\n\n  if TExpressionParser.Validate(user,('(20 \u003e 30) OR (5 \u003e 3)') then\n  begin\n    //do something\n  end;\n```\n\n**Quick.Linq:**\n--\nMakes Linq queries to any TObjectList\u003cT\u003e, TList\u003cT\u003e, TArray\u003cT\u003e and TXArray\u003cT\u003e, 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. \nNow includes and TArray\u003cstring\u003e helper to add, remove and search with regular expressions into array.\n- **From:** Array, XArray or TObjectList to use.\n- **Where:** Expression to search. You can use a dots to define property path.\n- **SelectAll:** Returns an array of objects matching where clause\n- **SelectTop:** Returns top x objects matching where clause.\n- **SelectFirst:** Returns first object matching where clause.\n- **SelectLast:** Returns last object matching where clause.\n- **OrderBy:** Define order of returned list.\n- **Update:** Update fields of matching where clause.\n- **Delete:** Delete objectes matching where clause.\n- **Count:** Return number of elements matching where clause.\n```delphi\n  //Select multi conditional\n  for user in TLinq\u003cTUser\u003e.From(userslist).Where('(Name = ?) OR (SurName = ?) OR (SurName = ?)',['Peter','Smith','Huan']).Select do\n  begin\n    //do something\n  end;\n  \n  //Select like and update field\n  TLinq\u003cTUser\u003e.From(userlist).Where('SurName Like ?',['%son']).SelectFirst.Name := 'Robert';\n  \n  //Select top and Order by field\n  for user in TLinq\u003cTUser\u003e.From(userlist).Where('Age \u003e ?',[18]).SelectTop(10).OrderBy('Name') do\n  begin\n    //do something\n  end;\n  \n  //update fields by conditional\n  TLinq\u003cTUser\u003e.From(userlist).Where('Name = ?',['Peter']).Update(['Name'],['Joe']);\n  \n  //count results\n  numusers := TLinq\u003cTUser\u003e.From(userlist).Where('(Age \u003e ?) AND (Age \u003c ?)',[30,40]).Count;\n```\n\n**Quick.HTTPServer:**\n--\nTCustomHttpServer is a simple interfaced HttpServer with own HttpRequest and HttpResponse implementations to allow easy httpserver engine changes. \nYou can enable custom error pages to return customized pages and dynamic error pages.\nTHttpServer is the IndyHttpServer implementation, but you can define your own.\n```delphi\nTMyHttpServer = class(THttpServer)\n  public\n    procedure ProcessRequest(aRequest: IHttpRequest; aResponse: IHttpResponse); override;\n  end;\n\n  procedure TMyHttpServer.ProcessRequest(aRequest: IHttpRequest; aResponse: IHttpResponse);\n  begin\n    aResponse.ContentText := 'Hello world!';\n  end;\n```\n\n**Quick.MemoryCache:**\n--\nCaches 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\u003cT\u003e allows to cache a defined type only.\n- Create: Could be defined Purge interval and Serialization and Compression engines. By default serializes as Json and compress with gzip.\n```delphi\n //create MemoryCache with 10 seconds purge interval\n cache := TMemoryCache.Create(10);\n\n //create MemoryCache for a type\n cache := TMemoryCache\u003cTMyObj\u003e.Create;\n```\n- Compression: Enables/Disables cache data compression.\n- CachedObjects: Returns number of objects currently in cache.\n- 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.\n- PurgeInterval: Interval purge job tries to find expired objects to remove from cache (Default value 20 seconds).\n- OnCacheFlushed: When cache is flushed.\n- OnBeginPurgerJob: When PurgerJob starts.\n- OnEndPurgerJob: When PurgerJob ends.\n- Flush: Removes all cache objects.\n- 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.\n```delphi\n//set string to cache without expiration\ncache.SetValue('mystring','hello world');\n\n//set string to cache with expiration to 10 seconds\ncache.SetValue('mystring','this cache will expire in 10 seconds';\n\n//set object to cache\ncache.SetValue('Obj1',valueobj);\n```\n- TryGetValue: Tries to get and object from cache. Returns false if object doesn't exists or it's expired.\n```delphi\n//get string query result\ncache.GetValue('Query12');\n\n//get integer\ncache.TryGetValue\u003cInteger\u003e('number',valueint);\n\n//get object\ncache.TryGetValue('Obj1',valueobj);\n```\n\n- RemoveValue: Removes an object from cache.\n\n- **Cache Engine providers:**\n\n- TCacheSerializerJSON: Uses JSON to serialize cache data.  \n- TCacheCompressorGzip: Uses Gzip to compress cache data.\n- TCacheCompressorLZO: Uses LZO to compress cache data.\n   \n```delphi \n //create MemoryCache with 20 seconds purge interval and compression with LZO engine\n cache := TMemoryCache.Create(10,nil,TCacheCompressorLZO.Create);\n ```\n\n **Quick.IOC:**\n --\n  Inversion of Control manager allows autocreate interfaced o instanced object or autoinject them in constructor classes, to avoid dependency.\n\n Create a container to manage dependency injection. \n ```delphi\niocContainer := TIocContainer.Create;\n```\n**Register Types:**\n\nYou need to register types before you can inject them. A Type can be registered as Singleton, Transient.\n**Singleton**: Life cycle will be one single instance for all injections, similar to a Global variable.\n**Transient**: Life cycle will be one instance per each injection.\nRegister an interface type into container as transient:\n```delphi\niocContainer.RegisterType\u003cIMultService,TMultService\u003e.AsTransient;\n```\nRegister an interface type as singleton, delegating construction:\n```delphi\niocContainer.RegisterType\u003cISumService,TSumService\u003e.AsSingleTon.DelegateTo(\n  function : TSumService\n  begin\n    Result := TSumService.Create;\n  end\n);\n```\n**Register Instances:**\n\nRegister a named instance object as transient, delegating construction:\n```delphi\niocContainer.RegisterInstance\u003cTDivideService\u003e('one').AsTransient.DelegateTo(\n  function : TDivideService\n  begin\n    Result := TDivideService.Create(True);\n  end\n);\n```\n**Register Options:**\n\nRegister IOptions (only singleton):\n```delphi\n iocContainer.RegisterOptions\u003cTMyOptions\u003e(MyOptions);\n```\n**Resolve Types:**\n\nAbtractFactory:\nCreates a class trying to resolve all creation method parameter with dependency injection.\n```delphi\nMyClass := iocContainer.AbstractFactory\u003cTMyBaseClass\u003e(TMyClass);\n```\nResolve an interface dependency:\n```delphi\nmultservice := iocContainer.Resolve\u003cIMultService\u003e;\nresult := multservice.Mult(2,4);\n```\n**Resolve Instances:**\n\nResolve a named instance dependency:\n```delphi\ndivideservice := iocContainer.Resolve\u003cTDivideService\u003e('other');\nresult := divideservice.Divide(100,2);\n```\nInterface instances will be freed automatically, but instance dependencies only will be freed if defined as singleton, transient instances will be destroyed by code.\n\n**Quick.Options:**\n --\nYou define sections as classes and saves as single file settings. Works similar to dotnet Options. Options file can be in JSON or YAML format.\n\nDefine your option class inherited from TOptions and all published properties will be load/save.\nCreate options container, with JsonSerializer and reloading on change:\n```delphi\nOptions := TOptionsContainer.Create('.\\options.conf',TJsonOptionsSerializer.Create,True);\n```\nAdd a section to your container options:\n```delphi\nOptions.AddSection\u003cTLoggingOptions\u003e('Logging')\n```\n**Configure Options:**\n\nYou can define section name to save into file and delegate configuration default settings and validating values:\n```delphi\nOptions.AddSection\u003cTLoggingOptions\u003e('Logging').ConfigureOptions(\n  procedure(aOptions : TLoggingOptions)\n  begin\n    aOptions.Path := 'C:\\';\n  end\n).ValidateOptions;\n```\n**Validate Options:**\n\nValidate options allows verify if option settings are setted between defined ranges. This validation needs previously assigned custom attributes to properties in your TOptions class.\n- **StringLength(max,messagestr):** Allows define max length in string properties, returning messagestr if length greater than max.\n- **Range(min,max,messagestr):** Allows define a range of min and max values permitted, returning messagestr if value length outbounds margins.\n```delphi\nTLoggingOptions = class(TOptions)\n  private\n    fPath : string;\n  published\n    [Required, StringLength(255,'Path too long')]\n    property Path : string read fPath write fPath;\n    [Range(0.0,5.2)]\n    property Level : Double read fLevel write fLevel;\n  end;\n```\n**Use Options:**\nTo retrieve option section:\n```delphi\nLoggingOptions := Options.GetSection\u003cTLoggingOptions\u003e;\nLoggginOptions.Path := 'C:\\Path';\n```\n**Use IOptions:**\nIOptions is a dependency injectable interface to TOptions. You can register with IocContainer.RegisterOptions\u003cTOptions\u003e to make injectable into constructor methods.\n```delphi\nUIOptions := Options.GetSectionInterface\u003cTUIOptions\u003e.Value;\nUIOptions.WindowColor := clBlue;\n```\n**Load/Save Options:**\n\nLoad options from file settings:\n```delphi\noptions.Load;\n```\nSave options to file settings:\n```delphi\noptions.Save;\n```\nIf 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:\n```\nOptions.OnFileModified := procedure\n  begin\n    cout('Detected config file modification!',etWarning);\n  end;\n```\n\n**Quick.Pooling:**\n --\n Define pool of connection, threads or any object you want to control to avoid resource consum like database connections, http clients, etc...\n\nCreate http client pool:\n```delphi\n pool := TObjectPool\u003cTHTTPClient\u003e.Create(5,5000,procedure(var aInstance : THTTPClient)\n        begin\n          aInstance := THTTPClient.Create;\n          aInstante.UserAgent := 'MyAgent';\n        end);\n```\nGet object from pool:\n\n```delphi\nhttpcli := pool.Get.Item;\nstatuscode := httpcli.Get('https://www.mydomain.com').StatusCode;\n```\n\n**Quick.Collections:**\n --\nDefines interfaced List and Objectlist with linQ support inherited.\n\n- TXList\u003cT\u003e / IList\u003cT\u003e: Interfaced List allowing LinQ regEx search/remove/update.\n\n```delphi\nmyarray := ['Joe','Mat','Lee'];\n//search for regex match\ncout('Search for regex match',ccYellow);\nfor name in myarray.Where('e$',True).Select do\nbegin\n  cout('User %s ends with \"e\"',[name],etInfo);\nend;\n```\n\n- TXObjectList\u003cT\u003e / IObjectList\u003cT\u003e: Interfaced ObjectList allowing LinQ predicate or expression search/remove/update.\nExpression search:\n```delphi\nuser := ListObj.Where('Profile.Name = ?',['Lee']).SelectFirst;\n```\nExpression search for item array:\n```delphi\nusers := ListObj.Where('Roles CONTAINS ?',['SuperAdmin']).Select;\n```\nPredicate search:\n\n```delphi\nuser := ListObj.Where(function(aUser : TUser) : Boolean\n      begin\n        Result := aUser.Name.StartsWith('J');\n      end).SelectFirst;\n    if user \u003c\u003e nil then cout('%s starts with J letter',[user.Name],etInfo);\n```\nSee Quick.Linq section to view more functions allowed.\n\n**Quick.Template:**\n --\nString template replacing using a dictionary or delegate function. You can specify quoted token chars. \n\nReplace passing a dictionary:\n```delphi\ndict := TDictionary\u003cstring,string\u003e.Create;\ndict.Add('User','John');\ndict.Add('Age','20');\ndict.Add('SurName','Peterson');\nmytemplate := 'User {{User}} {{SurName}} are {{Age}} years old.';\ntemplate := TStringTemplate.Create('{{','}}',dict);\nResult := template.Replace(mytemplate);\n```\nReplace with delegate function:\n```delphi\nmytemplate := 'User {{User}} {{SurName}} are {{Age}} years old.';\ntemplate := TStringTemplate.Create('{{','}}',function(const aToken : string) : string\n  begin\n    if token = 'User' then Result := 'John'\n    else if token = 'Surname' then Result := 'Peterson'\n    else if token = 'Age' then Result := '20';\n  end);\nResult := template.Replace(mytemplate);\n```\n\n**Quick.Debug.Utils:**\n --\nDebug 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.\nOn console apps uses console out by default. You can pass a logger to output in:\n```delphi\nTDebugUtils.SetLogger(ilogger);\n```\nTrace a part of your code:\n```delphi\nfunction TCalculator.Subs(a, b: Int64): Int64;\nbegin\n  {$IFDEF DEBUG}\n  TDebugger.Trace(Self,Format('Substract %d - %d',[a,b]));\n  {$ENDIF}\n  Result := a - b;\n  //simulate working for 200ms\n  Sleep(200);\nend;\n//Returns:\n//29-06-2020 23:31:41.391  [TRACE] TCalculator -\u003e Substract 30 - 12\n```\nCalculate time to process from point to exit function:\n```delphi\nfunction TCalculator.Sum(a, b: Int64): Int64;\nbegin\n  {$IFDEF DEBUG}\n  TDebugger.TimeIt(Self,'Sum',Format('Sum %d + %d',[a,b]));\n  {$ENDIF}\n  Result := a + b;\n  //simulate working for 1 seconds\n  Sleep(1000);\nend;\n//Returns:\n//29-06-2020 22:58:45.808  [CHRONO] TCalculator.Sum -\u003e Sum 100 + 50 = 1,00s\n```\nCalculate time to process from point to point and exit function:\n```delphi\nfunction TCalculator.Divide(a, b: Int64): Double;\nbegin\n  {$IFDEF DEBUG}\n  var crono := TDebugger.TimeIt(Self,'Divide',Format('Divide %d / %d',[a,b]));\n  {$ENDIF}\n  Result := a / b;\n  //simulate working for 500ms\n  Sleep(500);\n  {$IFDEF DEBUG}\n  crono.BreakPoint('Only divide');\n  {$ENDIF}\n  //simulate working for 1 second\n  Sleep(1000);\n  {$IFDEF DEBUG}\n  crono.BreakPoint('Only Sleep');\n  {$ENDIF}\nend;\n//Returns:\n//29-06-2020 23:25:46.223  [CHRONO] TCalculator.Divide -\u003e First point = 500,18ms\n//29-06-2020 23:25:47.224  [CHRONO] TCalculator.Divide -\u003e Second point = 1,00s\n//29-06-2020 23:25:47.225  [CHRONO] TCalculator.Divide -\u003e Divide 10 / 2 = 1,50s\n```\nGet notification when enter and exit function, and times it:\n```delphi\nfunction TCalculator.Mult(a, b: Int64): Int64;\nbegin\n  {$IFDEF DEBUG}\n  TDebugger.Enter(Self,'Mult').TimeIt;\n  {$ENDIF}\n  Result := a * b;\n  //simulate working for 2 seconds\n  Sleep(2000);\nend;\n//Returns:\n//29-06-2020 22:58:45.808  [ENTER] \u003e\u003e TCalculator.Mult\n//29-06-2020 22:58:47.810  [EXIT] \u003e\u003e TCalculator.Mult in 2,00s\n```\n\n**Quick.Parameters:**\n--\nWorking with commandline parameters will be easy using commandline extension.\nDefine a class inherited from TParameters or TServiceParameters (if working with QuickAppServices) with your possible arguments as published properties:\n```delphi\nuses\n  Quick.Parameters;\ntype\n  TCommand = (Copy, Move, Remove);\n  TMyMode = (mdAdd, mdSelect, mdRemove);\n\n  [CommandDescription('Simple console application example with Quick.Parameters')]\n  TMyParameter = class(TParameters)\n  private\n    fCommand : TCommand;\n    fHost : string;\n    fPort : Integer;\n    fRetries : Integer;\n    fUseTCP : Boolean;\n    fConfigFile: string;\n    fSilent: Boolean;\n    fMyMode: TMyMode;\n    fLogErrorsConsole: Boolean;\n    fLogErrors: Boolean;\n    fShowReport: Boolean;\n  published\n    [ParamCommand(1)]\n    [ParamRequired]\n    [ParamHelp('Command action.','command-action')]\n    property Command : TCommand read fCommand write fCommand;\n\n    [ParamName('HostName'),ParamRequired]\n    [ParamHelp('Define host to connect.','host')]\n    property Host : string read fHost write fHost;\n\n    [ParamName('Port','p')]\n    [ParamValueIsNextParam]\n    [ParamHelp('Define Port to connect (default 80)','port')]\n    property Port : Integer read fPort write fPort;\n\n    [ParamHelp('Number of max retries.')]\n    property Retries : Integer read fRetries write fRetries;\n\n    [ParamHelp('Path to config.','path')]\n    [ParamName('Config-file')]\n    property ConfigFile : String read fConfigFile write fConfigFile;\n\n    [ParamHelp('Silent mode.')]\n    property Silent : Boolean read fSilent write fSilent;\n\n    [ParamHelp('Modes (mdAdd, mdSelect, mdRemove)')]\n    property Mode : TMyMode read fMyMode write fMyMode;\n  end;\n\n```\nUse param:\n```delphi\nparams := TMyParameter.Create;\n```\nWhen you call your exe with --help you get documentation. If you need to check for a switch or value, you can do like this:\n```delphi\nif params.Port = 0 then ...\nif params.Silent then ...\n```\nQuickParameters uses custom attributes to define special parameter conditions:\n\n- **CommandDescription:** Defines text to describe your application in help documentation.\n\n- **ParamCommand(number):** Defines static position into commandline for single parameters.\n\n- **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.\n\n- **ParamHelp(helptext,valuename):** Defines a commandline help text and value name in usage section.\n\n- **ParamSwitchChar(sign):** Defines string or char to indicate switch or parameter. If not defined, a double dash (--) will be used by default.\n\n- **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.\n\n- **ParamValueIsNextParam:** Defines a parameter with a value without value separator (filename c:\\config.ini)\n\n- **ParamRequired:** Defines a parameter as required. If param not found, an exception will be raised.\n\nQuickParameter automatically checks for value types. If you define a parameter value as Integer, and pass an alfanumeric, an exception will be raised.\n\nHelp customization:\nYou can define your own color customization with ColorizeHelp. Enabled property will use custom colors, otherwise b/w will be used.\n```delphi\nParameters.ColorizeHelp.Enabled := True;\nParameters.ColorizeHelp.CommandName := ccCyan;\nParameters.ColorizeHelp.CommandUsage := ccBlue;\n```\nWhen parameters detects help parameter, help documentation will be showed.\n\nParameters.ShowHelp: Shows help documentation, generated automatically:\n```\nParameters v.1.0\nUsage: Parameters \u003ccommand-action\u003e \u003c--HostName=\u003chost\u003e\u003e [--Port \u003cport\u003e] [--Retries=\u003cvalue\u003e]\n                  [--Config-file=\u003cpath\u003e] [--UseTCP] [--Silent] [--Mode=\u003cvalue\u003e]\n                  [--ShowReport] [--Help]\n\nSimple console application example with Quick.Parameters\n\nArguments:\n\n    Command                  Command action.\n  --HostName                 Define host to connect.\n  --Port, -p                 Define Port to connect (default 80)\n  --Retries                  Number of max retries.\n  --Config-file              Path to config.\n  --UseTCP                   Use TCP connection if present.\n  --Silent                   Silent mode.\n  --Mode                     Modes (mdAdd, mdSelect, mdRemove)\n  --Help, -h                 Show this documentation\n```\n\n**Quick.Url.Utils:**\n--\n- **GetProtocol:** Get protocol from an url.\n- **GetHost:** Get hostname from an url.\n- **GetPath:** Get path from an url.\n- **GetQuery:** Get Query part from an url.\n- **RemoveProtocol:** Remove protocol from an url.\n- **RemoveQuery:** Remove query part from an url.\n- **EncodeUrl:** Encode path and query from and url.\n\n**Quick.RegEx.Utils:**\n--\nCommonly used validations.\n```delphi\n```\n\n**Quick.Conditions:**\n--\nPre and postcondition validations in fluent style.\nCondition.Requires evaluates a variable for conditions before do some operations.\nCondition.Ensures evaluates a variable result for conditions after do some operations.\n```delphi\n    Condition.Requires(num, \"num\")\n        .IsInRange(1,10,'value for num is out of range');   // throws custom error if not in range\n        .IsNotGreater(50);   // throws ArgumentException if not equal to 128\n\n    Condition.Requires(myobj, \"myobj\")\n        .WithExceptionOnFailure(EMyException) //throws specific exception on failure\n        .IsNotNull()          // throws ArgumentNullException if null\n        .Evaluate(myobj.id \u003e 10); // myobj.id must be greater than 10\n\n    Condition.Requires(text, \"text\")\n        .IsNotEmpty()          // throws ArgumentNullException if empty\n        .StartsWith(\"\u003chtml\u003e\") // throws ArgumentException if not starts with \u003chtml\u003e\n        .EndsWith(\"\u003c/html\u003e\") // throws ArgumentException if not ends with \u003c/html\u003e\n        .IsNotLowerCase // thows ArgumentException if not lowercase\n        .Evaluate(text.Contains(\"sometxt\") or test.Contains('othertxt')); // throws ArgumentException if not evaluates\n```\n\n\u003eDo you want to learn delphi or improve your skills? [learndelphi.org](https://learndelphi.org)\n","funding_links":["https://www.paypal.com/donate/?hosted_button_id=BKLKPNEYKSBKL"],"categories":["General Libraries ##"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fexilon%2FQuickLib","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fexilon%2FQuickLib","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fexilon%2FQuickLib/lists"}