mirror of
https://github.com/danieleteti/delphimvcframework.git
synced 2024-11-15 15:55:54 +01:00
247 lines
7.6 KiB
ObjectPascal
247 lines
7.6 KiB
ObjectPascal
// ***************************************************************************
|
|
//
|
|
// Delphi MVC Framework
|
|
//
|
|
// Copyright (c) 2010-2024 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, lModelName: string;
|
|
lLuaFilter: TLuaEmbeddedTextFilter;
|
|
lViewFileName: String;
|
|
lFileName, lCompiledFileName: string;
|
|
lLuaCompiledCode: string;
|
|
lStreamReader: TStreamReader;
|
|
DecodeJSONStrings: string;
|
|
lSer: TMVCJsonDataObjectsSerializer;
|
|
lScriptOutput: TStreamWriter;
|
|
lJSON: TJDOJsonObject;
|
|
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 := '';
|
|
if Assigned(FJSONModel) then
|
|
begin
|
|
Lua.DeclareGlobalString('__data__', FJSONModel.ToJSON());
|
|
DecodeJSONStrings := AnsiString('data') + ' = json.decode(__data__)';
|
|
end;
|
|
{ continuare da questo problema }
|
|
if Assigned(ViewModel) then
|
|
begin
|
|
lJSON := TJDOJsonObject.Create;
|
|
try
|
|
lSer := TMVCJsonDataObjectsSerializer.Create(nil);
|
|
try
|
|
for lModelName in ViewModel.Keys do
|
|
begin
|
|
lJSON.Clear;
|
|
lSer.TValueToJSONObjectProperty(lJSON, lModelName, ViewModel[lModelName], TMVCSerializationType.stDefault, nil, nil);
|
|
if lJSON.Values[lModelName].Typ = jdtArray then
|
|
Lua.DeclareGlobalString(lModelName, lJSON.A[lModelName].ToJSON())
|
|
else
|
|
Lua.DeclareGlobalString(lModelName, lJSON.O[lModelName].ToJSON());
|
|
DecodeJSONStrings := DecodeJSONStrings + sLineBreak + ' ' +
|
|
AnsiString(lModelName) + ' = json.decode(' + AnsiString(lModelName) + ')';
|
|
end;
|
|
finally
|
|
lSer.Free;
|
|
end;
|
|
finally
|
|
lJSON.Free;
|
|
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.
|