Code Monkey home page Code Monkey logo

Comments (18)

danieleteti avatar danieleteti commented on May 30, 2024

Let dmvcframework depends from Spring4d would be a huge change and huge dependency. We've to find a different approach.

from delphimvcframework.

fastbike avatar fastbike commented on May 30, 2024

Probably we could avoid the Spring4D dependency if we can take a Duck Typing approach. I.e. Support Lists by looking for some iteration interface, and Objects by scanning the properties.
I'd need to play around some more to see what is available.

from delphimvcframework.

danieleteti avatar danieleteti commented on May 30, 2024

The unit MVCFramework.DuckTyping.pas already implement lot of Duck Typing oriented code and has been around for years. Maybe with some tweaks and we could use it for ViewData...

from delphimvcframework.

fastbike avatar fastbike commented on May 30, 2024

I'll add it to my todo list, to investigate.
Although I've probably promised my boss more than I can deliver :(

from delphimvcframework.

fastbike avatar fastbike commented on May 30, 2024

I've got some code in my toolbox that is a container that can handle both TObject derived instances as well as reference counted IInterface derived instances. As an aside, I've been using it to implement a class helper for TWebContext (so I can can have application specific pseudo properties anywhere the WebContext is available) by binding it to the CustomIntfObject property.

The interface for this container is below - note that it automatically increments and decrements the reference count for IInterface instances to prevent them going out of scope and thus keeps the auto memory management working correctly.

  /// <summary>Helper container for MVC Context auxilliary objects</summary>
  /// <remarks>Supports adding and retrieving both (classic) TObject and (reference counted) IInterface objects
  /// The container will increment the reference count when item is added, and decrement when it is removed
  ///</remarks>
  IWebContextObjectsDictionary = interface
    ['{B70454F5-DD2E-4727-9B3B-D910CEE113AC}']
    procedure Add(const Key: string; const Value: TObject); overload;
    procedure Add(const Key: string; const Value: IInterface); overload;
    function TryGetValue(const Key: string; out Value: TObject): Boolean; overload;
    function TryGetValue(const Key: string; out Value: IInterface): Boolean; overload;
    procedure Remove(const Key: string);
  end;

I could probably refactor this to be more general so it would then be suitable as the container for the ViewData, although as implemented it assumes ownership of the contained objects and will call their destructor as part of its owner destructor (this could be changed though).

from delphimvcframework.

fastbike avatar fastbike commented on May 30, 2024

The other way is to add an additional ViewData2 container that is generalised for reference counted instances and change the rendering code to iterate over both containers.

from delphimvcframework.

fastbike avatar fastbike commented on May 30, 2024

Either way will require a change to the TMVCBaseViewEngine class as this takes the AViewModel (type TMVCViewDataObject) in the constructor.
Either we change the type that is passed in, or we add an additional parameter.
Given that this change should be seamless to the user of the Framework (i.e. they should be fairly agnostic about what type of object is passed in) I'd be looking to implement the ViewModel with a container that can handle either type.

from delphimvcframework.

fastbike avatar fastbike commented on May 30, 2024

OK, so my first naive implementation just stands up a parallel ViewModel that has a TDictionary<string, IInterface> as the storage, and the Mustache renderer takes this as an additional param which it iterates over when preparing the models.

procedure TMVCMustacheViewEngine.PrepareModels;
// snip
begin
  if Assigned(ViewModel) then
  begin
    for DataObj in ViewModel do
    begin
// snip
    end;
  end;
// new code to process the IInterface instances
  if Assigned(ViewModel2) then
  begin
    for DataObj2 in ViewModel2 do
    begin
      lList := TDuckTypedList.Wrap(TObject(DataObj2.Value));
      if lList <> nil then
        lJSON := lSer.SerializeCollection(DataObj2.Value)
      else
        lJSON := lSer.SerializeObject(DataObj2.Value);
      if not lFirst then
        lSJSON := lSJSON + ',';
      lSJSON := lSJSON + '"' + DataObj2.Key + '":' + lJSON;
      lFirst := False;
    end;
  end;
// resume existing code
  if Assigned(ViewDataSets) then
  begin
// snip
  end;
// snip
end;

This renders out a Spring4D list with no references to that framework apart from in my controller unit. So we are not coupling DMVC with Spring4D !
Also I check for memory leaks and there are none - I was a little hesitant in casting the reference counted instance to a TObject so wanted to double check.

I would prefer to propose some changes that preserve the existing interface by modifying the underlying storage so look forward to your feedback when you have time to review.

from delphimvcframework.

fastbike avatar fastbike commented on May 30, 2024

Hmm, I wonder if I'm over complicating this LOL.
I've been back in the code and have just cast the Spring framework IList instance to a TObject and then added it to the ViewData.
So that code in the first post of this ticket now becomes

procedure TMovieController.GetMoviesPage;
begin
  // get  IList<TMovie> (from spring.collections.pas) and cast it as a vanilla Delphi object
  ViewData['Movies'] := TObject(GetMovieService.ListAll);   
  LoadView(['Header', 'Movie', 'Footer']);
  RenderResponseStream;
end;
// the Interfaced object returned by the "ListAll" method above will be out of scope by here so its destructor will be called via the reference counting mechanism

My next task will be looking at the MVCFramework.DataSet.Utils unit so I can write some code to output the TDataset as a Spring4D IList<T> instance. Modifying the TDataSetUtils.DataSetToObjectList<T> method to accept a duck typed list would enable standard Delphi lists as well as other lists that exhibit list like behaviour (specifically the ability to Add an object) to be used.

from delphimvcframework.

HealthOneNZ avatar HealthOneNZ commented on May 30, 2024

I've found that the TDataSetUtils class has been declared as sealed so this is making it hard to subclass for use with an interfaced list.

