sexta-feira, 28 de outubro de 2011

Mudança de URL do Genexando

Pessoal,
Estou mudando a URL do blog de http://profdouglasoliveira.blogspot.com/ para http://www.genexando.com, por gentileza atualizem seus links.
A atualização levará alguns dias para ser processada.
Ab
Douglas

segunda-feira, 24 de outubro de 2011

Afinal, o que significa Context Sensitive Interface?

Se você já se perguntou afinal o que são as interfaces sensitivas a contexto, ou já teve curiosidade de entender um pouco a respeito disso, vou te passar algumas dicas, já se você nunca ouviu falar, ou não gosta muito de interfaces web, então esse post pode te dar algumas novas visões de como planejar e construir uma interface gráfica web a partir de componentes, e quem sabe te anima a entrar nesse mundo tão interessante. Portanto, acho que esse assunto pode ser do interesse de muitos, foi pra mim, e acredito que poderá te ajudar um pouco, pois não tenho dúvidas que esse é um mecanismo que merece atenção no projeto de boas interfaces web.

Imagine uma interface montada com componentes apenas, onde não se passa nenhum parametro entre elas,assim como essa imagem apresentada a seguir.


Agora considere que as três possuem Grids e na medida em que se clica em alguma linha de um dos grids as demais respondem de acordo com o registro selecionado.  Ou seja, ao se selecionar um cliente qualquer na interface Clientes, a interface Compras e Produtos respondem filtrando informações relativas a esse cliente.


Não perca de vista a questão de que não passamos parametros, via Parm(), para os componentes, então como fazemos isso? como realizar a comunicação entre componentes isolados e agrupados em um painel?  A resposta: Interfaces Sensitivas a Contexto.

Em Genexus você já deve ter observado que quanto menos se programar melhor, veja os domínios que com um pouco de planejamento podem auxiliar na manutenção do sistema todo, o próprio for...each, por exemplo, pode ser otimizado com o objeto Data Selector, enfim, temos na montagem da interface elementos que poderiam ser reaproveitados em outras interfaces similares. 

Poderíamos dizer que um grid que seleciona um registro de uma tabela poderia muito bem ser aproveitado em diversas outras interfaces, desde que se tenha um pouco de planejamento anterior.  Assim poderíamos ter um modelo de projeto onde teríamos diversos componentes previamente planejados e nosso trabalho seria o de conectá-los ao nosso interesse. Soa bem isso não?  Isso é possível nas interfaces sensitivas a contexto.

O Que é Contexto?
Entenda contexto como sendo o item atualmente em foco na interface, alcançado por meio da tecla Tab ou do click do mouse.  Desta forma se ocorreu um click em um Grid, o contexto terá a linha corrente do grid, se em uma coluna do grid o contexto armazenará o valor da coluna selecionada, se em uma variável o valor da mesma e assim por diante.

Um evento chamado Track Context é utilizado para ler o contexto dos diversos controles na interface, e o interessante é que percebe a mudança assim que ocorre o foco no controle.  Em outras palavras, em nossa interface modelo, ao clicar em algum cliente no Grid apresentado à esquerda, o código do cliente ‘selecionado’ poderia então ser utilizado nos demais grids para apresentar suas compras e os produtos adquiridos pelo cliente.

Para facilitar um pouco, pense em Context como sendo um controle na interface (variável ou atributo), e que ao entrar em foco, imediatamente conseguimos ler seu valor.


Programando o Exemplo
Um web panel é montado apenas para integrar os três webcomponents, ou seja, não é necessário programar nada, a não ser o posicionamento dos componentes na interface, um exemplo poderia ser o apresentado na figura 1 acima.

O web componente Clientes, possui um grid simples do tipo Freestyle, com variáveis e atributos, e a propriedade NotifyContextChange é definida como True.


Os eventos programados nessa interface são meramente de carga do grid, nenhuma informação adicional.
 
Event Load
     &ClienteId   = ClienteId
     &ClienteFoto = ClienteFoto
     load
EndEvent

A segunda e terceira interface também são Web Componentes, programados com grids simples, como podem ser observados na Figura 4 e 5 abaixo.




O grid de Compras agrega um total de compras apenas, calculado na carga do grid.

Ambos os grids possuem um filtro ClienteId = &ClienteId. Observe que &ClienteId não existe em ambos os webcomponents, sendo o valor obtido do primeiro WebComponent (Figura 3), quando o usuário clicar em algum registro do Grid.  A pergunta é como &ClienteId é carregado? 

Um evento TrackContext foi adicionado para realizar a leitura do valor do contexto atual de &ClienteId, que na prática foi definido no outro no componente Cliente (Figura 3).  Não foi necessário programar nada no evento a não ser receber por parâmetro o valor de &ClienteId, do outro componente, por isso que o evento se encontra definido de forma tão simples. 

