Como ignorar arquivos no git que já tenham sido adicionados ao projeto

Olá! Estou aqui para postar sobre uma solução que precisei essa semana: ignorar arquivos modificados e existentes nos repositórios git.

Quando se quer ignorar arquivos que ainda não foram adicionados ao repositório git, basta adicioná-lo em um arquivo .gitignore. Também é possível utilizar o arquivo .git/info/exclude. Mas quando o arquivo já foi adicionado ao repositório, então é necessária uma outra solução.

Arquivos Dfm são necessários para iniciar o projeto , mas quando se foca o código .pas não é interessante que qualquer modificação no visual do Form , por exemplo , mudar a simples posição  de um componente ( atê mesmo não visual como um ClientDataSet), gere um post (Salvar)  do Arquivo . Pronto , so isso é o suficiente para que essa  alteração seja Visionada e comitada para o Servidor e todos os n-programadores ou colaboradores que fizerem  parte do projeto receba esta modificação e a compartilhe .

Para contornar isto realizei os quatro passos abaixo .


git update-index --assume-unchanged   Diretório/.gitignore

git rm --cached *.dfm

git commit -m "Adicionei a extensão .Dfm pq não seja Visionada"

Alterei o Arquivo .gitignore e inclui a condição *.dfm

Publicidade
Publicado em Avisos | Marcado com , , , , , | 1 Comentário

Editores de Propriedades de um Lista suspensa ( ComboBox) Dellphi

Estou meio que sem tempo , portanto não irei entrar em detalhes . Adoro escrever , mesmo que não seja um exímio escritor , portanto é com ressalvas que posto somente o resultado . Resumindo , quando se tem uma propriedade em forma de Lista sem nenhum esforço o Delphi a cria e a mesma aparece no Objcto Inspector neste formato . A questão que tento passar aqui , é trabalhar esta Lista , para se ter um controle desses Objetos adicionados a lista em Tempo de designed ( Para o Projetista de Componentes ) . Quais são os eventos e métodos que temos que escrever , lembrando que em uma pesquisa no Google só aparece quando temos uma propriedade em formato de   paDialog …Isto é quando dizemos a nosso  TPropertyAttribute  que deve vir aquele botãozinho com reticências do Lado … Mas queremos mais , queremos que nosso Atributo seja uma lista (paValueList) , que tenha aqueles sinalzinho de + para abrir as configurações do Objeto setado (paSubProperties) e que esta Lista esteja Ordenada (paSortList) . A propriedade que iremos Editar será do tipo TCustomConnection onde Herdam os principais componentes de conexão ( DBX , Firedac , Unidac etc ..) . Temos um componente ( TIDBEditDialog que no seu caso seria TSeuComponentEleitor) que tem uma propriedade Connection do Tipo TCustomConnection (que no seu caso seria TSuaPropriedadeEleitor) .  A ideia deste Artigo é que caso necessite vc possa adaptar ao seu componente e a sua necessidade . Portanto como eu disse no Inicio , mínima escrita e mais códigos . Mãos a Obra


unit uConnectionEditors;

interface

uses

DesignEditors ,System.Classes , DesignIntf ;

type

TConnectionEditors = class( TPropertyEditor)
public
FGetValuesStrProc: TGetStrProc;
procedure ReceiveComponentNames(const S: string);
function GetAttributes: TPropertyAttributes; override;
procedure GetProperties(Proc: TGetPropProc);override;
function GetSelections: IDesignerSelections;
function FilterFunc(const ATestEditor: IProperty): Boolean;
function AllEqual: Boolean; virtual;
procedure GetValues(Proc: TGetStrProc); override;
function GetValue(): string; override;
procedure SetValue(const Value: string); override;
end;

procedure Register;

implementation

O que se tem aqui , uma classe a TConnectionEditors (que genericamente será a TSuaClasseEleitor )que descende da classe TPropertyEditor ( premissa básica ) . E redefinimos vários métodos marcando-os com a directiva Override . O Primeiro método é o GetAttributes . Vamos a implementação do mesmo .

implementation

uses
System.TypInfo , Data.DB ,System.SysUtils , uOndeEstaseuComponteEleitor;


function TConnectionEditors.GetAttributes: TPropertyAttributes;
begin
 result:= inherited GetAttributes + 
         [paSubProperties,paValueList,paSortList];
end;

