delphimvcframework/samples/serversideviews_lua/MVCFramework.View.Renderers.Lua.pas

240 lines
7.3 KiB
ObjectPascal

// ***************************************************************************
//
// 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_stream_out(L: Plua_State): Integer; cdecl;
var
lPointerToStreamWriter: Pointer;
lData: PAnsiChar;
begin
if lua_gettop(L) <> 2 then
begin
luaL_error(L, PAnsiChar('Wrong parameters number, expected 2'));
Exit;
end;
lData := lua_tostring(L, -1);
lPointerToStreamWriter := lua_topointer(L, -2);
TStreamWriter(lPointerToStreamWriter).Write(UTF8Encode(lData));
Result := 0;
end;
{ TMVCEmbeddedLuaView }
procedure TMVCLuaViewEngine.Execute(const ViewName: string;
const OutputStream: TStream);
var
Lua: TLuaEngine;
lDataSetName: string;
lLuaFilter: TLuaEmbeddedTextFilter;
lViewFileName: String;
lFileName, lCompiledFileName: string;
lLuaCompiledCode: string;
lStreamReader: TStreamReader;
DecodeJSONStrings: string;
lSer: TMVCJsonDataObjectsSerializer;
lJSONStr: string;
lScriptOutput: TStreamWriter;
begin
lScriptOutput := 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
lFileName := ExpandFileName
(IncludeTrailingPathDelimiter(Config.Value[TMVCConfigKey.ViewPath]) +
lViewFileName)
else
lFileName := ExpandFileName
(IncludeTrailingPathDelimiter(GetApplicationFileNamePath +
Config.Value[TMVCConfigKey.ViewPath]) + lViewFileName);
lViewFileName := lFileName;
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 lDataSetName in ViewModel.Keys do
begin
lSer := TMVCJsonDataObjectsSerializer.Create(nil);
try
if TDuckTypedList.CanBeWrappedAsList(ViewModel[lDataSetName]) then
begin
lJSONStr := lSer.SerializeCollection(ViewModel[lDataSetName]);
end
else
begin //PODO
lJSONStr := lSer.SerializeObject(ViewModel[lDataSetName]);
end;
finally
lSer.Free;
end;
Lua.DeclareGlobalString(lDataSetName, lJSONStr);
DecodeJSONStrings := DecodeJSONStrings + sLineBreak + ' ' +
AnsiString(lDataSetName) + ' = json.decode(' + AnsiString(lDataSetName) + ')';
end;
end;
end;
if Assigned(ViewDataSets) then
begin
for lDataSetName in ViewDataSets.Keys do
begin
ExposeDataSet(Lua, ViewDataSets[lDataSetName], ViewDataSets[lDataSetName].Name);
end;
end;
Lua.DeclareGlobalString('__ROOT__', ExtractFilePath(ParamStr(0)));
Lua.DeclareGlobalString('__log_file', LOG_FILE_NAME);
Lua.DeclareGlobalLightUserData('__webcontext', WebContext);
Lua.DeclareGlobalLightUserData('__stringbuilder', lScriptOutput);
lCompiledFileName := GetCompiledFileName(lViewFileName);
if not IsCompiledVersionUpToDate(lViewFileName, lCompiledFileName) then
begin
TMonitor.Enter(Self);
try
if not IsCompiledVersionUpToDate(lViewFileName, lCompiledFileName) then
begin
lLuaFilter := TLuaEmbeddedTextFilter.Create;
try
lLuaFilter.OutputFunction := 'elua_out';
lLuaFilter.TemplateCode := TFile.ReadAllText(lViewFileName,
TEncoding.UTF8);
lLuaFilter.Execute;
TFile.WriteAllText(lCompiledFileName,
lLuaFilter.LuaCode,
TEncoding.ANSI);
finally
lLuaFilter.Free;
end;
end;
finally
TMonitor.Exit(Self);
end;
end;
// read lua compiled code
lStreamReader := TStreamReader.Create(TFileStream.Create(lCompiledFileName,
fmOpenRead or fmShareDenyNone), TEncoding.UTF8);
try
lStreamReader.OwnStream;
lLuaCompiledCode := lStreamReader.ReadToEnd;
finally
lStreamReader.Free;
end;
Lua.DeclareGlobalFunction('internal_elua_out', @__lua_stream_out);
Lua.LoadScript('require "Lua.boot" ' + sLineBreak + DecodeJSONStrings + sLineBreak + lLuaCompiledCode);
// EXECUTE IT!!!
Lua.Execute;
finally
Lua.Free;
end;
finally
FreeAndNil(lScriptOutput);
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.