Recent

Author Topic: MVP made easier.  (Read 11899 times)

cdbc

  • Hero Member
  • *****
  • Posts: 2128
    • http://www.cdbc.dk
Re: MVP made easier.
« Reply #60 on: April 23, 2025, 06:19:55 pm »
Hi Hans
Yup, I kind of expected that  ;D
The thing is, you can't just copy the main "food-chain" and expect it to work side-by-side... There are way too much mechanics working together, that's why I mentioned the 'making the new presenter and model children of Main', because the 'transaction-manager' is /shared/, which means the new presenter /borrows/ a reference to it...
Main is main - The Backbone of your app, everything gets controlled by main.
Ex: You'll let PresenterMain create a new 'PresenterConfigure', which in turn will create 'ModelConfigure'. PresenterMain is also responsible for freeing the child-branch.
Anyway, I'll have a 'LookSee' at your project  ...stay tuned  :D
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 3.6 up until Jan 2024 from then on it's both above &: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 4.99

cdbc

  • Hero Member
  • *****
  • Posts: 2128
    • http://www.cdbc.dk
Re: MVP made easier.
« Reply #61 on: April 23, 2025, 06:38:49 pm »
Hi again
First the AV - easy peasy:
Code: Pascal  [Select][+][-]
  1. unit model.configure.base;
  2.  
  3. procedure UpdateTransactionManager(aTraxMap: ITraxClassList);
  4. begin
  5.   TCL.AssignTo(aTraxMap); { everything and the kitchen-sink }  <-- access violation when view is openend for the second time.
  6. /// just comment the following line ///
  7. /// HERE ///  TCL:= nil; { release this reference, it's not needed after the manager is up }
  8. end;
The factory shouldn't care, 'cause it's already checking for /doublets/...
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 3.6 up until Jan 2024 from then on it's both above &: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 4.99

Hansvb

  • Hero Member
  • *****
  • Posts: 801
Re: MVP made easier.
« Reply #62 on: April 23, 2025, 06:52:43 pm »
You're fast. I was no further than thinking that it might be wise to build everything up step by step for the second view. Now I have actually copied the existing files, renamed them and changed all the interface nd functionnames. Removing the transaction examples already caused problems and I left it.

cdbc

  • Hero Member
  • *****
  • Posts: 2128
    • http://www.cdbc.dk