Conforme Combinado definimos que o Objeto redefina as Subpropredades (paSubProperties) e que esteja em uma Lista Suspensa (paValueList ) e que esteja em Ordem (paSortList) . O Primeiro passo é ler a Lista . Isto é , ler os possíveis componentes em designed que podem serem associados ao tipo de propriedade . No meu caso a propriedade é TCustonConnection , logo um SqlConnection , um UniConnetion , um FdConnection tem que ser Lido e acrescentado á Lista caso necessite e não faça restrição. ( Já disse antes que isto é feito por sorte automaticamente pelo delphi desde que não se interfira no Processo . Como eu estou interferindo vou ter que montar esta Lista no Braço . Segue o método

//GetValues Não é o seu Habitual GetValue
procedure TConnectionEditors.GetValues(Proc: TGetStrProc);
begin
 inherited;
 FGetValuesStrProc := Proc;
 try
 Designer.GetComponentNames(GetTypeData(TypeInfo(TCustomConnection)),
 ReceiveComponentNames);
 finally
 FGetValuesStrProc := nil;
 end;
end;

//Podemos fazer o que quiser com este Tcomponent ai embaixo . 
//Um Mundo ... ReceiceComponent de dá poder de vôo
procedure TConnectionEditors.ReceiveComponentNames(const S: string);
var
 Temp: TComponent; //poder de Vôo
 Intf: IInterface;
begin
 Temp := Designer.GetComponent(S);
 FGetValuesStrProc(S);
 if Assigned(FGetValuesStrProc) and
 Assigned(Temp) and
 Supports(TObject(Temp), GetTypeData(GetPropType)^.Guid, Intf) then
 FGetValuesStrProc(S);
end;
Publicado em Artigos | Marcado com , , , , , , | Deixe um comentário

Retirar extrair Números ou Letras de uma String

Copy , For in  , length , String  , Pos , TRegEx , etc , são inúmeros os caminhos que levam a Roma . Mas gostaria mesmo que este caminho fosse nativo . Algo simples sem ter que ficar inventando ou reinventando e criando novos caminhos. Atê xe2 não conheço , o que fiz foi apenas um modo que pode ser adaptado e redefinido conforme necessidade de Extrair pedaçoes da String . Com o mesmo código posso retirar todo os números , todos as letras ou uma letra específica , ou uma letra e determinados números  . Enfim a função abaixo na minha concepção é mais estendida e flexivel do que as “n” genéricas que obtemos ao procurar algo do género . Por isto compartilho


const
IntegerSet = [#33..#47,#58..#255] ;
StringSet = [#46..#57];

//defina o seu set ??? Extensão de funcionalidade

function ExtractStringsOnly(_Separators:TSysCharSet;const Str:String):String;
Var
lista:TStrings;
begin
lista:=TStringList.Create;
try
ExtractStrings(_Separators,[],pchar(str),lista);
lista.StrictDelimiter:=True;
lista.QuoteChar:=#0;
result:=StringReplace(lista.DelimitedText,lista.Delimiter,EmptyStr,[rfReplaceAll]);
finally
lista.Free;
end;
end;

Para utilizar é simples


showmessage(ExtractStringsOnly(IntegerSet,Edit1.Text));
showmessage(ExtractStringsOnly(StringSet,Edit1.Text));

Apenas uma simples função que pode ser adaptada a gosto e reutilizada sem maiores transtornos. No mais meu muito obrigado .

ps) Link simples sobre operações com strings

http://delphi.about.com/od/beginners/l/blrtlstringhand.htm

Publicado em Dicas | Marcado com , , , , , , , | Deixe um comentário

Simples , simplesmente simples mas genial …

Quando leio um artigo , uma das primeiras coisas que observo é o bloco try finally quando utilizado na proteção de vazamento de recursos . Se o autor errar na estrutura deste bloco desconfio do restante , olho meio que desconfiado e pensativo …. Hummmmm

Bloco UM

try
Objeto:=TObjeto.create;
codigodelphi.. bla bla bla
finally
objeto.free;

Bloco Dois

Objeto:=TObjeto.create;
try
codigodelphi ...bla bla bla
finally
objeto.free;

Já sabido e notório que a estrutura (Bloco Dois) anterior é a correta .

A primeira estrutura é falha , visto que se ocorrer uma exceção na
instanciação do Objeto e ele não for criado o finally será executado
e teremos um Free de um Objeto que simplesmente pode ser Inválido , logo
podemos obter AV (Acesso Violados) na estrutura Um , o que é evitado ao
utilizarmos a estrutura Dois .

Só para constar existem muitos objetos que para serem instanciados de modo
correto depende de uma série de parâmetros . Por exemplo a instrução

var
Leitor:TDbxReader;
Stream: TStream;
begin
.....
//obtendo o retorno da consulta em formato Streamm
Stream:= Leitor.Value[_BlobFieldName].GetStream(false);

Veja que o Objeto Stream será instanciado pelo método GetStream da
da Class TDbxValue

function TDBXValue.GetStream(AInstanceOwner: Boolean): TStream;
begin
TDBXValueType.InvalidTypeAccess(ValueType.DataType,
TDBXDataTypes.BytesType);
Result := nil;
end;

So estou fazendo estas ponderações , para explicar que a instanciação
de um objeto pode ocorrer ou não e não devemos olhar simplesmente para
a instrução Stream:= Tstream.Create ; ou hipoteticamente
Objeto:=TObject.create ; .

O Conceito é acima disso , então passamos a entender a diferença entre
os Blocs try finally apresentados acima

Isto é de fato simples , simplesmente simples mas GENIAL ??? Eu não acho .

Mas continuemos vamos olhar para blocos try finally aninhados . Seguindo o
conceito apresentado aqui é de se esperar o seguinte Modelo

ObjetoUm:=TObject.create;
try
codigos delphi ... bla bla bla
ObjectDois:=TObject.Create;
try
codigos delphi ... bla bla bla
finally
ObjectDois.Free;
end;
finally
objectUm.Free;
end;

GENIAL ??? Esta estrutura é a usual , é a corriqueira , é funcional .
A medida que cresce o número de objetos a leitura é dificultada , e para
cada quadro try-finally será necessário alguns ciclos extra de CPU .
Logo esta estrutura é tudo MENOS GENIAL ..

Agora olhem

ObjetoUm:=Nil;
Objetodois:=Nil;
try
ObjetoUm:=TObject.create;
codigos delphi ... bla bla bla
ObjectDois:=TObject.Create;
codigos delphi ... bla bla bla
finally
ObjectDois.Free;
ObjetoUm.Free;
end;

Simples , Simplesmente simples e acima de tudo GENIAL ..

http://www.monien.net/creating-multiple-objects-using-try-finally/

Tem outras variantes , principalmente nos comentários , merece os olhares
atentos dos leitores famintos por conhecimento

Utilizando esta tecnica estamos de acordo com o Padrão de projeto
KISS . Keep it Simple Stupid (KISS): que simplesmente pede para sermos
simples . Simples e genial . um grande abraço e muito obrigado

ps) Me referi a este link , mas encontrei esta genialidade em outros
lugares . Apenas justifico e respaldo a minha opinião e sinceramente
não sei qual a origem de fato dos créditos . Mas agradeço .



 

Publicado em Dicas | Marcado com , , , | 5 Comentários

Permitindo Somente apenas números com Virgula num edit e muito mais

Impedir a digitalização de caracteres que não satisfazem determinadas condições como por exemplo caractere alfa em campos onde o esperado seja números inteiros ou reias é uma dica muito difundida nos meios de comunicação . Talvez para os iniciantes na “languagem” Delphi seja algo novo , a grande maioria sabe que não a é . Nosso objetivo não é massificar a Dica folclórica , e sim tentar acrescentar algo que possa trazer algum beneficio . Parto do princípio da Flexibilidade , da Reutilização e do  Reaproveitamento . Falar nesses pilares nos remete a Classe , a Orientação de Objetos , a Padrões de Projeto . Enfim , nos remete a um Delphi que poucos Javanianos conhecem .
Sobre esses Pilares (Flexibilidade , Reutilização , Reaproveitamento) , o Delphi caminhou em passos largos ao incorporar a Regexp (Delphixe) e reformular a Rtti (delphi2010). Novos recuros , novas funcionalidas e a Dica Foclórica ( Key in [‘0’..’9′] ) ja não assusta tanto . Eu particularmente ja a considero decapreted , literalmente virou Foclore.

Vamos a definição de nossa classe que esta definida na uses uKeyPress


unit uKeyPress;

interface

type
TKeyPress = class
public
//Definie qual o conjunto de caracter permitido para o Controle
class Var FPattern:String;
//Método que motificara ou não o Key digitado no controle
class procedure KeyPress(Sender: TObject; var Key:Char);
private
//Método interno que montará um previem do Texto digitado no controle
//esses texto será analizado se é um texto válido ou não
class function Formats_Inputs(sender:Tobject;const key:Char):String;
end;

Segue sua Implementation


Sua Implementation

class function TKeyPress.Formats_Inputs(sender: Tobject;const key:Char): String;
const
Property_Array : Array [0..2] of String = ('Text','SelLength','SelStart');
var
Context:TRttiContext;
Prop:TRttiProperty;
Tipo:TRttiType;
_SelStart:Integer;
_SelLength:integer;
begin
result:=EmptyStr; //inicializa
Tipo:=Context.GetType(Sender.ClassType);
for Prop in Tipo.GetProperties do //varre as propriedades
case AnsiIndexText(Prop.Name,Property_Array) of
0:result:=Prop.GetValue(sender).ToString; //obtem o texto
1:_SelLength:=strtoint(Prop.GetValue(sender).ToString); //obtem a selecao
2:_SelStart:= strtoint(Prop.GetValue(sender).ToString); //obtem a posicao cursor
end;
_SelStart:=_SelStart+_SelLength+1; //posicao de inserçao do carracter key sera a soma
Insert(Key,result,_SelStart); //inseri no texto original o Key digitado na Posicao
end;

class procedure TKeyPress.KeyPress(Sender: TObject; var Key: Char);
var
_Mathes:TMatchCollection;
_Mathe:TMatch;
_Input:String;
_Output:String;
begin
_Input:=TKeyPress.Formats_Inputs(sender,Key);//retorna o texto do Controle inserido o Key
_Mathes:=TRegEx.Matches(_Input,FPattern); //Carrega a colecao seguindo o texto e o Patternn
for _Mathe in _Mathes do //varre a colecao e armazena cada item na variavel output
_Output:=_Output+_Mathe.Value;
if (_Output <> _Input) then  //compara se são diferentes
key:=#0; //este caracter não esta permitido segundo o Pattern.
end;

end.

Vamos utilizar esta classe de várias formas diferentes seguindo algumas restrições . Essas restrições podem ser impostas por critérios técnicos na construção do Softaware ou por desejo do Cliente . De modo que temos Flexibilidade nas Regras de Negócio . Vou tentar dar alguns exemplos que acho que pode ser úteis . Alguns triviais ,como por exemplo de só aceitar números inteiros , outros um pouco mais complexos . Segue abaixo alguns Patterns para algumas situações descritas abaixo :

//Uses System.RegularExpressions;
procedure TForm4.Rdg_PatternClick(Sender: TObject);
var
x,y:integer;
begin
y:=SEdt_Y.Value;  //È um TSpinEdit;
x:=SEdt_X.Value;  //È um TSpinEdit;
Edit1.Clear;  //È um TEdit
case Rdg_Pattern.ItemIndex of  // Rdg_Pattern é TRadioGroup
//so inteiro
0:TKeyPress.FPattern:='\d*[\b]?';
//Real
1:TKeyPress.FPattern:='^\d+'+
TRegEx.Escape(FormatSettings.DecimalSeparator) +'?\d*[\b]?';
//Real com Duas casas decimais
2:TKeyPress.FPattern:='^\d+'+
TRegEx.Escape(FormatSettings.DecimalSeparator)+'?\d{0,2}[\b]?';
//Real com tres Casas decimais
3:TKeyPress.FPattern:='^\d+'+FormatSettings.DecimalSeparator+'?\d{0,3}[\b]?';
//Real com ate casas 4 Inteiras e 3 casas decimais
4:TKeyPress.FPattern:='^\d{1,4}('+
TRegEx.Escape(FormatSettings.DecimalSeparator)+'\d{0,3})?[\b]?';
//Real com ate Y Casas Inteiras e X Casas Decimais
5:TKeyPress.FPattern:='^\d{1,'+inttostr(Y)+'}('+
TRegEx.Escape(FormatSettings.DecimalSeparator)+'{1}\d{0,'+inttostr(x)+'})?[\b]?';
//Real com ate Y Casas Inteiras e X Casas Decimais precedido deSinal de + ou De -
6:TKeyPress.FPattern:='(^((-|\+)?[\b]?\d{0,'+inttostr(Y)+'}){1}('+
TRegEx.Escape(FormatSettings.DecimalSeparator)+'{1}\d{0,'+inttostr(x)+'})?)[\b]?';
//Procure a acessoria do Professor Mario Guedes
7:TKeyPress.FPattern:='Mario Guedes Resolve';
end;
Edit1.SetFocus;
self.Caption:=TKeyPress.FPattern;
end;

Os mais atentos devem ter notado uma menção singela ao Professor Mario Guedes .  na sétima opção do Radio Group. Para aqueles que não conhecem , acredito que a grande maioria da nossa comunidade já teve contato com os artigos do Amigo . Destaco seu pioneirismo tanto em e revista quanto em conferências oficiais da Embarcadero , na orientação de Expressões Regulares . Eu mesmo já me socorri várias vezes de seu conhecimento farto e nada mais justo do que agradecer neste espaço humilde ( não faço música de Fank e nem tenho popozão ) mas não poderia deixar o seu nome e nem o seu blog passar despercebido  http://eugostododelphi.blogspot.com.br/

Dando sequência , destaco  que esses Patterns não são únicos e nem os melhores para cada situação . Vcs podem  obter o mesmo resultado de outras formas e ate mesmo de modo mais eficientes . Esses Patterns são a minha abstração para o problema . Por exemplo , tento não permitir números como o   ” ,1233″ onde não existe nada antes da Vírgula . Enfim , testando e adaptando para cada situação .
Para Utilizar a classe é simples


procedure TForm4.Edit1KeyPress(Sender: TObject; var Key: Char);
begin
// TKeyPress.FPattern **** Definido No Oclick do Rdg_PatternClick
//verifica se o caracter digitado é permitido segundo a definicao do Pattern
TKeyPress.KeyPress(Sender,Key);
end;

Caso haja interesse no Exemplo Completo , onde estão as Implementações de todos métodos , Fique a vontade e entre em contato que estarei disponibilizando .

No mais meus agradecimentos pela paciência e leitura e caso alguém necessite
questionar esse trabalho  , bem como dúvidas na implementação do exemplo ,utilize à vontade 0 espaço de comentários abaixo. Peço por último , por questões de gentileza e cordialidade , bem como respeito aos direitos autorais , que se alguém quiser acrescentar esta classe em algum veiculo de comunicação , que faça referência a fonte da informação original .  Vou ficando por aqui , muito obrigado

Publicado em Dicas | Marcado com , , , , , , , , , | 2 Comentários

Exportar Visualizar Tabela no WebBrowser do HTML ou Vice Versa

Dando continuação ao artigo publicado anteriormente https://marcosalles.wordpress.com/2014/01/09/exportar-html-para-dataset/ , imagine que se tenha uma Tabela HTML em formato txt inserido no seu próprio Disco Físico e onde se tenha a necessidade de carrega-la em um WebBrowser . Abaixo segue uma visualizaçãodo que estamos tratando

Exporta_Tabela_HTML_do_WebBrowser_para_Txt_ou_Vice_Versa

Para Obter este resultado , utilizamos o procedimento abaixo

ps) Dê uses a Unidade  Winapi.ActiveX

