Hoje fiquei com vontade de escrever algo para “EU” mesmo. Um preguiçoso incondicional… Sabe aquele cara que não gosta de repetir tudo de novo… então… Já notou como RTTI fica tudo repetitivo… cansei.

Se um objeto tem um propriedade Caption, para atribuir um valor para ele usando RTTI é preciso iniciar um Context, fazer um loop para checar se a propriedade existe, ver o tipo… cansei…

Agora vou fazer assim:

label1.ContextProperties['caption'] := 'meu caption';

Alguém vai dizer que para fazer para um TLabel é moleza… tudo bem… e se fosse um objeto que não sei se tem a propriedade “caption”… tudo bem – isto não é problema meu… o objeto que se encarregue de tratar… não quero aquele erro infame na minha janela…

Vi a palestra do Thulio na Conference 2016 e ele nem piscou, mandou um “dinossauro” na sala quando falava de MongoDB – beleza meu amigo, vamos tocando aqui.

Quer saber se TMongoConnection tem uma propriedade “loginprompt” – não precisava, mas se estiver na dúvida então faz assim:

if MongoConnection.IsContextProperty('loginprompt') then
        fazUmEstragoNoDB;

Humm… pensei melhor e talvez não era uma propriedade o que esperava… na verdade era uma variável do objeto:

Opa, então faz assim:

form1.ContextFields['usuario'] := 'Eu mesmo';

Por fim, pensei em listar todas a propriedades do objeto em um memo…

Boa, isto é moleza:

label1.GetContextPropertiesItems( memo1.lines );

Se fosse uma lista de variáveis do objeto, da para fazer assim:

form1.ContextGetFieldsList( memo1.lines );

Difícil…. então deixa de falar que RTTI é complicado, bora usar aí nos seus projetos mais ousados.
Quer ver mais…. olha aqui nos fontes: https://github.com/amarildolacerda

Um boa dor de cabeça é resolver exceções em TThread, TTasks….

Base de Conhecimento
Como pré-requisito é preciso ter em mente (“recomenda-se”) não existir uma exceção não tratada dentro de uma TThread – então todos os processo deveriam tratar as suas exceções internamente com um Try/Exception.

[code]
Try
código com erro…
Except
on e:exception do
fazer algo…
end;
[/code]

 

No framework parte do “post” LogEvents é possível prever o tratamento de exception acrescentando o método RUN…

[code]
procedure TLogListItems.Run(proc: TProc);
begin
TThread.CreateAnonymousThread(
procedure
begin
try
proc;
except
on e: exception do
LogEvents.DoErro(nil, 0, e.message);
end;
end).start;
end;
[/code]

Com método RUN recebendo um ANONIMOUS Procedure – permite que a aplicação faça assim:

[code]
// usado metodo ANONIMOUS para tratar exception internamente
LogEvents.Run(
procedure begin
// código com potencial de exceção

// força um exception
raise Exception.Create(‘Error Message’);
end);

[/code]

Como mostrar o erro ao usuário

O Framework “LogEvents” possui um método de inicialização “register” que o formulário irá se inscrever para receber mensagens… e outro para retirar a inscrição “unregister“.

LogEvents.register(self, DoErro, 0); // recebe os registro de ERROS

onde:

  • self é o formulário que irá recebe mensagens…
  • DoErro é o método do furmulario…
  • e o terceiro parâmetro é um identificador que qualifica o tipo de mensagem que irá receber

O mesmo formulário pode subscrever a receber mais de um tipo de mensagem;

[code]

LogEvents.register(self, DoErro, 0); // recebe os registro de ERROS
LogEvents.register(self, DoSucesso, 1); // registra para receber os sucessos

[/code]

Para não receber mensagens – em geral quando o formulário fecha “close” usar: LogEvents.unregister(self);

Enviando mensagem para o formulário

O método genérico “Log” permite enviar uma mensagem para o identificador “0” (usado no register):

LogEvents.Log(‘Minha mensagem a ser mostrada’);

Para enviar uma mensagem com um identificador específico:

LogEvents.DoErro(nil, 1, ‘LOG…..’);  // register = 1

Código de Exemplo: LogEvents – Mostrando Erros ao usuário

 

 

Estava precisando de informações sobre uma exceção e o log de erro não dizia nada relevante possível de encontrar onde o problema ocorria.

Já vi vários posts sobre o assunto peguntando como fazer isto. Então não tive outra saída… mãos-a-obra.

(uso Delphi 10.1)

A instância  “Application” possui um evento “application.onException” que permite indicar um método para redirecionar a saída de todas as exceções não tratadas pelo aplicativo.

[code lang=”pascal”]
// preparando o evento no formulário principal
procedure TForm1.DoAppException(sender:TObject; E:Exception);
begin
DoAppExceptionEvent(sender,e,true);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
Application.OnException := DoAppException;

end;
[/code]

[code lang=”pascal”]
uses Forms, System.Classes,System.SysUtils,System.RTTI;