Re: MVP made easier.
« Reply #63 on: April 23, 2025, 08:23:55 pm »
Hi
Have a look at this 'model.intf':
Code: Pascal  [Select][+][-]
  1. unit model.intf;
  2. {$mode ObjFPC}{$H+}
  3. {-$define dbg}
  4. interface
  5. uses classes, sysutils, istrlist, model.base, obs_prosu;
  6. type
  7. {$interfaces corba}
  8.   IPresenterMain = interface; // forward
  9.   IPresenterConfigure = interface; // forward
  10.  
  11.   { aliases for provider & subscriber }
  12.   IobsProvider = obs_prosu.IobsProvider;
  13.   IobsSubscriber = obs_prosu.IobsSubscriber;
  14.  
  15.   { alias for transaction }
  16.   ITransaction = model.base.ITransaction;
  17.   { the actual model/datastore api, the "King" }
  18.   IModelMain = interface(ICorba)['{0349B40E-FBB4-4C6E-9CB0-A63690CF0898}']
  19.     function GetStaticTexts(const aSection: string; out aTarget: integer): IStringList;
  20.     procedure RefreshSections;
  21.     procedure ReloadTextCache;
  22.     function StringConcat(firstString, secondString : String) : String;
  23.   end; { IModel }
  24.  
  25.   { the child model/datastore api, the "BigBrother" }
  26.   IModelConfigure = interface(ICorba)['{F5E9E12D-F9B5-412F-9708-C751F50A14D2}']
  27.     function GetStaticTexts(const aSection: string; out aTarget: integer): IStringList;
  28.     procedure RefreshSections;
  29.     procedure ReloadTextCache;
  30.     function StringConcat(firstString, secondString : String) : String;
  31.   end; { IModelConfigure }
  32.  
  33.   { ITransactionManager reacts to user actions }
  34.   ITransactionManager = interface(ICorba)['{C75AE70E-43DA-4A76-A52D-061AFC6562D0}']
  35.     function get_ActiveTrx: ITransaction;
  36.     function get_ModelConfig: IModelConfigure;
  37.     function get_ModelMain: IModelMain;    
  38.     function get_OwnerConfig: IPresenterConfigure;
  39.     function get_OwnerMain: IPresenterMain;
  40.     procedure set_ModelConfig(aValue: IModelConfigure);
  41.     procedure set_OwnerConfig(aValue: IPresenterConfigure);
  42.     procedure CommitTransaction;
  43.     function InTransaction: boolean; { below is register for runtime jic, NOT startup!!! }
  44.     procedure RegisterTransactionClass(aModReason: ptrint; aTransactionClass: TTransactionClass);
  45.     procedure RollbackTransaction;
  46.     function StartTransaction(aModReason: ptrint): TTransaction;
  47.     property ActiveTrx: ITransaction read get_ActiveTrx;
  48.     property ModelConfig: IModelConfigure read get_ModelConfig write set_ModelConfig;
  49.     property ModelMain: IModelMain read get_ModelMain;
  50.     property OwnerConfig: IPresenterConfigure read get_OwnerConfig write set_OwnerConfig;
  51.     property OwnerMain: IPresenterMain read get_OwnerMain;      
  52.   end; { ITransactionManager }
  53.   { specialized transaction, sports an 'execute' method, i.e.: it knows how to commit itself ;-) }
  54.   ITrxExec = interface(ITransaction)['{2E618F58-DE15-4A79-ADEF-C4E0A7CBECA4}']
  55.     function Execute(aMgr: ITransactionManager): boolean;
  56.   end; { ITrxExec }
  57.   { sibling trx, but without returning a result }
  58.   ITrxExecNoRes = interface(ITransaction)['{5E4DCDBA-8CB0-45E4-8DC3-6CD859DB4F1B}']
  59.     procedure Execute(aMgr: ITransactionManager);
  60.   end; { ITrxExecNoRes }  
  61.   { the "Queen" }
  62.   IPresenterMain = interface(ICorba)['{736B14F8-8BB7-4F5D-ADAA-B90A1735765C}']
  63.     function get_Model: IModelMain;
  64.     function get_Provider: IobsProvider;
  65.     function get_TrxMan: ITransactionManager;
  66.     procedure set_Provider(aValue: IobsProvider);
  67.     procedure GetStaticTexts(const aSection: string);
  68.     procedure RefreshTextCache(const aSection,aLangStr: string);
  69.     property Model: IModelMain read get_Model; // TODO: write set_Model;
  70.     property Provider: IobsProvider read get_Provider write set_Provider;
  71.     property TrxMan: ITransactionManager read get_TrxMan;
  72.   end; { IPresenterMain }
  73.  
  74.   { the "BigSister" }
  75.   IPresenterConfigure = interface(ICorba)['{C948C2C9-2590-4030-B8B3-54B44AEB06F9}']
  76.     function get_Model: IModelConfigure;
  77.     function get_Provider: IobsProvider;
  78.     function get_TrxMan: ITransactionManager;
  79.     procedure set_Provider(aValue: IobsProvider);
  80.     procedure GetStaticTexts(const aSection: string);
  81.     procedure RefreshTextCache(const aSection,aLangStr: string);
  82.     property Model: IModelConfigure read get_Model; // TODO: write set_Model;
  83.     property Provider: IobsProvider read get_Provider write set_Provider;
  84.     property TrxMan: ITransactionManager read get_TrxMan;
  85.   end; { IPresenterPresenterMain }
  86.  
  87.   { this is the 'contract' the view has to fulfill, for us to work with it }
  88.   IViewMain = interface(ICorba)['{5AC3C283-C7EB-437D-8A72-3A0BD7A47B1C}']
  89.     function get_Observer: IobsSubscriber;
  90.     function get_Presenter: IPresenterMain;
  91.     procedure set_Observer(aValue: IobsSubscriber);
  92.     procedure set_Presenter(aValue: IPresenterMain);
  93.     procedure HandleObsNotify(aReason: ptrint; aNotifyObj: TObject; aData: pointer);
  94.     { mainly here for when implementing console-views for the same back-end / engine }
  95.     procedure Show; // TForm implements it too
  96.  
  97.     property Observer: IobsSubscriber read get_Observer write set_Observer;
  98.     property Presenter: IPresenterMain read get_Presenter write set_Presenter;
  99.   end; { IViewMain }
  100.  
  101.   { this is the 'contract' the view has to fulfill, for us to work with it }
  102.   IViewConfigure = interface(ICorba)['{07D9D3BE-8426-4E7A-BC7F-5AC0BBD70803}']
  103.     function get_Observer: IobsSubscriber;
  104.     function get_Presenter: IPresenterConfigure;
  105.     procedure set_Observer(aValue: IobsSubscriber);
  106.     procedure set_Presenter(aValue: IPresenterConfigure);
  107.     procedure HandleObsNotify(aReason: ptrint; aNotifyObj: TObject; aData: pointer);
  108.     { mainly here for when implementing console-views for the same back-end / engine }
  109.     procedure Show; // TForm implements it too
  110.  
  111.     property Observer: IobsSubscriber read get_Observer write set_Observer;
  112.     property Presenter: IPresenterConfigure read get_Presenter write set_Presenter;
  113.   end; { IViewConfigure }
  114.  
  115. {$interfaces com}
  116.  
  117. { registers sections for use in 'GetStaticTexts()', put it in 'initialization'
  118.   at the bottom of the unit, for the objects/units you'll be registering }
  119. procedure RegisterMainSection(const aSect: string);              
  120. procedure RegisterConfigureSection(const aSect: string);
  121. { gets called from model.main.constructor to update the 'Sects' array }
  122. procedure UpdateMainSections(var anArray: TStringArray); // <- - needs 'sysutils' added in top-uses
  123. procedure UpdateConfigureSections(var anArray: TStringArray);
  124.  
  125. implementation
  126. uses contnrs;
  127.  
  128. var { storage for our registrations till the models come online }
  129.   QSections,ConfSections: TQueue;
  130.  
  131. procedure RegisterMainSection(const aSect: string);
  132. begin
  133.   if aSect = '' then exit; { no empty allocations }
  134.   QSections.Push(strnew(pchar('['+aSect+']'))); { enqueue the [section] for later update }
  135. end;
  136.  
  137. procedure RegisterConfigureSection(const aSect: string);
  138. begin
  139.   if aSect = '' then exit; { no empty allocations }
  140.   ConfSections.Push(strnew(pchar('['+aSect+']'))); { enqueue the [section] for later update }
  141. end;
  142.  
  143. procedure UpdateMainSections(var anArray: TStringArray);
  144. var lai: integer; lp: pchar = nil;
  145. begin
  146.   if anArray = nil then exit; { we want at least '[view.main]' @ idx 0 present before we start }
  147.   lai:= length(anArray); // start index for additions ~ 1
  148.   SetLength(anArray,lai+QSections.Count); { make room for all the registered sections at once }
  149.   while QSections.AtLeast(1) do begin { now as long as there are items in the queue }
  150.     lp:= QSections.Pop;                  { dequeue/pop the sections from queue }
  151.     anArray[lai]:= string(lp);                  { and add to array as a string }
  152.     inc(lai);                       { step our (l)ocal(a)rray(i)ndex 1 forward }
  153.     StrDispose(lp);      { now release the memory the string was being held in }
  154.   end;
  155. end;
  156.  
  157. procedure UpdateConfigureSections(var anArray: TStringArray);
  158. var lai: integer; lp: pchar = nil;
  159. begin
  160.   if anArray = nil then exit; { we want at least '[view.configure]' @ idx 0 present before we start }
  161.   lai:= length(anArray); // start index for additions ~ 1
  162.   SetLength(anArray,lai+ConfSections.Count); { make room for all the registered sections at once }
  163.   while ConfSections.AtLeast(1) do begin { now as long as there are items in the queue }
  164.     lp:= ConfSections.Pop;                  { dequeue/pop the sections from queue }
  165.     anArray[lai]:= string(lp);                  { and add to array as a string }
  166.     inc(lai);                       { step our (l)ocal(a)rray(i)ndex 1 forward }
  167.     StrDispose(lp);      { now release the memory the string was being held in }
  168.   end;
  169. end;
  170.  
  171. procedure FinalizeQueues;
  172. begin
  173.   while QSections.AtLeast(1) do StrDispose(QSections.Pop); { clear the main queue, jic }
  174.   QSections.Free; { free the main queue }
  175.   while ConfSections.AtLeast(1) do StrDispose(ConfSections.Pop);
  176.   ConfSections.Free; { free the config queue }
  177. end;
  178.  
  179. initialization
  180.   QSections:= TQueue.Create;
  181.   ConfSections:= TQueue.Create;
  182.  
  183. finalization
  184.   FinalizeQueues;
  185.  
  186. end.
This and its sibling 'model.decl' are crucial to the control of the entire app, they have to be the only ones - NO copies!
Remember, the tool produces a "Bare Minimum" that compiles... The rest, you have to code your way out of  %)
I can show you the way  ;) But I'd rather not program the lot for you  ;D
Will be back as I come along...
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 3.6 up until Jan 2024 from then on it's both above &: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 4.99