function ShowHtml(mWebBrowser: TWebBrowser;const Arq:String):
                                                           Boolean;
var
vMemoryStream: TMemoryStream;
Sl:TStrings;
begin
Sl:=TStringList.Create;
try
Sl.LoadFromFile(Arq);
Result := False;
if not (Assigned(Sl) and Assigned(mWebBrowser)) then
Exit;
mWebBrowser.Navigate('about:blank');
if not Assigned(mWebBrowser.Document) then
Exit;
vMemoryStream := TMemoryStream.Create;
try
Sl.SaveToStream(vMemoryStream);
try
vMemoryStream.Position := 0;
Application.ProcessMessages;
(mWebBrowser.Document as IPersistStreamInit).
      Load(TStreamAdapter.Create(vMemoryStream));
Result := True;
except
Exit;
end;
finally
vMemoryStream.Free;
end;
finally
Sl.Free;
end;
end;

Para Utilizarmos esta function passamos o componente WebBrowser e o
caminho Físico do Arquivo HTML que esta armazenado no Disco Físico .

ShowHtml(SeuWebBrowser,'NomeDoArquivo.HTML');

Agora o Processo Inverso .. Que é exatamente ter um WebBrowser carregado
com uma Tabela de algum site ou de algum lugar e transformar o conteúdo
desta visualização em arquivo HTML no Disco rígido