// grava o log em disco
procedure ErrorLog(ATexto: string);
var
LArquivo: string;
LTextFile: textfile;
begin
LArquivo := ‘Erros_’ + formatDateTime(‘yyyymmdd’, date) + ‘.log’;
ForceDirectories(ExtractFilePath(LArquivo));

AssignFile(LTextFile, LArquivo);
try
{$I-}
Append(LTextFile);
{$I+}
if IOResult <> 0 then // se o arquivo nao existe, criar um novo;
Rewrite(LTextFile);
WriteLn(LTextFile, ATexto);
finally
CloseFile(LTextFile);
end;
end;

// monta a mensagem do log com base nos atributos do objecto que gerou a exceção
procedure DoAppExceptionEvent(Sender: TObject; E: Exception;
AShow: boolean = True);
var
LMsg: string;
function GetRTTILog(ASender: TObject): string;
var
LNome: string;
LContext: TRttiContext;
LType: TRttiType;
LProp: TRttiProperty;
LVar: TValue;
LTxt: String;
begin
result := ”;
if ASender=nil then exit;
result := ‘ClassName: ‘ + ASender.ClassName + #13#10;
LContext := TRttiContext.Create;
try
LType := LContext.GetType(ASender.ClassType);
for LProp in LType.GetProperties do
begin
try
LVar := LProp.GetValue(ASender);
LTxt := LVar.AsString;
if LTxt <> ” then
result := result + LProp.Name + ‘: ‘ + LTxt + #13#10;
except
end;
end;
finally
LContext.Free;
end;
end;

begin
try
LMsg := ”;
if assigned(Sender) then
begin
LMsg := GetRTTILog(Sender);
end;
LMsg := LMsg + ‘ Message: ‘ + E.Message;
ErrorLog(LMsg);
except
on ee: Exception do
ErrorLog(ee.Message);
end;
if AShow then
begin
E.Message := LMsg;
Application.ShowException(E);
end;
end;

[/code]

Quando trabalha com FMX no delphi é praticamente obrigatório trabalhar com um arsenal de imagens para melhorar o visual. Imagens são usadas para botões, títulos, rodapés, fundo…. imagem, imagem, imagem….

Para manter um padrão de tamanho e mesmo visual de imagem o TimageList é fundamental… além de não carregar a mesma imagem em vários locais diferentes…

Para isto tudo ficar bom, precisamos retirar as imagens da lista e passar para um componente que na maioria não tem aquela propriedade experta para marcar qual o indice da imagem.

Imagine que vc queira um fundo em um TRectangle e a imagem desejada esteja em um TimageList…

[code lang=”pascal”]
var
bmp:TCustomBitmapItem;
n:TSize;
begin
AImageList.BitmapItemByName(bgNome ,bmp,n);
ABitmap.Assign( bmp.Bitmap );
end;
[/code]

Nestes casos cai bem um TDatamodule para centralizar as imagens já que ele será utilizado em várias janelas do sistema.

When I was preparing the sample code for the article on Forin to FireDAC , I remembered an article by my friend Marcos Douglas  published in Object Pascal Programming  that speaks of the Imperative or Structured programming.

In computer science, imperative programming is a programming paradigm that describes computation as actions, statements or commands that change the state (variables) of a program. Much like the imperative behavior of natural languages ​​that express orders, imperative programs are a sequence of commands to the computer running.  (Wikipedia)

The article has as objective to discuss the end of FreeAndNil proposing the use of Interface PASCAL programming. Extracting the concept is possible to write code using directly interface which demonstrates the power of language forward to the new paradigms.

Rewriting TFDQuery interface, we can do:

[code lang=”pascal”]
TQueryIntf.New (FDConnection1)
   .table ( ‘sigcad a’)
   .FieldNames ( ‘code, name’)
   .Where ( ‘between code: codigo_de and: codigo_ate’)
   .ParamValue ( ‘codigo_de’, 1)
   .ParamValue ( ‘codigo_ate’, 5)
   .open
   .DoQuery (Procedure (ds: TDataset)
      begin
          memo1.Lines.Add ( ‘loaded’ + IntToStr (ds.RecordCount))
      end);
[/code]

A chave primária é responsável em manter a integridade da tabela no banco de dados e o índice de maior eficiência disponível.

Deveríamos ter uma regra obrigatória:
“NAO PODE DEIXAR DE INDICAR A CHAVE PRIMÁRIA”

Para localizar as tabelas que não possuem chave primária executar:

[code lang=”SQL”]
SELECT RDB$RELATION_NAME AS Tabela
FROM RDB$RELATIONS
WHERE RDB$RELATION_TYPE IN (0, 4, 5)
AND (RDB$SYSTEM_FLAG = 0 OR RDB$SYSTEM_FLAG IS NULL)
AND RDB$RELATION_NAME NOT IN
(SELECT RDB$RELATION_NAME FROM RDB$RELATION_CONSTRAINTS
WHERE RDB$CONSTRAINT_TYPE = ‘PRIMARY KEY’)
ORDER BY RDB$RELATION_NAME;
[/code]

fonte: Firebird Conference 2014 – Ivan Prenosil