mirror of
https://github.com/danieleteti/delphimvcframework.git
synced 2024-11-15 07:45:54 +01:00
+ added eLua server side view support. eLua views requires Lua dlls so the LuaViewEngine is not included in the core but in the sample folder
This commit is contained in:
parent
f3e8def287
commit
810282b89e
@ -487,10 +487,16 @@ begin
|
||||
Render(lDM.qryCustomers, False,
|
||||
procedure(const DS: TDataset; const Links: IMVCLinks)
|
||||
begin
|
||||
Links.AddRefLink.Add(HATEOAS.HREF, '/customers/' + DS.FieldByName('cust_no').AsString)
|
||||
.Add(HATEOAS.REL, 'self').Add(HATEOAS._TYPE, 'application/json');
|
||||
Links.AddRefLink.Add(HATEOAS.HREF, '/customers/' + DS.FieldByName('cust_no').AsString +
|
||||
'/orders').Add(HATEOAS.REL, 'orders').Add(HATEOAS._TYPE, 'application/json');
|
||||
Links
|
||||
.AddRefLink
|
||||
.Add(HATEOAS.HREF, '/customers/' + DS.FieldByName('cust_no').AsString)
|
||||
.Add(HATEOAS.REL, 'self')
|
||||
.Add(HATEOAS._TYPE, 'application/json');
|
||||
Links
|
||||
.AddRefLink
|
||||
.Add(HATEOAS.HREF, '/customers/' + DS.FieldByName('cust_no').AsString + '/orders')
|
||||
.Add(HATEOAS.REL, 'orders')
|
||||
.Add(HATEOAS._TYPE, 'application/json');
|
||||
end);
|
||||
finally
|
||||
lDM.Free;
|
||||
|
260
samples/serversideviews_lua/DAL.pas
Normal file
260
samples/serversideviews_lua/DAL.pas
Normal file
@ -0,0 +1,260 @@
|
||||
unit DAL;
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
System.JSON,
|
||||
MVCFramework.SystemJSONUtils,
|
||||
System.Generics.Collections,
|
||||
MVCFramework.Serializer.Commons;
|
||||
|
||||
type
|
||||
|
||||
[MVCNameCase(ncLowerCase)]
|
||||
TPerson = class
|
||||
private
|
||||
FFirstName: string;
|
||||
FLastName: string;
|
||||
FAge: Integer;
|
||||
FItems: string;
|
||||
FGUID: string;
|
||||
procedure SetFirstName(const Value: string);
|
||||
procedure SetLastName(const Value: string);
|
||||
procedure SetAge(const Value: Integer);
|
||||
procedure SetGUID(const Value: string);
|
||||
procedure SetItems(const Value: string);
|
||||
public
|
||||
[MVCNameAs('first_name')]
|
||||
property FirstName: string read FFirstName write SetFirstName;
|
||||
[MVCNameAs('last_name')]
|
||||
property LastName: string read FLastName write SetLastName;
|
||||
property Age: Integer read FAge write SetAge;
|
||||
property Items: string read FItems write SetItems;
|
||||
property GUID: string read FGUID write SetGUID;
|
||||
end;
|
||||
|
||||
TPeople = class(TObjectList<TPerson>)
|
||||
end;
|
||||
|
||||
TDevice = class
|
||||
private
|
||||
fDeviceName: string;
|
||||
fSelected: Boolean;
|
||||
public
|
||||
property DeviceName: string read fDeviceName write fDeviceName;
|
||||
property Selected: Boolean read fSelected write fSelected;
|
||||
constructor Create(aDeviceName: string; aSelected: Boolean);
|
||||
end;
|
||||
|
||||
TDeviceList = class(TObjectList<TDevice>)
|
||||
public
|
||||
function Contains(const aDeviceName: string): Boolean;
|
||||
function IndexOf(const aDeviceName: string): Integer;
|
||||
end;
|
||||
|
||||
IPeopleDAL = interface
|
||||
['{3E534A3E-EAEB-44ED-B74E-EFBBAAAE11B4}']
|
||||
function GetPeople: TPeople;
|
||||
procedure AddPerson(FirstName, LastName: string; Age: Integer; Items: TArray<string>);
|
||||
procedure DeleteByGUID(GUID: string);
|
||||
function GetPersonByGUID(GUID: string): TPerson;
|
||||
function GetDevicesList: TDeviceList;
|
||||
end;
|
||||
|
||||
TPeopleDAL = class(TInterfacedObject, IPeopleDAL)
|
||||
private const
|
||||
DATAFILE: string = 'people.data';
|
||||
public
|
||||
function GetPeople: TPeople;
|
||||
procedure AddPerson(FirstName, LastName: string; Age: Integer; Items: TArray<string>);
|
||||
procedure DeleteByGUID(GUID: string);
|
||||
function GetPersonByGUID(GUID: string): TPerson;
|
||||
function GetDevicesList: TDeviceList;
|
||||
end;
|
||||
|
||||
TServicesFactory = class sealed
|
||||
class function GetPeopleDAL: IPeopleDAL;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
System.SyncObjs,
|
||||
System.IOUtils,
|
||||
MVCFramework.Serializer.Defaults,
|
||||
System.SysUtils;
|
||||
|
||||
var
|
||||
// Hey! The storage is a simple json file, so some synchronization is needed
|
||||
_CS: TCriticalSection = nil;
|
||||
|
||||
{ TSimpleDAL }
|
||||
|
||||
procedure TPeopleDAL.AddPerson(FirstName, LastName: string; Age: Integer; Items: TArray<string>);
|
||||
var
|
||||
lPeople: TPeople;
|
||||
lPerson: TPerson;
|
||||
begin
|
||||
_CS.Enter;
|
||||
try
|
||||
lPeople := GetPeople;
|
||||
try
|
||||
lPerson := TPerson.Create;
|
||||
lPeople.Add(lPerson);
|
||||
lPerson.FirstName := FirstName;
|
||||
lPerson.LastName := LastName;
|
||||
lPerson.Age := Age;
|
||||
lPerson.Items := string.Join(',', Items);
|
||||
lPerson.GUID := TGuid.NewGuid.ToString.Replace('{', '').Replace('}', '').Replace('-', '');
|
||||
TFile.WriteAllText(DATAFILE, GetDefaultSerializer.SerializeCollection(lPeople));
|
||||
finally
|
||||
lPeople.Free;
|
||||
end;
|
||||
finally
|
||||
_CS.Leave;
|
||||
end;
|
||||
end;
|
||||
|
||||
class function TServicesFactory.GetPeopleDAL: IPeopleDAL;
|
||||
begin
|
||||
Result := TPeopleDAL.Create;
|
||||
end;
|
||||
|
||||
procedure TPeopleDAL.DeleteByGUID(GUID: string);
|
||||
var
|
||||
LJPeople: TPeople;
|
||||
I: Integer;
|
||||
begin
|
||||
_CS.Enter;
|
||||
try
|
||||
LJPeople := GetPeople;
|
||||
try
|
||||
for I := 0 to LJPeople.Count - 1 do
|
||||
begin
|
||||
if LJPeople[I].GUID = GUID then
|
||||
begin
|
||||
LJPeople.Delete(i);
|
||||
break;
|
||||
end;
|
||||
end;
|
||||
TFile.WriteAllText(DATAFILE, GetDefaultSerializer.SerializeCollection(LJPeople));
|
||||
finally
|
||||
LJPeople.Free;
|
||||
end;
|
||||
finally
|
||||
_CS.Leave;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TPeopleDAL.GetDevicesList: TDeviceList;
|
||||
begin
|
||||
Result := TDeviceList.Create(true);
|
||||
Result.Add(TDevice.Create('smartphone', false));
|
||||
Result.Add(TDevice.Create('dumbphone', false));
|
||||
Result.Add(TDevice.Create('laptop', false));
|
||||
Result.Add(TDevice.Create('desktop', false));
|
||||
end;
|
||||
|
||||
function TPeopleDAL.GetPeople: TPeople;
|
||||
var
|
||||
LData: string;
|
||||
begin
|
||||
_CS.Enter;
|
||||
try
|
||||
Result := TPeople.Create;
|
||||
if TFile.Exists(DATAFILE) then
|
||||
LData := TFile.ReadAllText(DATAFILE).Trim;
|
||||
if not LData.IsEmpty then
|
||||
begin
|
||||
GetDefaultSerializer.DeserializeCollection(LData, Result, TPerson);
|
||||
end;
|
||||
finally
|
||||
_CS.Leave;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TPeopleDAL.GetPersonByGUID(GUID: string): TPerson;
|
||||
var
|
||||
lPeople: TPeople;
|
||||
lPerson: TPerson;
|
||||
begin
|
||||
Result := nil;
|
||||
lPeople := GetPeople;
|
||||
try
|
||||
for lPerson in lPeople do
|
||||
begin
|
||||
if lPerson.GUID = GUID then
|
||||
begin
|
||||
Result := lPeople.Extract(lPerson);
|
||||
Break;
|
||||
end;
|
||||
end;
|
||||
finally
|
||||
lPeople.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
{ TPerson }
|
||||
|
||||
procedure TPerson.SetAge(const Value: Integer);
|
||||
begin
|
||||
FAge := Value;
|
||||
end;
|
||||
|
||||
procedure TPerson.SetFirstName(const Value: string);
|
||||
begin
|
||||
FFirstName := Value;
|
||||
end;
|
||||
|
||||
procedure TPerson.SetGUID(const Value: string);
|
||||
begin
|
||||
FGUID := Value;
|
||||
end;
|
||||
|
||||
procedure TPerson.SetItems(const Value: string);
|
||||
begin
|
||||
FItems := Value;
|
||||
end;
|
||||
|
||||
procedure TPerson.SetLastName(const Value: string);
|
||||
begin
|
||||
FLastName := Value;
|
||||
end;
|
||||
|
||||
{ TDevice }
|
||||
|
||||
constructor TDevice.Create(aDeviceName: string; aSelected: Boolean);
|
||||
begin
|
||||
inherited Create;
|
||||
fDeviceName := aDeviceName;
|
||||
fSelected := aSelected;
|
||||
end;
|
||||
|
||||
{ TDeviceList }
|
||||
|
||||
function TDeviceList.Contains(const aDeviceName: string): Boolean;
|
||||
begin
|
||||
Result := IndexOf(aDeviceName) > -1;
|
||||
end;
|
||||
|
||||
function TDeviceList.IndexOf(const aDeviceName: string): Integer;
|
||||
var
|
||||
I: Integer;
|
||||
begin
|
||||
Result := -1;
|
||||
for I := 0 to Self.Count - 1 do
|
||||
begin
|
||||
if SameText(Self[i].DeviceName, aDeviceName) then
|
||||
Exit(i);
|
||||
end;
|
||||
end;
|
||||
|
||||
initialization
|
||||
|
||||
_CS := TCriticalSection.Create;
|
||||
|
||||
finalization
|
||||
|
||||
_CS.Free;
|
||||
|
||||
end.
|
542
samples/serversideviews_lua/MVCFramework.View.Renderers.Lua.pas
Normal file
542
samples/serversideviews_lua/MVCFramework.View.Renderers.Lua.pas
Normal file
@ -0,0 +1,542 @@
|
||||
// ***************************************************************************
|
||||
//
|
||||
// Delphi MVC Framework
|
||||
//
|
||||
// Copyright (c) 2010-2022 Daniele Teti and the DMVCFramework Team
|
||||
//
|
||||
// https://github.com/danieleteti/delphimvcframework
|
||||
//
|
||||
// ***************************************************************************
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// ***************************************************************************
|
||||
|
||||
unit MVCFramework.View.Renderers.Lua;
|
||||
|
||||
{$WARNINGS OFF}
|
||||
{$I dmvcframework.inc}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
MVCFramework,
|
||||
MVCFramework.Commons,
|
||||
System.Generics.Collections,
|
||||
Data.DB,
|
||||
MVCFramework.View.Cache,
|
||||
MVCFramework.Serializer.JsonDataObjects,
|
||||
System.SysUtils, System.Classes;
|
||||
|
||||
type
|
||||
TMVCLuaViewEngine = class(TMVCBaseViewEngine)
|
||||
private
|
||||
function GetCompiledFileName(const FileName: string): string;
|
||||
public
|
||||
procedure Execute(const ViewName: string;
|
||||
const OutputStream: TStream); override;
|
||||
protected
|
||||
function IsCompiledVersionUpToDate(const FileName, CompiledFileName: string)
|
||||
: Boolean; override;
|
||||
public const
|
||||
LOG_FILE_NAME = 'mvc_%s.log';
|
||||
DEFAULT_VIEW_EXT = 'elua';
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
MVCFramework.DuckTyping,
|
||||
LuaBind,
|
||||
LuaBind.Filters.Text,
|
||||
LuaBind.CustomType.DataSet,
|
||||
LuaBind.Intf,
|
||||
System.ioutils,
|
||||
System.JSON,
|
||||
JsonDataObjects, MVCFramework.Serializer.Commons, System.Rtti;
|
||||
|
||||
//function __lua_form_parameter(L: Plua_State): Integer; cdecl;
|
||||
//var
|
||||
// parname: string;
|
||||
// res: string;
|
||||
// // rq: TMVCWebRequest;
|
||||
// p: Pointer;
|
||||
// WebContext: TWebContext;
|
||||
//begin
|
||||
// if lua_gettop(L) <> 2 then
|
||||
// begin
|
||||
// luaL_error(L, PAnsiChar('Wrong parameters number'));
|
||||
// Exit;
|
||||
// end;
|
||||
// parname := TLuaValue.GetTValueFromLuaValueType(TLuaValueType.lvtString, L,
|
||||
// -1).AsString;
|
||||
// lua_getfield(L, -2, '__self');
|
||||
// p := lua_topointer(L, -1);
|
||||
// WebContext := TWebContext(TObject(p));
|
||||
// res := WebContext.Request.Params[parname];
|
||||
// TLuaUtils.PushTValue(L, res);
|
||||
// Result := 1;
|
||||
//end;
|
||||
//
|
||||
//function __lua_headers_get_all(L: Plua_State): Integer; cdecl;
|
||||
//var
|
||||
// // parname: string;
|
||||
// res: string;
|
||||
// // rq: TMVCWebRequest;
|
||||
// p: Pointer;
|
||||
// WebContext: TWebContext;
|
||||
//begin
|
||||
// if lua_gettop(L) <> 1 then
|
||||
// begin
|
||||
// luaL_error(L, PAnsiChar('Wrong parameters number (0 expected)'));
|
||||
// Exit;
|
||||
// end;
|
||||
// lua_getfield(L, -1, '__self');
|
||||
// p := lua_topointer(L, -1);
|
||||
// WebContext := TWebContext(TObject(p));
|
||||
// res := WebContext.Request.RawWebRequest.Content;
|
||||
// { TODO -oDaniele -cGeneral : Do not works }
|
||||
// TLuaUtils.PushTValue(L, res);
|
||||
// Result := 1;
|
||||
//end;
|
||||
//
|
||||
//function __lua_headers(L: Plua_State): Integer; cdecl;
|
||||
//var
|
||||
// parname: string;
|
||||
// res: string;
|
||||
// // rq: TMVCWebRequest;
|
||||
// p: Pointer;
|
||||
// WebContext: TWebContext;
|
||||
// // parvalue: string;
|
||||
//begin
|
||||
// if (lua_gettop(L) <> 2) then
|
||||
// begin
|
||||
// luaL_error(L, PAnsiChar('Wrong parameters number'));
|
||||
// Exit;
|
||||
// end;
|
||||
//
|
||||
// parname := TLuaValue.GetTValueFromLuaValueType(TLuaValueType.lvtString, L,
|
||||
// -1).AsString;
|
||||
// lua_getfield(L, -2, '__self');
|
||||
// p := lua_topointer(L, -1);
|
||||
// WebContext := TWebContext(TObject(p));
|
||||
// res := WebContext.Request.RawWebRequest.GetFieldByName(parname.ToUpper);
|
||||
// TLuaUtils.PushTValue(L, res);
|
||||
// Result := 1;
|
||||
//end;
|
||||
//
|
||||
//function __lua_set_http_code(L: Plua_State): Integer; cdecl;
|
||||
//var
|
||||
// // parname: string;
|
||||
// // res: string;
|
||||
// // rq: TMVCWebRequest;
|
||||
// p: Pointer;
|
||||
// WebContext: TWebContext;
|
||||
// // parvalue: string;
|
||||
// errocode: Integer;
|
||||
//begin
|
||||
// if lua_gettop(L) <> 2 then
|
||||
// begin
|
||||
// luaL_error(L, PAnsiChar('Wrong parameters number'));
|
||||
// Exit;
|
||||
// end;
|
||||
//
|
||||
// // setting the http return code request:http_code(404)
|
||||
// errocode := TLuaValue.GetTValueFromLuaValueType(TLuaValueType.lvtInteger, L,
|
||||
// -1).AsInteger;
|
||||
// lua_getfield(L, -2, '__self');
|
||||
// p := lua_topointer(L, -1);
|
||||
//
|
||||
// WebContext := TWebContext(TObject(p));
|
||||
// WebContext.Response.StatusCode := errocode;
|
||||
// Result := 0;
|
||||
//end;
|
||||
//
|
||||
//function __lua_set_response_headers(L: Plua_State): Integer; cdecl;
|
||||
//var
|
||||
// parname: string;
|
||||
// // res: string;
|
||||
// // rq: TMVCWebRequest;
|
||||
// p: Pointer;
|
||||
// WebContext: TWebContext;
|
||||
// parvalue: string;
|
||||
//begin
|
||||
// if lua_gettop(L) <> 3 then
|
||||
// begin
|
||||
// luaL_error(L, PAnsiChar('Wrong parameters number'));
|
||||
// Exit;
|
||||
// end;
|
||||
//
|
||||
// // setting an header request:headers(name, newvalue)
|
||||
// parname := TLuaValue.GetTValueFromLuaValueType(TLuaValueType.lvtString, L,
|
||||
// -2).AsString;
|
||||
// parvalue := TLuaValue.GetTValueFromLuaValueType(TLuaValueType.lvtString, L,
|
||||
// -1).AsString;
|
||||
// lua_getfield(L, -3, '__self');
|
||||
// p := lua_topointer(L, -1);
|
||||
//
|
||||
// WebContext := TWebContext(TObject(p));
|
||||
// WebContext.Response.CustomHeaders.Values[parname] := parvalue;
|
||||
// Result := 0;
|
||||
//end;
|
||||
//
|
||||
//function __lua_post_parameter(L: Plua_State): Integer; cdecl;
|
||||
//var
|
||||
// parname: string;
|
||||
// res: string;
|
||||
// // rq: TMVCWebRequest;
|
||||
// p: Pointer;
|
||||
// WebContext: TWebContext;
|
||||
//begin
|
||||
// if lua_gettop(L) <> 2 then
|
||||
// begin
|
||||
// luaL_error(L, PAnsiChar('Wrong parameters number'));
|
||||
// Exit;
|
||||
// end;
|
||||
// parname := TLuaValue.GetTValueFromLuaValueType(TLuaValueType.lvtString, L,
|
||||
// -1).AsString;
|
||||
// lua_getfield(L, -2, '__self');
|
||||
// p := lua_topointer(L, -1);
|
||||
// WebContext := TWebContext(TObject(p));
|
||||
// res := WebContext.Request.ContentParam(parname);
|
||||
// TLuaUtils.PushTValue(L, res);
|
||||
// Result := 1;
|
||||
//end;
|
||||
//
|
||||
//function __lua_set_header_field(L: Plua_State): Integer; cdecl;
|
||||
//var
|
||||
// parname: string;
|
||||
// // res: string;
|
||||
// // rq: TMVCWebRequest;
|
||||
// p: Pointer;
|
||||
// WebContext: TWebContext;
|
||||
// parvalue: string;
|
||||
//begin
|
||||
// if lua_gettop(L) <> 2 then
|
||||
// begin
|
||||
// luaL_error(L, PAnsiChar('Wrong parameters number'));
|
||||
// Exit;
|
||||
// end;
|
||||
// parname := TLuaValue.GetTValueFromLuaValueType(TLuaValueType.lvtString, L,
|
||||
// -2).AsString;
|
||||
// parvalue := TLuaValue.GetTValueFromLuaValueType(TLuaValueType.lvtString, L,
|
||||
// -1).AsString;
|
||||
// lua_getfield(L, -3, '__self');
|
||||
// p := lua_topointer(L, -1);
|
||||
// WebContext := TWebContext(TObject(p));
|
||||
// WebContext.Response.SetCustomHeader(parname, parvalue);
|
||||
// Result := 0;
|
||||
//end;
|
||||
//
|
||||
//function __lua_get_parameter(L: Plua_State): Integer; cdecl;
|
||||
//var
|
||||
// parname: string;
|
||||
// res: string;
|
||||
// // rq: TMVCWebRequest;
|
||||
// p: Pointer;
|
||||
// WebContext: TWebContext;
|
||||
//begin
|
||||
// if lua_gettop(L) <> 2 then
|
||||
// begin
|
||||
// luaL_error(L, PAnsiChar('Wrong parameters number'));
|
||||
// Exit;
|
||||
// end;
|
||||
// parname := TLuaValue.GetTValueFromLuaValueType(TLuaValueType.lvtString, L,
|
||||
// -1).AsString;
|
||||
// lua_getfield(L, -2, '__self');
|
||||
// p := lua_topointer(L, -1);
|
||||
// WebContext := TWebContext(TObject(p));
|
||||
// res := WebContext.Request.QueryStringParam(parname);
|
||||
// TLuaUtils.PushTValue(L, res);
|
||||
// Result := 1;
|
||||
//end;
|
||||
|
||||
// function __lua_stream_out(L: Plua_State): Integer; cdecl;
|
||||
// var
|
||||
// p: Pointer;
|
||||
// WebContext: TWebContext;
|
||||
// v: string;
|
||||
// begin
|
||||
// if lua_gettop(L) <> 2 then
|
||||
// begin
|
||||
// luaL_error(L, PAnsiChar('Wrong parameters number'));
|
||||
// Exit;
|
||||
// end;
|
||||
// v := lua_tostring(L, -1);
|
||||
// lua_getfield(L, -2, '__self');
|
||||
// p := lua_topointer(L, -1);
|
||||
//
|
||||
// WebContext := TWebContext(TObject(p));
|
||||
// raise Exception.Create('WIP');
|
||||
// // TStringBuilder(WebContext.ReservedData).Append(v);
|
||||
// Result := 0;
|
||||
// end;
|
||||
|
||||
function __lua_stream_out(L: Plua_State): Integer; cdecl;
|
||||
var
|
||||
lPointerToWebContext, lPointerToStreamWriter: Pointer;
|
||||
WebContext: TWebContext;
|
||||
v: PAnsiChar;
|
||||
lParCount: Integer;
|
||||
lArr: TArray<Byte>;
|
||||
begin
|
||||
lParCount := lua_gettop(L);
|
||||
if lua_gettop(L) <> 2 then
|
||||
begin
|
||||
luaL_error(L, PAnsiChar('Wrong parameters number'));
|
||||
Exit;
|
||||
end;
|
||||
v := lua_tostring(L, -1);
|
||||
|
||||
lPointerToStreamWriter := lua_topointer(L, -2);
|
||||
// lua_getfield(L, -2, '__stringbuilder');
|
||||
// lPointerToStringBuilder := lua_topointer(L, -1);
|
||||
|
||||
// lua_getfield(L, -2, '__self');
|
||||
// lPointerToWebContext := lua_topointer(L, -1);
|
||||
//TWebContext(TObject(lPointerToWebContext)).Response.Content := v;
|
||||
|
||||
// SetLength(lArr, Length(v));
|
||||
// CopyArray(lArr, v, TypeInfo(byte), Length(lArr));
|
||||
|
||||
TStreamWriter(lPointerToStreamWriter).Write(UTF8Encode(v));
|
||||
Result := 0;
|
||||
end;
|
||||
|
||||
|
||||
// var
|
||||
// s: string;
|
||||
// o: TObject;
|
||||
// C: TWebContext;
|
||||
// begin
|
||||
// if lua_gettop(L) <> 2 then
|
||||
// begin
|
||||
// luaL_error(L, PAnsiChar('Wrong parameters number'));
|
||||
// Exit;
|
||||
// end;
|
||||
//
|
||||
// if lua_isstring(L, - 1) = 1 then
|
||||
// begin
|
||||
// s := lua_tostring(L, - 1);
|
||||
// // lua_pop(L, 1);
|
||||
// end
|
||||
// else
|
||||
// begin
|
||||
// luaL_error(L, PAnsiChar('Type mismatch, expected String'));
|
||||
// Exit;
|
||||
// end;
|
||||
//
|
||||
// // if lua_islightuserdata(L, - 2) then
|
||||
// // begin
|
||||
// o := TObject(lua_topointer(L, - 2));
|
||||
// // lua_pop(L, 1);
|
||||
// // end
|
||||
// // else
|
||||
// // begin
|
||||
// // luaL_error(L, PAnsiChar('Type mismatch, expected LightUserData'));
|
||||
// // Exit;
|
||||
// // end;
|
||||
//
|
||||
// C := o as TWebContext;
|
||||
// TStringBuilder(C.ReservedData).Append(s);
|
||||
// Result := 0;
|
||||
// end;
|
||||
|
||||
{ TMVCEmbeddedLuaView }
|
||||
|
||||
procedure TMVCLuaViewEngine.Execute(const ViewName: string;
|
||||
const OutputStream: TStream);
|
||||
var
|
||||
Lua: TLuaEngine;
|
||||
k: string;
|
||||
LuaFilter: TLuaEmbeddedTextFilter;
|
||||
lViewFileName: String;
|
||||
_FFileName, CompiledFileName: string;
|
||||
v: string;
|
||||
sr: TStreamReader;
|
||||
DecodeJSONStrings: string;
|
||||
LuaRequestFunctions: TDictionary<string, lua_CFunction>;
|
||||
LuaResponseFunctions: TDictionary<string, lua_CFunction>;
|
||||
pn: string;
|
||||
lSer: TMVCJsonDataObjectsSerializer;
|
||||
lJSONStr: string;
|
||||
lJSONObj: TJDOJsonObject;
|
||||
lJSONArr: TJDOJsonArray;
|
||||
LuaResponseData: TDictionary<string, TValue>;
|
||||
ScriptOutputStringBuilder: TStreamWriter;
|
||||
begin
|
||||
ScriptOutputStringBuilder := TStreamWriter.Create(OutputStream, TEncoding.UTF8);
|
||||
try
|
||||
Lua := TLuaEngine.Create;
|
||||
try
|
||||
Lua.LoadExternalLibraries(TLuaDataSetExposerLibraries.Create);
|
||||
|
||||
lViewFileName := StringReplace(ViewName, '/', '\', [rfReplaceAll]);
|
||||
// $0.02 of normalization
|
||||
if lViewFileName = '\' then
|
||||
lViewFileName := '\index.' + DEFAULT_VIEW_EXT
|
||||
else
|
||||
lViewFileName := lViewFileName + '.' + DEFAULT_VIEW_EXT;
|
||||
|
||||
if DirectoryExists(Config.Value[TMVCConfigKey.ViewPath]) then
|
||||
_FFileName := ExpandFileName
|
||||
(IncludeTrailingPathDelimiter(Config.Value[TMVCConfigKey.ViewPath]) +
|
||||
lViewFileName)
|
||||
else
|
||||
_FFileName := ExpandFileName
|
||||
(IncludeTrailingPathDelimiter(GetApplicationFileNamePath +
|
||||
Config.Value[TMVCConfigKey.ViewPath]) + lViewFileName);
|
||||
lViewFileName := _FFileName;
|
||||
|
||||
if not FileExists(lViewFileName) then
|
||||
raise EMVCViewError.CreateFmt('View [%s.%s] not found',
|
||||
[ViewName, DEFAULT_VIEW_EXT])
|
||||
else
|
||||
begin
|
||||
DecodeJSONStrings := '';
|
||||
{ continuare da questo problema }
|
||||
if Assigned(ViewModel) then
|
||||
begin
|
||||
for k in ViewModel.Keys do
|
||||
begin
|
||||
if not(ViewModel[k] is TJDOJsonBaseObject) then
|
||||
begin
|
||||
lSer := TMVCJsonDataObjectsSerializer.Create(nil);
|
||||
try
|
||||
if TDuckTypedList.CanBeWrappedAsList(ViewModel[k]) then
|
||||
begin
|
||||
//List
|
||||
lJSONArr := TJDOJsonArray.Create;
|
||||
try
|
||||
lSer.ListToJsonArray(TDuckTypedList.Wrap(ViewModel[k],
|
||||
False), lJSONArr,
|
||||
TMVCSerializationType.stDefault, [], nil);
|
||||
lJSONStr := lJSONArr.ToJSON(True);
|
||||
finally
|
||||
lJSONArr.Free;
|
||||
end;
|
||||
end
|
||||
else if ViewModel[k] is System.JSON.TJSONValue then
|
||||
begin
|
||||
//System.JSON
|
||||
lJSONStr := System.JSON.TJSONValue(ViewModel[k]).ToJSON;
|
||||
end
|
||||
else
|
||||
begin //PODO
|
||||
lJSONObj := TJDOJsonObject.Create;
|
||||
try
|
||||
lSer.ObjectToJSONObject(ViewModel[k], lJSONObj,
|
||||
TMVCSerializationType.stDefault, []);
|
||||
lJSONStr := lJSONObj.ToJSON(True);
|
||||
finally
|
||||
lJSONObj.Free;
|
||||
end;
|
||||
end;
|
||||
finally
|
||||
lSer.Free;
|
||||
end;
|
||||
end
|
||||
else
|
||||
begin
|
||||
//JsonDataObjects
|
||||
lJSONStr := TJDOJsonBaseObject(ViewModel[k]).ToJSON(True);
|
||||
end;
|
||||
Lua.DeclareGlobalString(k, lJSONStr);
|
||||
DecodeJSONStrings := DecodeJSONStrings + sLineBreak + 'local ' +
|
||||
AnsiString(k) + ' = json.decode(' + AnsiString(k) + ')';
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
if Assigned(ViewDataSets) then
|
||||
begin
|
||||
for k in ViewDataSets.Keys do
|
||||
begin
|
||||
ExposeDataSet(Lua, ViewDataSets[k], ViewDataSets[k].Name);
|
||||
end;
|
||||
end;
|
||||
|
||||
Lua.DeclareGlobalString('__ROOT__', ExtractFilePath(ParamStr(0)));
|
||||
Lua.DeclareGlobalString('__log_file', LOG_FILE_NAME);
|
||||
Lua.DeclareGlobalLightUserData('__webcontext', WebContext);
|
||||
Lua.DeclareGlobalLightUserData('__stringbuilder', ScriptOutputStringBuilder);
|
||||
CompiledFileName := GetCompiledFileName(lViewFileName);
|
||||
|
||||
TMonitor.Enter(Self);
|
||||
try
|
||||
if true or (not IsCompiledVersionUpToDate(lViewFileName, CompiledFileName)) then
|
||||
begin
|
||||
LuaFilter := TLuaEmbeddedTextFilter.Create;
|
||||
try
|
||||
LuaFilter.OutputFunction := 'elua_out';
|
||||
LuaFilter.TemplateCode := TFile.ReadAllText(lViewFileName,
|
||||
TEncoding.ANSI);
|
||||
LuaFilter.Execute;
|
||||
TFile.WriteAllText(CompiledFileName,
|
||||
LuaFilter.LuaCode,
|
||||
TEncoding.ANSI);
|
||||
finally
|
||||
LuaFilter.Free;
|
||||
end;
|
||||
end;
|
||||
finally
|
||||
TMonitor.Exit(Self);
|
||||
end;
|
||||
|
||||
// read lua compiled code
|
||||
sr := TStreamReader.Create(TFileStream.Create(CompiledFileName,
|
||||
fmOpenRead or fmShareDenyNone), TEncoding.UTF8);
|
||||
try
|
||||
sr.OwnStream;
|
||||
v := sr.ReadToEnd;
|
||||
finally
|
||||
sr.Free;
|
||||
end;
|
||||
Lua.DeclareGlobalFunction('internal_elua_out', @__lua_stream_out);
|
||||
Lua.LoadScript('require "Lua.boot" ' + sLineBreak + DecodeJSONStrings + sLineBreak + v);
|
||||
// EXECUTE IT!!!
|
||||
Lua.Execute;
|
||||
finally
|
||||
Lua.Free;
|
||||
end;
|
||||
finally
|
||||
FreeAndNil(ScriptOutputStringBuilder);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TMVCLuaViewEngine.GetCompiledFileName(const FileName: string): string;
|
||||
var
|
||||
CompiledFileDir: string;
|
||||
CompiledFileName: string;
|
||||
begin
|
||||
// Exit(FileName + '.compiled');
|
||||
CompiledFileDir := IncludeTrailingPathDelimiter(ExtractFilePath(FileName) +
|
||||
'__compiled');
|
||||
CompiledFileName := CompiledFileDir +
|
||||
ChangeFileExt(ExtractFileName(FileName), '.lua');
|
||||
ForceDirectories(CompiledFileDir);
|
||||
Result := CompiledFileName;
|
||||
end;
|
||||
|
||||
function TMVCLuaViewEngine.IsCompiledVersionUpToDate(const FileName,
|
||||
CompiledFileName: string): Boolean;
|
||||
var
|
||||
dt1: TDateTime;
|
||||
dt2: TDateTime;
|
||||
begin
|
||||
dt2 := 0;
|
||||
FileAge(FileName, dt1);
|
||||
FileAge(CompiledFileName, dt2);
|
||||
Result := dt1 < dt2;
|
||||
end;
|
||||
|
||||
end.
|
59
samples/serversideviews_lua/ServerSideViewsLua.dpr
Normal file
59
samples/serversideviews_lua/ServerSideViewsLua.dpr
Normal file
@ -0,0 +1,59 @@
|
||||
program ServerSideViewsLua;
|
||||
|
||||
{$APPTYPE CONSOLE}
|
||||
|
||||
|
||||
uses
|
||||
System.SysUtils,
|
||||
{$IFDEF MSWINDOWS}
|
||||
Winapi.ShellAPI,
|
||||
Winapi.Windows,
|
||||
{$ENDIF }
|
||||
IdHTTPWebBrokerBridge,
|
||||
Web.WebReq,
|
||||
Web.WebBroker,
|
||||
WebModuleU in 'WebModuleU.pas' {WebModule1: TWebModule},
|
||||
WebSiteControllerU in 'WebSiteControllerU.pas',
|
||||
DAL in 'DAL.pas',
|
||||
MyDataModuleU in '..\renders\MyDataModuleU.pas' {MyDataModule: TDataModule},
|
||||
MVCFramework.View.Renderers.Lua in 'MVCFramework.View.Renderers.Lua.pas';
|
||||
|
||||
{$R *.res}
|
||||
|
||||
|
||||
procedure RunServer(APort: Integer);
|
||||
var
|
||||
LServer: TIdHTTPWebBrokerBridge;
|
||||
begin
|
||||
ReportMemoryLeaksOnShutdown := True;
|
||||
Writeln(Format('Starting HTTP Server on port %d', [APort]));
|
||||
LServer := TIdHTTPWebBrokerBridge.Create(nil);
|
||||
try
|
||||
LServer.DefaultPort := APort;
|
||||
LServer.Active := True;
|
||||
Writeln('Press RETURN to stop the server');
|
||||
|
||||
{$IFDEF MSWINDOWS}
|
||||
|
||||
ShellExecute(0, 'open', 'http://localhost:8080', nil, nil, SW_SHOW);
|
||||
|
||||
{$ENDIF}
|
||||
|
||||
ReadLn;
|
||||
finally
|
||||
LServer.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
begin
|
||||
ReportMemoryLeaksOnShutdown := True;
|
||||
try
|
||||
if WebRequestHandler <> nil then
|
||||
WebRequestHandler.WebModuleClass := WebModuleClass;
|
||||
RunServer(8080);
|
||||
except
|
||||
on E: Exception do
|
||||
Writeln(E.ClassName, ': ', E.Message);
|
||||
end;
|
||||
|
||||
end.
|
1184
samples/serversideviews_lua/ServerSideViewsLua.dproj
Normal file
1184
samples/serversideviews_lua/ServerSideViewsLua.dproj
Normal file
File diff suppressed because it is too large
Load Diff
13
samples/serversideviews_lua/WebModuleU.dfm
Normal file
13
samples/serversideviews_lua/WebModuleU.dfm
Normal file
@ -0,0 +1,13 @@
|
||||
object WebModule1: TWebModule1
|
||||
OnCreate = WebModuleCreate
|
||||
OnDestroy = WebModuleDestroy
|
||||
Actions = <
|
||||
item
|
||||
Default = True
|
||||
Name = 'DefaultHandler'
|
||||
PathInfo = '/'
|
||||
end>
|
||||
Height = 230
|
||||
Width = 415
|
||||
PixelsPerInch = 96
|
||||
end
|
64
samples/serversideviews_lua/WebModuleU.pas
Normal file
64
samples/serversideviews_lua/WebModuleU.pas
Normal file
@ -0,0 +1,64 @@
|
||||
unit WebModuleU;
|
||||
|
||||
interface
|
||||
|
||||
uses System.SysUtils, System.Classes, Web.HTTPApp, MVCFramework;
|
||||
|
||||
type
|
||||
TWebModule1 = class(TWebModule)
|
||||
procedure WebModuleCreate(Sender: TObject);
|
||||
procedure WebModuleDestroy(Sender: TObject);
|
||||
private
|
||||
FMVCEngine: TMVCEngine;
|
||||
{ Private declarations }
|
||||
public
|
||||
{ Public declarations }
|
||||
end;
|
||||
|
||||
var
|
||||
WebModuleClass: TComponentClass = TWebModule1;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
MVCFramework.View.Renderers.Mustache,
|
||||
WebSiteControllerU,
|
||||
MVCFramework.Commons,
|
||||
MVCFramework.Middleware.StaticFiles, MVCFramework.View.Renderers.Lua;
|
||||
|
||||
{ %CLASSGROUP 'Vcl.Controls.TControl' }
|
||||
|
||||
{$R *.dfm}
|
||||
|
||||
procedure TWebModule1.WebModuleCreate(Sender: TObject);
|
||||
begin
|
||||
FMVCEngine := TMVCEngine.Create(Self,
|
||||
procedure(Config: TMVCConfig)
|
||||
begin
|
||||
// session timeout (0 means session cookie)
|
||||
Config[TMVCConfigKey.SessionTimeout] := '0';
|
||||
// default content-type
|
||||
Config[TMVCConfigKey.DefaultContentType] :=
|
||||
TMVCConstants.DEFAULT_CONTENT_TYPE;
|
||||
// default content charset
|
||||
Config[TMVCConfigKey.DefaultContentCharset] :=
|
||||
TMVCConstants.DEFAULT_CONTENT_CHARSET;
|
||||
// unhandled actions are permitted?
|
||||
Config[TMVCConfigKey.AllowUnhandledAction] := 'false';
|
||||
// default view file extension
|
||||
Config[TMVCConfigKey.DefaultViewFileExtension] := 'mustache';
|
||||
// view path
|
||||
Config[TMVCConfigKey.ViewPath] := 'templates';
|
||||
// Enable Server Signature in response
|
||||
Config[TMVCConfigKey.ExposeServerSignature] := 'true';
|
||||
end)
|
||||
.AddController(TWebSiteController)
|
||||
.SetViewEngine(TMVCLuaViewEngine);
|
||||
end;
|
||||
|
||||
procedure TWebModule1.WebModuleDestroy(Sender: TObject);
|
||||
begin
|
||||
FMVCEngine.Free;
|
||||
end;
|
||||
|
||||
end.
|
218
samples/serversideviews_lua/WebSiteControllerU.pas
Normal file
218
samples/serversideviews_lua/WebSiteControllerU.pas
Normal file
@ -0,0 +1,218 @@
|
||||
unit WebSiteControllerU;
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
MVCFramework, System.Diagnostics, System.JSON, MVCFramework.Commons;
|
||||
|
||||
type
|
||||
|
||||
[MVCPath('/')]
|
||||
TWebSiteController = class(TMVCController)
|
||||
protected
|
||||
procedure OnBeforeAction(Context: TWebContext; const AActionNAme: string;
|
||||
var Handled: Boolean); override;
|
||||
procedure GeneratePeopleListAsCSV;
|
||||
public
|
||||
[MVCPath('/people')]
|
||||
[MVCHTTPMethods([httpGET])]
|
||||
[MVCProduces(TMVCMediaType.TEXT_HTML)]
|
||||
procedure PeopleList;
|
||||
|
||||
[MVCPath('/people')]
|
||||
[MVCHTTPMethods([httpGET])]
|
||||
[MVCProduces(TMVCMediaType.TEXT_CSV)]
|
||||
// RESTful API, requires ACCEPT=text/csv
|
||||
procedure ExportPeopleListAsCSV_API;
|
||||
|
||||
[MVCPath('/people/formats/csv')]
|
||||
[MVCHTTPMethods([httpGET])]
|
||||
// Route usable by the browser, doesn't requires ACCEPT=text/csv
|
||||
procedure ExportPeopleListAsCSV;
|
||||
|
||||
[MVCPath('/people')]
|
||||
[MVCHTTPMethods([httpPOST])]
|
||||
[MVCConsumes(TMVCMediaType.APPLICATION_FORM_URLENCODED)]
|
||||
procedure SavePerson;
|
||||
|
||||
[MVCPath('/deleteperson')]
|
||||
[MVCHTTPMethods([httpPOST])]
|
||||
[MVCConsumes(TMVCMediaType.APPLICATION_FORM_URLENCODED)]
|
||||
procedure DeletePerson;
|
||||
|
||||
[MVCPath('/new')]
|
||||
[MVCHTTPMethods([httpGET])]
|
||||
[MVCProduces(TMVCMediaType.TEXT_HTML)]
|
||||
procedure NewPerson;
|
||||
|
||||
[MVCPath('/edit/($guid)')]
|
||||
[MVCHTTPMethods([httpGET])]
|
||||
[MVCProduces(TMVCMediaType.TEXT_HTML)]
|
||||
procedure EditPerson(guid: string);
|
||||
|
||||
[MVCPath('/')]
|
||||
[MVCHTTPMethods([httpGET])]
|
||||
[MVCProduces(TMVCMediaType.TEXT_HTML)]
|
||||
procedure Index;
|
||||
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
{ TWebSiteController }
|
||||
|
||||
uses DAL, System.SysUtils, Web.HTTPApp, JsonDataObjects;
|
||||
|
||||
procedure TWebSiteController.DeletePerson;
|
||||
var
|
||||
lGUID: string;
|
||||
LDAL: IPeopleDAL;
|
||||
begin
|
||||
lGUID := Context.Request.Params['guid'];
|
||||
LDAL := TServicesFactory.GetPeopleDAL;
|
||||
LDAL.DeleteByGUID(lGUID);
|
||||
Redirect('/people');
|
||||
end;
|
||||
|
||||
procedure TWebSiteController.EditPerson(guid: string);
|
||||
var
|
||||
LDAL: IPeopleDAL;
|
||||
lPerson: TPerson;
|
||||
lDevices: TDeviceList;
|
||||
lItem: TDevice;
|
||||
begin
|
||||
LDAL := TServicesFactory.GetPeopleDAL;
|
||||
lPerson := LDAL.GetPersonByGUID(guid);
|
||||
try
|
||||
lDevices := LDAL.GetDevicesList;
|
||||
try
|
||||
ViewData['person'] := lPerson;
|
||||
for lItem in lDevices do
|
||||
begin
|
||||
lItem.Selected := lPerson.Items.Contains(lItem.DeviceName);
|
||||
end;
|
||||
ViewData['deviceslist'] := lDevices;
|
||||
LoadView(['header', 'editperson', 'footer']);
|
||||
RenderResponseStream;
|
||||
finally
|
||||
lDevices.Free;
|
||||
end;
|
||||
finally
|
||||
lPerson.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TWebSiteController.ExportPeopleListAsCSV;
|
||||
begin
|
||||
GeneratePeopleListAsCSV;
|
||||
// define the correct behaviour to download the csv inside the browser
|
||||
ContentType := TMVCMediaType.TEXT_CSV;
|
||||
Context.Response.CustomHeaders.Values['Content-Disposition'] :=
|
||||
'attachment; filename=people.csv';
|
||||
end;
|
||||
|
||||
procedure TWebSiteController.ExportPeopleListAsCSV_API;
|
||||
begin
|
||||
GeneratePeopleListAsCSV;
|
||||
end;
|
||||
|
||||
procedure TWebSiteController.GeneratePeopleListAsCSV;
|
||||
var
|
||||
LDAL: IPeopleDAL;
|
||||
lPeople: TPeople;
|
||||
begin
|
||||
LDAL := TServicesFactory.GetPeopleDAL;
|
||||
lPeople := LDAL.GetPeople;
|
||||
try
|
||||
ViewData['people'] := lPeople;
|
||||
LoadView(['people_header.csv', 'people_list.csv']);
|
||||
RenderResponseStream; // rember to call RenderResponseStream!!!
|
||||
finally
|
||||
lPeople.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TWebSiteController.Index;
|
||||
begin
|
||||
Redirect('/people');
|
||||
end;
|
||||
|
||||
procedure TWebSiteController.NewPerson;
|
||||
var
|
||||
lDAL: IPeopleDAL;
|
||||
lDevices: TDeviceList;
|
||||
lJSONPerson: TJDOJSONObject;
|
||||
begin
|
||||
lDAL := TServicesFactory.GetPeopleDAL;
|
||||
lDevices := LDAL.GetDevicesList;
|
||||
try
|
||||
ViewData['deviceslist'] := lDevices;
|
||||
lJSONPerson := TJDOJsonObject.Create;
|
||||
try
|
||||
lJSONPerson.S['guid'] := '';
|
||||
lJSONPerson.S['first_name'] := '';
|
||||
lJSONPerson.S['last_name'] := '';
|
||||
lJSONPerson.S['age'] := '';
|
||||
ViewData['person'] := lJSONPerson;
|
||||
LoadView(['header', 'editperson', 'footer']);
|
||||
RenderResponseStream;
|
||||
finally
|
||||
lJSONPerson.Free;
|
||||
end;
|
||||
finally
|
||||
lDevices.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TWebSiteController.OnBeforeAction(Context: TWebContext;
|
||||
const AActionNAme: string; var Handled: Boolean);
|
||||
begin
|
||||
inherited;
|
||||
ContentType := 'text/html';
|
||||
Handled := False;
|
||||
end;
|
||||
|
||||
procedure TWebSiteController.PeopleList;
|
||||
var
|
||||
LDAL: IPeopleDAL;
|
||||
lPeople: TPeople;
|
||||
begin
|
||||
LDAL := TServicesFactory.GetPeopleDAL;
|
||||
lPeople := LDAL.GetPeople;
|
||||
try
|
||||
ViewData['people'] := lPeople;
|
||||
LoadView(['header', 'people_list', 'footer']);
|
||||
RenderResponseStream; // rember to call RenderResponseStream!!!
|
||||
finally
|
||||
lPeople.Free;
|
||||
end;
|
||||
|
||||
end;
|
||||
|
||||
procedure TWebSiteController.SavePerson;
|
||||
var
|
||||
LFirstName: string;
|
||||
LLastName: string;
|
||||
LAge: string;
|
||||
LPeopleDAL: IPeopleDAL;
|
||||
lDevices: TArray<string>;
|
||||
begin
|
||||
LFirstName := Context.Request.Params['first_name'].Trim;
|
||||
LLastName := Context.Request.Params['last_name'].Trim;
|
||||
LAge := Context.Request.Params['age'];
|
||||
lDevices := Context.Request.ParamsMulti['items'];
|
||||
|
||||
if LFirstName.IsEmpty or LLastName.IsEmpty or LAge.IsEmpty then
|
||||
begin
|
||||
{ TODO -oDaniele -cGeneral : Show how to properly render an exception }
|
||||
raise EMVCException.Create('Invalid data',
|
||||
'First name, last name and age are not optional', 0);
|
||||
end;
|
||||
|
||||
LPeopleDAL := TServicesFactory.GetPeopleDAL;
|
||||
LPeopleDAL.AddPerson(LFirstName, LLastName, LAge.ToInteger(), lDevices);
|
||||
|
||||
Redirect('/people');
|
||||
end;
|
||||
|
||||
end.
|
20
samples/serversideviews_lua/bin/Lua/boot.lua
Normal file
20
samples/serversideviews_lua/bin/Lua/boot.lua
Normal file
@ -0,0 +1,20 @@
|
||||
dump = require "Lua.lib.inspect"
|
||||
json = require "Lua.lib.dkjson"
|
||||
|
||||
function elua_out(value)
|
||||
internal_elua_out(__stringbuilder, tostring(value))
|
||||
end
|
||||
|
||||
function htmlize(s)
|
||||
s = s:gsub('&', '&')
|
||||
s = s:gsub('<', '<')
|
||||
s = s:gsub('>', '>')
|
||||
if s == ' ' then
|
||||
s = ' '
|
||||
end
|
||||
return s
|
||||
end
|
||||
|
||||
function dumppre(value)
|
||||
return '<pre style="padding: 10px; border: thin #a0a0a0 solid; background-color: #f0f0f0">' .. htmlize(dump(value)) .. '</pre>'
|
||||
end
|
29
samples/serversideviews_lua/bin/Lua/lib/datasets.lua
Normal file
29
samples/serversideviews_lua/bin/Lua/lib/datasets.lua
Normal file
@ -0,0 +1,29 @@
|
||||
function open_dataset(ud)
|
||||
_dataset_open(ud);
|
||||
m = {
|
||||
pointer = ud,
|
||||
eof = function()
|
||||
return _dataset_eof(ud)
|
||||
end,
|
||||
movenext = function()
|
||||
_dataset_move_next(ud)
|
||||
end,
|
||||
get = function(fieldname)
|
||||
return _dataset_field_by_name(ud, fieldname)
|
||||
end,
|
||||
close = function()
|
||||
_dataset_close(ud)
|
||||
end,
|
||||
first = function()
|
||||
_dataset_first(ud)
|
||||
end,
|
||||
is_open = function()
|
||||
return _dataset_is_open(ud)
|
||||
end,
|
||||
open = function()
|
||||
_dataset_open(ud)
|
||||
end
|
||||
};
|
||||
|
||||
return m;
|
||||
end
|
42
samples/serversideviews_lua/bin/Lua/lib/delphiobjects.lua
Normal file
42
samples/serversideviews_lua/bin/Lua/lib/delphiobjects.lua
Normal file
@ -0,0 +1,42 @@
|
||||
function connect_to_delphi_object(ud)
|
||||
m = {
|
||||
pointer = ud,
|
||||
};
|
||||
|
||||
meta = {
|
||||
__index = function (self, name)
|
||||
if _delphi_is_a_property(ud, name) then
|
||||
return _delphi_call_method(ud,name,{maxindex = -1})
|
||||
else
|
||||
return function(...)
|
||||
local parent_table = self
|
||||
local method_name = name
|
||||
local methodargs = {}
|
||||
local index = 0
|
||||
for k,v in ipairs(arg) do
|
||||
methodargs['_'..tostring(index)] = v
|
||||
index = index + 1
|
||||
end
|
||||
methodargs['maxindex'] = index - 1
|
||||
logger:debug("Ecco i parametri:")
|
||||
logger:debug(methodargs)
|
||||
return _delphi_call_method(ud,method_name,methodargs)
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
__newindex = function (self, key, value)
|
||||
logger:debug("setting "..key.." to "..tostring(value))
|
||||
return _delphi_set_property(ud, key, value)
|
||||
end
|
||||
}
|
||||
|
||||
setmetatable(m, meta);
|
||||
|
||||
return m;
|
||||
end
|
||||
|
||||
function create_delphi_object(ClassName, ...)
|
||||
return connect_to_delphi_object(_delphi_create_object(ClassName, ...))
|
||||
end
|
||||
|
803
samples/serversideviews_lua/bin/Lua/lib/dkjson.lua
Normal file
803
samples/serversideviews_lua/bin/Lua/lib/dkjson.lua
Normal file
@ -0,0 +1,803 @@
|
||||
-- Module options:
|
||||
local always_try_using_lpeg = false
|
||||
local register_global_module_table = false
|
||||
local global_module_name = 'json'
|
||||
|
||||
--[==[
|
||||
|
||||
David Kolf's JSON module for Lua 5.1/5.2
|
||||
========================================
|
||||
|
||||
*Version 2.3*
|
||||
|
||||
In the default configuration this module writes no global values, not even
|
||||
the module table. Import it using
|
||||
|
||||
json = require ("dkjson")
|
||||
|
||||
In environments where `require` or a similiar function are not available
|
||||
and you cannot receive the return value of the module, you can set the
|
||||
option `register_global_module_table` to `true`. The module table will
|
||||
then be saved in the global variable with the name given by the option
|
||||
`global_module_name`.
|
||||
|
||||
Exported functions and values:
|
||||
|
||||
`json.encode (object [, state])`
|
||||
--------------------------------
|
||||
|
||||
Create a string representing the object. `Object` can be a table,
|
||||
a string, a number, a boolean, `nil`, `json.null` or any object with
|
||||
a function `__tojson` in its metatable. A table can only use strings
|
||||
and numbers as keys and its values have to be valid objects as
|
||||
well. It raises an error for any invalid data types or reference
|
||||
cycles.
|
||||
|
||||
`state` is an optional table with the following fields:
|
||||
|
||||
- `indent`
|
||||
When `indent` (a boolean) is set, the created string will contain
|
||||
newlines and indentations. Otherwise it will be one long line.
|
||||
- `keyorder`
|
||||
`keyorder` is an array to specify the ordering of keys in the
|
||||
encoded output. If an object has keys which are not in this array
|
||||
they are written after the sorted keys.
|
||||
- `level`
|
||||
This is the initial level of indentation used when `indent` is
|
||||
set. For each level two spaces are added. When absent it is set
|
||||
to 0.
|
||||
- `buffer`
|
||||
`buffer` is an array to store the strings for the result so they
|
||||
can be concatenated at once. When it isn't given, the encode
|
||||
function will create it temporary and will return the
|
||||
concatenated result.
|
||||
- `bufferlen`
|
||||
When `bufferlen` is set, it has to be the index of the last
|
||||
element of `buffer`.
|
||||
- `tables`
|
||||
`tables` is a set to detect reference cycles. It is created
|
||||
temporary when absent. Every table that is currently processed
|
||||
is used as key, the value is `true`.
|
||||
|
||||
When `state.buffer` was set, the return value will be `true` on
|
||||
success. Without `state.buffer` the return value will be a string.
|
||||
|
||||
`json.decode (string [, position [, null]])`
|
||||
--------------------------------------------
|
||||
|
||||
Decode `string` starting at `position` or at 1 if `position` was
|
||||
omitted.
|
||||
|
||||
`null` is an optional value to be returned for null values. The
|
||||
default is `nil`, but you could set it to `json.null` or any other
|
||||
value.
|
||||
|
||||
The return values are the object or `nil`, the position of the next
|
||||
character that doesn't belong to the object, and in case of errors
|
||||
an error message.
|
||||
|
||||
Two metatables are created. Every array or object that is decoded gets
|
||||
a metatable with the `__jsontype` field set to either `array` or
|
||||
`object`. If you want to provide your own metatables use the syntax
|
||||
|
||||
json.decode (string, position, null, objectmeta, arraymeta)
|
||||
|
||||
To prevent the assigning of metatables pass `nil`:
|
||||
|
||||
json.decode (string, position, null, nil)
|
||||
|
||||
`<metatable>.__jsonorder`
|
||||
-------------------------
|
||||
|
||||
`__jsonorder` can overwrite the `keyorder` for a specific table.
|
||||
|
||||
`<metatable>.__jsontype`
|
||||
------------------------
|
||||
|
||||
`__jsontype` can be either `"array"` or `"object"`. This value is only
|
||||
checked for empty tables. (The default for empty tables is `"array"`).
|
||||
|
||||
`<metatable>.__tojson (self, state)`
|
||||
------------------------------------
|
||||
|
||||
You can provide your own `__tojson` function in a metatable. In this
|
||||
function you can either add directly to the buffer and return true,
|
||||
or you can return a string. On errors nil and a message should be
|
||||
returned.
|
||||
|
||||
`json.null`
|
||||
-----------
|
||||
|
||||
You can use this value for setting explicit `null` values.
|
||||
|
||||
`json.version`
|
||||
--------------
|
||||
|
||||
Set to `"dkjson 2.3"`.
|
||||
|
||||
`json.quotestring (string)`
|
||||
---------------------------
|
||||
|
||||
Quote a UTF-8 string and escape critical characters using JSON
|
||||
escape sequences. This function is only necessary when you build
|
||||
your own `__tojson` functions.
|
||||
|
||||
`json.addnewline (state)`
|
||||
-------------------------
|
||||
|
||||
When `state.indent` is set, add a newline to `state.buffer` and spaces
|
||||
according to `state.level`.
|
||||
|
||||
LPeg support
|
||||
------------
|
||||
|
||||
When the local configuration variable `always_try_using_lpeg` is set,
|
||||
this module tries to load LPeg to replace the `decode` function. The
|
||||
speed increase is significant. You can get the LPeg module at
|
||||
<http://www.inf.puc-rio.br/~roberto/lpeg/>.
|
||||
When LPeg couldn't be loaded, the pure Lua functions stay active.
|
||||
|
||||
In case you don't want this module to require LPeg on its own,
|
||||
disable the option `always_try_using_lpeg` in the options section at
|
||||
the top of the module.
|
||||
|
||||
In this case you can later load LPeg support using
|
||||
|
||||
### `json.use_lpeg ()`
|
||||
|
||||
Require the LPeg module and replace the functions `quotestring` and
|
||||
and `decode` with functions that use LPeg patterns.
|
||||
This function returns the module table, so you can load the module
|
||||
using:
|
||||
|
||||
json = require "dkjson".use_lpeg()
|
||||
|
||||
Alternatively you can use `pcall` so the JSON module still works when
|
||||
LPeg isn't found.
|
||||
|
||||
json = require "dkjson"
|
||||
pcall (json.use_lpeg)
|
||||
|
||||
### `json.using_lpeg`
|
||||
|
||||
This variable is set to `true` when LPeg was loaded successfully.
|
||||
|
||||
---------------------------------------------------------------------
|
||||
|
||||
Contact
|
||||
-------
|
||||
|
||||
You can contact the author by sending an e-mail to 'david' at the
|
||||
domain 'dkolf.de'.
|
||||
|
||||
---------------------------------------------------------------------
|
||||
|
||||
*Copyright (C) 2010-2013 David Heiko Kolf*
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
<!-- This documentation can be parsed using Markdown to generate HTML.
|
||||
The source code is enclosed in a HTML comment so it won't be displayed
|
||||
by browsers, but it should be removed from the final HTML file as
|
||||
it isn't a valid HTML comment (and wastes space).
|
||||
-->
|
||||
|
||||
<!--]==]
|
||||
|
||||
-- global dependencies:
|
||||
local pairs, type, tostring, tonumber, getmetatable, setmetatable, rawset =
|
||||
pairs, type, tostring, tonumber, getmetatable, setmetatable, rawset
|
||||
local error, require, pcall, select = error, require, pcall, select
|
||||
local floor, huge = math.floor, math.huge
|
||||
local strrep, gsub, strsub, strbyte, strchar, strfind, strlen, strformat =
|
||||
string.rep, string.gsub, string.sub, string.byte, string.char,
|
||||
string.find, string.len, string.format
|
||||
local concat = table.concat
|
||||
|
||||
local json = { version = "dkjson 2.3" }
|
||||
|
||||
if register_global_module_table then
|
||||
_G[global_module_name] = json
|
||||
end
|
||||
|
||||
local _ENV = nil -- blocking globals in Lua 5.2
|
||||
|
||||
pcall (function()
|
||||
-- Enable access to blocked metatables.
|
||||
-- Don't worry, this module doesn't change anything in them.
|
||||
local debmeta = require "debug".getmetatable
|
||||
if debmeta then getmetatable = debmeta end
|
||||
end)
|
||||
|
||||
json.null = setmetatable ({}, {
|
||||
__tojson = function () return "null" end
|
||||
})
|
||||
|
||||
local function isarray (tbl)
|
||||
local max, n, arraylen = 0, 0, 0
|
||||
for k,v in pairs (tbl) do
|
||||
if k == 'n' and type(v) == 'number' then
|
||||
arraylen = v
|
||||
if v > max then
|
||||
max = v
|
||||
end
|
||||
else
|
||||
if type(k) ~= 'number' or k < 1 or floor(k) ~= k then
|
||||
return false
|
||||
end
|
||||
if k > max then
|
||||
max = k
|
||||
end
|
||||
n = n + 1
|
||||
end
|
||||
end
|
||||
if max > 10 and max > arraylen and max > n * 2 then
|
||||
return false -- don't create an array with too many holes
|
||||
end
|
||||
return true, max
|
||||
end
|
||||
|
||||
local escapecodes = {
|
||||
["\""] = "\\\"", ["\\"] = "\\\\", ["\b"] = "\\b", ["\f"] = "\\f",
|
||||
["\n"] = "\\n", ["\r"] = "\\r", ["\t"] = "\\t"
|
||||
}
|
||||
|
||||
local function escapeutf8 (uchar)
|
||||
local value = escapecodes[uchar]
|
||||
if value then
|
||||
return value
|
||||
end
|
||||
local a, b, c, d = strbyte (uchar, 1, 4)
|
||||
a, b, c, d = a or 0, b or 0, c or 0, d or 0
|
||||
if a <= 0x7f then
|
||||
value = a
|
||||
elseif 0xc0 <= a and a <= 0xdf and b >= 0x80 then
|
||||
value = (a - 0xc0) * 0x40 + b - 0x80
|
||||
elseif 0xe0 <= a and a <= 0xef and b >= 0x80 and c >= 0x80 then
|
||||
value = ((a - 0xe0) * 0x40 + b - 0x80) * 0x40 + c - 0x80
|
||||
elseif 0xf0 <= a and a <= 0xf7 and b >= 0x80 and c >= 0x80 and d >= 0x80 then
|
||||
value = (((a - 0xf0) * 0x40 + b - 0x80) * 0x40 + c - 0x80) * 0x40 + d - 0x80
|
||||
else
|
||||
return ""
|
||||
end
|
||||
if value <= 0xffff then
|
||||
return strformat ("\\u%.4x", value)
|
||||
elseif value <= 0x10ffff then
|
||||
-- encode as UTF-16 surrogate pair
|
||||
value = value - 0x10000
|
||||
local highsur, lowsur = 0xD800 + floor (value/0x400), 0xDC00 + (value % 0x400)
|
||||
return strformat ("\\u%.4x\\u%.4x", highsur, lowsur)
|
||||
else
|
||||
return ""
|
||||
end
|
||||
end
|
||||
|
||||
local function fsub (str, pattern, repl)
|
||||
-- gsub always builds a new string in a buffer, even when no match
|
||||
-- exists. First using find should be more efficient when most strings
|
||||
-- don't contain the pattern.
|
||||
if strfind (str, pattern) then
|
||||
return gsub (str, pattern, repl)
|
||||
else
|
||||
return str
|
||||
end
|
||||
end
|
||||
|
||||
local function quotestring (value)
|
||||
-- based on the regexp "escapable" in https://github.com/douglascrockford/JSON-js
|
||||
value = fsub (value, "[%z\1-\31\"\\\127]", escapeutf8)
|
||||
if strfind (value, "[\194\216\220\225\226\239]") then
|
||||
value = fsub (value, "\194[\128-\159\173]", escapeutf8)
|
||||
value = fsub (value, "\216[\128-\132]", escapeutf8)
|
||||
value = fsub (value, "\220\143", escapeutf8)
|
||||
value = fsub (value, "\225\158[\180\181]", escapeutf8)
|
||||
value = fsub (value, "\226\128[\140-\143\168-\175]", escapeutf8)
|
||||
value = fsub (value, "\226\129[\160-\175]", escapeutf8)
|
||||
value = fsub (value, "\239\187\191", escapeutf8)
|
||||
value = fsub (value, "\239\191[\176-\191]", escapeutf8)
|
||||
end
|
||||
return "\"" .. value .. "\""
|
||||
end
|
||||
json.quotestring = quotestring
|
||||
|
||||
local function addnewline2 (level, buffer, buflen)
|
||||
buffer[buflen+1] = "\n"
|
||||
buffer[buflen+2] = strrep (" ", level)
|
||||
buflen = buflen + 2
|
||||
return buflen
|
||||
end
|
||||
|
||||
function json.addnewline (state)
|
||||
if state.indent then
|
||||
state.bufferlen = addnewline2 (state.level or 0,
|
||||
state.buffer, state.bufferlen or #(state.buffer))
|
||||
end
|
||||
end
|
||||
|
||||
local encode2 -- forward declaration
|
||||
|
||||
local function addpair (key, value, prev, indent, level, buffer, buflen, tables, globalorder)
|
||||
local kt = type (key)
|
||||
if kt ~= 'string' and kt ~= 'number' then
|
||||
return nil, "type '" .. kt .. "' is not supported as a key by JSON."
|
||||
end
|
||||
if prev then
|
||||
buflen = buflen + 1
|
||||
buffer[buflen] = ","
|
||||
end
|
||||
if indent then
|
||||
buflen = addnewline2 (level, buffer, buflen)
|
||||
end
|
||||
buffer[buflen+1] = quotestring (key)
|
||||
buffer[buflen+2] = ":"
|
||||
return encode2 (value, indent, level, buffer, buflen + 2, tables, globalorder)
|
||||
end
|
||||
|
||||
encode2 = function (value, indent, level, buffer, buflen, tables, globalorder)
|
||||
local valtype = type (value)
|
||||
local valmeta = getmetatable (value)
|
||||
valmeta = type (valmeta) == 'table' and valmeta -- only tables
|
||||
local valtojson = valmeta and valmeta.__tojson
|
||||
if valtojson then
|
||||
if tables[value] then
|
||||
return nil, "reference cycle"
|
||||
end
|
||||
tables[value] = true
|
||||
local state = {
|
||||
indent = indent, level = level, buffer = buffer,
|
||||
bufferlen = buflen, tables = tables, keyorder = globalorder
|
||||
}
|
||||
local ret, msg = valtojson (value, state)
|
||||
if not ret then return nil, msg end
|
||||
tables[value] = nil
|
||||
buflen = state.bufferlen
|
||||
if type (ret) == 'string' then
|
||||
buflen = buflen + 1
|
||||
buffer[buflen] = ret
|
||||
end
|
||||
elseif value == nil then
|
||||
buflen = buflen + 1
|
||||
buffer[buflen] = "null"
|
||||
elseif valtype == 'number' then
|
||||
local s
|
||||
if value ~= value or value >= huge or -value >= huge then
|
||||
-- This is the behaviour of the original JSON implementation.
|
||||
s = "null"
|
||||
else
|
||||
s = tostring (value)
|
||||
end
|
||||
buflen = buflen + 1
|
||||
buffer[buflen] = s
|
||||
elseif valtype == 'boolean' then
|
||||
buflen = buflen + 1
|
||||
buffer[buflen] = value and "true" or "false"
|
||||
elseif valtype == 'string' then
|
||||
buflen = buflen + 1
|
||||
buffer[buflen] = quotestring (value)
|
||||
elseif valtype == 'table' then
|
||||
if tables[value] then
|
||||
return nil, "reference cycle"
|
||||
end
|
||||
tables[value] = true
|
||||
level = level + 1
|
||||
local isa, n = isarray (value)
|
||||
if n == 0 and valmeta and valmeta.__jsontype == 'object' then
|
||||
isa = false
|
||||
end
|
||||
local msg
|
||||
if isa then -- JSON array
|
||||
buflen = buflen + 1
|
||||
buffer[buflen] = "["
|
||||
for i = 1, n do
|
||||
buflen, msg = encode2 (value[i], indent, level, buffer, buflen, tables, globalorder)
|
||||
if not buflen then return nil, msg end
|
||||
if i < n then
|
||||
buflen = buflen + 1
|
||||
buffer[buflen] = ","
|
||||
end
|
||||
end
|
||||
buflen = buflen + 1
|
||||
buffer[buflen] = "]"
|
||||
else -- JSON object
|
||||
local prev = false
|
||||
buflen = buflen + 1
|
||||
buffer[buflen] = "{"
|
||||
local order = valmeta and valmeta.__jsonorder or globalorder
|
||||
if order then
|
||||
local used = {}
|
||||
n = #order
|
||||
for i = 1, n do
|
||||
local k = order[i]
|
||||
local v = value[k]
|
||||
if v then
|
||||
used[k] = true
|
||||
buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder)
|
||||
prev = true -- add a seperator before the next element
|
||||
end
|
||||
end
|
||||
for k,v in pairs (value) do
|
||||
if not used[k] then
|
||||
buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder)
|
||||
if not buflen then return nil, msg end
|
||||
prev = true -- add a seperator before the next element
|
||||
end
|
||||
end
|
||||
else -- unordered
|
||||
for k,v in pairs (value) do
|
||||
buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder)
|
||||
if not buflen then return nil, msg end
|
||||
prev = true -- add a seperator before the next element
|
||||
end
|
||||
end
|
||||
if indent then
|
||||
buflen = addnewline2 (level - 1, buffer, buflen)
|
||||
end
|
||||
buflen = buflen + 1
|
||||
buffer[buflen] = "}"
|
||||
end
|
||||
tables[value] = nil
|
||||
else
|
||||
return nil, "type '" .. valtype .. "' is not supported by JSON."
|
||||
end
|
||||
return buflen
|
||||
end
|
||||
|
||||
function json.encode (value, state)
|
||||
state = state or {}
|
||||
local oldbuffer = state.buffer
|
||||
local buffer = oldbuffer or {}
|
||||
local ret, msg = encode2 (value, state.indent, state.level or 0,
|
||||
buffer, state.bufferlen or 0, state.tables or {}, state.keyorder)
|
||||
if not ret then
|
||||
error (msg, 2)
|
||||
elseif oldbuffer then
|
||||
state.bufferlen = ret
|
||||
return true
|
||||
else
|
||||
return concat (buffer)
|
||||
end
|
||||
end
|
||||
|
||||
local function loc (str, where)
|
||||
local line, pos, linepos = 1, 1, 0
|
||||
while true do
|
||||
pos = strfind (str, "\n", pos, true)
|
||||
if pos and pos < where then
|
||||
line = line + 1
|
||||
linepos = pos
|
||||
pos = pos + 1
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
return "line " .. line .. ", column " .. (where - linepos)
|
||||
end
|
||||
|
||||
local function unterminated (str, what, where)
|
||||
return nil, strlen (str) + 1, "unterminated " .. what .. " at " .. loc (str, where)
|
||||
end
|
||||
|
||||
local function scanwhite (str, pos)
|
||||
while true do
|
||||
pos = strfind (str, "%S", pos)
|
||||
if not pos then return nil end
|
||||
if strsub (str, pos, pos + 2) == "\239\187\191" then
|
||||
-- UTF-8 Byte Order Mark
|
||||
pos = pos + 3
|
||||
else
|
||||
return pos
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local escapechars = {
|
||||
["\""] = "\"", ["\\"] = "\\", ["/"] = "/", ["b"] = "\b", ["f"] = "\f",
|
||||
["n"] = "\n", ["r"] = "\r", ["t"] = "\t"
|
||||
}
|
||||
|
||||
local function unichar (value)
|
||||
if value < 0 then
|
||||
return nil
|
||||
elseif value <= 0x007f then
|
||||
return strchar (value)
|
||||
elseif value <= 0x07ff then
|
||||
return strchar (0xc0 + floor(value/0x40),
|
||||
0x80 + (floor(value) % 0x40))
|
||||
elseif value <= 0xffff then
|
||||
return strchar (0xe0 + floor(value/0x1000),
|
||||
0x80 + (floor(value/0x40) % 0x40),
|
||||
0x80 + (floor(value) % 0x40))
|
||||
elseif value <= 0x10ffff then
|
||||
return strchar (0xf0 + floor(value/0x40000),
|
||||
0x80 + (floor(value/0x1000) % 0x40),
|
||||
0x80 + (floor(value/0x40) % 0x40),
|
||||
0x80 + (floor(value) % 0x40))
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
local function scanstring (str, pos)
|
||||
local lastpos = pos + 1
|
||||
local buffer, n = {}, 0
|
||||
while true do
|
||||
local nextpos = strfind (str, "[\"\\]", lastpos)
|
||||
if not nextpos then
|
||||
return unterminated (str, "string", pos)
|
||||
end
|
||||
if nextpos > lastpos then
|
||||
n = n + 1
|
||||
buffer[n] = strsub (str, lastpos, nextpos - 1)
|
||||
end
|
||||
if strsub (str, nextpos, nextpos) == "\"" then
|
||||
lastpos = nextpos + 1
|
||||
break
|
||||
else
|
||||
local escchar = strsub (str, nextpos + 1, nextpos + 1)
|
||||
local value
|
||||
if escchar == "u" then
|
||||
value = tonumber (strsub (str, nextpos + 2, nextpos + 5), 16)
|
||||
if value then
|
||||
local value2
|
||||
if 0xD800 <= value and value <= 0xDBff then
|
||||
-- we have the high surrogate of UTF-16. Check if there is a
|
||||
-- low surrogate escaped nearby to combine them.
|
||||
if strsub (str, nextpos + 6, nextpos + 7) == "\\u" then
|
||||
value2 = tonumber (strsub (str, nextpos + 8, nextpos + 11), 16)
|
||||
if value2 and 0xDC00 <= value2 and value2 <= 0xDFFF then
|
||||
value = (value - 0xD800) * 0x400 + (value2 - 0xDC00) + 0x10000
|
||||
else
|
||||
value2 = nil -- in case it was out of range for a low surrogate
|
||||
end
|
||||
end
|
||||
end
|
||||
value = value and unichar (value)
|
||||
if value then
|
||||
if value2 then
|
||||
lastpos = nextpos + 12
|
||||
else
|
||||
lastpos = nextpos + 6
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if not value then
|
||||
value = escapechars[escchar] or escchar
|
||||
lastpos = nextpos + 2
|
||||
end
|
||||
n = n + 1
|
||||
buffer[n] = value
|
||||
end
|
||||
end
|
||||
if n == 1 then
|
||||
return buffer[1], lastpos
|
||||
elseif n > 1 then
|
||||
return concat (buffer), lastpos
|
||||
else
|
||||
return "", lastpos
|
||||
end
|
||||
end
|
||||
|
||||
local scanvalue -- forward declaration
|
||||
|
||||
local function scantable (what, closechar, str, startpos, nullval, objectmeta, arraymeta)
|
||||
local len = strlen (str)
|
||||
local tbl, n = {}, 0
|
||||
local pos = startpos + 1
|
||||
if what == 'object' then
|
||||
setmetatable (tbl, objectmeta)
|
||||
else
|
||||
setmetatable (tbl, arraymeta)
|
||||
end
|
||||
while true do
|
||||
pos = scanwhite (str, pos)
|
||||
if not pos then return unterminated (str, what, startpos) end
|
||||
local char = strsub (str, pos, pos)
|
||||
if char == closechar then
|
||||
return tbl, pos + 1
|
||||
end
|
||||
local val1, err
|
||||
val1, pos, err = scanvalue (str, pos, nullval, objectmeta, arraymeta)
|
||||
if err then return nil, pos, err end
|
||||
pos = scanwhite (str, pos)
|
||||
if not pos then return unterminated (str, what, startpos) end
|
||||
char = strsub (str, pos, pos)
|
||||
if char == ":" then
|
||||
if val1 == nil then
|
||||
return nil, pos, "cannot use nil as table index (at " .. loc (str, pos) .. ")"
|
||||
end
|
||||
pos = scanwhite (str, pos + 1)
|
||||
if not pos then return unterminated (str, what, startpos) end
|
||||
local val2
|
||||
val2, pos, err = scanvalue (str, pos, nullval, objectmeta, arraymeta)
|
||||
if err then return nil, pos, err end
|
||||
tbl[val1] = val2
|
||||
pos = scanwhite (str, pos)
|
||||
if not pos then return unterminated (str, what, startpos) end
|
||||
char = strsub (str, pos, pos)
|
||||
else
|
||||
n = n + 1
|
||||
tbl[n] = val1
|
||||
end
|
||||
if char == "," then
|
||||
pos = pos + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
scanvalue = function (str, pos, nullval, objectmeta, arraymeta)
|
||||
pos = pos or 1
|
||||
pos = scanwhite (str, pos)
|
||||
if not pos then
|
||||
return nil, strlen (str) + 1, "no valid JSON value (reached the end)"
|
||||
end
|
||||
local char = strsub (str, pos, pos)
|
||||
if char == "{" then
|
||||
return scantable ('object', "}", str, pos, nullval, objectmeta, arraymeta)
|
||||
elseif char == "[" then
|
||||
return scantable ('array', "]", str, pos, nullval, objectmeta, arraymeta)
|
||||
elseif char == "\"" then
|
||||
return scanstring (str, pos)
|
||||
else
|
||||
local pstart, pend = strfind (str, "^%-?[%d%.]+[eE]?[%+%-]?%d*", pos)
|
||||
if pstart then
|
||||
local number = tonumber (strsub (str, pstart, pend))
|
||||
if number then
|
||||
return number, pend + 1
|
||||
end
|
||||
end
|
||||
pstart, pend = strfind (str, "^%a%w*", pos)
|
||||
if pstart then
|
||||
local name = strsub (str, pstart, pend)
|
||||
if name == "true" then
|
||||
return true, pend + 1
|
||||
elseif name == "false" then
|
||||
return false, pend + 1
|
||||
elseif name == "null" then
|
||||
return nullval, pend + 1
|
||||
end
|
||||
end
|
||||
return nil, pos, "no valid JSON value at " .. loc (str, pos)
|
||||
end
|
||||
end
|
||||
|
||||
local function optionalmetatables(...)
|
||||
if select("#", ...) > 0 then
|
||||
return ...
|
||||
else
|
||||
return {__jsontype = 'object'}, {__jsontype = 'array'}
|
||||
end
|
||||
end
|
||||
|
||||
function json.decode (str, pos, nullval, ...)
|
||||
local objectmeta, arraymeta = optionalmetatables(...)
|
||||
return scanvalue (str, pos, nullval, objectmeta, arraymeta)
|
||||
end
|
||||
|
||||
function json.use_lpeg ()
|
||||
local g = require ("lpeg")
|
||||
local pegmatch = g.match
|
||||
local P, S, R, V = g.P, g.S, g.R, g.V
|
||||
|
||||
local function ErrorCall (str, pos, msg, state)
|
||||
if not state.msg then
|
||||
state.msg = msg .. " at " .. loc (str, pos)
|
||||
state.pos = pos
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function Err (msg)
|
||||
return g.Cmt (g.Cc (msg) * g.Carg (2), ErrorCall)
|
||||
end
|
||||
|
||||
local Space = (S" \n\r\t" + P"\239\187\191")^0
|
||||
|
||||
local PlainChar = 1 - S"\"\\\n\r"
|
||||
local EscapeSequence = (P"\\" * g.C (S"\"\\/bfnrt" + Err "unsupported escape sequence")) / escapechars
|
||||
local HexDigit = R("09", "af", "AF")
|
||||
local function UTF16Surrogate (match, pos, high, low)
|
||||
high, low = tonumber (high, 16), tonumber (low, 16)
|
||||
if 0xD800 <= high and high <= 0xDBff and 0xDC00 <= low and low <= 0xDFFF then
|
||||
return true, unichar ((high - 0xD800) * 0x400 + (low - 0xDC00) + 0x10000)
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
local function UTF16BMP (hex)
|
||||
return unichar (tonumber (hex, 16))
|
||||
end
|
||||
local U16Sequence = (P"\\u" * g.C (HexDigit * HexDigit * HexDigit * HexDigit))
|
||||
local UnicodeEscape = g.Cmt (U16Sequence * U16Sequence, UTF16Surrogate) + U16Sequence/UTF16BMP
|
||||
local Char = UnicodeEscape + EscapeSequence + PlainChar
|
||||
local String = P"\"" * g.Cs (Char ^ 0) * (P"\"" + Err "unterminated string")
|
||||
local Integer = P"-"^(-1) * (P"0" + (R"19" * R"09"^0))
|
||||
local Fractal = P"." * R"09"^0
|
||||
local Exponent = (S"eE") * (S"+-")^(-1) * R"09"^1
|
||||
local Number = (Integer * Fractal^(-1) * Exponent^(-1))/tonumber
|
||||
local Constant = P"true" * g.Cc (true) + P"false" * g.Cc (false) + P"null" * g.Carg (1)
|
||||
local SimpleValue = Number + String + Constant
|
||||
local ArrayContent, ObjectContent
|
||||
|
||||
-- The functions parsearray and parseobject parse only a single value/pair
|
||||
-- at a time and store them directly to avoid hitting the LPeg limits.
|
||||
local function parsearray (str, pos, nullval, state)
|
||||
local obj, cont
|
||||
local npos
|
||||
local t, nt = {}, 0
|
||||
repeat
|
||||
obj, cont, npos = pegmatch (ArrayContent, str, pos, nullval, state)
|
||||
if not npos then break end
|
||||
pos = npos
|
||||
nt = nt + 1
|
||||
t[nt] = obj
|
||||
until cont == 'last'
|
||||
return pos, setmetatable (t, state.arraymeta)
|
||||
end
|
||||
|
||||
local function parseobject (str, pos, nullval, state)
|
||||
local obj, key, cont
|
||||
local npos
|
||||
local t = {}
|
||||
repeat
|
||||
key, obj, cont, npos = pegmatch (ObjectContent, str, pos, nullval, state)
|
||||
if not npos then break end
|
||||
pos = npos
|
||||
t[key] = obj
|
||||
until cont == 'last'
|
||||
return pos, setmetatable (t, state.objectmeta)
|
||||
end
|
||||
|
||||
local Array = P"[" * g.Cmt (g.Carg(1) * g.Carg(2), parsearray) * Space * (P"]" + Err "']' expected")
|
||||
local Object = P"{" * g.Cmt (g.Carg(1) * g.Carg(2), parseobject) * Space * (P"}" + Err "'}' expected")
|
||||
local Value = Space * (Array + Object + SimpleValue)
|
||||
local ExpectedValue = Value + Space * Err "value expected"
|
||||
ArrayContent = Value * Space * (P"," * g.Cc'cont' + g.Cc'last') * g.Cp()
|
||||
local Pair = g.Cg (Space * String * Space * (P":" + Err "colon expected") * ExpectedValue)
|
||||
ObjectContent = Pair * Space * (P"," * g.Cc'cont' + g.Cc'last') * g.Cp()
|
||||
local DecodeValue = ExpectedValue * g.Cp ()
|
||||
|
||||
function json.decode (str, pos, nullval, ...)
|
||||
local state = {}
|
||||
state.objectmeta, state.arraymeta = optionalmetatables(...)
|
||||
local obj, retpos = pegmatch (DecodeValue, str, pos, nullval, state)
|
||||
if state.msg then
|
||||
return nil, state.pos, state.msg
|
||||
else
|
||||
return obj, retpos
|
||||
end
|
||||
end
|
||||
|
||||
-- use this function only once:
|
||||
json.use_lpeg = function () return json end
|
||||
|
||||
json.using_lpeg = true
|
||||
|
||||
return json -- so you can get the module using json = require "dkjson".use_lpeg()
|
||||
end
|
||||
|
||||
if always_try_using_lpeg then
|
||||
pcall (json.use_lpeg)
|
||||
end
|
||||
|
||||
return json
|
||||
|
||||
-->
|
5
samples/serversideviews_lua/bin/Lua/lib/html.lua
Normal file
5
samples/serversideviews_lua/bin/Lua/lib/html.lua
Normal file
@ -0,0 +1,5 @@
|
||||
html = {}
|
||||
|
||||
function html:input(type, value)
|
||||
return string.format('<input type="%s" value="%s"/>', type, value)
|
||||
end
|
243
samples/serversideviews_lua/bin/Lua/lib/inspect.lua
Normal file
243
samples/serversideviews_lua/bin/Lua/lib/inspect.lua
Normal file
@ -0,0 +1,243 @@
|
||||
-----------------------------------------------------------------------------------------------------------------------
|
||||
-- inspect.lua - v1.2.1 (2013-01)
|
||||
-- Enrique García Cota - enrique.garcia.cota [AT] gmail [DOT] com
|
||||
-- human-readable representations of tables.
|
||||
-- inspired by http://lua-users.org/wiki/TableSerialization
|
||||
-----------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
local inspect ={}
|
||||
inspect.__VERSION = '1.2.0'
|
||||
|
||||
-- Apostrophizes the string if it has quotes, but not aphostrophes
|
||||
-- Otherwise, it returns a regular quoted string
|
||||
local function smartQuote(str)
|
||||
if string.match( string.gsub(str,"[^'\"]",""), '^"+$' ) then
|
||||
return "'" .. str .. "'"
|
||||
end
|
||||
return string.format("%q", str )
|
||||
end
|
||||
|
||||
local controlCharsTranslation = {
|
||||
["\a"] = "\\a", ["\b"] = "\\b", ["\f"] = "\\f", ["\n"] = "\\n",
|
||||
["\r"] = "\\r", ["\t"] = "\\t", ["\v"] = "\\v", ["\\"] = "\\\\"
|
||||
}
|
||||
|
||||
local function unescapeChar(c) return controlCharsTranslation[c] end
|
||||
|
||||
local function unescape(str)
|
||||
local result, _ = string.gsub( str, "(%c)", unescapeChar )
|
||||
return result
|
||||
end
|
||||
|
||||
local function isIdentifier(str)
|
||||
return type(str) == 'string' and str:match( "^[_%a][_%a%d]*$" )
|
||||
end
|
||||
|
||||
local function isArrayKey(k, length)
|
||||
return type(k)=='number' and 1 <= k and k <= length
|
||||
end
|
||||
|
||||
local function isDictionaryKey(k, length)
|
||||
return not isArrayKey(k, length)
|
||||
end
|
||||
|
||||
local sortOrdersByType = {
|
||||
['number'] = 1, ['boolean'] = 2, ['string'] = 3, ['table'] = 4,
|
||||
['function'] = 5, ['userdata'] = 6, ['thread'] = 7
|
||||
}
|
||||
|
||||
local function sortKeys(a,b)
|
||||
local ta, tb = type(a), type(b)
|
||||
if ta ~= tb then return sortOrdersByType[ta] < sortOrdersByType[tb] end
|
||||
if ta == 'string' or ta == 'number' then return a < b end
|
||||
return false
|
||||
end
|
||||
|
||||
local function getDictionaryKeys(t)
|
||||
local length = #t
|
||||
local keys = {}
|
||||
for k,_ in pairs(t) do
|
||||
if isDictionaryKey(k, length) then table.insert(keys,k) end
|
||||
end
|
||||
table.sort(keys, sortKeys)
|
||||
return keys
|
||||
end
|
||||
|
||||
local function getToStringResultSafely(t, mt)
|
||||
local __tostring = type(mt) == 'table' and rawget(mt, '__tostring')
|
||||
local string, status
|
||||
if type(__tostring) == 'function' then
|
||||
status, string = pcall(__tostring, t)
|
||||
string = status and string or 'error: ' .. tostring(string)
|
||||
end
|
||||
return string
|
||||
end
|
||||
|
||||
local Inspector = {}
|
||||
|
||||
function Inspector:new(t, depth)
|
||||
local inspector = {
|
||||
buffer = {},
|
||||
depth = depth,
|
||||
level = 0,
|
||||
maxIds = {
|
||||
['function'] = 0,
|
||||
['userdata'] = 0,
|
||||
['thread'] = 0,
|
||||
['table'] = 0
|
||||
},
|
||||
ids = {
|
||||
['function'] = setmetatable({}, {__mode = "kv"}),
|
||||
['userdata'] = setmetatable({}, {__mode = "kv"}),
|
||||
['thread'] = setmetatable({}, {__mode = "kv"}),
|
||||
['table'] = setmetatable({}, {__mode = "kv"})
|
||||
},
|
||||
tableAppearances = setmetatable({}, {__mode = "k"})
|
||||
}
|
||||
|
||||
setmetatable(inspector, {__index = Inspector})
|
||||
|
||||
inspector:countTableAppearances(t)
|
||||
|
||||
return inspector:putValue(t)
|
||||
end
|
||||
|
||||
function Inspector:countTableAppearances(t)
|
||||
if type(t) == 'table' then
|
||||
if not self.tableAppearances[t] then
|
||||
self.tableAppearances[t] = 1
|
||||
for k,v in pairs(t) do
|
||||
self:countTableAppearances(k)
|
||||
self:countTableAppearances(v)
|
||||
end
|
||||
self:countTableAppearances(getmetatable(t))
|
||||
else
|
||||
self.tableAppearances[t] = self.tableAppearances[t] + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Inspector:tabify()
|
||||
self:puts("\n", string.rep(" ", self.level))
|
||||
return self
|
||||
end
|
||||
|
||||
function Inspector:up()
|
||||
self.level = self.level - 1
|
||||
end
|
||||
|
||||
function Inspector:down()
|
||||
self.level = self.level + 1
|
||||
end
|
||||
|
||||
function Inspector:puts(...)
|
||||
local args = {...}
|
||||
local len = #self.buffer
|
||||
for i=1, #args do
|
||||
len = len + 1
|
||||
self.buffer[len] = tostring(args[i])
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
function Inspector:commaControl(comma)
|
||||
if comma then self:puts(',') end
|
||||
return true
|
||||
end
|
||||
|
||||
function Inspector:putTable(t)
|
||||
if self:alreadyVisited(t) then
|
||||
self:puts('<table ', self:getId(t), '>')
|
||||
elseif self.depth and self.level >= self.depth then
|
||||
self:puts('{...}')
|
||||
else
|
||||
if self.tableAppearances[t] > 1 then
|
||||
self:puts('<',self:getId(t),'>')
|
||||
end
|
||||
self:puts('{')
|
||||
self:down()
|
||||
|
||||
local length = #t
|
||||
local mt = getmetatable(t)
|
||||
|
||||
local string = getToStringResultSafely(t, mt)
|
||||
if type(string) == 'string' and #string > 0 then
|
||||
self:puts(' -- ', unescape(string))
|
||||
if length >= 1 then self:tabify() end -- tabify the array values
|
||||
end
|
||||
|
||||
local comma = false
|
||||
for i=1, length do
|
||||
comma = self:commaControl(comma)
|
||||
self:puts(' '):putValue(t[i])
|
||||
end
|
||||
|
||||
local dictKeys = getDictionaryKeys(t)
|
||||
|
||||
for _,k in ipairs(dictKeys) do
|
||||
comma = self:commaControl(comma)
|
||||
self:tabify():putKey(k):puts(' = '):putValue(t[k])
|
||||
end
|
||||
|
||||
if mt then
|
||||
comma = self:commaControl(comma)
|
||||
self:tabify():puts('<metatable> = '):putValue(mt)
|
||||
end
|
||||
|
||||
self:up()
|
||||
|
||||
if #dictKeys > 0 or mt then -- dictionary table. Justify closing }
|
||||
self:tabify()
|
||||
elseif length > 0 then -- array tables have one extra space before closing }
|
||||
self:puts(' ')
|
||||
end
|
||||
self:puts('}')
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
function Inspector:alreadyVisited(v)
|
||||
return self.ids[type(v)][v] ~= nil
|
||||
end
|
||||
|
||||
function Inspector:getId(v)
|
||||
local tv = type(v)
|
||||
local id = self.ids[tv][v]
|
||||
if not id then
|
||||
id = self.maxIds[tv] + 1
|
||||
self.maxIds[tv] = id
|
||||
self.ids[tv][v] = id
|
||||
end
|
||||
return id
|
||||
end
|
||||
|
||||
function Inspector:putValue(v)
|
||||
local tv = type(v)
|
||||
|
||||
if tv == 'string' then
|
||||
self:puts(smartQuote(unescape(v)))
|
||||
elseif tv == 'number' or tv == 'boolean' or tv == 'nil' then
|
||||
self:puts(tostring(v))
|
||||
elseif tv == 'table' then
|
||||
self:putTable(v)
|
||||
else
|
||||
self:puts('<',tv,' ',self:getId(v),'>')
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
function Inspector:putKey(k)
|
||||
if isIdentifier(k) then return self:puts(k) end
|
||||
return self:puts( "[" ):putValue(k):puts("]")
|
||||
end
|
||||
|
||||
function Inspector:tostring()
|
||||
return table.concat(self.buffer)
|
||||
end
|
||||
|
||||
setmetatable(inspect, { __call = function(_,t,depth)
|
||||
return Inspector:new(t, depth):tostring()
|
||||
end })
|
||||
|
||||
return inspect
|
||||
|
149
samples/serversideviews_lua/bin/Lua/userlib/commons.lua
Normal file
149
samples/serversideviews_lua/bin/Lua/userlib/commons.lua
Normal file
@ -0,0 +1,149 @@
|
||||
function comma_value(amount)
|
||||
local formatted = amount
|
||||
while true do
|
||||
formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
|
||||
if (k==0) then
|
||||
break
|
||||
end
|
||||
end
|
||||
return formatted
|
||||
end
|
||||
|
||||
function htmltable_row(tt)
|
||||
local s = '<tr>'
|
||||
for k,v in pairs(tt) do
|
||||
s = s ..'<td>' .. htmlize(tostring(v)) .. '</td>'
|
||||
end
|
||||
return s .. '</tr>'
|
||||
end
|
||||
|
||||
function keys(t)
|
||||
local tt = {}
|
||||
for k,v in pairs(t) do
|
||||
table.insert(tt, k)
|
||||
end
|
||||
return tt
|
||||
end
|
||||
|
||||
function round(val, decimal)
|
||||
if (decimal) then
|
||||
return math.floor( (val * 10^decimal) + 0.5) / (10^decimal)
|
||||
else
|
||||
return math.floor(val+0.5)
|
||||
end
|
||||
end
|
||||
|
||||
function format_num(amount, decimal, prefix, neg_prefix)
|
||||
local str_amount, formatted, famount, remain
|
||||
|
||||
decimal = decimal or 2 -- default 2 decimal places
|
||||
neg_prefix = neg_prefix or "-" -- default negative sign
|
||||
|
||||
famount = math.abs(round(amount,decimal))
|
||||
famount = math.floor(famount)
|
||||
|
||||
remain = round(math.abs(amount) - famount, decimal)
|
||||
|
||||
-- comma to separate the thousands
|
||||
formatted = comma_value(famount)
|
||||
|
||||
-- attach the decimal portion
|
||||
if (decimal > 0) then
|
||||
remain = string.sub(tostring(remain),3)
|
||||
formatted = formatted .. "." .. remain ..
|
||||
string.rep("0", decimal - string.len(remain))
|
||||
end
|
||||
|
||||
-- attach prefix string e.g '$'
|
||||
formatted = (prefix or "") .. formatted
|
||||
|
||||
-- if value is negative then format accordingly
|
||||
if (amount<0) then
|
||||
if (neg_prefix=="()") then
|
||||
formatted = "("..formatted ..")"
|
||||
else
|
||||
formatted = neg_prefix .. formatted
|
||||
end
|
||||
end
|
||||
|
||||
return formatted
|
||||
end
|
||||
|
||||
|
||||
function table_size(t)
|
||||
local c = 0
|
||||
for i in pairs(t) do
|
||||
c=c+1
|
||||
end
|
||||
return c
|
||||
end
|
||||
|
||||
function html_table(t, headers, celldumpers, table_attributes)
|
||||
local headers = headers or {}
|
||||
local celldumpers = celldumpers or {}
|
||||
local html = {}
|
||||
|
||||
if table_size(headers) == 0 then --[[doesn't work]]
|
||||
local firstobj = t[1]
|
||||
for k in pairs(firstobj) do
|
||||
headers[k] = k
|
||||
end
|
||||
end
|
||||
|
||||
--[[calculate table attributes]]
|
||||
local table_attributes = table_attributes or {cellpadding="2", cellspacing="10"}
|
||||
local attrs = ""
|
||||
for k,v in pairs(table_attributes) do
|
||||
attrs = attrs .. tostring(k) .. '="' .. tostring(v) .. '" '
|
||||
end
|
||||
|
||||
table.insert(html, '<table ' .. attrs .. ' ><thead><tr>')
|
||||
|
||||
for k,v in ipairs(headers) do
|
||||
table.insert(html, '<th>'..v.coltitle..'</th>')
|
||||
end
|
||||
|
||||
table.insert(html, '</tr></thead><tbody>')
|
||||
local cell
|
||||
for k, a in pairs(t) do
|
||||
table.insert(html, "<tr>")
|
||||
for idx, obj in ipairs(headers) do
|
||||
if celldumpers[obj.fieldname] then
|
||||
cell = tostring(celldumpers[obj.fieldname](obj.fieldname, a))
|
||||
else
|
||||
if obj.fieldname == nil then
|
||||
cell = ' '
|
||||
else
|
||||
cell = tostring(a[obj.fieldname])
|
||||
end
|
||||
end
|
||||
table.insert(html, "<td>".. cell .."</td>")
|
||||
end
|
||||
table.insert(html, "</tr>")
|
||||
end
|
||||
table.insert(html, '</tbody></table>')
|
||||
return table.concat(html)
|
||||
end
|
||||
|
||||
function lookup(fieldvalue, lookuptable, keyfield, descfield)
|
||||
for k,v in pairs(lookuptable) do
|
||||
if fieldvalue == v[keyfield] then
|
||||
return v[descfield]
|
||||
end
|
||||
end
|
||||
return ' '
|
||||
end
|
||||
|
||||
function fmtfloat(n)
|
||||
return string.format("%2.2f", n)
|
||||
end
|
||||
|
||||
|
||||
--- Pads str to length len with char from left
|
||||
string.lpad = function(str, len, char)
|
||||
if char == nil then
|
||||
char = ' '
|
||||
end
|
||||
return string.rep(char, len - #str) .. str
|
||||
end
|
||||
|
1
samples/serversideviews_lua/bin/people.data
Normal file
1
samples/serversideviews_lua/bin/people.data
Normal file
@ -0,0 +1 @@
|
||||
[{"first_name":"Bruce","last_name":"Banner","age":56,"items":"smartphone,dumbphone","guid":"2290EE213DFB4855894A3FC91FE52C17"},{"first_name":"Reed","last_name":"Richards","age":45,"items":"laptop,smartphone","guid":"298CE047B4C24D67B29710BF4ABE290C"},{"first_name":"Scott","last_name":"Summers","age":54,"items":"desktop","guid":"3DACB879E83749EDA68389EBA2286A13"},{"first_name":"Daniele","last_name":"Teti","age":40,"items":"dumbphone,laptop","guid":"C2C002A595694C7CBD3CA1F3123F0EEB"},{"first_name":"Bruce","last_name":"Banner","age":56,"items":"smartphone,dumbphone","guid":"060B7ADFD00949FFA7001ADB85B6724C"},{"first_name":"Daniele","last_name":"Teti","age":40,"items":"smartphone","guid":"92A2145A235F414897C2B90789B28A13"},{"first_name":"Bruce","last_name":"Banner","age":56,"items":"desktop","guid":"97D40FD94580407FA60B9A09EAE2DA8E"},{"first_name":"Bruce","last_name":"Banner","age":66,"items":"smartphone,dumbphone","guid":"77104246D86C4FD298231D481895C212"},{"first_name":"àèéìòù","last_name":"aaaaa","age":12,"items":"","guid":"76B5A57D169D4A96A0FEBA43595C19ED"}]
|
77
samples/serversideviews_lua/bin/templates/editperson.elua
Normal file
77
samples/serversideviews_lua/bin/templates/editperson.elua
Normal file
@ -0,0 +1,77 @@
|
||||
<script>
|
||||
function doDelete(id) {
|
||||
if (confirm('Are you sure?')) {
|
||||
let form = document.getElementById("myForm");
|
||||
form.action = "/deleteperson";
|
||||
form.submit();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<div class="row_fluid">
|
||||
<div class="col-sm-12">
|
||||
<form class="form form-horizontal" id="myForm" name="myForm" method="POST" action="/people">
|
||||
<input type="hidden" value="<?lua=person.guid ?>" name="guid">
|
||||
<div class="row">
|
||||
<div class="col-sm-offset-2 col-sm-8">
|
||||
<?lua if person.guid == '' then?>
|
||||
<h3>New Person</h3>
|
||||
<?lua else ?>
|
||||
<h3>Edit Person</h3>
|
||||
<?lua end ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="form-group">
|
||||
<label for="first_name" class="col-sm-2 control-label">First name</label>
|
||||
<div class="col-sm-4">
|
||||
<input type="text" value="<?lua=person.first_name ?>" class="form-control" id="first_name" placeholder="First name" name="first_name">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="form-group">
|
||||
<label for="last_name" class="col-sm-2 control-label">Last name</label>
|
||||
<div class="col-sm-4">
|
||||
<input type="text" value="<?lua=person.last_name ?>" class="form-control" id="last_name" placeholder="Last name" name="last_name">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="form-group">
|
||||
<label for="age" class="col-sm-2 control-label">Age</label>
|
||||
<div class="col-sm-4">
|
||||
<input type="number" value="<?lua=person.age ?>" class="form-control" id="age" placeholder="Age" name="age">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-group">
|
||||
<label for="items" class="col-sm-2 control-label">Devices</label>
|
||||
<div class="col-sm-4">
|
||||
<select name="items" multiple class="form-control">
|
||||
<?lua for _, device in ipairs(deviceslist) do ?>
|
||||
<option value="<?lua= device.DeviceName ?>" <?lua if device.Selected then elua_out('selected') end?>><?lua= device.DeviceName ?></option>
|
||||
<?lua end ?>
|
||||
</select>
|
||||
<span style="font-size: 80%">(Ctrl+Click to select multiple devices)</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-2">
|
||||
<button type="button" class="btn btn-default btn-block" onclick="history.back()">Return to the list</button>
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<?lua if person ~= nil then ?>
|
||||
<button type="button" onclick="doDelete()" class="btn btn-primary btn-block">Delete</button>
|
||||
<?lua end ?>
|
||||
<button type="submit" class="btn btn-primary btn-block">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
22
samples/serversideviews_lua/bin/templates/footer.elua
Normal file
22
samples/serversideviews_lua/bin/templates/footer.elua
Normal file
@ -0,0 +1,22 @@
|
||||
<div class="row_fluid">
|
||||
<div class="col-sm-12">
|
||||
<div style="height: 100px"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row_fluid">
|
||||
<div class="col-sm-12">
|
||||
<span>N.B. All these views are UTF-8 encoded with BOM</span>
|
||||
</div>
|
||||
<div class="col-sm-8 bg-primary">
|
||||
<span>Powered by DMVCFramework</span>
|
||||
</div>
|
||||
<div class="col-sm-4 bg-success">
|
||||
<span>Server Side Views</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
16
samples/serversideviews_lua/bin/templates/header.elua
Normal file
16
samples/serversideviews_lua/bin/templates/header.elua
Normal file
@ -0,0 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<header>
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
|
||||
<style>
|
||||
body {
|
||||
padding: 20px 50px 20px 50px;
|
||||
}
|
||||
</style>
|
||||
</header>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="main" class="container">
|
||||
<h1>eLua Server Side Views Sample <small>DMVCFramework</small></h1>
|
||||
|
@ -0,0 +1 @@
|
||||
guid;first_name;last_name;age
|
@ -0,0 +1,2 @@
|
||||
<?lua for _, person in pairs(people) do?>"<?lua=person.guid?>";"<?lua=person.first_name?>";"<?lua=person.last_name?>";<?lua=person.age?>
|
||||
<?lua end ?>
|
37
samples/serversideviews_lua/bin/templates/people_list.elua
Normal file
37
samples/serversideviews_lua/bin/templates/people_list.elua
Normal file
@ -0,0 +1,37 @@
|
||||
<div class="row_fluid">
|
||||
<div class="col-sm-12">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>First name</th>
|
||||
<th>Last name</th>
|
||||
<th>Age</th>
|
||||
<th> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?lua for _, person in ipairs(people) do ?>
|
||||
<tr>
|
||||
<td><?lua=_?></td>
|
||||
<td><?lua=person.first_name?></td>
|
||||
<td><?lua=person.last_name?></td>
|
||||
<td><?lua=person.age?></td>
|
||||
<td class="text-right">
|
||||
<a class="btn btn-default" href="/edit/<?lua=person.guid?>"><span class="glyphicon glyphicon-pencil"></span> View</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?lua end ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<div class="row_fluid">
|
||||
<div class="col-sm-2">
|
||||
<a class="btn btn-default btn-block" href="/people/formats/csv">Export as CSV</a>
|
||||
</div>
|
||||
<div class="col-sm-2 col-sm-offset-8">
|
||||
<a class="btn btn-primary btn-block" href="/new">Add New Person</a>
|
||||
</div>
|
||||
</div>
|
@ -1,7 +1,7 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{C829684B-145E-49F2-8C37-2562C6C5904E}</ProjectGuid>
|
||||
<ProjectVersion>19.2</ProjectVersion>
|
||||
<ProjectVersion>19.3</ProjectVersion>
|
||||
<FrameworkType>VCL</FrameworkType>
|
||||
<MainSource>ServerSideViews.dpr</MainSource>
|
||||
<Base>True</Base>
|
||||
@ -97,10 +97,6 @@
|
||||
<FormType>dfm</FormType>
|
||||
<DesignClass>TDataModule</DesignClass>
|
||||
</DCCReference>
|
||||
<BuildConfiguration Include="Release">
|
||||
<Key>Cfg_2</Key>
|
||||
<CfgParent>Base</CfgParent>
|
||||
</BuildConfiguration>
|
||||
<BuildConfiguration Include="Base">
|
||||
<Key>Base</Key>
|
||||
</BuildConfiguration>
|
||||
@ -108,6 +104,10 @@
|
||||
<Key>Cfg_1</Key>
|
||||
<CfgParent>Base</CfgParent>
|
||||
</BuildConfiguration>
|
||||
<BuildConfiguration Include="Release">
|
||||
<Key>Cfg_2</Key>
|
||||
<CfgParent>Base</CfgParent>
|
||||
</BuildConfiguration>
|
||||
</ItemGroup>
|
||||
<ProjectExtensions>
|
||||
<Borland.Personality>Delphi.Personality.12</Borland.Personality>
|
||||
@ -138,6 +138,16 @@
|
||||
<Operation>0</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<DeployClass Name="AndroidClasses">
|
||||
<Platform Name="Android">
|
||||
<RemoteDir>classes</RemoteDir>
|
||||
<Operation>64</Operation>
|
||||
</Platform>
|
||||
<Platform Name="Android64">
|
||||
<RemoteDir>classes</RemoteDir>
|
||||
<Operation>64</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<DeployClass Name="AndroidClassesDexFile">
|
||||
<Platform Name="Android">
|
||||
<RemoteDir>classes</RemoteDir>
|
||||
@ -437,6 +447,10 @@
|
||||
<Operation>1</Operation>
|
||||
<Extensions>.framework</Extensions>
|
||||
</Platform>
|
||||
<Platform Name="OSXARM64">
|
||||
<Operation>1</Operation>
|
||||
<Extensions>.framework</Extensions>
|
||||
</Platform>
|
||||
<Platform Name="Win32">
|
||||
<Operation>0</Operation>
|
||||
</Platform>
|
||||
@ -450,6 +464,10 @@
|
||||
<Operation>1</Operation>
|
||||
<Extensions>.dylib</Extensions>
|
||||
</Platform>
|
||||
<Platform Name="OSXARM64">
|
||||
<Operation>1</Operation>
|
||||
<Extensions>.dylib</Extensions>
|
||||
</Platform>
|
||||
<Platform Name="Win32">
|
||||
<Operation>0</Operation>
|
||||
<Extensions>.dll;.bpl</Extensions>
|
||||
@ -476,6 +494,10 @@
|
||||
<Operation>1</Operation>
|
||||
<Extensions>.dylib</Extensions>
|
||||
</Platform>
|
||||
<Platform Name="OSXARM64">
|
||||
<Operation>1</Operation>
|
||||
<Extensions>.dylib</Extensions>
|
||||
</Platform>
|
||||
<Platform Name="Win32">
|
||||
<Operation>0</Operation>
|
||||
<Extensions>.bpl</Extensions>
|
||||
@ -503,6 +525,9 @@
|
||||
<Platform Name="OSX64">
|
||||
<Operation>0</Operation>
|
||||
</Platform>
|
||||
<Platform Name="OSXARM64">
|
||||
<Operation>0</Operation>
|
||||
</Platform>
|
||||
<Platform Name="Win32">
|
||||
<Operation>0</Operation>
|
||||
</Platform>
|
||||
@ -1056,6 +1081,10 @@
|
||||
<RemoteDir>Contents\Resources</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="OSXARM64">
|
||||
<RemoteDir>Contents\Resources</RemoteDir>
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<DeployClass Required="true" Name="ProjectOutput">
|
||||
<Platform Name="Android">
|
||||
@ -1084,6 +1113,9 @@
|
||||
<Platform Name="OSX64">
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="OSXARM64">
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
<Platform Name="Win32">
|
||||
<Operation>0</Operation>
|
||||
</Platform>
|
||||
@ -1122,16 +1154,17 @@
|
||||
<Operation>1</Operation>
|
||||
</Platform>
|
||||
</DeployClass>
|
||||
<ProjectRoot Platform="iOSDevice64" Name="$(PROJECTNAME).app"/>
|
||||
<ProjectRoot Platform="Win64" Name="$(PROJECTNAME)"/>
|
||||
<ProjectRoot Platform="iOSDevice32" Name="$(PROJECTNAME).app"/>
|
||||
<ProjectRoot Platform="Win32" Name="$(PROJECTNAME)"/>
|
||||
<ProjectRoot Platform="Linux64" Name="$(PROJECTNAME)"/>
|
||||
<ProjectRoot Platform="OSX32" Name="$(PROJECTNAME)"/>
|
||||
<ProjectRoot Platform="Android" Name="$(PROJECTNAME)"/>
|
||||
<ProjectRoot Platform="OSX64" Name="$(PROJECTNAME)"/>
|
||||
<ProjectRoot Platform="iOSSimulator" Name="$(PROJECTNAME).app"/>
|
||||
<ProjectRoot Platform="Win32" Name="$(PROJECTNAME)"/>
|
||||
<ProjectRoot Platform="Android64" Name="$(PROJECTNAME)"/>
|
||||
<ProjectRoot Platform="Android" Name="$(PROJECTNAME)"/>
|
||||
<ProjectRoot Platform="Win64" Name="$(PROJECTNAME)"/>
|
||||
<ProjectRoot Platform="OSXARM64" Name="$(PROJECTNAME)"/>
|
||||
<ProjectRoot Platform="iOSDevice32" Name="$(PROJECTNAME).app"/>
|
||||
<ProjectRoot Platform="iOSDevice64" Name="$(PROJECTNAME).app"/>
|
||||
<ProjectRoot Platform="Linux64" Name="$(PROJECTNAME)"/>
|
||||
<ProjectRoot Platform="iOSSimulator" Name="$(PROJECTNAME).app"/>
|
||||
</Deployment>
|
||||
<Platforms>
|
||||
<Platform value="Win32">True</Platform>
|
||||
|
@ -78,6 +78,13 @@ procedure SaveColors;
|
||||
procedure RestoreSavedColors;
|
||||
procedure Init;
|
||||
procedure SetDefaults;
|
||||
function ConsoleAttr: Integer;
|
||||
procedure SetConsoleAttr(const TextAttr: Integer);
|
||||
function TextAttr: Word;
|
||||
procedure SetTextAttr(const TextAttr: Word);
|
||||
function BackgroundAttr: Word;
|
||||
procedure SetBackgroundAttr(const BackgroundAttr: Word);
|
||||
|
||||
|
||||
function ColorName(const color: TConsoleColor): String;
|
||||
|
||||
@ -90,8 +97,8 @@ const
|
||||
ESC = Chr(27);
|
||||
|
||||
var
|
||||
GForeGround, GSavedForeGround: Integer;
|
||||
GBackGround, GSavedBackGround: Integer;
|
||||
GForeGround, GSavedForeGround: Int16;
|
||||
GBackGround, GSavedBackGround: Int16;
|
||||
GOutHandle: THandle = INVALID_HANDLE_VALUE;
|
||||
GInputHandle: THandle = INVALID_HANDLE_VALUE;
|
||||
|
||||
@ -300,6 +307,46 @@ begin
|
||||
UpdateMode;
|
||||
end;
|
||||
|
||||
function ConsoleAttr: Integer;
|
||||
begin
|
||||
Result := GForeGround;
|
||||
Result := (Result shl 16) or GBackGround;
|
||||
end;
|
||||
|
||||
procedure SetConsoleAttr(const TextAttr: Integer);
|
||||
var
|
||||
lAttr: Integer;
|
||||
begin
|
||||
lAttr := TextAttr;
|
||||
GBackGround := lAttr and $0000FFFF;
|
||||
GForeGround := lAttr shr 16;
|
||||
UpdateMode;
|
||||
end;
|
||||
|
||||
|
||||
function TextAttr: Word;
|
||||
begin
|
||||
Result := GForeGround;
|
||||
end;
|
||||
|
||||
procedure SetTextAttr(const TextAttr: Word);
|
||||
begin
|
||||
GForeGround := TextAttr;
|
||||
UpdateMode;
|
||||
end;
|
||||
|
||||
function BackgroundAttr: Word;
|
||||
begin
|
||||
Result := GBackGround;
|
||||
end;
|
||||
|
||||
procedure SetBackgroundAttr(const BackgroundAttr: Word);
|
||||
begin
|
||||
GBackGround := BackgroundAttr;
|
||||
UpdateMode;
|
||||
end;
|
||||
|
||||
|
||||
|
||||
initialization
|
||||
|
||||
|
@ -163,10 +163,10 @@ begin
|
||||
begin
|
||||
raise EMVCException.Create('StaticFilePath must begin with "/" and cannot be empty');
|
||||
end;
|
||||
if fStaticFilesPath = '/' then
|
||||
begin
|
||||
raise EMVCException.Create('StaticFilePath cannot be "/"');
|
||||
end;
|
||||
// if fStaticFilesPath = '/' then
|
||||
// begin
|
||||
// raise EMVCException.Create('StaticFilePath cannot be "/"');
|
||||
// end;
|
||||
if not TDirectory.Exists(fDocumentRoot) then
|
||||
begin
|
||||
raise EMVCException.CreateFmt('DocumentRoot [%s] is not a valid directory', [fDocumentRoot]);
|
||||
|
@ -51,6 +51,7 @@ uses
|
||||
System.Generics.Collections,
|
||||
SynMustache,
|
||||
SynCommons,
|
||||
JsonDataObjects,
|
||||
MVCFramework.Serializer.Defaults,
|
||||
MVCFramework.Serializer.Intf,
|
||||
MVCFramework.DuckTyping,
|
||||
@ -112,6 +113,7 @@ var
|
||||
lList: IMVCList;
|
||||
DataObj: TPair<string, TObject>;
|
||||
lDSPair: TPair<string, TDataSet>;
|
||||
lJSONPair: TPair<string, TJSONObject>;
|
||||
lSJSON: string;
|
||||
lJSON: string;
|
||||
lSer: IMVCSerializer;
|
||||
@ -153,6 +155,19 @@ begin
|
||||
lFirst := False;
|
||||
end;
|
||||
end;
|
||||
|
||||
if Assigned(ViewJSON) then
|
||||
begin
|
||||
for lJSONPair in ViewJSON do
|
||||
begin
|
||||
lJSON := lJSONPair.Value.ToJSON(True);
|
||||
if not lFirst then
|
||||
lSJSON := lSJSON + ',';
|
||||
lSJSON := lSJSON + '"' + lDSPair.Key + '":' + lJSON;
|
||||
lFirst := False;
|
||||
end;
|
||||
end;
|
||||
|
||||
lSJSON := lSJSON + '}';
|
||||
FJSONModel := lSJSON;
|
||||
end;
|
||||
|
@ -49,6 +49,7 @@ uses
|
||||
System.DateUtils,
|
||||
System.Generics.Collections,
|
||||
System.Rtti,
|
||||
JSONDataObjects,
|
||||
Data.DB,
|
||||
MVCFramework.Session,
|
||||
MVCFramework.DuckTyping,
|
||||
@ -338,8 +339,6 @@ type
|
||||
|
||||
end;
|
||||
|
||||
|
||||
|
||||
// test
|
||||
// TMVCHackHTTPAppRequest = class(TIdHTTPAppRequest)
|
||||
// private
|
||||
@ -529,11 +528,14 @@ type
|
||||
FLoggedUser: TUser;
|
||||
FWebSession: TWebSession;
|
||||
FData: TMVCStringDictionary;
|
||||
fIntfObject: IInterface;
|
||||
function GetWebSession: TWebSession;
|
||||
function GetLoggedUser: TUser;
|
||||
function GetParamsTable: TMVCRequestParamsTable;
|
||||
procedure SetParamsTable(const AValue: TMVCRequestParamsTable);
|
||||
function GetHostingFrameworkType: TMVCHostingFrameworkType;
|
||||
function GetIntfObject: IInterface;
|
||||
procedure SetIntfObject(const Value: IInterface);
|
||||
protected
|
||||
procedure Flush; virtual;
|
||||
procedure BindToSession(const ASessionId: string);
|
||||
@ -561,6 +563,7 @@ type
|
||||
property Session: TWebSession read GetWebSession;
|
||||
property Config: TMVCConfig read FConfig;
|
||||
property Data: TMVCStringDictionary read GetData;
|
||||
property CustomIntfObject: IInterface read GetIntfObject write SetIntfObject;
|
||||
property ParamsTable: TMVCRequestParamsTable read GetParamsTable write SetParamsTable;
|
||||
end;
|
||||
|
||||
@ -801,9 +804,6 @@ type
|
||||
property Session: TWebSession read GetSession;
|
||||
property ContentType: string read GetContentType write SetContentType;
|
||||
property StatusCode: Integer read GetStatusCode write SetStatusCode;
|
||||
property ViewModelList: TMVCViewDataObject read GetViewModel;
|
||||
property ViewDataSetList: TMVCViewDataSet read GetViewDataSets;
|
||||
|
||||
procedure PushObjectToView(const aModelName: string; const AModel: TObject);
|
||||
deprecated 'Use "ViewData"';
|
||||
procedure PushDataSetToView(const aModelName: string; const ADataSet: TDataSet);
|
||||
@ -896,6 +896,8 @@ type
|
||||
TMVCRouterLogState = (rlsRouteFound, rlsRouteNotFound);
|
||||
TMVCRouterLogHandlerProc = reference to procedure(const Router: TMVCCustomRouter;
|
||||
const RouterLogState: TMVCRouterLogState; const WebContext: TWebContext);
|
||||
TWebContextCreateEvent = reference to procedure(const AContext: TWebContext);
|
||||
TWebContextDestroyEvent = reference to procedure(const AContext: TWebContext);
|
||||
|
||||
TMVCEngine = class(TComponent)
|
||||
private const
|
||||
@ -919,6 +921,8 @@ type
|
||||
FSavedOnBeforeDispatch: THTTPMethodEvent;
|
||||
FOnException: TMVCExceptionHandlerProc;
|
||||
fOnRouterLog: TMVCRouterLogHandlerProc;
|
||||
fWebContextCreateEvent: TWebContextCreateEvent;
|
||||
fWebContextDestroyEvent: TWebContextDestroyEvent;
|
||||
procedure FillActualParamsForAction(const ASelectedController: TMVCController;
|
||||
const AContext: TWebContext; const AActionFormalParams: TArray<TRttiParameter>;
|
||||
const AActionName: string; var AActualParams: TArray<TValue>; out ABodyParameter: TObject);
|
||||
@ -927,6 +931,8 @@ type
|
||||
procedure HandleDefaultValueForInjectedParameter(var InjectedParamValue: String;
|
||||
const InjectableParamAttribute: MVCInjectableParamAttribute);
|
||||
protected
|
||||
procedure DoWebContextCreateEvent(const AContext: TWebContext); inline;
|
||||
procedure DoWebContextDestroyEvent(const AContext: TWebContext); inline;
|
||||
function GetActualParam(const AFormalParam: TRttiParameter; const AStringValue: String): TValue;
|
||||
function CustomExceptionHandling(const Ex: Exception; const ASelectedController: TMVCController;
|
||||
const AContext: TWebContext): Boolean;
|
||||
@ -963,6 +969,11 @@ type
|
||||
|
||||
function GetSessionBySessionId(const ASessionId: string): TWebSession;
|
||||
|
||||
{ webcontext events}
|
||||
procedure OnWebContextCreate(const WebContextCreateEvent: TWebContextCreateEvent);
|
||||
procedure OnWebContextDestroy(const WebContextDestroyEvent: TWebContextDestroyEvent);
|
||||
{ end - webcontext events}
|
||||
|
||||
function AddSerializer(const AContentType: string; const ASerializer: IMVCSerializer)
|
||||
: TMVCEngine;
|
||||
function AddMiddleware(const AMiddleware: IMVCMiddleware): TMVCEngine;
|
||||
@ -1045,6 +1056,7 @@ type
|
||||
FWebContext: TWebContext;
|
||||
FViewModel: TMVCViewDataObject;
|
||||
FViewDataSets: TObjectDictionary<string, TDataSet>;
|
||||
FViewJSON: TMVCViewJSONDataObject;
|
||||
FContentType: string;
|
||||
FOutput: string;
|
||||
protected
|
||||
@ -1063,11 +1075,13 @@ type
|
||||
property ViewName: string read FViewName;
|
||||
property WebContext: TWebContext read FWebContext;
|
||||
property ViewModel: TMVCViewDataObject read FViewModel;
|
||||
property ViewJSON: TMVCViewJSONDataObject read FViewJSON;
|
||||
property ViewDataSets: TObjectDictionary<string, TDataSet> read FViewDataSets;
|
||||
property ContentType: string read FContentType;
|
||||
property Output: string read FOutput;
|
||||
end;
|
||||
|
||||
|
||||
function IsShuttingDown: Boolean;
|
||||
procedure EnterInShutdownState;
|
||||
function CreateResponse(const StatusCode: UInt16; const ReasonString: string;
|
||||
@ -1935,6 +1949,7 @@ begin
|
||||
FSerializers := ASerializers;
|
||||
FData := nil;
|
||||
FLoggedUser := nil;
|
||||
fIntfObject := nil;
|
||||
end;
|
||||
|
||||
destructor TWebContext.Destroy;
|
||||
@ -1951,9 +1966,11 @@ begin
|
||||
FData.Free;
|
||||
except
|
||||
end;
|
||||
|
||||
fIntfObject := nil;
|
||||
|
||||
try
|
||||
if Assigned(FLoggedUser) then
|
||||
FLoggedUser.Free;
|
||||
FLoggedUser.Free;
|
||||
except
|
||||
end;
|
||||
inherited Destroy;
|
||||
@ -1990,6 +2007,11 @@ begin
|
||||
Exit(hftIndy);
|
||||
end;
|
||||
|
||||
function TWebContext.GetIntfObject: IInterface;
|
||||
begin
|
||||
Result := fIntfObject;
|
||||
end;
|
||||
|
||||
{ MVCFromBodyAttribute }
|
||||
|
||||
constructor MVCFromBodyAttribute.Create(const RootNode: string; const DataType: TMVCDataType);
|
||||
@ -2128,6 +2150,11 @@ begin
|
||||
FSessionMustBeClose := True;
|
||||
end;
|
||||
|
||||
procedure TWebContext.SetIntfObject(const Value: IInterface);
|
||||
begin
|
||||
fIntfObject := Value;
|
||||
end;
|
||||
|
||||
procedure TWebContext.SetParamsTable(const AValue: TMVCRequestParamsTable);
|
||||
begin
|
||||
FRequest.ParamsTable := AValue;
|
||||
@ -2287,6 +2314,22 @@ begin
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
procedure TMVCEngine.DoWebContextCreateEvent(const AContext: TWebContext);
|
||||
begin
|
||||
if Assigned(fWebContextCreateEvent) then
|
||||
begin
|
||||
fWebContextCreateEvent(AContext);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TMVCEngine.DoWebContextDestroyEvent(const AContext: TWebContext);
|
||||
begin
|
||||
if Assigned(fWebContextDestroyEvent) then
|
||||
begin
|
||||
fWebContextDestroyEvent(AContext);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TMVCEngine.ExecuteAction(const ASender: TObject; const ARequest: TWebRequest;
|
||||
const AResponse: TWebResponse): Boolean;
|
||||
var
|
||||
@ -2326,6 +2369,7 @@ begin
|
||||
lContext := TWebContext.Create(ARequest, AResponse, FConfig, FSerializers);
|
||||
try
|
||||
DefineDefaultResponseHeaders(lContext);
|
||||
DoWebContextCreateEvent(lContext);
|
||||
lHandled := False;
|
||||
lRouter := TMVCRouter.Create(FConfig, gMVCGlobalActionParamsCache);
|
||||
try // finally
|
||||
@ -2526,6 +2570,7 @@ begin
|
||||
lRouter.Free;
|
||||
end;
|
||||
finally
|
||||
DoWebContextDestroyEvent(lContext);
|
||||
lContext.Free;
|
||||
end;
|
||||
finally
|
||||
@ -3027,6 +3072,18 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TMVCEngine.OnWebContextCreate(
|
||||
const WebContextCreateEvent: TWebContextCreateEvent);
|
||||
begin
|
||||
fWebContextCreateEvent := WebContextCreateEvent;
|
||||
end;
|
||||
|
||||
procedure TMVCEngine.OnWebContextDestroy(
|
||||
const WebContextDestroyEvent: TWebContextDestroyEvent);
|
||||
begin
|
||||
fWebContextDestroyEvent := WebContextDestroyEvent;
|
||||
end;
|
||||
|
||||
function TMVCEngine.PublishObject(const AObjectCreatorDelegate: TMVCObjectCreatorDelegate;
|
||||
const AURLSegment: string): TMVCEngine;
|
||||
begin
|
||||
@ -3736,7 +3793,9 @@ var lView: TMVCBaseViewEngine; lViewName: string; lStrStream: TStringStream;
|
||||
begin
|
||||
lStrStream := TStringStream.Create('', TEncoding.UTF8);
|
||||
try
|
||||
lView := FEngine.ViewEngineClass.Create(Engine, Context, ViewModelList, ViewDataSetList,
|
||||
lView := FEngine.ViewEngineClass.Create(
|
||||
Engine, Context,
|
||||
FViewModel, FViewDataSets,
|
||||
ContentType);
|
||||
try
|
||||
for lViewName in AViewNames do
|
||||
@ -3994,8 +4053,9 @@ end;
|
||||
{ TMVCBaseView }
|
||||
|
||||
constructor TMVCBaseViewEngine.Create(const AEngine: TMVCEngine; const AWebContext: TWebContext;
|
||||
const AViewModel: TMVCViewDataObject; const AViewDataSets: TObjectDictionary<string, TDataSet>;
|
||||
const AContentType: string);
|
||||
const AViewModel: TMVCViewDataObject;
|
||||
const AViewDataSets: TObjectDictionary<string, TDataSet>;
|
||||
const AContentType: string);
|
||||
begin
|
||||
inherited Create;
|
||||
Engine := AEngine;
|
||||
|
Loading…
Reference in New Issue
Block a user