function SaveHTMLSourceToFile(WB:TWebBrowser;const FileName: string)
                                                          :Boolean;
var
PersistStream: IPersistStreamInit;
MStream  :TStringStream;
Stream: IStream;
SaveResult: HRESULT;
begin
result:=False;
Application.ProcessMessages;
PersistStream := WB.Document as IPersistStreamInit;
MStream := TStringStream.Create('');
try
Stream :=  TStreamAdapter.Create(MStream , soReference) as IStream;
SaveResult := PersistStream.Save(Stream, True);
if FAILED(SaveResult) then
MessageBox(Handle, 'Erro ao salvar o conteudo HTML', 'Error', 0)
else
result:=true;
finally
end;
end;

Para Utilizarmos esta function passamos o componente WebBrowser e o
caminho Físico do Arquivo HTML que esta armazenado no Disco Físico .

SaveHTMLSourceToFile(WebBrowser1,'NomeDoArquivo.HTML');

Se tudo ocorrer bem o Arquivo foi salvo no Disco e pode ser visualizado
em um Memo por exemplo , com a seguinte instrução

DocHtmlMemo.Lines.Clear;
DocHtmlMemo.Lines.LoadFromFile(NomeDoArquivo.HTML);

Sua Visualização , para esta tabela deve ser próxima da imagem a seguir

