**QuickLib** -------- Small 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. **Areas of functionality:** * **Mapping**: Map fields from a class to other class, copy objects, etc.. * **Config**: Thread 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. * **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... **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. * **Quick.Process:** Manages windows processes. * **Quick.Services:** Manages windows services. * **Quick.Format:** String format. * **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. **Updates:** * 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 **Documentation:** ---------- **Quick.AppService:** Allow a console app to run as console mode or service mode with same code simplifying debug tasks. ```delphi 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. ```delphi //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. ```delphi //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. ```delphi //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. ```delphi //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... ```delphi //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. ```delphi //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. ```delphi //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. ```delphi 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. ```delphi //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. ```delphi //Send email SMTP := TSMTP.Create('mail.domain.com',25,False); SMTP.SendMail('my@email.com','to@email.com','Email subject','My message body'); //You can define more advanced options SMTP.SenderName := 'John'; SMTP.From := 'my@email.com'; SMTP.Recipient := 'one@email.com,two@email.com'; SMTP.Subject := 'Email subject'; SMTP.AddBodyFromFile := '.\body.html'; SMTP.CC := 'other@email.com'; SMTP.BCC := 'more@email.com'; 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. ```delphi //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 TO EXIT',etInfo); end) .Start; ``` - **TBackgroundsTasks:** Launch tasks in background allowing number of concurrent workers. Use AddTask_Sync and OnTerminate_Sync methods if code needs to update UI. - *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). - *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. - *Start:* Starts tasks execution. ```delphi 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 ).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. 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). - *Define events to control:* - *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. - *Start/Stop scheduler:* - *Start:* Starts scheduler. - *Stop:* Stops scheduler. ```delphi 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; ``` **Quick.Process:** Manages windows processes. ```delphi //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. ```delphi //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. ```delphi //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) ```delphi 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. ```delphi //Map values from User1 to User2 TMapper.Map(User); //Map custom mappings AutoMapper := TAutoMapper.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. ```delphi 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. ```delphi var users : TIndexedObjectList; begin users := TIndexedObjectList.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. ```delphi 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. ```delphi var users : TXArray; begin users.Add(User); if users.Count:= TIndexedObjectList.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. ```delphi 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. ```delphi 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. ```delphi //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. ```delphi yamlarray.AddElement(TYamlPair.Create('Age',30)); yamlobj.AddPair('myarray',yamlarray); ``` - TYamlPair: Name-Value pair. Value can be object, array or scalar. ```delphi n := yamlobj.GetPair('Name').Value as TYamlInteger; ``` **Quick.YAML.Serializer:** Serialize/Deserialize object from/to Yaml. ```delphi //Serialize text := YamlSerializer.ObjectToYaml(obj); //Deserialize YamlSerializer.YamlToObject(obj,yamltext); ``` **Quick.Expression:** Evaluate object properties using expressions. ```delphi if TExpressionParser.Validate(user,('(Age > 30) AND (Dept.Name = "Financial")') 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. ```delphi //Select multi conditional for user in TLinq.From(userslist).Where('(Name = ?) OR (SurName = ?) OR (SurName = ?)',['Peter','Smith','Huan']).Select do begin //do something end; //Select like and update field TLinq.From(userlist).Where('SurName Like ?',['%son']).SelectFirst.Name := 'Robert'; //Select top and Order by field for user in TLinq.From(userlist).Where('Age > ?',[18]).SelectTop(10).OrderBy('Name') do begin //do something end; //update fields by conditional TLinq.From(userlist).Where('Name = ?',['Peter']).Update(['Name'],['Joe']); //count results numusers := TLinq.From(userlist).Where('(Age > ?) AND (Age < ?)',[30,40]).Count; ```