Hansvb

  • Hero Member
  • *****
  • Posts: 801
Re: MVP made easier.
« Reply #64 on: April 23, 2025, 09:45:27 pm »
Thanks, I'll look into it. I still have a few days off. :)

cdbc

  • Hero Member
  • *****
  • Posts: 2128
    • http://www.cdbc.dk
Re: MVP made easier.
« Reply #65 on: April 23, 2025, 11:51:34 pm »
Pheeewww....  :P
Got it working \o/
Hang on and I'll get an upload-zip sorted... =^

...And I'm done, see attachment  8-)

Regards Benny
« Last Edit: April 24, 2025, 12:12:20 am by cdbc »
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 3.6 up until Jan 2024 from then on it's both above &: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 4.99

Hansvb

  • Hero Member
  • *****
  • Posts: 801
Re: MVP made easier.
« Reply #66 on: April 24, 2025, 09:17:56 am »
Hi,

I thought you left the work to me and now I'm getting a project after all. Is it the intention that I create model.configure.base based on model.base or did it accidentally not come in the zip?

cdbc

  • Hero Member
  • *****
  • Posts: 2128
    • http://www.cdbc.dk
Re: MVP made easier.
« Reply #67 on: April 24, 2025, 09:46:39 am »
Hi Hans
Nope, -- There can be only one -- 'model.base' is the "Base", for all the framework! Let me see if I can visualize it a bit:
Code: Text  [Select][+][-]
  1. level 0 =  obs_prosu  -> common.consts -> model.base <- istrlist
  2. level 1 =  model.intf---^-----v-----^------^------v----^
  3. level 2 =  model.decl <- presenter.trax <-- presenter.configure.trax
  4. level 3 =  model.main, model.configure, presenter.main, presenter.configure
  5. level 4 =  view.main, view.configure
  6. level 5 = You :)