Visualizar_Tabela_HTML_do_WebBrowser_para_Txt_ou_Vice_Versa
A ultima função ou procedimento das quatro citadas anteriormente é a
transformação de DataSet em HTML para ser consumido ou no WebBrowser
ou um navegador qualquer . Esta função é bem conhecida no meio e não
há dificuldade em encontra-la através de uma simples “Googlada” .
Deixo registrado que as duas funções apresentadas não são minhas,
sofreram salvo engano pequenas modificações e mantive o nome das
variáveis originas para caso necessitar possa ter recurso para localizar
o Link Original bem como os seus créditos .

No mais meus agradecimentos pela paciência e leitura e caso alguém necessite
questionar esse trabalho esteja à vontade no espaço de comentários abaixo , bem
como dúvidas na implementação do exemplo . Vou ficando por aqui , muito obrigado

Publicado em Dicas | Marcado com , , , , , , , , | 2 Comentários

Exportar HTML To para DataSet

São quatro pequenas dicas mas que podem serem úteis no momento que a troca de informações em vários formatos entre as mais diversas tecnologias se fazem necessária corriqueiramente . Três destas dicas são encontradas em sites afins espalhados pela net e cujos créditos apesar de não estar explícitos , ressalto a sua existência . Porém a transformação ou a Exportação de HTML para DataSet , de nodo direto e fácil não encontrei apesar de ter feito  uma busca minuciosa pelo google . È verdade que encontrei componentes pagos que fazem mais do que isto e sugestões de bibliotecas que aparentemente resolveriam esta necessidade , porém decidi postar uma solução que   possa ser útil em algum momento de nossas vidas …

Após análise da classe TDSTableProducer da Unit Web.DBWeb verifiquei o modo específico da Transformação que o Delphi faz quando transforma o DataSet em HTML .. No meu caso que  sou Delphiano  , me interressa  a integração com o  Delphi . Portanto vou  fazer a transformação do HTML para DataSet seguindo as mesmas regras específicas atribuídas pelo Delphi quando ele faz o processo Inverso de modo nativo , que é exatamente , transformar o DataSet em HTML . Este processo se desenrola na funcion HTMLTable que esta definida na uses Web.DBWeb;

function HtmlTable(DataSet: TDataSet;
                   DataSetHandler: TDSTableProducer;
                   MaxRows: Integer): string;

A função nativa utiliza os atributos da classe TDSTableProducer e algumas constantes como é o caso da   EndRow  definido como  EndRow = ‘</TR>’;    { do not localize }  , as Tags <TH>,   <TD> e claro o fechamento  </TH> e </TD> respectivamente . Lembramos ainda possíveis erros por case sensitive e  espaços vazios  indesejáveis entre as tags que definem a Tabela . Então se pressupõe que haja uma correta formatação prévia no documento HTML o qual se deseja transformar . Visto este “PARSINGs” preliminares , vamos a classe que irá fazer esta transformação


unit uHtmlToDataSet;

interface

uses
Datasnap.DBClient , Web.DBWeb, System.Classes ,  System.SysUtils;

Type
TExceptionLoad_FArray_Fields = class (Exception);
TExceptionLoad_Pattern =class (Exception);
TExceptionLoad_FList_Dados =class (Exception);
TExceptionLoad_FArray_Width =class (Exception);
TException_Create_Fields =class (Exception);
TExceptionLoad_FCdsDataSet =class (Exception);

Type
THtmlToDataSet = class
private
class Var FCdsDataSet:TClientDataSet;
class var FsHtml:TStrings;
class var FList_Dados:Tstrings;
class Var  FArray_Fields:TArray<String>;
class Var  FArray_Width:TArray<Integer>;
class Var sPattern:String;
class procedure Load_FArray_Fields;
class procedure Load_Pattern;
class procedure Load_FList_Dados;
class procedure Load_FArray_Width;
class procedure Create_Fields;
class procedure Load_FCdsDataSet;
class function RemoveEspaco(const Texto: string): string;
public
class procedure ExpHtmlToDataSet(CdsDataSet: TClientDataSet;
                       const Arq:String;const cDelimiter:Char='#');
end;

implementation

Antes das implementações dos métodos destas classes , quero destacar que o coração de tudo esta na chamada ao método  ExpHtmlToDataSet da classe  THtmlToDataSet . Observe que é o único método público , os demais só serão utilizados dentro desta Unidade .

Vamos a implementação do Método  ExpHtmlToDataSet

implementation

uses
System.RegularExpressions , Data.DB;

{ THtmlToDataSet }
//Note o const cDelimiter:Char='#'

class procedure THtmlToDataSet.ExpHtmlToDataSet
                  (CdsDataSet: TClientDataSet;const Arq: String ;
                                        const cDelimiter:Char='#');
begin
try //Este Try não é Do Bloco FsHtml antes que alguém corrija
FsHtml:=TStringList.Create;
try
//carrega o Arquivo HTML em formato txt na Lista
FsHtml.LoadFromFile(Arq);

FList_Dados:=TStringList.Create;
try
 //Define um Delimitados . Este Delimitador por sua vez é um tag E
//não pode estar contido no HTML. Escolhi como default o Caractere #
FList_Dados.Delimiter:=cDelimiter;
FList_Dados.StrictDelimiter:=True;
//Associa
FCdsDataSet:=CdsDataSet;
//Operações básicas caso esteja ativo , limpa o conteúdo
if FCdsDataSet.Active then
begin
FCdsDataSet.EmptyDataSet;
FCdsDataSet.Close;
end;

FCdsDataSet.Fields.Clear; //Limpamos os Fields caso existam