I might just clone the unit, unless there is a better idea.

from delphimvcframework.

danieleteti avatar danieleteti commented on May 30, 2024

If there is a good reason we can remove the sealed clause.

from delphimvcframework.

fastbike avatar fastbike commented on May 30, 2024

If there is a good reason we can remove the sealed clause.

Sorry, that was me posting from a work account. I'll take a look and see what duck type list changes would make it work with different lists.

from delphimvcframework.

fastbike avatar fastbike commented on May 30, 2024

I'm stuck between trying to add "generic" interfaced list functionality - I've looked at IMVCList (used by TDuckTypedList) but this does not support the IMVCList<T> generic functionality.

Or just run with a copy of the unit and rewrite to support Spring4D (so outside of a contribution) to this excellent library.

Option 1 is preferable from a community perspective, option 2 from a get-the-job-done perspective.

from delphimvcframework.

danieleteti avatar danieleteti commented on May 30, 2024

I know the feeling. Writing a framework is not like just "make it work" job.
In this case, we could follow the aproach of sample and contributed units. Then we could find the way to bring that funzionality in the core without adding dependencies.

from delphimvcframework.

fastbike avatar fastbike commented on May 30, 2024

Option 2 yields the following - unfortunately due to the lack of a generic version of TDuckTyped list I've need to replicate the DataSetToObjectList on the utility class. It also means I have pulled in reference to the Spring collections rather than being able to use TDuckTypedList.IsWrappedList to decide if the list can be wrapped so the Add method can be accessed.
You'll see I've commented the line where the item to be added to the list is created.
As an aside the inheritance of the class helper hides the method with the same name (overload does not fix the visibility)

unit uDatasetHelpers;

interface

uses
  System.SysUtils,
  System.Classes,
  Data.DB,
  System.Rtti,
  MVCFramework.DataSet.Utils,
  Spring, Spring.Collections, Spring.Collections.Lists;

type

  TDataSetHelper2 = class helper(TDataSetHelper) for TDataSet
    function AsObjectList<T: class, constructor>(CloseAfterScroll: boolean = false; OwnsObjects: boolean = true): IList<T>;
  end;

  TDataSetUtils2 = class // (TDataSetUtils)// sealed
  public
    class procedure DataSetToObjectList<T: class, constructor>(ADataSet: TDataSet; AObjectList: IList<T>;
      ACloseDataSetAfterScroll: boolean = true);
  end;

implementation

uses
  System.Generics.Collections;

{ TDataSetHelper2 }
function TDataSetHelper2.AsObjectList<T>(CloseAfterScroll, OwnsObjects: boolean): IList<T>;
begin
  Result := TCollections.CreateObjectList<T>(OwnsObjects);
  try
    TDataSetUtils2.DataSetToObjectList<T>(Self, Result, CloseAfterScroll);
  except
    Result := nil;
    raise;
  end;
end;

{ TDataSetUtils2 }
class procedure TDataSetUtils2.DataSetToObjectList<T>(ADataSet: TDataSet; AObjectList: IList<T>;
  ACloseDataSetAfterScroll: boolean);
// NB: no functional change here - just a change of signature for the method
var
  Obj: T;
  SavedPosition: TArray<Byte>;
begin
  ADataSet.DisableControls;
  try
    SavedPosition := ADataSet.Bookmark;
    while not ADataSet.Eof do
    begin
      Obj := T.Create; // this requires a generic type
      TDataSetUtils.DataSetToObject(ADataSet, Obj);
      AObjectList.Add(Obj);
      ADataSet.Next;
    end;
    if ADataSet.BookmarkValid(SavedPosition) then
      ADataSet.Bookmark := SavedPosition;
  finally
    ADataSet.EnableControls;
  end;
  if ACloseDataSetAfterScroll then
    ADataSet.Close;
end;

end.

from delphimvcframework.

danieleteti avatar danieleteti commented on May 30, 2024

Did you tried to just hard-cast the interface reference to TObject?

Example:

uses
   MVCFramework.DuckTyping;

{ snip snip snip }

function TWebSiteController.PeopleList: String;
var
  LDAL: IPeopleDAL;
  lPeople: TPeople;
  lPeople2: IInterface;
begin
  LDAL := TServicesFactory.GetPeopleDAL;
  lPeople := LDAL.GetPeople;
  try
    lPeople2 := WrapAsList(LDAL.GetPeople); //just for test, lPeople2 is now an interface
    ViewData['people'] := lPeople;
    ViewData['people2'] :=  TObject(lPeople2); //having already an interface this should be enough
    Result := GetRenderedView(['header', 'people_list', 'footer']);
  finally
    lPeople.Free;
  end;
end;

from delphimvcframework.

sglienke avatar sglienke commented on May 30, 2024

ef6edd5 should already solve this issue - the only thing is that you cannot directly assign any interface to TValue but need to write ViewData['...'] := TValue.From(someInterface);
I don't know though if internally everything is set up to properly work with interfaces inside those TValue that are stored inside the viewdata.

from delphimvcframework.

fastbike avatar fastbike commented on May 30, 2024

It looks like there have been some major changes prior to the referenced commit, in particular line 236 of the MVCFramework.View.Renderers.Mustache unit cannot be compiled

lSer.TValueToJSONObjectProperty(lJSONModel, DataObj.Key, DataObj.Value, TMVCSerializationType.stDefault, nil, nil);

Is this part of a released branch or still in the development branch ?

from delphimvcframework.

Related Issues (20)

Recommend Projects

  • React photo React

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

  • Vue.js photo Vue.js

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

  • Typescript photo Typescript

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

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

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

Recommend Topics

  • javascript

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

  • web

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

  • server

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

  • Machine learning

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

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

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

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.