mirror of
https://github.com/danieleteti/delphimvcframework.git
synced 2024-11-15 15:55:54 +01:00
Merge remote-tracking branch 'upstream/master' into middleware_etag
This commit is contained in:
commit
2aeb322f55
198
README.md
198
README.md
@ -79,10 +79,206 @@ Congratulations to Daniele Teti and all the staff for the excellent work!" -- Ma
|
||||
> WARNING! Considering the huge amount of features added in 3.1.1-beryllium during its RC phase, the dmvcframework-3.1.1-beryllium has been renamed to dmvcframework-3.2.0-boron
|
||||
|
||||
- New! Added Nullable support in MVCActiveRecord! Check *activerecord_showcase* sample.
|
||||
|
||||
- New! Added non autogenerated primary keys in MVCActiveRecord! Check *activerecord_showcase* sample.
|
||||
|
||||
- New! Complete support for nullable types in the default serializer.
|
||||
|
||||
- New! Added `ncCamelCase` and `ncPascalCase` to the available attribute formatters.
|
||||
|
||||
| MVCNameCase | Property/Field Name | Rendered Name |
|
||||
| ------------ | --------------------- | --------------- |
|
||||
| ncUpperCase | Cod_Article | COD_ARTICLE |
|
||||
| ncLowerCase | Cod_Article | cod_article |
|
||||
| ncPascalCase | Cod_Article | CodArticle |
|
||||
| ncPascalCase | CodArticle | CodArticle |
|
||||
| ncPascalCase | `_WITH__UNDERSCORES_` | WithUnderscores |
|
||||
| ncCamelCase | Cod_Article | codArticle |
|
||||
| ncCamelCase | CodArticle | codArticle |
|
||||
| ncCamelCase | `_WITH__UNDERSCORES_` | WithUnderscores |
|
||||
| | | |
|
||||
|
||||
- New! Added Swagger support (thanks to [João Antônio Duarte](https://github.com/joaoduarte19) and [Geoffrey Smith](https://github.com/geoffsmith82))
|
||||
- New! **ObjectDict** function is the suggested way to render all the most common data types. It returns a `IMVCObjectDictionary` which is automatically rendered by the renders. Check the `renders.dproj` sample. Here's some example of the shining new `ObjectDict()`
|
||||
|
||||
**Example 1: Rendering a list of objects not freeing them after rendering**
|
||||
|
||||
*Classic*
|
||||
|
||||
```delphi
|
||||
procedure TRenderSampleController.GetLotOfPeople;
|
||||
begin
|
||||
Render<TPerson>(GetPeopleList, False);
|
||||
end;
|
||||
|
||||
```
|
||||
|
||||
*New approach with ObjectDict*
|
||||
|
||||
```delphi
|
||||
procedure TRenderSampleController.GetLotOfPeople;
|
||||
begin
|
||||
Render(ObjectDict(False).Add('data', GetPeopleList));
|
||||
end;
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
**Example 2: Rendering a list of objects and automatically free them after rendering**
|
||||
|
||||
*Classic*
|
||||
|
||||
```delphi
|
||||
procedure TRenderSampleController.GetLotOfPeople;
|
||||
begin
|
||||
Render<TPerson>(GetPeopleList);
|
||||
end;
|
||||
|
||||
```
|
||||
|
||||
*New approach with ObjectDict*
|
||||
|
||||
```delphi
|
||||
procedure TRenderSampleController.GetLotOfPeople;
|
||||
begin
|
||||
Render(ObjectDict().Add('data', GetPeopleList));
|
||||
end;
|
||||
|
||||
```
|
||||
|
||||
**Example 3: Rendering a list of objects adding links for HATEOAS support**
|
||||
|
||||
*Classic*
|
||||
|
||||
```delphi
|
||||
procedure TRenderSampleController.GetPeople_AsObjectList_HATEOAS;
|
||||
var
|
||||
p: TPerson;
|
||||
People: TObjectList<TPerson>;
|
||||
begin
|
||||
People := TObjectList<TPerson>.Create(True);
|
||||
|
||||
{$REGION 'Fake data'}
|
||||
p := TPerson.Create;
|
||||
p.FirstName := 'Daniele';
|
||||
p.LastName := 'Teti';
|
||||
p.DOB := EncodeDate(1979, 8, 4);
|
||||
p.Married := True;
|
||||
People.Add(p);
|
||||
|
||||
p := TPerson.Create;
|
||||
p.FirstName := 'John';
|
||||
p.LastName := 'Doe';
|
||||
p.DOB := EncodeDate(1879, 10, 2);
|
||||
p.Married := False;
|
||||
People.Add(p);
|
||||
|
||||
p := TPerson.Create;
|
||||
p.FirstName := 'Jane';
|
||||
p.LastName := 'Doe';
|
||||
p.DOB := EncodeDate(1883, 1, 5);
|
||||
p.Married := True;
|
||||
People.Add(p);
|
||||
{$ENDREGION}
|
||||
|
||||
Render<TPerson>(People, True,
|
||||
procedure(const APerson: TPerson; const Links: IMVCLinks)
|
||||
begin
|
||||
Links
|
||||
.AddRefLink
|
||||
.Add(HATEOAS.HREF, '/people/' + APerson.ID.ToString)
|
||||
.Add(HATEOAS.REL, 'self')
|
||||
.Add(HATEOAS._TYPE, 'application/json')
|
||||
.Add('title', 'Details for ' + APerson.FullName);
|
||||
Links
|
||||
.AddRefLink
|
||||
.Add(HATEOAS.HREF, '/people')
|
||||
.Add(HATEOAS.REL, 'people')
|
||||
.Add(HATEOAS._TYPE, 'application/json');
|
||||
end);
|
||||
end;
|
||||
|
||||
```
|
||||
|
||||
*New approach with ObjectDict*
|
||||
|
||||
```delphi
|
||||
procedure TRenderSampleController.GetPeople_AsObjectList_HATEOAS;
|
||||
var
|
||||
p: TPerson;
|
||||
People: TObjectList<TPerson>;
|
||||
begin
|
||||
People := TObjectList<TPerson>.Create(True);
|
||||
|
||||
{$REGION 'Fake data'}
|
||||
p := TPerson.Create;
|
||||
p.FirstName := 'Daniele';
|
||||
p.LastName := 'Teti';
|
||||
p.DOB := EncodeDate(1979, 8, 4);
|
||||
p.Married := True;
|
||||
People.Add(p);
|
||||
|
||||
p := TPerson.Create;
|
||||
p.FirstName := 'John';
|
||||
p.LastName := 'Doe';
|
||||
p.DOB := EncodeDate(1879, 10, 2);
|
||||
p.Married := False;
|
||||
People.Add(p);
|
||||
|
||||
p := TPerson.Create;
|
||||
p.FirstName := 'Jane';
|
||||
p.LastName := 'Doe';
|
||||
p.DOB := EncodeDate(1883, 1, 5);
|
||||
p.Married := True;
|
||||
People.Add(p);
|
||||
|
||||
{$ENDREGION}
|
||||
|
||||
Render(ObjectDict().Add('data', People,
|
||||
procedure(const APerson: TObject; const Links: IMVCLinks)
|
||||
begin
|
||||
Links
|
||||
.AddRefLink
|
||||
.Add(HATEOAS.HREF, '/people/' + TPerson(APerson).ID.ToString)
|
||||
.Add(HATEOAS.REL, 'self')
|
||||
.Add(HATEOAS._TYPE, 'application/json')
|
||||
.Add('title', 'Details for ' + TPerson(APerson).FullName);
|
||||
Links
|
||||
.AddRefLink
|
||||
.Add(HATEOAS.HREF, '/people')
|
||||
.Add(HATEOAS.REL, 'people')
|
||||
.Add(HATEOAS._TYPE, 'application/json');
|
||||
end));
|
||||
end;
|
||||
|
||||
```
|
||||
|
||||
`ObjectDict` is able to render multiple data sources (datasets, objectlists, objects or StrDict) at the same time using different casing, HATEOAS callbacks and modes.
|
||||
|
||||
```delphi
|
||||
procedure TTestServerController.TestObjectDict;
|
||||
var
|
||||
lDict: IMVCObjectDictionary;
|
||||
begin
|
||||
lDict := ObjectDict(false)
|
||||
.Add('ncUpperCase_List', GetDataSet, nil, dstAllRecords, ncUpperCase)
|
||||
.Add('ncLowerCase_List', GetDataSet, nil, dstAllRecords, ncLowerCase)
|
||||
.Add('ncCamelCase_List', GetDataSet, nil, dstAllRecords, ncCamelCase)
|
||||
.Add('ncPascalCase_List', GetDataSet, nil, dstAllRecords, ncPascalCase)
|
||||
.Add('ncUpperCase_Single', GetDataSet, nil, dstSingleRecord, ncUpperCase)
|
||||
.Add('ncLowerCase_Single', GetDataSet, nil, dstSingleRecord, ncLowerCase)
|
||||
.Add('ncCamelCase_Single', GetDataSet, nil, dstSingleRecord, ncCamelCase)
|
||||
.Add('ncPascalCase_Single', GetDataSet, nil, dstSingleRecord, ncPascalCase)
|
||||
.Add('meta', StrDict(['page'], ['1']));
|
||||
Render(lDict);
|
||||
end;
|
||||
|
||||
```
|
||||
>ObjectDict is the suggested way to renders data. However, the other ones are still there and works as usual.
|
||||
|
||||
- New! Added SQLGenerator and RQL compiler for PostgreSQL, SQLite and MSSQLServer (in addition to MySQL, MariaDB, Firebird and Interbase)
|
||||
- New! *MVCNameAs* attribute got the param `Fixed` (default: false). If Fixed is true, then the name is not processed by the `MVCNameCase` attribute assigned to the owner type.
|
||||
- New! Added support for interfaces serialization - now it is possible to serialize Spring4D collections (thanks to [João Antônio Duarte](https://github.com/joaoduarte19))
|
||||
- New! Added support for Spring4D Nullable Types - check (thanks to [João Antônio Duarte](https://github.com/joaoduarte19))
|
||||
- New! Added `OnRouterLog` event to log custom information for each request (thanks to [Andrea Ciotti](https://github.com/andreaciotti) for the first implementation and its PR)
|
||||
@ -334,7 +530,7 @@ end;
|
||||
|
||||
- **Breaking Change!** Middleware `OnAfterControllerAction` are now invoked in the same order of `OnBeforeControllerAction` (previously were invoked in reversed order).
|
||||
|
||||
- **Breaking Change!** `TDataSetHolder` doesn't renders dataset in a property called `items` but in a property named `data` (to be more standard).
|
||||
- **Deprecated!** `TDataSetHolder` is deprecated! Use the shining new `ObjectDict(boolean)` instead.
|
||||
|
||||
- Fixed! Has been patched a serious security bug affecting deployment configurations which uses internal WebServer to serve static files (do not affect all Apache, IIS or proxied deployments). Thanks to **Stephan Munz** to have discovered it. *Update to dmvcframework-3.2-RC5+ is required for all such kind of deployments.*
|
||||
|
||||
|
@ -496,7 +496,7 @@ begin
|
||||
lDM := TMyDataModule.Create(nil);
|
||||
try
|
||||
lDM.qryCustomers.Open;
|
||||
lDict := ObjectDict(False)
|
||||
lDict := ObjectDict(False { data are not freed after ObjectDict if freed } )
|
||||
.Add('customers', lDM.qryCustomers,
|
||||
procedure(const DS: TDataset; const Links: IMVCLinks)
|
||||
begin
|
||||
@ -576,7 +576,10 @@ end;
|
||||
|
||||
procedure TRenderSampleController.GetLotOfPeople;
|
||||
begin
|
||||
Render<TPerson>(GetPeopleList, False);
|
||||
{ classic approach }
|
||||
// Render<TPerson>(GetPeopleList, False);
|
||||
{ new approach with ObjectDict }
|
||||
Render(ObjectDict(False).Add('data', GetPeopleList));
|
||||
end;
|
||||
|
||||
procedure TRenderSampleController.GetManyNullableObjects;
|
||||
@ -763,7 +766,10 @@ begin
|
||||
People.Add(p);
|
||||
|
||||
{$ENDREGION}
|
||||
Render<TPerson>(HTTP_STATUS.OK, People, True);
|
||||
{ classic approach }
|
||||
// Render<TPerson>(HTTP_STATUS.OK, People, True);
|
||||
{ new approach with ObjectDict }
|
||||
Render(HTTP_STATUS.OK, ObjectDict().Add('data', People));
|
||||
end;
|
||||
|
||||
procedure TRenderSampleController.GetPeople_AsObjectList_HATEOAS;
|
||||
@ -796,21 +802,40 @@ begin
|
||||
People.Add(p);
|
||||
|
||||
{$ENDREGION}
|
||||
Render<TPerson>(People, True,
|
||||
{ classic approach }
|
||||
{
|
||||
Render<TPerson>(People, True,
|
||||
procedure(const APerson: TPerson; const Links: IMVCLinks)
|
||||
begin
|
||||
Links
|
||||
.AddRefLink
|
||||
.Add(HATEOAS.HREF, '/people/' + APerson.ID.ToString)
|
||||
.Add(HATEOAS.REL, 'self')
|
||||
.Add(HATEOAS._TYPE, 'application/json')
|
||||
.Add('title', 'Details for ' + APerson.FullName);
|
||||
Links
|
||||
.AddRefLink
|
||||
.Add(HATEOAS.HREF, '/people')
|
||||
.Add(HATEOAS.REL, 'people')
|
||||
.Add(HATEOAS._TYPE, 'application/json');
|
||||
end);
|
||||
}
|
||||
{ new approach with ObjectDict }
|
||||
Render(ObjectDict().Add('data', People,
|
||||
procedure(const APerson: TObject; const Links: IMVCLinks)
|
||||
begin
|
||||
Links
|
||||
.AddRefLink
|
||||
.Add(HATEOAS.HREF, '/people/' + APerson.ID.ToString)
|
||||
.Add(HATEOAS.HREF, '/people/' + TPerson(APerson).ID.ToString)
|
||||
.Add(HATEOAS.REL, 'self')
|
||||
.Add(HATEOAS._TYPE, 'application/json')
|
||||
.Add('title', 'Details for ' + APerson.FullName);
|
||||
.Add('title', 'Details for ' + TPerson(APerson).FullName);
|
||||
Links
|
||||
.AddRefLink
|
||||
.Add(HATEOAS.HREF, '/people')
|
||||
.Add(HATEOAS.REL, 'people')
|
||||
.Add(HATEOAS._TYPE, 'application/json');
|
||||
end);
|
||||
end));
|
||||
end;
|
||||
|
||||
procedure TRenderSampleController.GetPersonById(const ID: Integer);
|
||||
|
@ -446,22 +446,22 @@ type
|
||||
function LinksData: TMVCStringDictionaryList;
|
||||
end;
|
||||
|
||||
// IMVCStringDictionary = interface
|
||||
// ['{164117AD-8DDD-47F7-877C-453979707D10}']
|
||||
// function GetItems(const Key: string): string;
|
||||
// procedure SetItems(const Key, Value: string);
|
||||
// procedure Clear;
|
||||
// function Add(const Name, Value: string): IMVCStringDictionary;
|
||||
// function TryGetValue(const Name: string; out Value: string): Boolean; overload;
|
||||
// function TryGetValue(const Name: string; out Value: Integer): Boolean; overload;
|
||||
// function Count: Integer;
|
||||
// function GetEnumerator: TDictionary<string, string>.TPairEnumerator;
|
||||
// function ContainsKey(const Key: string): Boolean;
|
||||
// function Keys: TArray<string>;
|
||||
// property Items[const Key: string]: string read GetItems write SetItems; default;
|
||||
// end;
|
||||
// IMVCStringDictionary = interface
|
||||
// ['{164117AD-8DDD-47F7-877C-453979707D10}']
|
||||
// function GetItems(const Key: string): string;
|
||||
// procedure SetItems(const Key, Value: string);
|
||||
// procedure Clear;
|
||||
// function Add(const Name, Value: string): IMVCStringDictionary;
|
||||
// function TryGetValue(const Name: string; out Value: string): Boolean; overload;
|
||||
// function TryGetValue(const Name: string; out Value: Integer): Boolean; overload;
|
||||
// function Count: Integer;
|
||||
// function GetEnumerator: TDictionary<string, string>.TPairEnumerator;
|
||||
// function ContainsKey(const Key: string): Boolean;
|
||||
// function Keys: TArray<string>;
|
||||
// property Items[const Key: string]: string read GetItems write SetItems; default;
|
||||
// end;
|
||||
|
||||
TMVCStringDictionary = class //(TInterfacedObject, IMVCStringDictionary)
|
||||
TMVCStringDictionary = class // (TInterfacedObject, IMVCStringDictionary)
|
||||
strict private
|
||||
function GetItems(const Key: string): string;
|
||||
procedure SetItems(const Key, Value: string);
|
||||
@ -1318,15 +1318,25 @@ var
|
||||
I: Integer;
|
||||
lNextUpCase: Boolean;
|
||||
lSB: TStringBuilder;
|
||||
C: Char;
|
||||
lIsLowerCase: Boolean;
|
||||
lIsUpperCase, lPreviousWasUpperCase: Boolean;
|
||||
lIsAlpha: Boolean;
|
||||
begin
|
||||
lNextUpCase := MakeFirstUpperToo;
|
||||
lPreviousWasUpperCase := True;
|
||||
lSB := TStringBuilder.Create;
|
||||
try
|
||||
for I := 0 to Length(Value) - 1 do
|
||||
begin
|
||||
if not CharInSet(Value.Chars[I], ['A' .. 'Z', 'a' .. 'z']) then
|
||||
C := Value.Chars[I];
|
||||
lIsLowerCase := CharInSet(C, ['a' .. 'z']);
|
||||
lIsUpperCase := CharInSet(C, ['A' .. 'Z']);
|
||||
lIsAlpha := lIsLowerCase or lIsUpperCase;
|
||||
if not lIsAlpha then
|
||||
begin
|
||||
lNextUpCase := True;
|
||||
lPreviousWasUpperCase := False;
|
||||
Continue;
|
||||
end
|
||||
else
|
||||
@ -1334,13 +1344,21 @@ begin
|
||||
if lNextUpCase then
|
||||
begin
|
||||
lNextUpCase := False;
|
||||
lSB.Append(UpCase(Value.Chars[I]));
|
||||
lSB.Append(UpCase(C));
|
||||
end
|
||||
else
|
||||
begin
|
||||
lSB.Append(LowerCase(Value.Chars[I]));
|
||||
if lPreviousWasUpperCase then
|
||||
begin
|
||||
lSB.Append(LowerCase(C));
|
||||
end
|
||||
else
|
||||
begin
|
||||
lSB.Append(C);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
lPreviousWasUpperCase := lIsUpperCase;
|
||||
end;
|
||||
Result := lSB.ToString;
|
||||
finally
|
||||
|
@ -27,6 +27,7 @@
|
||||
unit MVCFramework.Serializer.Commons;
|
||||
|
||||
{$I dmvcframework.inc}
|
||||
{$WARN SYMBOL_DEPRECATED OFF}
|
||||
|
||||
interface
|
||||
|
||||
@ -117,13 +118,14 @@ type
|
||||
|
||||
MVCNameAsAttribute = class(TCustomAttribute)
|
||||
private
|
||||
FName: string;
|
||||
function GetName: string;
|
||||
fName: string;
|
||||
fFixed: Boolean;
|
||||
protected
|
||||
{ protected declarations }
|
||||
public
|
||||
constructor Create(const AName: string);
|
||||
property name: string read GetName;
|
||||
constructor Create(const AName: string; const Fixed: Boolean = False);
|
||||
property name: string read fName;
|
||||
property Fixed: Boolean read fFixed;
|
||||
end;
|
||||
|
||||
MapperJSONSer = MVCNameAsAttribute deprecated 'Use MVCNameAsAttribute';
|
||||
@ -167,7 +169,7 @@ type
|
||||
procedure SetFieldName(const Value: string);
|
||||
procedure SetIsPK(const Value: Boolean);
|
||||
public
|
||||
constructor Create(AFieldName: string; AIsPK: Boolean = false);
|
||||
constructor Create(AFieldName: string; AIsPK: Boolean = False);
|
||||
property FieldName: string read FFieldName write SetFieldName;
|
||||
property IsPK: Boolean read FIsPK write SetIsPK;
|
||||
end;
|
||||
@ -285,20 +287,20 @@ type
|
||||
function GetData: TObject;
|
||||
override;
|
||||
public
|
||||
constructor Create(const AObject: TObject; const AOwns: Boolean = false;
|
||||
constructor Create(const AObject: TObject; const AOwns: Boolean = False;
|
||||
const ADataSetSerializationType: TMVCDatasetSerializationType = TMVCDatasetSerializationType.
|
||||
dstAllRecords);
|
||||
virtual;
|
||||
destructor Destroy;
|
||||
override;
|
||||
function SerializationType: TMVCDatasetSerializationType;
|
||||
[MVCNameAs('data')]
|
||||
[MVCNameAs('items')]
|
||||
property Items: TObject read GetData;
|
||||
[MVCNameAs('meta')]
|
||||
property MetaData: TMVCStringDictionary read GetMetadata;
|
||||
end;
|
||||
end deprecated 'Use "ObjectDict"';
|
||||
|
||||
TDataObjectHolder = TMVCResponseData deprecated 'Use one of the specialized versions';
|
||||
TDataObjectHolder = TMVCResponseData deprecated 'Use "ObjectDict"';
|
||||
|
||||
THTTPStatusCode = 100 .. 599;
|
||||
|
||||
@ -410,11 +412,6 @@ const
|
||||
JSONNameLowerCase = ncLowerCase deprecated 'Use MVCNameCaseAttribute(ncLowerCase)';
|
||||
JSONNameUpperCase = ncUpperCase deprecated 'Use MVCNameCaseAttribute(ncUpperCase)';
|
||||
|
||||
function NewObjectHolder(const AObject: TObject; const AMetaFiller: TProc<TMVCStringDictionary> = nil;
|
||||
const AOwns: Boolean = false): TMVCObjectResponse;
|
||||
function NewCollectionHolder(const AList: TObject; const AMetaFiller: TProc<TMVCStringDictionary> = nil;
|
||||
const AOwns: Boolean = false): TMVCObjectListResponse;
|
||||
|
||||
function StrDict: TMVCStringDictionary; overload;
|
||||
function StrDict(const aKeys: array of string; const aValues: array of string)
|
||||
: TMVCStringDictionary; overload;
|
||||
@ -480,26 +477,6 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
function NewObjectHolder(const AObject: TObject; const AMetaFiller: TProc<TMVCStringDictionary> = nil;
|
||||
const AOwns: Boolean = false): TMVCObjectResponse;
|
||||
begin
|
||||
Result := TMVCObjectResponse.Create(AObject, AOwns);
|
||||
if Assigned(AMetaFiller) then
|
||||
begin
|
||||
AMetaFiller(Result.fMetaData);
|
||||
end;
|
||||
end;
|
||||
|
||||
function NewCollectionHolder(const AList: TObject; const AMetaFiller: TProc<TMVCStringDictionary> = nil;
|
||||
const AOwns: Boolean = false): TMVCObjectListResponse;
|
||||
begin
|
||||
Result := TMVCObjectListResponse.Create(AList, AOwns);
|
||||
if Assigned(AMetaFiller) then
|
||||
begin
|
||||
AMetaFiller(Result.fMetaData);
|
||||
end;
|
||||
end;
|
||||
|
||||
function DateTimeToISOTimeStamp(const ADateTime: TDateTime): string;
|
||||
begin
|
||||
// fs.TimeSeparator := ':';
|
||||
@ -586,37 +563,30 @@ var
|
||||
Attrs: TArray<TCustomAttribute>;
|
||||
Attr: TCustomAttribute;
|
||||
begin
|
||||
{
|
||||
Dear future me...
|
||||
Yes, this method is called a lot of times, but after some tests
|
||||
seems that the performance loss is very low, so if you don't have any
|
||||
new evidence don't try to improve it...
|
||||
}
|
||||
Result := AField.Name;
|
||||
|
||||
Attrs := AField.GetAttributes;
|
||||
for Attr in Attrs do
|
||||
begin
|
||||
if Attr is MVCNameAsAttribute then
|
||||
begin
|
||||
Exit(MVCNameAsAttribute(Attr).Name);
|
||||
end;
|
||||
end;
|
||||
|
||||
Attrs := AType.GetAttributes;
|
||||
for Attr in Attrs do
|
||||
begin
|
||||
if Attr is MVCNameCaseAttribute then
|
||||
begin
|
||||
Exit(TMVCSerializerHelper.ApplyNameCase(MVCNameCaseAttribute(Attr).KeyCase, AField.Name));
|
||||
// case MVCNameCaseAttribute(Attr).KeyCase of
|
||||
// ncUpperCase:
|
||||
// begin
|
||||
// Exit(UpperCase(AField.Name));
|
||||
// end;
|
||||
// ncLowerCase:
|
||||
// begin
|
||||
// Exit(LowerCase(AField.Name));
|
||||
// end;
|
||||
// ncCamelCase:
|
||||
// begin
|
||||
// Exit(CamelCase(AField.Name));
|
||||
// end;
|
||||
// ncPascalCase:
|
||||
// begin
|
||||
// Exit(CamelCase(AField.Name, True));
|
||||
// end;
|
||||
// end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
class function TMVCSerializerHelper.AttributeExists<T>(const AAttributes: TArray<TCustomAttribute>;
|
||||
@ -667,7 +637,7 @@ class function TMVCSerializerHelper.AttributeExists<T>(const AAttributes: TArray
|
||||
var
|
||||
Att: TCustomAttribute;
|
||||
begin
|
||||
Result := false;
|
||||
Result := False;
|
||||
for Att in AAttributes do
|
||||
if Att is T then
|
||||
Exit(True);
|
||||
@ -769,37 +739,36 @@ var
|
||||
Attrs: TArray<TCustomAttribute>;
|
||||
Attr: TCustomAttribute;
|
||||
begin
|
||||
{ in un rendering di una lista, quante volte viene chiamata questa funzione? }
|
||||
Result := AProperty.Name;
|
||||
|
||||
Attrs := AProperty.GetAttributes;
|
||||
for Attr in Attrs do
|
||||
begin
|
||||
{ TODO -oDaniele -cGeneral : Time this! }
|
||||
if Attr is MVCNameAsAttribute then
|
||||
Exit(MVCNameAsAttribute(Attr).Name);
|
||||
begin
|
||||
// Exit(MVCNameAsAttribute(Attr).Name);
|
||||
Result := MVCNameAsAttribute(Attr).Name;
|
||||
if MVCNameAsAttribute(Attr).Fixed then { if FIXED the attribute NameAs remains untouched }
|
||||
begin
|
||||
Exit
|
||||
end
|
||||
else
|
||||
begin
|
||||
Break;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
Attrs := AType.GetAttributes;
|
||||
for Attr in Attrs do
|
||||
begin
|
||||
if Attr is MVCNameCaseAttribute then
|
||||
begin
|
||||
Exit(TMVCSerializerHelper.ApplyNameCase(MVCNameCaseAttribute(Attr).KeyCase, AProperty.Name));
|
||||
// case MVCNameCaseAttribute(Attr).KeyCase of
|
||||
// ncUpperCase:
|
||||
// begin
|
||||
// Exit(UpperCase(AProperty.Name));
|
||||
// end;
|
||||
// ncLowerCase:
|
||||
// begin
|
||||
// Exit(LowerCase(AProperty.Name));
|
||||
// end;
|
||||
// ncCamelCase:
|
||||
// begin
|
||||
// Exit(CamelCase(AProperty.Name));
|
||||
// end;
|
||||
// ncPascalCase:
|
||||
// begin
|
||||
// Exit(CamelCase(AProperty.Name, True));
|
||||
// end;
|
||||
// end;
|
||||
Exit(TMVCSerializerHelper.ApplyNameCase(MVCNameCaseAttribute(Attr).KeyCase, Result));
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
class function TMVCSerializerHelper.GetTypeKindAsString(const ATypeKind: TTypeKind): string;
|
||||
@ -813,10 +782,10 @@ var
|
||||
Attrs: TArray<TCustomAttribute>;
|
||||
Attr: TCustomAttribute;
|
||||
begin
|
||||
Result := false;
|
||||
Result := False;
|
||||
Attrs := AMember.GetAttributes;
|
||||
if Length(Attrs) = 0 then
|
||||
Exit(false);
|
||||
Exit(False);
|
||||
for Attr in Attrs do
|
||||
if Attr is T then
|
||||
Exit(True);
|
||||
@ -828,7 +797,7 @@ var
|
||||
Attr: TCustomAttribute;
|
||||
begin
|
||||
AAttribute := nil;
|
||||
Result := false;
|
||||
Result := False;
|
||||
Attrs := AMember.GetAttributes;
|
||||
for Attr in Attrs do
|
||||
if Attr is T then
|
||||
@ -876,15 +845,11 @@ end;
|
||||
|
||||
{ MVCNameAsAttribute }
|
||||
|
||||
constructor MVCNameAsAttribute.Create(const AName: string);
|
||||
constructor MVCNameAsAttribute.Create(const AName: string; const Fixed: Boolean = False);
|
||||
begin
|
||||
inherited Create;
|
||||
FName := AName;
|
||||
end;
|
||||
|
||||
function MVCNameAsAttribute.GetName: string;
|
||||
begin
|
||||
Result := FName;
|
||||
fName := AName;
|
||||
fFixed := Fixed;
|
||||
end;
|
||||
|
||||
{ MVCListOfAttribute }
|
||||
@ -1169,7 +1134,7 @@ function MapDataSetFieldToNullableRTTIField(const AValue: TValue; const AField:
|
||||
const AObject: TObject): Boolean;
|
||||
begin
|
||||
Assert(AValue.Kind = tkRecord);
|
||||
Result := false;
|
||||
Result := False;
|
||||
if AValue.IsType(TypeInfo(NullableString)) then
|
||||
begin
|
||||
if AField.IsNull then
|
||||
@ -1357,7 +1322,7 @@ const aRTTIProp: TRttiProperty;
|
||||
const AObject: TObject): Boolean;
|
||||
begin
|
||||
Assert(AValue.Kind = tkRecord);
|
||||
Result := false;
|
||||
Result := False;
|
||||
if AValue.IsType(TypeInfo(NullableString)) then
|
||||
begin
|
||||
if AField.IsNull then
|
||||
|
@ -655,6 +655,7 @@ type
|
||||
procedure Render(const AStatusCode: Integer; AObject: TObject; const AOwns: Boolean;
|
||||
const ASerializationAction: TMVCSerializationAction = nil); overload;
|
||||
procedure Render(const AObject: IInterface; const ASerializationAction: TMVCSerializationAction = nil); overload;
|
||||
procedure Render(const AStatusCode: Integer; const AObject: IInterface; const ASerializationAction: TMVCSerializationAction = nil); overload;
|
||||
// PODOs Collection render
|
||||
procedure Render<T: class>(const ACollection: TObjectList<T>;
|
||||
const ASerializationAction: TMVCSerializationAction<T> = nil); overload;
|
||||
@ -3285,6 +3286,14 @@ begin
|
||||
raise EMVCException.Create('Can not render an empty dataset.');
|
||||
end;
|
||||
|
||||
procedure TMVCRenderer.Render(const AStatusCode: Integer;
|
||||
const AObject: IInterface;
|
||||
const ASerializationAction: TMVCSerializationAction);
|
||||
begin
|
||||
SetStatusCode(AStatusCode);
|
||||
Render(AObject, ASerializationAction);
|
||||
end;
|
||||
|
||||
procedure TMVCRenderer.Render(const AObject: IInterface; const ASerializationAction: TMVCSerializationAction);
|
||||
begin
|
||||
Render(TObject(AObject), False, ASerializationAction);
|
||||
|
@ -226,7 +226,7 @@ uses System.DateUtils, System.Math,
|
||||
{$ENDIF}
|
||||
TestServerControllerU, System.Classes,
|
||||
MVCFramework.DuckTyping, System.IOUtils, MVCFramework.SystemJSONUtils,
|
||||
IdGlobal;
|
||||
IdGlobal, System.TypInfo;
|
||||
|
||||
var
|
||||
JWT_SECRET_KEY_TEST: string = 'myk3y';
|
||||
@ -1847,6 +1847,7 @@ var
|
||||
lNameCaseIdx: TMVCNameCase;
|
||||
lOrig: string;
|
||||
lOutData: string;
|
||||
lActualOutData: string;
|
||||
begin
|
||||
for lNameCaseIdx := ncAsIs to ncPascalCase do
|
||||
begin
|
||||
@ -1854,7 +1855,9 @@ begin
|
||||
begin
|
||||
lOrig := fOrigDATA[I];
|
||||
lOutData := fOutDATA[I][lNameCaseIdx];
|
||||
Assert.areEqual(lOutData, TMVCSerializerHelper.ApplyNameCase(lNameCaseIdx, lOrig), lOrig);
|
||||
lActualOutData := TMVCSerializerHelper.ApplyNameCase(lNameCaseIdx, lOrig);
|
||||
Assert.areEqual(lOutData, lActualOutData, False, lOrig + ' for ' + GetEnumName(TypeInfo(TMVCNameCase),
|
||||
Ord(lNameCaseIdx)));
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
@ -499,7 +499,7 @@ begin
|
||||
Assert.isTrue(Dm.EntityAsIsName.AsString = 'Ezequiel Juliano Müller');
|
||||
|
||||
Dm.EntityAsIs.EmptyDataSet;
|
||||
Dm.EntityAsIs.LoadJSONArrayFromJSONObjectProperty(JSON_ITEMS, 'items', ncAsIs);
|
||||
Dm.EntityAsIs.LoadJSONArrayFromJSONObjectProperty('items', JSON_ITEMS, ncAsIs);
|
||||
Dm.EntityAsIs.First;
|
||||
Assert.isTrue(Dm.EntityAsIsId.AsLargeInt = 1);
|
||||
Assert.isTrue(Dm.EntityAsIsName.AsString = 'Pedro Henrique de Oliveira');
|
||||
|
Loading…
Reference in New Issue
Block a user