//carrega os Nomes fields que estão na Tag <TR><TH>...</TH></TR>
//do HTML . Guarda essas informações no Array FArray_Fields
Load_FArray_Fields;
//define uma Pesquisa que será utilizado em expressões Regulares para
//Varrer tudo que esta dentro das tags <TR><TD>...</TD></TR>  do HTML
Load_Pattern;
//Utlizando o Pattern guarda esse dados em um Lista . Todos os Dados
//contidos dentre as Tags <TR>...</TR> São guardados Linha por Linha
Load_FList_Dados;
//define o Tamanho Máximo dos campos Strings . Guardamos
//essas informações No Array FArray_Width;
Load_FArray_Width;
//Utilizando os Nomes dos Fields e os tamanhos dos respectivos
//campos Criamos os Fields e o associamos ao FCdsDataSet
Create_Fields;
//Carregamos o FcdsDataSet , varrendo um a um os Registros
//dos dados armazenados na Lista FList_Dados
Load_FCdsDataSet;
finally
FList_Dados.Free;
end;
finally
FsHtml.Free;
end;
finally
//setamos as Variáveis estáticas para podermos reutilizar os métodos
setlength(FArray_Fields,0);
SetLength(FArray_Width,0);
sPattern:=EmptyStr;
end;
end;

Para esta Dica imagine que vc tenha uma Tabela armazenada  como Arquivo Txt formatada como sendo  HTML de modo que qualquer Browser leia este Arquivo . Um exemplo Simples de uma tabela desta seria

<Table Width="100%" Border=1>
<TR><Th> Name    </TH><TH>Capital</TH><TH>Continent</TH><TH>Area</TH><TH>Population</TH></TR>
<TR><TD>Argentina_</TD><TD>Buenos Aires</TD><TD>South America</TD><TD>2777815</TD><TD>32300003</TD></TR>
<TR><TD>Bolivia</TD><TD>La Paz</TD><TD>South America</TD><TD>1098575</TD><TD>7300000</TD></TR>
<TR><T D>Brazil</TD><TD>Brasilia</TD><TD>South America</TD><TD>8511196</TD><TD>150400000</TD></TR>
<TR><TD>Canada</TD><TD>Ottawa</TD><TD>North America</TD><TD>9976147</TD><TD>26500000</TD></TR>
<TR><TD>Chile</TD><TD>Santiago</TD><TD>South America</TD><TD>756943</TD><TD>13200000</TD></TR>
<TR><TD>Colombia</TD><TD>Bagota</TD><TD>South America</TD><TD>1138907</TD><TD>33000000</TD></TR>
<TR><TD>Cuba</TD><TD>Havana</TD><TD>North America</TD><TD>114524</TD><TD>10600000</TD></TR>
<TR><TD>Ecuador</TD><TD>Quito</TD><TD>South America</TD><TD>455502</TD><TD>10600000</TD></TR>
<TR><TD>El Salvador</TD><TD>San Salvador</TD><TD>North America</TD><TD>20865</TD><TD>5300000</TD></TR>
<TR><TD>Guyana</TD><TD>Georgetown</TD><TD>South America</TD><TD>214969</TD><TD>800000</TD></TR>
<TR><TD>Jamaica</TD><TD>Kingston</TD><TD>North America</TD><TD>11424</TD><TD>2500000</TD></TR>
<TR><TD>Mexico</TD><TD>Mexico City</TD><TD>North America</TD><TD>1967180</TD><TD>88600000</TD></TR>
<TR><TD>Nicaragua</TD><TD>Managua</TD><TD>North America</TD><TD>139000</TD><TD>3900000</TD></TR>
<TR><TD>Paraguay</TD><TD>Asuncion</TD><TD>South America</TD><TD>406576</TD><TD>4660000</TD></TR>
<TR><TD>Peru</TD><TD>Lima</TD><TD>South America</TD><TD>1285215</TD><TD>21600000</TD></TR>
<TR><TD>United States of America</TD><TD>Washington</TD><TD>North America</TD><TD>9363130</TD><TD>249200000</TD></TR>
<TR><TD>Uruguay</TD><TD>Montevideo</TD><TD>South America</TD><TD>176140</TD><TD>3002000</TD></TR>
<TR><TD>Venezuela</TD><TD>Caracas</TD><TD>South America</TD><TD>912047</TD><TD>19700000</TD></TR>
</Table>

Para utilizar a classe é bem simples basta chamar

