You are not logged in.
Hi all,
I'm converting a mormot1/DelphiXE4 project to mormot2/Delphi11 and got almost everything refactored.
However I find no RestServer.ExportServerNamedPipe ?
How can I export NamedPipes from TRestServer/TRestHttpServer I'm M2?
Regards
Anton
Thank you for the reply.
I somehow missed it; But now come back to this project and stumbled upon your reply searching for something else:)
I saw the RTL support, but since I use synopse json<>TDocVariantData almost exclusively, I hoped to avoid 'heavy' RTL classes.
I suspect the SQLite/JSON support will be good (i.t.o. fast/memory efficient) for large datasets so I'll try that in future and I'll probably have a look at the FPC implementation at some stage.
A way to query similar to JsonPath, (even just in code) will be very useful, especially if you can aggregate and group as well, but for now I just write queries by hand.
Thank you
Keep up the great work
Hi, I am more impressed by mORMot1&2 everytime I use it. Respect!
TDocVariantData is very powerful. I want to use it as an in-memory 'json object List' for classes (TOrm or others) that must constantly be marshalled but seldom changed.
E.g. a (mostly) lookup table of TOrm classes loaded into a TDocVariantData-Array, perhaps sorted by TID for FastFind.
However, something like https://jsonpath.com/ for TDocVariant(Data) can make a for a very powerful and versatile tool, especially in harmony with copyOnReference/ByValue.
E.g. server-side cache of data objects, sorted by some keyin a TDocVariantData array that is already quick to serialize to Json, but now to add ability to do on-the-fly basic 'queriesq to get subset of objects/fields (like sqlWhare/Args) in nested structures.
Or any utility/tool that can help me not write every json-query out by and as code will help,
thanks!
Anton
For high-performance library such as mORMot, this might be interesting in future :
https://www.chromium.org/quic/
Hi Ab,
I somehow missed these messages...
What is your feedback with Flutter?
Is it easy to work with?
Which tutorial do you recommend?
I think Dart and Flutter is amazing and getting better with updates almost every day.
The best tutorial I saw is on AppBrewery "The Complete 2021 Flutter Development Bootcamp with Dart", Flutter/Dart changed a bit since that course, especially with null-safety, but it's not to bad to figure out what need to change. Really good progression into Dart and Flutter, covering basic design all the way to using and writing APIs, Interfaces, Services (in Delphi-speak).
The whole idea of 'Widgets' in Flutter to dynamically build the UI elements (sometimes on each frame), seems weird at first, but it is so to create dynamic, multi-platform apps. Everything, even an Integer is an Object and everything in the UI is a Widget, so the language get really expressive and compact (sometimes).
I'm not turning back, no more expensive Delphi licenses, which I only need for client-side UI apps, but thanks to mORMot, not for server![8-}>
https://appbrewery.com/courses/enrolled/548873
I have not tested/deployed a full-blown non-debug app, to see performance, etc, yet; Almost...
Good luck!
Anton E
I have TJSONSerializer.RegisterClassForJSON(TObjectList) on Client app (XE4/mormot1) but had to comment that line out on server (D11/mormot2) as described in OP.
I pass the objectlist via Intertfaced based service method,e.g.
function CalList(var Lst:TObjectList):Boolean;
Regards
AntonE
As TObjectList is not a TPersistentClass it cannot be registered for JSON via Rtti.RegisterClass and TJSONSerializer.RegisterClassForJSON is not available anymore.
How can I register it as I use it extensively in mormot1 code migrated to mormot2
Thanks
AntonE
Happy to revive an old thread.
I've never worked with Javascript but started to use Flutter (Dart language) and I must say it's starting to grow on me.
I struggled to get authentication to a mORMot server working and almost posted here a few times.
Many thanks to the posters above, I couldn't have done it without you.
FWIW, if anyone need to auth to mORMot from Dart:
import 'dart:convert';
import 'package:archive/archive.dart';
import 'package:crypto/crypto.dart';
import 'package:http/http.dart';
main() async {
MORMOT_client http_client = MORMOT_client('http://10.1.1.200:8092/','main/');
await http_client.login('User', 'synopse');
final List<dynamic> products = await http_client.GetList('product');
print(products);
final Map<String,dynamic> product = await http_client.GetItem('product/106');
print(product);
}
class MORMOT_client {
MORMOT_client(String this.SERVER_URL,String this.SERVER_MODEL){}
String SERVER_URL = '';
String SERVER_MODEL = '';
String SESSION_ID = '';
String SESSION_PRIVATEKEY = '';
String SESSION_USERNAME = '';
int SESSION_TICK_COUNT = 1;
int SESSION_START_TIME = 0;
login(String userName, String userPass) async {
String clientnonce = '';
String servernonce = '';
String password = '';
String hashedPassword = '';
SESSION_ID = '';
SESSION_PRIVATEKEY = '';
SESSION_USERNAME = userName;
SESSION_TICK_COUNT = 1;
SESSION_START_TIME = DateTime.now().millisecondsSinceEpoch;
clientnonce = SHA256((DateTime.now().millisecondsSinceEpoch ~/ (1000 * 60 * 5)).toString()).toString();
final Map<String, dynamic> sessionResult = await urlToMap(SERVER_URL+SERVER_MODEL+'auth?Username=$userName');
if (sessionResult.isEmpty) {return;}
servernonce = sessionResult['result'];
hashedPassword = SHA256('salt' + userPass).toString();
password = SHA256('main' + servernonce + clientnonce + userName + hashedPassword).toString();
final Map<String, dynamic> authResult = await urlToMap(SERVER_URL+SERVER_MODEL+'auth?Username=$userName&Password=$password&ClientNonce=$clientnonce');
if (authResult.isEmpty) {return;}
String session_str = authResult['result'].toString();
SESSION_ID = int.parse(session_str.split('+')[0]).toRadixString(16).padLeft(8, '0');
SESSION_PRIVATEKEY = session_str + hashedPassword;
print('SID:$SESSION_ID');
print('SPK:$SESSION_PRIVATEKEY');
}
String GetSessionSignature(String url) {
String? prefix;
String? nonce;
String? keynonce;
String? final_SIGN;
SESSION_TICK_COUNT = DateTime.now().millisecondsSinceEpoch - SESSION_START_TIME;
print('SessionTickCount:$SESSION_TICK_COUNT');
nonce = SESSION_TICK_COUNT.toRadixString(16).padLeft(8, '0');
keynonce = getCrc32(utf8.encode(SESSION_PRIVATEKEY + nonce + SERVER_MODEL+url)).toRadixString(16).padLeft(8, '0');
print('nonce :$nonce');
print('keynonce:$keynonce');
return SESSION_ID + nonce + keynonce;
}
Future<List<dynamic>> GetList(String url)async{
final String signature = GetSessionSignature(url);
final String prefix = (url.indexOf('?')>0) ? '&' : '?';
final String theUrl = SERVER_URL+SERVER_MODEL+url+prefix+'session_signature=$signature';
print('URL:$theUrl');
final List<dynamic> auth2 = await urlToList(theUrl);
print(auth2);
return auth2;
}
Future<Map<String,dynamic>> GetItem(String url)async{
final String signature = GetSessionSignature(url);
final String prefix = (url.indexOf('?')>0) ? '&' : '?';
final String theUrl = SERVER_URL+SERVER_MODEL+url+prefix+'session_signature=$signature';
print('URL:$theUrl');
final Map<String,dynamic> item = await urlToMap(theUrl);
print(item);
return item;
}
}
Digest SHA256(String value){
var bytes = utf8.encode(value);
return sha256.convert(bytes);
}
Future<Map<String,dynamic>> urlToMap(String url)async {
Response r = await get(Uri.parse(url));
if (r.statusCode != 200) {
print(r.statusCode);
print(r.body);
final Map<String,dynamic> tmp = {};
return tmp;
}
final Map<String, dynamic> res = jsonDecode(r.body);
return res;
}
Future<List<dynamic>> urlToList(String url)async {
Response r = await get(Uri.parse(url));
if (r.statusCode != 200) {
print(r.statusCode);
print(r.body);
final List<dynamic> tmp = [];
return tmp;
}
final List<dynamic> res = jsonDecode(r.body);
return res;
}
It is a very raw testing unit, needs exception handling etc.
Output an array(list) and object(map):
[{ID: 1}, {ID: 3}, {ID: 4}, {ID: 5}, {ID: 10},...
{ID: 106, Created: 0, Modified: 135522307659, CODE: SLUC40MBPS, DESCRIPTION: SLUC40MBPS...
Thanks for the samples!
Testing Project04 Server&Client in Win64, Client compiles and run fine.
However trying to compile Project04InterfaceBasedServer for Win64:
In Delphi 10.4 : [dcc32 Fatal Error] F2092 Program or unit 'mormot.core.variants' recursively uses itself
In FPC 3.3.1 : mormot.core.json.pas(9217,32) Error: Wrong number of parameters specified for call to "<Procedure Variable>"
Changing said lines to :
if not Assigned(OnMatch) or
(not Assigned(@KeyCompare) and
not Assigned(@ValueCompare)) then
exit;
Compiles and run, but when sending a text msg from client app, get error:
{ "errorCode":406, "errorText":"IExample.Add failed parsing ASample: TSample from input JSON" }
Hi all,
SHORT:
Q1) Best server/client setup in mORMot2 for secure Server[Winx64]/Client[FMX Android].
Q2) How to wire-up 3rd party WebSocket client-component into mORMot2?
LONG:
Q1)I need to start a new multi-platform (Win/Android) Client app in Firemonkey, server running on Windows.
I've been trying to get WebSockets working with mORMot1 Android, installed Certificates and have bought latest scgWebsockets license for an Android WebSockets client.
Coming here for help, I discovered mORMot2! Congrats and thank you! So it makes me think if some new development might make a difference here.
The app's storage+network must be fully encrypted (preferably end-to-end for net) and async WebSockets would be ideal, with the usual mORMot TRest and InterfacedBasedServices.
All clients will be mORMot compiled for Win/Android/macOs.
What would be the recommended secure server/client setup be, especially for Android?
I've read before that one need to compile mORMot's WebSocket client in Larazus which can be used in Delphi FMX/Android? Is this still the case?
Q2) Alternatively; How can I 'wire-up' scgWebsocketClient in Android so the rest of my code work with a TRest mORMot class/intf as usual?
Thanks, hope it make sense.
TDocVariant might simplify your app?
Thank you,
my oversight,
the SSID my Android device was connecting to on my WiFi had Client-Isolation on, working fine now at least for a basic http connection.
Good bye datasnap for good!
AntonE
Hi, I'm trying to get Delphi 10.2 Multi-device to work with mORMot.
I run the Project14ServerHttpWrapper project, then extract the mORMotClient.pas file, create a multi-device application with one button that does:
procedure TForm1.Button1Click(Sender: TObject);
var C : TSQLRestClientHTTP;
begin
C:=mORMotClient.GetClient('192.168.0.13','User','synopse',8888);
ShowMessage(TTimeLogToIso8601(C.ServerTimeStamp));
end;
On Win32 it works 100% from a remote PC(so firewall is no issue), but on Android I get "Impossible to connect to 192.168.0.13:8888 server."
I do not have older Delphi that can compile for Android so I cannot test if it's a Delphi 10.2 issue or not.
What can I do to further narrow down the issue or do I need to do something else Android-specific (https?).
Thanks!
Edit: I might add that the server side is compiled on XE4 but with same version mORMot library.
I also compiled the sample by Peter Evens from here https://synopse.info/forum/viewtopic.php?id=3418 (server and client in Delphi10.2) and I get the same error an Android, Win32 works fine.
Hi,
I have a data object as such:
type
TIPAddress : Cardinal;
type
TSQLTrafficItem=class(TSQLRecord)
private
FIP : TIPAddress;
FDate : TDateTime;
FUL : Int64;
FDL : Int64;
published
property IP : TIPAddress read FIP write FIP ;
property DateSlot : TDateTime read FDate write FDate ;
property UL : Int64 read FUL write FUL ;
property DL : Int64 read FDL write FDL ;
end;
But in my app, each client can have many IPs, in fact some IPs might be subnets, containing multiple IPs.
So to get some aggregated data, I used to pass a dynamic array :
type TIPsPair= record
IP:String;
IP1,IP2:Cardinal;
end;
TIPsPairArr = Array of TIPsPair;
where IP is the text subnet, e.g. "10.1.2.0/24" and IP1 will then hold calculated cardinal value of 10.1.2.0 and IP2 will hold the cardinal value of 10.1.2.255 to indicate the range.
So passing a client with say these IPs:
10.2.2.2/32 (10.2.2.2-10.2.2.2) call these cardinal values IPA1-IPA2
10.3.3.0/30 (10.3.3.0-10.3.3.3) call these cardinal values IPB1-IPB2
10.4.4.0/24 (10.4.4.0-10.4.4.255) call these cardinal values IPC1-IPC2
I had to built a SQL query like so:
select * from TRAFFIC where DateSlot>=:DateF and DateSlot<:DateT and
(
((IP>=IPA1)and(IP<=IPA2))or
((IP>=IPB1)and(IP<=IPB2))or
((IP>=IPC1)and(IP<=IPC2))
)
OFC values IPA1, IPA2,etc replaced with cardinal numbers in real query.
Is there a better, more efficient way to do it in mORMot to harness cached prepared statements where I can maybe create a larger 'flat' array of the required IPs, e.g.
Var Arr : Array of Cardinal;
Arr[0]:=IPStrToCardinal(10.2.2.2);
Arr[1]:=IPStrToCardinal(10.3.3.0);
Arr[2]:=IPStrToCardinal(10.3.3.1);
Arr[3]:=IPStrToCardinal(10.3.3.2);
....
Then use something like:
aList:=Server.RetrieveList<TSQLTrafficItem>(' IP IN ? AND DATESLOT>= ? AND DATESLOT < ?',[Arr,DateF,DateT]);
Or some other advice to make this query as efficient/fast as possible?
Thanks
AntonE
Hi,
I'm busy migrating a legacy app and need to access some Firebird data in multiple threads.
First time Synopse library use for direct Firebird access.
(Very) Stripped down code is:
function GetUsageTotal(DB:TSQLDBConnectionProperties;DataParams:TDataParams):Int64; (*Global function in a unit*)
var RES : ISQLDBROWS;
begin
RES:=DB.Execute(SQL.Text,[]);
if RES.Step
then Result:=RES['Tot']
else Result:=0;
end;
Type TCheckThread = class(TThread);
procedure TCheckThread.Execute;
begin
DBCP:= TSQLDBZEOSConnectionProperties.Create(TSQLDBZEOSConnectionProperties.URI(dFirebird,IP),DB,UN,PW);
while not Terminated do
begin
(* some other stuff, fetch task, back-off yield/sleep, etc *)
Tot:=GetUsageTotal(DBCP,DataParams);
(* some other stuff *)
end;
DBCP.Free;
end;
Is this in principle correct?
I initially had all threads share one TSQLDBZEOSConnectionProperties but got this error, so I made a copy for each thread and still get the issue.
If I run it in 1 thread there is no problem, but if I run multiple threads then I periodically get 'corrupt' results.
Error in ZDbcInterbase6Utils line 829, e.g. errors converting from DateTime '022/12/2016' or '02/16/201717'. (no typos)
I have other less-intensive simultaneous threads running with also TSQLDBConnectionProperties but they access different tables, this is the only place where I simultaneously need to access the same database table (many GBs big).
I have {$DEFINE ZEOS72UP} in zeos.inc but not sure if other changes need to be made there.
Using latest Zeos 7.2.1 & mORMot downloaded about two weeks ago. Firebird 3.0.1
If there is nothing obviously wrong here then I'll write a test case to try replicate it outside my code.
Thank you
AntonE
Hi.
Looking at the MVC sample...
To segregate logical units of the app, can/should I create an IMVCApplication for each 'module' in the server? As one (not so good) example, in accounting app you can have billing, inventory, blog, etc. as separate logical 'modules'.
AntonE
I need to implement an IDispatch interface to be able to access TDocVariant 'properties' from scripts.
Has anyone done this before that care to share some code?
Regards
AntonE
Hello.
I need to create a TSQLTableJSON on the fly with custom columns, i.e. not tied to a TSQLRecord class.
(Actually it must be created from custom fields derived from TDocVariant within a TSQLRecord variant property)
Is it possible to create the TSQLTableJSON with column types, then add rows or it it read-only?
If it is read-only, what would be the best way to create a table like this?
1) Create the whole resultset as a JSON string by using TTextWriter
2) Create TSQLTableJSON.CreateWithColumnTypes (so that TSQLTableToGrid can display nicely)?
Or is there maybe some other mORMot-magic method to get same result?
Thanks
AntonE
I looked at that.
One thing that caught my eye:
Most TSQLDBConnectionProperties will inherit from TSQLDBConnectionPropertiesThreadSafe, so will create one connection per thread. This is efficient, but some providers may have issues with it.
Do I need to create another connection to local SQLite db for each internal server thread?
Thank you for your reply.
BTW: aServer.AcquireExecutionMode[execORMWrite]=amLocked
Thanks. I'll definitely use that for some processes, but my problem is here that I need either:
1) Increment a value
2) Check if a record exists, if so, increment some fields, if not, create.
So I don't think a batch can work in this scenario?
I removed all but 1 usage of transactions so there can be no conflicting transactions and still I get the error after some random time.
I might be doing something else wrong...
e.g.:
(*Update sites traffic*)
while not Server.DB.TransactionBegin(TSQLWISPSite) do Sleep(50);
Site:=TSQLWispSite.Create;
for ID in SitesTouched.Keys do
begin
I64:=SitesTouched[ID];
Server.DB.Retrieve(ID,Site);
Site.CapUsed:=Site.CapUsed+I64;
Server.DB.Update(Site,'CapUsed');
end;
Site.Free;
Server.DB.Commit;
(*Now flush results to DB*)
while not Server.DB.TransactionBegin(TSQLWISPTraffic_IP) do Sleep(50);
SQL_IP :=TSQLWISPTraffic_IP.Create;
for TrafficIP in ListIP do
begin
SQL_IP.FillPrepare(Server.DB,'Year = ? and Month = ? and Day = ? and Hour = ? and IP = ?',[Y,M,D,H,TrafficIP.IP]);
if SQL_IP.FillOne
then begin
SQL_IP.UL:=SQL_IP.UL+TrafficIP.UL;
SQL_IP.DL:=SQL_IP.DL+TrafficIP.DL;
Server.DB.Update(SQL_IP);
end
else begin
SQL_IP.ClearProperties;
SQL_IP.Year :=Y;
SQL_IP.Month :=M;
SQL_IP.Day :=D;
SQL_IP.Hour :=H;
SQL_IP.CalcDate:=RecordDate;
SQL_IP.IP :=TrafficIP.IP;
SQL_IP.UL :=TrafficIP.UL;
SQL_IP.DL :=TrafficIP.DL;
Server.DB.Add(SQL_IP,True);
end;
end;
Server.DB.Commit;
SQL_IP.Free;
I use it without transaction and performance is still very good with no problems:) but I'll have to see when app goes live and real data flows.
AntonE
I have an app that run 2 threads in background that record and edit data.
They run relatively tight loops (<50ms each time) and I have at beginning and end of each:
while not Server.DB.TransactionBegin(TSQLWISPSite) do
Sleep(50);
...
Server.DB.Commit;
With even no client connected, from time to time, I get "SQLITE ERROR (1) -cannot rollback transaction - no transaction is active."
When it get that error, I never get a 'True' result from TransactionBegin and the server loops indefinately until I restart it.
Am I handling transactions wrong? Where are no errors between TransactionBegin and Commit.
In line 25117 in mORMot.pas , fTransactionActiveSession stays =1, so after initial error, TransactionBegin just always returns False without error.
Regards
PS: Am I right in assuming that SessionID is not relevant as only one transaction can be active at a time in anycase? Or should each call have a unique SessionID?
I'm afraid I'm still a bit in the dark with mORMot's Rtti. Using TSQLRecord is easy with PPropInfo, etc.
But how do I enumerate a record type similar to this with mORMot's built-in functions ?:
function EnumRecordType(ATypeInfo:PTypeInfo):RawUTF8;
var Ctx : TRttiContext;
Rec : TRttiRecordType;
Fld : TRttiField;
begin
Ctx:=TRttiContext.Create;
Rec:=TRttiRecordType(Ctx.GetType(ATypeInfo));
for Fld in Rec.GetFields do
Result:=Result+Fld.Name+' : '+Fld.FieldType.Handle^.Name+#13#10;
Ctx.Free;
end;
Trying to use code below but do not know how to get record field names:
function EnumRecordType(ATypeInfo:PTypeInfo):RawUTF8;
var RecType : PRecordType;
RecField : PRecordField;
begin;
RecType:=ATypeInfo^.RecordType;
for I:=0 to Pred(RecType.Count) do
begin
RecField:=@RecType.Fields[i];
(*How to get field name, not just type name*)
Result:=Result+RecField^.TypeInfo^.{Field?}Name+' : '+RecField^.TypeInfo^.Name;
case RecField^.TypeInfo^.Kind of
tkDynArray : begin
end;
end;
end;
TSQLPropInfoRecordRTTI seems to have the fields/methods I need but it is only used as an object's published properties? Or how can I instantiate it for any record type?
Thanks
AntonE
Hey Arnaud, thanks for doing this to my little unit.
I'm redoing it in a more mORMot way using TSQLPropInfoRtti family.
How do I get a DynArray element type from TSQLPropInfoRTTIDynArray ?
Once I have an instance, I can get it from TDynArray.ElemType but I need to construct from TSQLRecordClass.
...or must I still use Delphi Rtti to get that?
Also, if a DynArray have Record as element type, how do I get TSQLPropInfoRecordRTTI for that type? I can only see how to construct TSQLPropInfoRecordRTTI as a new member, don't know how to reference it for arbitrary type.
Thanks
Regards
I uploaded an updated version to that same link above.
Fixed a few bugs a.o. adding/deleting items in a DynArray.
However, I have a problem with memory leaks using DynArrays, but only when resizing them. It seems when a copy is made, I'm not deferencing the old copy properly.
If I run FastMM4 in FullDebugMode I even get error that memory that is freed are referenced and the app crash. Without FullDebugMode I get AnsiString memory leaks (when resizing arrays) but the app works fine. (Win32). In ORMCDS.pas lines 537 onwards is where the trouble lies I think.
I've tried various methods, using mORMot TDynArray and Rtti but it seems it's in the usage of TValue that old reference to array does not get cleaned up, so not production ready yet.
Regards
Awesome, working like a charm.
Thank you so much.
Sorry to revive an old thread, but trying with a TRawUTF8DynArrayDynArray doesn't work?
var Arr:TRawUTF8DynArrayDynArray;
begin
Arr:=DebtorsList_; (*Can see Arr have correct data in debugger*)
Result:=DynArraySaveJSON(Arr,TypeInfo(TRawUTF8DynArrayDynArray)); (*Output is just gibberish*)
end;
Am I doing it wrong?
Thanks
AntonE
Ok, I've improved quite a bit since that version, will upload soon.
I'll check out TSynVirtualDataset.
AntonE
Thanks for that, I updated the files.
I wrote a unit to convert TSQLRecord and it's sub-arrays/records to Nested TClientdatasets.
Some key features:
Create TClientdataset hierarchy dynamically based on data.
Also work with static TClientDatasets+Static fields.
Handle sub-TSQLRecord lists. (See sample)
Convert Set of ENUM to/from multiple Boolean fields for grid checkboxes
Most importantly: Apply delta-changes back to mORMot. i.e. only changed fields.
(With RTTI adjustments), should work on any platform that support TClientdataset, e.g. Intraweb
It is very first version so not tested on insert/delete yet nor many types of data, guaranteed to be buggy & lacking at this stage, but working.
ftp://ftp.true.co.za/ORMCDS.7z (Working in Delphi XE4 with JEDI installed for TJvDBUltimGrid, change to TDBGrid if needed.)
In demo:
Choose Static or Dynamic demo, select 'Load' to load data, edit any field or nested data, click 'Apply'.
Best is to see code to get more info.
My first try with RTTI and new at mORMot so I'm sure it could have been done more elegantly with mORMot's RTTI built-support.
Also not optimized for speed but should be pretty fast.
Regards
Antone
Hello,
trying to add mORMot to an IntraWeb XIV app in Delphi XE4.
Just create simple stand-alone IW-app, add:
type
TIWServerController = class(TIWServerControllerBase)
procedure IWServerControllerBaseNewSession(ASession: TIWApplication);
procedure IWServerControllerBaseConfig(Sender: TObject);
procedure IWServerControllerBaseDestroy(Sender: TObject);
private
public
Model : TSQLModel;
SQLDB : TSQLRest;
end;
procedure TIWServerController.IWServerControllerBaseConfig(Sender: TObject);
begin
Model := TSQLModel.Create([TSQLWISPRouter,TSQLWISPClient,TSQLWISPSite,TSQLWISPEMailAcc,TSQLWISPVoIPAcc,TSQLIPTraffic]);
SQLDB := TSQLRestServerDB.Create(Model,ChangeFileExt(paramstr(0),'.db3'));
TSQLRestServerDB(SQLDB).CreateMissingTables(0);
end;
procedure TIWServerController.IWServerControllerBaseDestroy(Sender: TObject);
begin
SQLDB.Free;
Model.Free;
end;
Just run server and try close, it gives AV error in SynCommons line 43472 in TSynLogFamily.Create(aSynLog: TSynLogClass);
If I just create/close DB it's fine, but once IW app runs, then I can't close without AV.
Best regards
AntonE
Wow I go away for a month and come back to see this... :-)
Great work, showcasing the expandable capabilities of mORMot.
I previous UI generation discussions the focus is always on displaying data, which in my opinion is the easy part.
What I'm interested in is interactive, i.e. INPUT/EDIT of data by users and especially handling concurrency, delta of changes, etc.
E.g. you might have a large TSQLRecord, say "TSQLClient" with many fields and sub-structures (using extensive sharding).
Simple Scenario:
UserA opens Client:ABC001 'view' and go on coffee break, displaying e.g. phone number as '123 456 7890'
UserB opens and edit Client:ABC001 and change phone number to '555 456 7890'
UserB comes back from break, edit 'email address' and save.
Now if I just post everything on UserA 'edit' screen, I will overwrite the phone number change made by UserB.
So in short:
1) how to handle updates and edits from views?
2) how to prevent unexpected data-loss due to concurrent user actions?
Really looking forward to see where this is going.
Best regards
AntonE
LiveBindings is not very cool IMHO.
Which is why I started this, not perfect either: (Which has moved along a bit since)
http://synopse.info/forum/viewtopic.php … 635#p11635
But have a look at samples here:
http://synopse.info/forum/viewtopic.php?id=1144
To get LiveBinding sample
I had a bit of a rethink. NOT a high-level MVC anymore (or yet), just a code-generation tool, but still very handy! Download & run sample to see possibilities.
A new version available.
ftp://ftp.true.co.za/TrueORM_1.2.zip
(binary included, so just download & run)
ReadMe:
TrueORM 1.2
Tool to help generate classes, units, forms, etc. from basic class/property info.
Quick start from sample:
1)Run TrueORM2
2)Click 'Load project'
3)Go to Project/Tasks tab. (Note Destination folder "FirstApp") Click 'Run tasks'.
4)In Delphi, open SampleApp.dpr from "FirstApp" sub-folder, compile/run! (Need JVCL)
Quick start from new project:
1)Give project name
2)Add classes, in each class, add some fields+controls
3)Add tasks to link project/classes to templates.
4)Save
5)Run tasks
(or just add new class to current sample project, link your new class to the task "Data&Lists", run tasks and see what happens )
TrueORM just wrote a whole app from basic info and mustache templates.
Detailed description:
Templates:
A template is a collection of related files. E.g. a form that has .pas & .dfm files but you can add many loosely related files(see sample 'Class list & Edit forms' that include mORMot Data unit, etc.)
Templates are separate from the project, so can be re-used in multiple projects.
Templates can be external files or embedded in the DB. Filenames are also mustache templates.
Apart from normal context variables in mustache, use translate to pass some custom variables, like {{"Top}} {{"TopAdd}} {{"TabOrder}} that is used in Delphi code to increment UI controls for DFM files.
Classes:
Classes contain a collection of Fields that is the input required to generate code.
Tasks:
Tasks is bringing the Classes and templates together and is where the actual code is generated.
If any classes are marked as 'Involved' in a task, then those classes will each be sent to the specified template, in turn, with just the Class(+fields) as mustache-context, e.g. for class editing-forms)
If a task does not 'involve' any classes, then the entire project is sent as mustache context to the template, e.g. for main-forms or app-wide templates like .dpr files.
Notes:
There are still few problems/bugs and I haven't tested all DelphiTypes in combination with all control types yet.
Since the app is generated from templates, you can create any type of app (client/server/firemonkey/etc.) or form, the ones included are just simple examples to illustrate possibilities.
The app is still far from perfect, but can be used to generate basic skeleton forms/units that can be copy/pasted/edited in your real app/project. (e.g. Memos, RadioGroups, etc. need to be manually resized in the generated form. packed record and Array/TObjectList support not working yet.)
Hope someone else can find it usefull.
Greetings
I was thinking to use Translate to generate custom values.
e.g. in template have : {{"MyVar}}
then
procedure MyTranslate(var English:String);
begin;
if SameText(English,'MyVar')
then begin;
English:=CalcIt;
end;
end;
?
Then it can even insert more mustache code and run a second parse/render cycle to render that.
Regards
I figured my problem out.
Subitem that have properties that need init code, like TObjectList only have e.g. TObject.Create called and it's not virtual.
If I descend from TSQLRecord instead of TObject (even not register it to Model), then I can make
Constructor Create;override;
and it therefore works perfectly multiple-levels deep.
Regards
Thanks tech,
yes I agree, a universal client as you call it can be very cool if done right, with mORMot's superior performance and flexibility, that might be a possibility.
However, I think this project is for now intended to be a.o. a code-generator helper tool, to generate code/units for you to prevent repetitive coding and maybe help generate multi-platform UI code for you as well (think .DFM and .HTML)
Also to help you handle things like enum/set in radio/checkboxes, higher-types like email,phone numbers, instead of just 'RawUTF8', and generating the UI code that goes with it, etc. Stuff I do manually all the time that is mindless, repetitive and annoying.
Because it use templates, you can do with it what you want, I'll use it to generate Delphi code that I can then manually fine-tune.
Scripting for now will be more to help generate custom code that cannot be easily represented via a templates, if at all.
Eventually it might evolve into a universal client/server, we'll see.
Thanks for your input
Please see
http://synopse.info/forum/viewtopic.php … 635#p11635
Where this is evolving into.
Please see CSV2ORM : http://synopse.info/forum/viewtopic.php?id=1911
TrueORM
A high-level Delphi ORM/MVC(?) built on top of mORMot.
It is an idea spawn from CSV2ORM, to expand on that, not all as described below is working yet, but it's the idea that count.
"The idea" is an app that (will have) have 3 parts:
Projects (Model| TSQLproject that contains Classes/Records)
Templates (View| TSQLTemplate that contains Mustache templates)
Scripts (Controller?| TSQLScript that contain Pascal/Javascript code)
You can have multiple projects. Each projects can contain a list of T_Class
"T_Class" each contain a list of T_Variable descendants, just like normal OOP.
A)Projects (TSQLProject):
As a bare-bone start you create a new Project.
This project contain a list of T_Class that can be marked as either :
packed record
TObject
TSQLRecord
Classes (T_Class):
Classes contain a polymorphic ObjectList of T_Variables (properties/fields) descendants.
T_Variable (abstract)
T_SimpleType (abstract)
T_Integer
T_Float
T_Currency
T_Boolean
T_DateTime
T_String
T_Collection (abstract)
T_Array
T_ObjectList
T_Class
The idea is to use only basic types, e.g. T_Integer but within T_Integer there are options to select if it's Int32/Int64, signed/unsigned, ENum, Set, etc.
Also e.g. there is only 1 DateTime type, but within it you set to use Date only, Time only or DateTime, Duration, high-precision, etc.
T_Array, T_ObjectList, T_Class variables let you select another T_Class from your project to created nested Classes.
USAGE: (Not all working yet, concept
As in CSV2ORM, import a text (csv) file like
User.csv:
UserName,String,Edit,50
Password,String,Edit,50
This will add a T_Class 'User' to you project. Mark it as a 'packed record'.
Now import Connection.csv:
FromIP,String,Edit,50
Started,DateTime,DateEdit
This will add a T_Class 'Connection' to you project. Mark it as a 'TObject'.
Now import Device.csv:
Name,String,Edit,50
Number,Integer,Edit
Users,Array:User,ListView
Connections,ObjectList:Connection,ListView
This will add a T_Class 'Connection' to you project. Mark it as a 'TSQLRecord'.
You have setup the model with nested DynArray/ObjectList. Alternatively you can greate quick/easy in the UI.
Each T_Variable can have a Control_Type associated to it. This is for UI generation, e.g. TEdit, TRadioGroup, etc. (Like in CSV2ORM)
This is still an 'not-good idea' as a UI mapping external to the model is needed, so you can have e.g. multiple forms where 'Device.Name' get TEdit or TLabel on one form, TCombobox on another, etc.
B)TEMPLATING (TSQLTemplate):
Similar to CSV2ORM, but more powerfull, using Mustache templates that can be edited in a JvHLEditor with syntax highlighting for many languages.
Here you will be able to setup multi-file templates (like in CSV2ORM) to create e.g.:
mORMot Data units/classes
Delphi units/forms
SMS forms (?)
HTML pages
SQL-scripts
Future idea: form creation, i.o.w. WYSIWIG
C)SCRIPTING (TSQLScript):
Also with HLEditor, able to create Pascal scripts (JvInterpreter) or Javascript (SpiderMonkey).
This code might be run in JvInterpreter or SpiderMonkey, but is intended to be able to create code snips that might be used in Templating.
Summary:
The notion of data, templates and scripting evolved naturally into a kind of model/view/controller concept, and the project's idea is still in 'alpha' phase.
I would like any ideas/comments if anyone find it interesting and have some insights that might be helpfull or thought-provoking.
I can see it possible to create a bare-bone client app that get only TSQLProject, TSQLTemplate, TSQLScript from server and can create it's own UI, scripts, etc. from that.
I.o.w. a client (and/or server) Delphi-app that is just a shell that generate UI-elements and can update without closing as forms/scripts is not compiled but sent from server via TSQLProject, TSQLTemplate, TSQLScript classes.
Regards
AntonE
PS: I should have part A (data modelling) finished in next day or two.
Thank you very much, it's working one level deep.
Test project here:
ftp://ftp.true.co.za/TestORM.zip
If compiled as-is it works, compile with conditional directive TWOLEVELS to make another TObjectList within a TObject class and it fails to persist even first level.
JSONvalues looks right when updating but I'm a bit lost 'deeper inside' mORMot yet.
This can be very powerful.
I would really like to use TObjectList as it makes LiveBindings easier for nested (sharded?) objects.
I have my objects defined as this and register the ITem class:
uses mORMot,SynCommons,Contnrs;
type TItem = class(TObject)
private
fItemName: RawUTF8;
fItemCode: Integer;
published
property ItemName : RawUTF8 read fItemName write fItemName;
property ItemCode : Integer read fItemCode write fItemCode;
end;
type TSQLNested = class(TSQLRecord)
private
fName: RawUTF8;
fItems: TObjectList;
public
constructor Create;
protected
destructor Destroy;override;
published
property Name : RawUTF8 read fName write fName;
property Items : TObjectList read fItems write fItems;
end;
function CreateDataModel: TSQLModel;
implementation
function CreateDataModel: TSQLModel;
begin
result := TSQLModel.Create([TSQLNested]);
TJSONSerializer.RegisterClassForJSON(TItem);
end;
But I still get error:
Unhandled stfUnknown/tkClass type for property Items.
Seems unrelated to TItem and it does not like the TObjectList. Also tried with Generics.Collections version.
Hello.
Is this still the case? On page 112 of SAD 1.18 it says "The following published properties types are handled by the ORM..." and lists TObjectList.
Or am I misunderstanding it?
Hi Arnaud,
yes CDS is also painfully slow but I like the briefcase-model/changelog so I still use it for now.
ClientEngineU.pas is just a unit where I centralize client components, basically where TSQLRestClient descendant resides so users should just edit that code to point to their TSQLRestClient unit/component. I'll make next version better commented and generic. + included sample template should be changed to generate any different unit/form/file you want.
Can now also generate non-DB components so I should be able to write a similar Livebinding template if needed, instead of CDS/Datasource-based form.
I never really planned this project, so a complete re-think might be needed for it to be a serious/proper tool...
I never worked with Mustache and thought it is just for HTML/JS. I'll check it out some time and revamp this, thank you.
Best regards
AntonE
Updated version 1.1 can be found on:
ftp://ftp.true.co.za/CSV2ORM1_1.zip
I included the compiled binary so users can use/test if they don't have Jedi library to compile.
Now supports:
Multiple templates with multiple files for each template, e.g. a form generates .pas & .dfm file.
Template-code editing in app. (Need to install syntax highlighter )
Comes with sample templates & CSV, just run and test.
Can generate different code for fields/properties/controls that have size limits (Strings) by using [Size] & [!Size] tags in template.
Still scruffy but a lot more versatile.
If anyone want a specific feature or find bug, please let me know.
Regards
Basic explanation:
Create code like:
type TSQLMyObj = class(TSQLRecord)
private
[Fields]fMyName : MyType;[/Fields]
published
[Fields]property MyName : MyType read fMyName write fMyName;[/Fields]
end;
or
[Controls] object LabelMyName: TLabel
Left = 56
Top = MyTop
Width = 23
Height = 13
Alignment = taRightJustify
Caption = 'MyName'
end
object MyControlName: MyDBControl
Left = 84
Top = MyTop
Width = 121
Height = 21
DataField = 'MyName'
DataSource = DS
end[/Controls]
and it will expand to the properties/fields/controls you pass in CSV file.
It still needs a lot of changes but it's a good start and sufficient to create bulk of my repetitive code.
"MyObj" get replaced with your Object-name
"MyName" get replaced with property names, etc. See ReadMe file.
I'd be honored, sure you can include it. But I think let me make some changes first, because as-is it's kind of scruffy to do just what I wanted quick and dirty.
I'll post back here if there's a bit more versatile version.
Regards
CSV2ORM simple class/unit/form generator tool
I only saw migajek's project now, I made something similar, but with form/TClientDataset creation as well:
http://synopse.info/forum/viewtopic.php … 548#p11548
Edit: Included as third-party sample in https://github.com/synopse/mORMot/tree/ … nE/CSV2ORM
I just made a simple class/unit/form generator tool.
Simply you create a CSV (E.g. MyObj.csv) file like so:
Code,RawUTF8,Edit,30
Desc,RawUTF8,Edit,512
ItemType,RawUTF8,ComboBox,30
Cost,Currency,Edit
LastCostD,TDateTime,DateEdit
VatCat,Integer,RadioGroup
Active,Boolean,CheckBox
Format:<Property/Field Name>,<Type>,<Control>[,RawUTF8 size for CDS]
And it will create units:
DataMyObj.pas (TSQLMyObj class)
MyObjFormU.pas (TMyObjForm class)
MyObjFormU.dfm
It will also make a TClientDataset on the form and add necessary fields to it, then create TDBxxxx versions of Controls specified (Edit=TDBEdit, etc) + code to get/set it from/to the TSQLMyObj class.
It is very immature and cannot handle Arrays, subobjects, etc. but it will at least create the bulk of your code and form.
Not intended to be full generator, just to create a skeleton of code/controls that you can copy to your project and edit normally from there on. Use RawUTF8 for unsupported types and just edit your code afterwards.
I'm not going to spend lots of more time on it soon, but I can see that in future:
More options like combobox/RadioButton listitems, MaskEdit, break a SET up into multiple CheckBoxes, and so on.
I might make the code generated 'template-able' so you can have different variations of forms that can be created (TObjectList-based instead of TClientdataset, HTML/JS/etc).
change name to CSV2ORM.
Can be found here:
ftp://ftp.true.co.za/TrueORMconv.zip
I really like the idea of the Dynamic arrays and packed records and it 'hashed' features, multi-sort etc. Very very nice thinking!
I'm trying to use DynArrs in my ORM classes, but maybe I'm looking at it wrong:
type TContactAttachType = set of (ifFile,itPhoto,itLogo);
type TContactAttachment = packed record
fAttachType : TContactAttachType;
fAttachID : Integer;
property AttachType : TContactAttachType read fAttachType write fAttachType;
property AttachID : Integer read fAttachID write fAttachID;
end;
TContactAttachArr=Array of TContactAttachment;
type TSQLContact = class(TSQLRecord)
private
fName : RawUTF8;
fAttach : TContactAttachArr;
published
property Name : RawUTF8 read fName write fName;
property Attachments : TContactAttachArr read fAttach write fAttach;
end;
Very cool as I can write simple code to fill e.g. custom Dataset for UI:
for ATT in Contact.Attachments do
begin
CP.Insert;
CPName .AsString :=ATT.Number;
CPFile .AsBoolean:=ptFile in ATT.PhoneType;
CPPhoto .AsBoolean:=ptPhoto in ATT.PhoneType;
CPLogo .AsBoolean:=ptLogo in ATT.PhoneType;
CP.Post;
end;
But to write/change data via the property I'm having a problem:
DA.Init(TypeInfo(TContactAttachment),Item.Attachments,@DAC);
DA.Capacity:=CP.RecordCount;
SetLength(Item.Attachments,CP.RecordCount);
Both cannot compile as I need to access the private variable of TSQLContact.fAttach?
How can I overcome this without polluting the TSQLContact with UI related code?
Or am I looking at it wrong?
Thanks
Regards
AntonE
Thank you for your kind words.
The manual as under the 'downloads' section is still 1.17
http://synopse.info/files/synproject/sampledoc.zip
But I see it further down
aha! Hybrid 1.17 & 1.18 code
Thanks.
Now I see Ctxt.Parameters as:
SearchStr=abc&Fields=ID....
But:
UrlDecodeValue(Ctxt.Parameters,'SearchStr=',zStr);
Does not fill in zStr.
Edit:
After looking at the code, I see I had to pass the parameter as UPPERCASE. Strange, no?
UrlDecodeValue(Ctxt.Parameters,'SEARCHSTR=',zStr);
Hello, trying to implement first method service:
Server side
type TSQLRestServerDBTW = class(TSQLRestServerDB)
published
procedure ContactSearch(var Ctxt: TSQLRestServerURIContext);
end;
procedure TSQLRestServerDBTW.ContactSearch(var Ctxt: TSQLRestServerURIContext);
var SQL : RawUTF8;
zStr,
zFld : RawUTF8;
begin
zFld:='';
zStr:='';
if UrlDecodeNeedParameters(Ctxt.Parameters,'SearchStr,Fields')
then begin
while Ctxt.Parameters<>nil do
begin
UrlDecodeValue(Ctxt.Parameters,'SearchStr=',zStr);
UrlDecodeValue(Ctxt.Parameters,'Fields=',zFld,@Ctxt.Parameters);
end;
SQL:='select '+zFld+' from Contact WHERE Name='''+zStr+''' ORDER BY Name;';
Ctxt.Results([ServerForm.DB.DB.ExecuteJSON(SQL)]);
end
else Ctxt.Error('Missing Parameter');
end;
Client side
type TSQLHttpClientTW = class(TSQLHttpClient)
published
function ContactSearch(ASearchStr:String;AFields:RawUTF8):RawUTF8;
end;
function TSQLHttpClientTW.ContactSearch(ASearchStr, AFields: RawUTF8): RawUTF8;
var T : TSQLTableJSON;
begin
Result:=self.CallBackGetResult('ContactSearch',['SearchStr',ASearchStr,'Fields',AFields],TSQLContact);
end;
But when I run code from client, I get to server method, but on first line breakpoint, I see no Ctxt.fInput (most Inaccesible value) and Parameters='' so UrlDecodeNeedParameters gives AV.
The samples in manual is little different for 1.17. so am I even using the correct classes (TSQLRestServerDB & TSQLHttpClient) and the rest is also a bit cloudy still. :)
Regards