Event TrackContext(&ClienteId)
    
EndEvent

O evento TrackContext é o responsável por interceptar o Click e obter o cliente selecionado.

Em resumo, para que o grid notifique mudanças no contexto é necessário programar o NotifyContextChange=true, e para interceptar os clicks é necessário programar o Event TrackContext.

Entre parenteses se coloca as variáveis (apenas) que se deseja obter, e podem haver mais que um TrackContext na mesma interface.

Conclusão
Esse tema é muito importante e esse post está ficando um tanto quanto grande, acho que futuramente poderemos voltar a esse assunto, quem sabe tratar um pouco mais a respeito de TrackContext multiplos, e o SetContext que também é um evento bem interessante.

Não sei se entendeu o impacto disso, mas poderiamos pensar que todo WebComponent já programado poderia servir de base para montagem de outras interfaces completamente distintas, bastando programar o mecanismo de comunicação entre componentes, muito interessante não?

ps. Coloquei um vìdeo no YouTube mostrando o funcionamento da interface, caso tenha curiosidade. Interfaces Baseadas em Contexto




sexta-feira, 21 de outubro de 2011

Shortcuts

Sabe quais são as teclas utilizadas no IDE do Genexus para executar os comandos da ferramenta? Pois é, eu não sabia que existiam tantos. Vale a pena conferir.

http://wiki.gxtechnical.com/commwiki/servlet/hwiki?GeneXus+IDE%2FShortcuts

Order, when e otimizações

Esse tema, meio polêmico deixa muita gente nervosa, de cabelo em pé, e reflete um pouco a respeito das pesquisas e acessos realizados na base de dados, que sempre são uma preocupação grande devido ao desempenho desejado na aplicação, pois, Genexus oferece a programação de cláusulas condicionais, when, tanto em order quanto em where, oferecendo um mecanismo bastante interessante e flexível para acesso ao banco, mas o uso indiscriminado pode trazer complicações.

A idéia é que além do efeito visual obtido em uma navegação, no preenchimento de grids, por exemplo, tenhamos também um bom desempenho, ou seja, sempre um pequeno número de registros ordenados e processados corretamente.  Desta forma sem ler o resultado da navegação gerada, você estará correndo grandes riscos em sua aplicação, visto que o usuário não gostará muito de esperar minutos para que uma resposta seja apresentada na interface.

Um exemplo de programação que utiliza when pode ser vista a seguir.

     for each
          order ManualTitle when not &ManualTitle.IsEmpty()
          order ProcessId when &ProcessId<>0
          where ProcessId = & ProcessId when & ProcessId<>0
          where ManualTitle like '%'+&ManualTitle+'%' when not &ManualTitle.IsEmpty()

Ela diz que se houver titulo fornecido ordena-se e filtra-se por titulo, se houver processo, o mesmo, ordena-se e filtra-se por processo.  Mas o fato é que o resultado final não é bom, pois no mapa de navegação teremos a pesquisa à tabela toda (Navigation Filters), ou seja, lentidão.


Um pequeno detalhe, pois quando optar por programar com a cláusula when, todas as ordenações devem ser colocadas o when, caso contrário teremos um erro de sintaxe.

     for each
          order UserManualTitle
          order ProcessId when &ProcessId<>0