As I noted in the code, if you need an unit for types, then create a 'common.types' and put it in /common/ -- like 'common.consts'
There are no files missing, the /cutaways/ were 'doublets' and we certainly cannot have that -- screws it all up --
So, forget you had x.intf, x.decl, x.base; It can never work like that, you're essentially creating 2 apps within 1 and that's not what you're after!
Discipline is the name of the game...
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 3.6 up until Jan 2024 from then on it's both above &: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 4.99

Hansvb

  • Hero Member
  • *****
  • Posts: 801
Re: MVP made easier.
« Reply #68 on: April 24, 2025, 10:41:19 am »
I have your project running. I had to remove some old units names from the lpr file.
The real conclusion for myself is. I have to go back to the drawing board and get to understand this completely. I used it too much as a trick instead of skill  :-[.

cdbc

  • Hero Member
  • *****
  • Posts: 2128
    • http://www.cdbc.dk
Re: MVP made easier.
« Reply #69 on: April 24, 2025, 11:18:12 am »
Hi Hans
Well, I was trying to give you a good foundation to build further on.
From there, every app / project has to evolve according to the programmers vision and the task at hand... The trick is __not__ to over-complicate things as you proceed along...
With transactions, the 1model<->1presenter<--<>more views, scales quite easily & good  --  before you have to branch out... But hey, now you've seen one way to do that too...  8)
Practice on some specific tasks you need to solve, gradually increasing the complexity... It's easy to setup a new 'practice-project' and just as easy to delete the whole project, if it turns out to be shijt...  %)
Regards Benny

eta: Btw, if need be, the older zips are still available on gitlab...
« Last Edit: April 24, 2025, 11:21:05 am by cdbc »
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 3.6 up until Jan 2024 from then on it's both above &: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 4.99

Hansvb

  • Hero Member
  • *****
  • Posts: 801
Re: MVP made easier.
« Reply #70 on: April 24, 2025, 12:06:47 pm »
hi Benny,

Most of the older zips a still have. (That is reference work for me).

I will (am again) practicing and in doing so I run into the following.  The create function of the model.main (and model.configure) is not completly right. If the mvptext file is not there, it goes wrong. This will never be reached:
Code: Pascal  [Select][+][-]
  1.  if fTextCache.Count = 0 then { user will see a view filled with 'dummy' ;) } fPresenter.Provider.NotifySubscribers(prStatus,nil,Str2Pch('(!) ERROR: Could NOT retrieve static texts!')
fTextCache is then nil. If you intercept the nil in the create, you will get into trouble elsewhere. I have to look further, it's time to eat now.

cdbc

  • Hero Member
  • *****
  • Posts: 2128
    • http://www.cdbc.dk
Re: MVP made easier.
« Reply #71 on: April 24, 2025, 12:28:51 pm »
Hi
Hmmm... These work fine for me: "model.main"
Code: Pascal  [Select][+][-]
  1. constructor TModelMain.Create(aPresenter: IPresenterMain; const aRoot: shortstring);
  2. begin
  3.   inherited Create;
  4.   fPresenter:= aPresenter;
  5.   fRoot:= aRoot;                                        
  6.   UpdateMainSections(Sects); { <- fetch our >>HERE<< sections, v- i18n }
  7.   fTextCache:= CreStrListFromFile(format(mvpTexts,[fRoot,Lang])); ///<- i18n
  8.   { we need the model, this is a minor flaw if it fails, because then the }
  9.   if fTextCache.Count = 0 then { user will see a view filled with 'dummy' ;) }
  10.     fPresenter.Provider.NotifySubscribers(prStatus,nil,Str2Pch('(!) ERROR: Could NOT retrieve static texts!'));
  11. end; /// the above text can't be translated in the i18n'ed mvptexts, the count is 0! ///