//Observe que estamos Utilizando o cDelimiter Padrão # definido
THtmlToDataSet.ExpHtmlToDataSet(SeuClientDataSet,'NomeDoArquivo.HTML');
SeuClientDataSet.Open;
SeuClientDataSet.SaveToFile(''NomeDoArquivo.xml');

Após isto será se tudo ocorrer bem , será criado no Diretório da aplicação , um Arquivo Xml em formato DataPacker ( Padrão ClientdatSet delphi ) , com o seguinte formato

HtmlToDataSet

Caso haja interesse na Classe Completa , onde estão as Implementações dos outros métodos que compõem a Classe Fique a vontade e entre em contato que estarei disponibilizando . O motivo é que estou sem tempo para escrever um segundo artigo

Vou ficando por aqui , no mais meus agradecimentos pela leitura e paciência . Se por ventura alguém tiver alguma dúvida na utilização desses métodos pode entrar em contado que será atendido. Tb poderei esta disponibilizando os fontes exemplos com os todos os métodos  . Peço por último , por questões de gentileza e cordialidade , bem como respeito aos direitos autorais , que se alguém quiser acrescentar esta classe em algum veiculo de comunicação , que faça referência a fonte da informação original . No mais meu muito obrigado.

Links Assuntos Relacionados

  • Exportar WebBrowser para Arquivo HTML …
  • Importar Arquivo HTML para WebBrowser
  • Expotar DataSet para Arquivo HTML

https://marcosalles.wordpress.com/2014/01/10/visualizar-tabela-no-webbrowser-do-html-ou-vice-versa/

Publicado em Dicas | Marcado com , , , , , , , , , | 22 Comentários

Classe genérica para instanciar Objetos Formularios dinamicamente

Há tempo eu publiquei neste espaço dois artigos intitulados

https://marcosalles.wordpress.com/2012/04/06/instanciar-objetos-create-objetos-por-strings-pelo-nome-da-classe-passado-por-parametros/

https://marcosalles.wordpress.com/2012/11/17/instanciar-objetos-create-objetos-por-strings-pelo-nome-da-classe-passado-por-parametros-parteii/

São dois post não muitos triviais , porém embute um conceito de fáctory , um Padrão , uma fábrica de objetos. E porque esses dois threads estão sendo mencionados agora ? Bem , devido a semelhança no assunto , pois no post atual veremos como instanciar objetos de modo genérico de um modo muito mais fácil quando comparado as duas formas acima citada .

unit uReturn_TObject;

interface

uses
rtti,
generics.collections;

Type
TReturn_TObject = class
class function Instanciar<T:Class>:T;
end;

implementation

{ TReturn_TObject<T> }

class function TReturn_TObject.Instanciar<T>: T;
var
valor: TValue;
ctx: TRttiContext;
tipo: TRttiType;
tipoInstancia: TRttiInstanceType;
begin
tipo := ctx.GetType(TypeInfo(T));
tipoInstancia:= (Ctx.FindType(Tipo.QualifiedName) as TRttiInstanceType);
Valor:=Tipoinstancia.MetaclassType.Create;
Result := valor.AsType<T>;
end;

end.

Para utilizar é simples

var
form:TSeuForm;
begin
form:=TReturn_TObject.Instanciar<TSeuForm>.Create(nil);
try
form.ShowModal;
finally
form.Free;
end;

de modo genérico , podemos instanciar qualquer objetos e formulários genericamente … Logo temos uma classe que pode ser reaproveitada em vários projetos e com responsabilidade única … Porém devemos destacar um adendo em relação aos dois post mencionados acima e com códigos mais complexos  . A desvantagem deste modo e das “n” formas espalhadas na NET para intanciar objetos de algum modo ,  é que o devemos dar uses as Classes dos objetos e forms que necessitamos instanciar . Ou seja , temos um acoplamento entre as entidades Envolvidas . O Form Principal deverá conhecer  o TSeusForm , os quais estarão sendo Instanciados pela Classe TReturn_TObject .Destaco então que esta dependência não ocorre nos dois modos apresentados anteriormente ..

Conclusão .. Custo x Benefício sempre é algo a ser considerado em diversos situações corriqueiras e na programação isto não se faz diferente . Não estou aqui falando mau da Classe  apresentado , só estou alertando para algo que possa passar despercebido .

Vou ficando por aqui , no mais meus agradecimentos pela leitura e paciência . Se por ventura alguém tiver alguma dúvida na utilização desses métodos pode entrar em contado que será atendido. Tb poderei esta disponibilizando os fontes exemplos com os todos os métodos  . Peço por último , por questões de gentileza e cordialidade , bem como respeito aos direitos autorais , que se alguém quiser acrescentar esta classe em algum veiculo de comunicação , que faça referência a fonte da informação original . No mais meu muito obrigado.

Publicado em Dicas | Marcado com , , , , , , , | 7 Comentários

Classe Genérica para Decorar Tipos Enumerados

Inicialmente vamos imaginar que tenhamos definido um tipo Enumerado como abaixo descrito

type

TTiposCartoes = (tcCredito ,tcDebito);

Como fazer para Decorar o Tipo TTiposCartoes para que  possamos recuperar uma informação específica em rumtime  ? Antes de prosseguir dois adendos se fazem necessário

1)    Para Decorar uma classe ou um Tipo utilizamos o TCustomAttribute e passamos essas informações no contructor da classe .

2)O grande entrave é a limitação do parâmetro que deve ser passado no contructor , que segue as seguintes regras :

  • Apenas expressões constantes são permitidas, incluindo conjuntos, strings s e expressões ordinais.
  • Out e parâmetros Var não podem ser usadas, porque eles necessitam de avaliação do tempo de execução de endereços dos parâmetros passados.
  • Add()  operador intrínseco @ e não poderá ser usado.
  • O TypeInfo operador () pode ser usado para passar tipo de informação, pois os endereços de blocos RTTI são conhecidos em tempo de compilação.
  • Referências de classe são permitidos, pois os endereços metaclasse (como o caso do TypeInfo ()) são conhecidos em tempo de compilação.

Esta limitação nos remete a escrever algo semelhante as instruções abaixo :

type

[TEnumAttribute('Cartão de crédito')]
[TEnumAttribute('Cartão de débito')]
TTiposCartoes = (tcCredito ,tcDebito);

Onde a classe TEnumAttribute descende da classe TCustomAttribute e definida e implementada como abaixo :

TEnumAttribute = class(TCustomAttribute)
private
FCaption: string;
public
constructor Create(ACaption : String);
property Caption : string read FCaption;
end;

..........

{ TEnumAttribute }
constructor TEnumAttribute.Create(ACaption: String);
begin
Self.FCaption:=ACaption;
end;

Atê agora nada de mais , estamos apenas definindo e moldando nosso problema sem ainda apresentar ainda a  solução . Vamos definir agora nossa classe  que será responsável por retornar o Valor decorado conforme o estado da variavel do Tipo enumerado


TGenerico = 0..255;

TConvert<T:record> = class
public
class function  GetCaptionCartao(const eEnum:T): string;
end;

implementation

uses
TypInfo,Rtti,classes;

{ TConvert<T> }

class function TConvert<T>.GetCaptionCartao(const eEnum: T): string;
var
P:PInteger;
Num:integer;
_ctx: TRttiContext;
_typ: TRttiType;
ALista : TStringList;
_ca: TCustomAttribute;
begin
Result := 'Tipo não encontrado';
ALista := TStringList.Create;
_ctx := TRttiContext.Create;
try
_typ := _ctx.GetType(TypeInfo(T));
if Assigned(_typ) then
begin
for _ca in _typ.GetAttributes do
begin
if _Ca is TEnumAttribute then
ALista.Add(TEnumAttribute(_ca).Caption);
end;
if Alista.Count <= 0 then
exit;
P:=@eEnum;
Num:=integer(TGenerico((P^)));
Result := ALista.Strings[Num];
end;
finally
_ctx.Free;
ALista.Free;
end;
end;