error: Expecting 'EndFor' command to close the 'For Each' block (Events, Line: 50, Char: 9#62ea5034-c4dd-4bb2-ad33-56cd112159d7)
error: '{0}' is an unexpected expression. (Events, Line: 50, Char: 25#1b09b22d-3400-46a8-8934-b7f4000a0192)
error: Unexpected command 'where' (Events, Line: 51, Char: 3#ea102e2d-fc92-4228-94bb-3cb856efd2b8)
error: '{0}' is an unexpected expression. (Events, Line: 51, Char: 9#7d0a990c-475e-4292-875e-65313180b500)
error: Unexpected command 'where' (Events, Line: 52, Char: 3#45f8ec05-0b6e-4b58-a4ac-8ed937fec794)


O operador like também contribui para uma situação de pesquisa não otimizada na base.
 
Uma saída (existem outras) para essa situação é criar um índice composto por várias ordenações, usando vírgulas, e eliminando-se a cláusula when quando for possível.

     for each
          order ProcessId, UserManualTitle
          where ProcessId = &ProcessId
          where UserManualTitle like '%'+&UserManualTitle+'%'

O resultado é uma pesquisa melhor, otimizada e mais rápida, apesar do efeito visual final não ser muito bom, visto que a consulta dependeria de dados para ser processada, desta forma o usuário teria uma interface vazia onde teria que fornecer informações para ver alguma coisa.



Considere otimizada o fato de Start from e Loop while apresentar valores condicionais ao invés de FirstRecord e NotEndOfTable.  Pode parecer meio estranha a principio, mas o resultado é muito melhor, principalmente na web onde tudo é mais lento, e o fato do usuário determinar o que quer ver, já significa que não perderá tempo com informações inúteis.

Enfim, cuidado com a cláusula when, principalmente em sistemas Web, caso contrário seu chefe vai colocar o exército chines atrás de voce.

quinta-feira, 20 de outubro de 2011

Randômicos

Gerar valores randômicos sempre foi uma necessidade do desenvolvimento de sistemas, principalmente com a garantia de unicidade, ou seja, valores que nunca se repetem, únicos e exclusivos.

Entre as muitas aplicações tive algumas recentes, relativamente simples, que era a geração de tokens randômicos para a transferência da sessão web de um sistema a outro e outra que necessitava gerar chaves na sessão. Rotinas que já se encontravam desenvolvidas e operantes com C# a um bom tempo.

O contratempo surgiu ao tentar migrar a aplicação para Ruby.
  • Allow non-standard functions on saving, mais especificamente getsitekey() não rodam em Ruby.
Enfim, essas limitações me levaram a estudar um pouco dos recursos atuais de geração randômica do Genexus, e em especial o tipo GUID, atualmente disponível na versão EV1, e o resultado compartilho com vocês.

Números Randômicos Tradicionais
Uma função geradora de randômicos programada anteriormente em C# dava muito bem conta do recado. Nessa tínhamos uma seqüência de letras e números formando uma string e o gerador de randômicos do C# se encarregava de obter um elemento dessa string até um determinado Size.

string[] chars = { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","x","z"  };
    Random rnd = new Random();
    string random = string.Empty;
    for (int i = 0; i < Size; i++) {
           random += chars[rnd.Next(0, 33)];
    }

Esse pequeno programa gerava números randômicos de tamanho Size, muito úteis para gerar chaves.

Em Genexus, essa mesma rotina foi programada com a função Random()para gerar números aleatórios. Essa função gera randomicos no formato Numeric(11.9), em intervalos de 0.1, portanto para ter um numero de um digito inteiro é necessário multiplicar o resultado por 10, dois dígitos por 100, e assim por diante, ficando:


&String    = 'ABCDEFGHIJKLMNOPQRSTUVXWYZ0123456789abcdefghijklmnopqrstuvxwyz'
     &Randomic = ''
     for &n= 1 to &Size
           &i = Random()*100
           &i = iif(&i > 62, &i-37, &i)
           &Randomic += &String.Substring(&i, 1)
     endfor

Essa lógica em Genexus é muito útil porque se trabalhássemos apenas com números inteiros o limite seria 18 dígitos, visto que esta é a maior dimensão possível de um tipo Numeric(18.0). Portanto, faz-se necessário executar mais de uma vez as chamadas ao Random(), por isso da necessidade do for &n= 1 to &Size.

(Uma pergunta aqui: porque preciso da expressão &i-37 no iif? )

Em alguns ambientes, o Random() gera a mesma seqüência de números, no Ruby deu certo de primeira, mas se tiver problemas com isso pode utilizar o RSeed().


Maiores informações:

Até aqui o problema havia sido resolvido com grande facilidade, mas por acaso me deparei com algo novo na Evolution 1.


Randômicos com GUID
Esse sujeito estava meio escondido nas definições de tipos de dados Genexus, e não lhe foi imputado o devido respeito, pois se trata de um tipo string que gera números pseudo-randômicos hexadecimais, creio haver muitas aplicações possíveis, inclusive no meu caso daria conta sem problema algum, apesar de hexadecimal significar digitos de 0..9 e de A..F, portanto, teriamos ai uma pequena restrição.

O mais impressionante é a capacidade de geração,  2128  que equivale a um número igual a 3,4028236692093846346337460743177e+38, segundo os cálculos da Artech, algo como um trilhão de GUID´s a cada nanossegudo durante 10 bilhões de anos seriam suficientes para gerar uma repetição, portanto, acho que dá pra usar.

Quanto ao uso, o Wikipedia oferece alguns bons exemplos, e a própria Artech o utiliza para identificar cada um dos objetos Genexus em sua KB, e garante que cada um será único em qualquer Kb que venha a ser criada no mundo todo.  Um dos exemplos propõe ainda a utilização de uma NotaFiscalId do tipo GUID, ou seja, dispensa-se o tão utilizado e querido e usado Autonumber=true.

A string GUID gerada possui o seguinte formato (fonte: Wikipédia):
Hex digits
Description
8
Data1
4
Data2
4
Data3
4
Initial two bytes from Data4
12
Remaining six bytes from Data4


Em termos práticos, são 30 caracteres em 5 regiões separadas por -, como no exemplo a seguir.

f5a88c30-db5b-012e-3dd2-1231380f2e23

Então como Usar isso?

O tipo GUID pode ser aplicado a atributos e variáveis e de quebra alguns métodos ficam disponibilizados, inclusive um que gera um novo valor randômico.

Para criar um novo GUID, por exemplo, pode-se programar em uma variável:


&guid = GUID.NewGuid()

Para se comparar com um GUID existente, que pode ser recebido por parâmetro, por exemplo, teriamos.


if &guid <> GUID.FromString("f5a88c30-db5b-012e-3dd2-1231380f2e23")
     msg("Bad Site Key")
endif

Dessa forma poderiamos também transformar um GUID em string.


msg(&guid.ToString())

Maiores informações:

Bom, fica ai mais uma opção para randômicos já em um formato de chave, só resta aplicar a um campo chave e ver no que dá.


segunda-feira, 10 de outubro de 2011

Prototipação na Cloud


Tem certos detalhes no EV2 que demonstram que o projeto está excelente, e uma que me chama muito a atenção é a facilidade para publicação na nuvem.   Já havia testado esse recurso com sucesso para projetos Android, mas não havia ainda tentado nada para projetos web tradicionais.

Como o gerador atual roda Mysql+Ruby temos alguns ajustes a serem realizados, principalmente a instalação correta do Mysql e do Ruby na máquina de desenvolvimento, e para o Ruby é necessário um ajuste na variável PATH do Sistema, pois, no meu caso ocorria um pequeno problema no momento da publicação.


\========== Execution started ==========
ruby gxdeploy_build_transfer.rb
error: System.ComponentModel.Win32Exception: The system cannot find the file specified
   at System.Diagnostics.Process.StartWithCreateProcess(ProcessStartInfo startInfo)
   at System.Diagnostics.Process.Start()
   at Artech.Genexus.Common.Run.RunBase.ExecuteCommand(IDictionary`2 tokens, String command, Boolean synchro, Boolean printCmd)
   at Artech.Genexus.Common.Run.RunBase.ExecuteCommand(IDictionary`2 tokens, String command, Boolean synchro)
   at Artech.Packages.Genexus.BL.Services.RunServices.RunRuby.BuildTransferTARFile(KBModel model)
   at Artech.Packages.Genexus.BL.Services.RunServices.RunRuby.DeployCloudApp(KBModel model, String& diag)
   at Artech.Packages.Genexus.BL.Services.RunServices.RunRuby.GetEntry(KBModel model, IEnumerable`1 tokens, Boolean isReorg, Boolean createDataBase, IEquatable`1 pgmType, IEquatable`1 mainType, EntryName entryName)
   at Artech.Genexus.Common.Run.RunBase.BuildCommand(KBModel model, IEnumerable`1 tokens, Boolean isReorg, Boolean createDataBase, IEquatable`1 pgmType, IEquatable`1 mainType, EntryName cmdType, String& command)
   at Artech.Packages.Genexus.BL.Services.RunServices.RunRuby.Execute(KBModel model, EntityKey objKey, String execParams)
Execution Failed
Run Developer Menu Failed

Analisando com cuidado, havia um espaço em branco a mais na definição do caminho a pasta BIN do Ruby, que ajustado, e em seguida após a maquina reiniciada, o processo concluiu com sucesso.  Portanto, aponte o PATH (Local e Sistema) para o diretório: C:\Program Files (x86)\Artech\GeneXus\GeneXusXEv2\gxruby\win\ruby\bin.


ruby gxdeploy_build_transfer.rb
Building file gx_last_transfer.zip...
Uploading 4086 Kbytes
3831 Kbytes left
3576 Kbytes left
3321 Kbytes left
3066 Kbytes left
2811 Kbytes left
2556 Kbytes left
2301 Kbytes left
2046 Kbytes left
1791 Kbytes left
1536 Kbytes left
1281 Kbytes left
1026 Kbytes left
771 Kbytes left
516 Kbytes left
261 Kbytes left
6 Kbytes left
Deploying website
Execution Success



O resultado vale a pena, pois ocorre a publicação da aplicação e do banco de dados na nuvem.  O acesso ao banco também pode ser realizado de forma tão tranqüila, e aqui um pequeno detalhe, o acesso remoto ao Banco de Dados também é possível, de as propriedades são configuradas automaticamente no Datastore default.

Server: apps.genexus.com
Database: Idd0c218ea805a43b7a2d67f24448ca31x
User: ule3L0rjrukTNaIJ
Password: *************

Um detalhe, a senha do banco é a  mesma do nome do usuário.

Portanto, tá esperando o que pra colocar sua cabeça nas nuvens?