"model.configure":
Code: Pascal  [Select][+][-]
  1. constructor TModelConfigure.Create(aPresenter: IPresenterConfigure; const aRoot: shortstring);
  2. begin
  3.   inherited Create;
  4.   fPresenter:= aPresenter;
  5.   fRoot:= aRoot;                                        
  6.   UpdateConfigureSections(Sects); { <- fetch our >>HERE<< sections, v- i18n }
  7.   fTextCache:= CreStrListFromFile(format(confTexts,[fRoot,Lang])); ///<- i18n
  8.   { we need the model, this is a minor flaw if it fails, because then the }
  9.   if fTextCache.Count = 0 then { user will see a view filled with 'dummy' ;) }
  10.     fPresenter.Provider.NotifySubscribers(prStatus,nil,Str2Pch('(!) ERROR: Could NOT retrieve static texts!'));
  11. end; /// the above text can't be translated in the i18n'ed mvptexts, the count is 0! ///
They're loading 2 different files, namely: "mvptexts.en"
Code: INI  [Select][+][-]
  1. [view.main]
  2. btnClose=&Close Application
  3. frmMain=Leeg Two Views Project
  4. Button1=Open second view
  5. Button2=Close;
  6. Button3=OFF LIMITS Test
  7.  
  8. [TModelMain]
  9. errNoStaticTxt=(!) ERROR: Could NOT retrieve static texts!
  10.  
  11. [TPresenterMain]
  12. msgUpnRun=(i) MVP is up and running, Hello :o)
and "configtexts.en"
Code: INI  [Select][+][-]
  1. [view.configure]
  2. Button1=Simple Concat Test
  3. frmConfigure=This is view number 2 of HvB's project
  4.  
  5. [TModelConfigure]
  6. errNoStaticTxt=(!) ERROR: Could NOT retrieve static texts!
  7.  
  8. [TPresenterConfigure]
  9. msgUpnRun=(i) Configure is up and running, Hello :o)
  10.  
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 3.6 up until Jan 2024 from then on it's both above &: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 4.99

Hansvb

  • Hero Member
  • *****
  • Posts: 801
Re: MVP made easier.
« Reply #72 on: April 24, 2025, 01:08:15 pm »
What I mean is that it doesn't run smoothly when the files are not present.

cdbc

  • Hero Member
  • *****
  • Posts: 2128
    • http://www.cdbc.dk
Re: MVP made easier.
« Reply #73 on: April 24, 2025, 01:36:17 pm »
Hi
Well, build in a check and switch to default texts, when the files are not present... No Biggie  8-)
Hint:  You can use the "bintoinc" tool from @KodeZwerg, to create a default and include that as a const. IStringlist will happily load that from memory, as a matter of fact, I use it a lot in MVP-Setup...  ;)
IStringlist has even got a special function 'CreStrListFromBytes(const aBlob: array of byte): IStringList;' for exactly this purpose...  8)
"Methinks you got some coding to do..."  :D
Regards Benny

eta: IIRC MVP-Setup does exactly that, if no mvptexts.en is present, it loads a default (...and tries to write it to disc for next time), so that it's translateable... "Use the source Luke"  ;D
« Last Edit: April 24, 2025, 01:48:10 pm by cdbc »
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 3.6 up until Jan 2024 from then on it's both above &: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 4.99

Hansvb

  • Hero Member
  • *****
  • Posts: 801
Re: MVP made easier.
« Reply #74 on: April 24, 2025, 03:31:55 pm »
Quote
Methinks you got some coding to do...

That's right, I still have plenty to do. For now, slow progression. I think I'll draw everything out this weekend. What happens, what calls what, etc.


 

TinyPortal © 2005-2018