O que gostaria de destacar é a Independência , o desacoplamento entre as Entidades envolvidas . Perceba que a classe TConvert<T> , não tem conhecimento do Tipo Enumerado … Possibitando o reaproveitamento entre os mais diversos Enums , desde que Decorado pela Classe TEnumAttribute . Isto nos dá uma gama enorme de reaproveitamento , um dos pilares da Orientação ao Objeto

Vamos alguns exemplo de utilização

Implemetando e Decorando

type

[TEnumAttribute('Cartão de crédito')]
[TEnumAttribute('Cartão de débito')]
TTiposCartoes = (tcCredito, tcDebito);

[TEnumAttribute('Pagamento Hora')]
[TEnumAttribute('Pagamento Diário')]
[TEnumAttribute('Pagamento Semanal')]
[TEnumAttribute('Pagamento Quinzenal')]
[TEnumAttribute('Pagamento Mensal')]
TRemuneracao = (ftHorario, ftDiario, FtSemanal , ftquinzenal , Ftmensal);

[TEnumAttribute('Moeda Real')]
[TEnumAttribute('Moeda Dollar')]
[TEnumAttribute('Moeda Euro')]
[TEnumAttribute('Moeda Yen')]
[TEnumAttribute('Moeda Peso')]
TDinheiro = (ftReal , ftDollar , FtEuros, ftLibra , ftYen , ftPeso );

Utilizando

var
Cartao:TTiposCartoes;
Pagamento:TRemuneracao;
Moeda:TDinheiro;
begin
Cartao:=tcDebito;
showmessage(TConvert<TTiposCartoes>.GetCaptionCartao(Cartao));
Pagamento:=FtSemanal;
showmessage(TConvert<TRemuneracao>.GetCaptionCartao(pagamento));
Moeda:=ftLibra;
showmessage(TConvert<TDinheiro>.GetCaptionCartao(moeda));

Vou ficando por aqui , no mais meus agradecimentos pela leitura e paciência . Se por ventura alguém tiver alguma dúvida na utilização desses métodos pode entrar em contado que será atendido. Tb poderei esta disponibilizando os fontes exemplos com os todos os métodos  . Peço por último , por questões de gentileza e cordialidade , bem como respeito aos direitos autorais , que se alguém quiser acrescentar esta classe em algum veiculo de comunicação , que faça referência a fonte da informação original . No mais meu muito obrigado.

[]sds

 

 

 

Publicado em Dicas | Marcado com , , | 9 Comentários

Dica Como Testar Verificar se um Componente MaskEdit está vazio

Esta pergunta pode ser simples , mas causa um certo desconforto à medida que o aplicativo passa a dar liberdade ao usuário de escolher a mascará . Como testar se o Maskedit esta Vazio ou não  ? Qual a diferença da mascará 00/00/0000;1;_  com final Um da mascará 00/00/0000;0;_ com final Zero . O “Um” significa que devemos salvar os literais ,assim so para este caso temos dois Ifs diferentes que devemos fazer para verificar se o Maskedit esta vázio ou não .


if Maskedit.text = '' then -->> Para mascará com Final Zero

if Maskedit.text = '  /  /    ' then para mascara com final Um

O Problema aumenta se dermos esta liberdade ao usuário conforme a introdução do post .. Quantos ifs teremos que fazer , como fazer este If e daria para reaproveitar esta lógica em outro Projeto ??? Nativamente não conheço nada no Delphi , então desenvolvi uma função que testa o conteúdo do Maskedit independemente da Mascará .


uses
System.MaskUtils ;

function CheckEmptyText(
const EditMask: TEditMask ;const Text:String):Boolean;

var
MaskOffset: Integer;
CType: TMaskCharType;
FMaskBlank:Char;
Mask:String;
default:boolean;
begin
default:=true;
FMaskBlank:= MaskGetMaskBlank(EditMask);
for MaskOffset := 1 to Length(Editmask) do
begin
CType := MaskGetCharType(EditMask, MaskOffset);
case CType of
mcLiteral, mcIntlLiteral: Mask:=Mask+EditMask[MaskOffset];
mcMaskOpt,mcMask:Mask:=Mask+FMaskBlank;
mcFieldSeparator:begin
if EditMask[MaskOffset+1] = '0' then
Mask:='';
Break;
default:=false;
end;
end;
end;
if default then
Mask:= FormatMaskText(EditMask,'');
result:=Text = Mask;
end;

Para utilizar a função é simples … Basta fazer


if  CheckEmptyText(maskedit1.editMak , maskedit1.Text) then

showmessage('esta Vazio')

else

Showmessage('Não esta Vazio');

Vou ficando por aqui , quero agradecer a paciência , e deixar registrado que se alguém conhecer um função nativa ou mais performática que preenche esta lacuna , favor sinta-se à vontade para utilizar o espaço deste blog . Qualquer crítica e sugestão será sempre muito bem vinda

Peço por fim por questões de gentileza e cordialidade que se alguém mencionar esta classe ou este artigo em algum meio de comunicação , que se faça por questões de gentileza e cordialidade , menção ao link original .  Pela sua atenção e paciência e sua leitura meu muito Obrigado

[Adendo] … Sugestão da leitura do  comentário do amigo Carlos Eduardo que imediatamente publicação deste post , sugeriu com “Maestreza” a utilização de Class Helpers . No meu entendimento caiu como uma luva esta funcionalidade ( a partir do Delphi 2006) a aplicação deste conceito em exemplos como este e bem equacionado pelo amigo Carlos eduardo
.

Publicado em Avisos | Marcado com , , , , , | 10 Comentários