quinta-feira, 23 de outubro de 2014

Fácil e reutilizável galeria de imagens em Jquery

Se você é programador vai achar esse código um tanto quanto é trivial, mas se você for designer, como o cara que me pediu isso, talvez isso seja muito útil pra você.

Direto vemos galerias de imagens e hotsites e afins, e hoje é simplesmente ridículo fazer esse tipo de coisa com flash. Flash aos poucos está sendo banido da Internet. 

HTML5 e um pouco de jQuery é o que você precisa para fazer uma galeria de imagens onde seja possível escolher a imagem com thumbnails, avançar, voltar e ter exibição automática em ciclo. 

Aqui eu corrijo um erro muito comum dessas galerias: se uma nova imagem aparece a cada 5 segundos, e o usuário passou 4 segundos na última imagem, não seria correto trocar a imagem novamente no timeout, um segundo depois. Com a interação do usuário o timer deveria ser resetado. 

Crie uma página HTML com o conteúdo abaixo. Você pode trazer o  jquery de um CDN também.

Repare nos comentários do código. Uso atributos do tipo data-* e algumas funções anônimas.


 
  
  Untitled Document
  


  
  



 

 

  



«     »

Tudo o que você precisa saber sobre CORS

Primeiro de tudo, a documentação: http://www.w3.org/TR/cors/
Segundo, como configurar no IIS 7: http://i-liger.com/article/cross-domain-http-request
Como configurar cors no IIS 6: http://enable-cors.org/server_iis6.html
É importante salientar que essa configuração também é possível via web.config. Como o web.config é hierárquico, hereditário e combinatório, então você pode colocar um web.config adicional pequenininho com cabeçalhos só para permitir CORS nas páginas de um diretório, em vez da aplicação inteira.
Se a configuração não for para a aplicação inteira nem para uma pasta inteira temos que colcoar cabeçalhos http na página. Os cabeçalhos são:
  • Access-Control-Allow-Origin -> indica quais domínios podem fazer um request no seu, colocando * permite todos os domínios.
  • Access-Control-Allow-Methods -> indica quais métodos são permitidos o cliente consultar no seu domínio. Não adianta colocar só post ou get, pois o client pode mandar uma requisição head para saber encoding e contenttype antes, ou uma requisição options, para saber mais configurações (é o browser / javascript que manda esses métodos), portanto, permita GET, POST, PUT, DELETE, HEAD, DEBUG, OPTIONS
  • Access-Control-Allow-Headers -> indica quais headers o client pode solicitar, configurar Content-Type, Accept
  • Access-Control-Max-Age -> indica quantos segundos um request já atendido deveria ser mantido em cache. Muita gente coloca 1728000, mas eu acho isso muito tempo. 72000 (20 horas) é um número muito mais aceitável, considerando que mesmo um conteúdo estático como o de um e-learning pode mudar de um dia para o outro.
Um exemplo de web.config para um único diretório é:

    
        
            
                
                
                
                                                               
            
        
    


Também é possível fazer isso em uma única página, colocando, como primeiro código a ser executado no page_load, o código
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, HEAD, DEBUG, OPTIONS");
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept");
HttpContext.Current.Response.AddHeader("Access-Control-Max-Age", "72000");
As configurações no IIS valem para todas as páginas, enquanto que as configurações via web.config são para um diretório e o código acima para uma única página. Todos eles representam a mesma configuração.
Mais informações e exemplos: http://encosia.com/using-cors-to-access-asp-net-services-across-domains/

sexta-feira, 17 de outubro de 2014

Domine a herança de construtores

Em hierarquias longas de objetos é importante saber onde colocar o código dos contructors (construtores) pois eles tem uma ordem lógica para executar e, se for um parameterless constructor (construtor sem parâmetros) então todos os parameterless constructors serão executados desde a raiz object.

Um parameterless constructor sempre executa o parameterless constructor da classe base (pai), mas um construtor com parâmetros você deve especificar:


  1. :this([parametros]) para executar um outro constructor na mesma classe e deixar a hierarquia seguir sucessivamente
  2. :base([parametros]) para executar um constructor específico da classe base
  3. NADA para executar o parameterless constructor da base.


O programa abaixo ilustra isso.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace HerancaConstrutor
{

    class ClasseBase
    {
        public ClasseBase()
        {
            Console.WriteLine("parameterless constructor da classe ClasseBase");
        }
    }

    class DerivadaSemConstructor : ClasseBase
    {
        
    }


    class DerivadaComConstructor : ClasseBase
    {
        public DerivadaComConstructor()
        {
            Console.WriteLine("parameterless constructor da classe DerivadaComConstructor");
        }

        public DerivadaComConstructor(string argumento)
        {
            Console.WriteLine("constructor com argumentos da classe DerivadaComConstructor. Argumento: " + argumento);
        }
    }


    class DerivadaDaDerivadaSemConstructor : DerivadaSemConstructor
    {
        
    }

    class DerivadaDaDerivadaComConstructor : DerivadaComConstructor
    {
        public DerivadaDaDerivadaComConstructor()
            : base("")
        {
            Console.WriteLine("parameterless constructor da classe DerivadaDaDerivadaComConstructor");
        }

        public DerivadaDaDerivadaComConstructor(string argumento) : base(argumento)
        {
            Console.WriteLine("constructor com argumentos da classe DerivadaDaDerivadaComConstructor. Argumento: " + argumento);
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            var v1 = new ClasseBase();

            var v2 = new DerivadaSemConstructor();
            var v3 = new DerivadaComConstructor();
            var v3b = new DerivadaComConstructor("b");

            var v4 = new DerivadaDaDerivadaSemConstructor();
            var v5 = new DerivadaDaDerivadaComConstructor();
            var v5b = new DerivadaDaDerivadaComConstructor("b");
        }
    }
}



Toda classe sem parameterless constructor padrão possui um parameterless constructor implícito herdado de object.
Toda classe com um parameterless constructor implicitamente executa o parameterless constructor da classe base, estando ele sendo chamado com :base() explicitamente ou não.

Se um constructor não é criado eecuta o parameterless constructor da classe base
se um constructor é criado mas não é definido um constructor base é executado o parameterless constructor da base.
se um construtor é criado e é especificado um outro construtor da classe base com argumentos para ser executado, é executado o constructor escolhido e o constructor da classe ancestral da base é executado.
Se um constructor chama outros constructors, com ou sem parâmetros, na classe atual ou na classe base, não importa, uma hora um constructor parameterless implícito ou explícito será executado.

As vezes eu esqueço por que programo




Mas o 9gag me faz o favor de me lembrar.


quinta-feira, 16 de outubro de 2014

Duas funções úteis para manipulação de datas

De tempos em tempos precisamos fazer aquele processamento que só pode ser feito em dias úteis. Seja a data de pagamento de um boleto, o agendamento de uma entrevista e assim por diante.

O desafio disso está no fato de que temos feriados nacionais com dia fixo, feriados com dia variável porém computável (através de algoritmos) e feriados municipais.

Os municipais não tem outra forma de atender senão consultando uma tabela do banco de dados pelo código do município, mas os feriados nacionais fixos nós podemos dar uma bom começo.

Segue abaixo a função fn_dia_util que retorna 1 se o dia for útil e 0 se for sábado, domingo ou feriado (usando a função weekday para isso) e a função fn_proximo_dia_util, que verifica se o próximo dia é util e se não for vai acrescentando um em loop. Mesmo se o feriado cair numa sexta-feira ela retornará o próximo dia útil.


-- ############################################################################################################################################
-- # NOME   : fn_dia_util
-- # PRODUTO  : blog.vitorrubio.com.br
-- # DEVELOPER  : Vitor Luiz Rubio
-- # COPYRIGHT  : 
-- # OBJETIVO  : Dada uma data, diz se é dia útil (1) ou não (0)
-- # VERSAO   : 1.0
-- # DT. CRIACAO : 2014-10-16
-- # DT. ALTERACAO : 
-- #
-- # DEPENDENCIAS :
-- # 
-- # DEPENDENTES :
-- #
-- # HISTÓRICO
-- # 2014-10-16 : Criação
-- #  
-- ############################################################################################################################################ 

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE FUNCTION dbo.fn_dia_util
(
 @data datetime
)
RETURNS integer
AS
BEGIN

  declare @result integer
  
  if (
    --VERIFICA SE É SÁBADO OU DOMINGO
    (datepart(weekday, @data) IN (1,7)) or 
    --VERIFICA SE É ALGUM FERIADO NACIONAL DE DIA FIXO
    (convert(Varchar, day(@data))+'/'+convert(Varchar, month(@data)) in (
     '1/1',
     '21/4',
     '1/5',
     '7/9',
     '12/10',
     '2/11',
     '15/11',
     '25/12' 
    ))
    
    --TODO: COLOCAR AQUI OS OUTROS FERIADOS NACIONAIS DE DIA "COMPUTÁVEL" E FAZER UMA CONSULTA A OUTRA TABELA PARA VER OS FERIADOS MUNICIPAIS
   )
  begin
   set @result =  0
  end;
  else
  begin
   set @result = 1
  end;


  RETURN @result

END
GO


/*
TESTE:

 SELECT dbo.fn_dia_util('2015-1-1') -- 0 
 SELECT dbo.fn_dia_util(GETDATE()) -- 1 SE HOJE NÃO FOR FERIADO :)
 --drop function fn_dia_util
*/


-- ############################################################################################################################################
-- # NOME   : fn_next_dia_util
-- # PRODUTO  : blog.vitorrubio.com.br
-- # DEVELOPER  : Vitor Luiz Rubio
-- # COPYRIGHT  : 
-- # OBJETIVO  : retorna o próximo dia útil a partir de uma data
-- # VERSAO   : 1.0
-- # DT. CRIACAO : 2014-10-16
-- # DT. ALTERACAO : 
-- #
-- # DEPENDENCIAS :
-- # 
-- # DEPENDENTES :
-- #
-- # HISTÓRICO
-- # 2014-10-16 : Criação
-- #  
-- ############################################################################################################################################ 

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE FUNCTION dbo.fn_next_dia_util
(
 @data datetime
)
RETURNS datetime
AS
BEGIN

  declare @result datetime
  
  set @result = dateadd(day, 1, @data)
  
  while (dbo.fn_dia_util(@result) = 0)
  begin
   set @result = dateadd(day, 1, @result)
  end 


  RETURN @result

END
GO


/*
TESTE:

 SELECT dbo.fn_next_dia_util('2014-12-31') -- 2015-1-2 
 SELECT dbo.fn_next_dia_util('2014-04-18') -- 2014-4-22 porque segunda-feira é feriado
 SELECT dbo.fn_next_dia_util(GETDATE()) -- amanhã SE NÃO FOR FERIADO :)
 --drop function fn_dia_util
*/


Duas maneiras de se fazer split no Delphi XE2 parecido com a função split do C#


Para se fazer uma função split no Delphi basta usar array dinâmico ou list de string.
O exemplo abaixo mostra como.


function split(original: string; separador: string) : TStringArray;
var idx: integer;
tmp, item: string;
resultado: TStringArray;
begin

  idx:=0;
  SetLength(resultado, idx);
  tmp := original;

  while Pos(separador, tmp) > 0 do
  begin

    SetLength(resultado, length(resultado)+1);
    item := Copy(tmp, 1, Pos(separador, tmp)-1);
    tmp := Copy(tmp, Pos(separador, tmp) + length(separador), length(tmp) +  Pos(separador, tmp) - length(separador));
    resultado[idx] := item;
    inc(idx);

  end;
  SetLength(resultado, length(resultado)+1);
  resultado[idx] := tmp;
  Result := resultado;

end;




function split2(original: string; separador: string) : TList < string >;
var
  tmp, item: string;
begin
  result := TList< string >.Create;
  tmp := original;

  while Pos(separador, tmp) > 0 do
  begin
    Result.Add( Copy(tmp, 1, Pos(separador, tmp)-1));
    tmp := Copy(tmp, Pos(separador, tmp) + length(separador), length(tmp) +  Pos(separador, tmp) - length(separador));
  end;

  Result.Add(tmp);

end;

É isso aí, isso é usado em alguns outros posts desse blog.

quarta-feira, 15 de outubro de 2014

Reflexão no Javascript

Dado um objeto javascript, é possível varrer seus métodos e propriedades e até mesmo serializá-lo.
Para isso devemos fazer uma conversão forçada do objeto para um vetor, e varre-lo. Cada item desse vetor pode ser um método ou uma propriedade.
O que vai dizer se ele é um método ou uma propriedade é que seu tipo, dado pela função typeof():
  • quando, como string, for igual a "function" significa que ele é um método ou função.
  • caso contrário é uma propriedade.

Código da função de serialização
   ///Transforma um objeto em string considerandp se é null ou undefined
   function ObjToString(obj)
   {
    if(obj === null)
    {
     return "null";
    }
    
    if(typeof obj === "undefined")
    {     
     return "undefined";
    }
     
    return obj.toString();
   }
  
   ///escreve as propriedades do objeto na página
   function serialize(obj)
   {
    //coleção de métodos
    var metodos = [];
    //coleção de propriedades
    var propriedades = [];
    
    //varre as propriedades
    for (i in obj) 
    {
     //verifica se não é a própria função
     if (i != "serialize")
     {
      if (typeof(obj[i]) == "function") 
      {
       //se for método adiciona em uma coleção
       metodos.push(typeof(obj[i]) + ": " + i + ": " + ObjToString(obj[i]) );
      }
      else
      {
       //senão adiciona na outra
       propriedades.push(typeof(obj[i]) + ": " + i + ": " + ObjToString(obj[i]) );
      }
     }
    }
    
    //varre as coleções escrevendo na página
    document.write("metodos
");
    for (m in metodos) 
    {
     document.write(metodos[m]+"
");
    }    
    document.write("

propriedades
");
    for (p in propriedades) 
    {
     document.write(propriedades[p]+"
");
    }    
   } 
No caso dessa função, ela simplesmente lista as propriedades e métodos e os concatena a seus devidos valores, pulando linha. Isso poderia ser feito de outra forma, por exemplo, a função de serialização poderia gerar um JSON e usá-lo em uma requisição ajax, ou interpretá-lo com eval() e gerar um clone do objeto em questão (apenas as propriedades, um DTO), ou ainda serializá-lo em XML. Por enquanto vamos listar os métodos e propriedades na página mesmo. Depois tentamos gravar em uma string ou objeto json.
Note o uso do comando for ... in. for (i in obj)
Ele equivale ao foreach do c# e de outras linguagens. Ele varre todos os elementos de um array atribuindo a I o elemento do array de indice x quando for um array, ou a propriedade quando for um ojeto (como se fosse um hashtable/dictionay). Usando esse comando, para uma varredura geral sem armazenar ou utilizar o índice, você pode fazer uma varredura geral em um array sem se preocupar com os limites inferior e superior.
A função ObjToString apenas verifica se o objeto é nulo ou undefined antes de chamar .toString() nele.
Abaixo um exemplo de serialização do objeto window:
 serialize(window);
Com a saída:
 metodos
 function: postMessage: function () { [native code] }
 function: close: function () { [native code] }
 function: blur: function () { [native code] }
 function: focus: function () { [native code] }
 function: getSelection: function getSelection() { [native code] }
 function: print: function print() { [native code] }
 function: stop: function stop() { [native code] }
 function: open: function open() { [native code] }
 function: alert: function alert() { [native code] }
 function: confirm: function confirm() { [native code] }
 function: prompt: function prompt() { [native code] }
 function: find: function find() { [native code] }
 function: scrollBy: function scrollBy() { [native code] }
 function: scrollTo: function scrollTo() { [native code] }
 function: scroll: function scroll() { [native code] }
 function: moveBy: function moveBy() { [native code] }
 function: moveTo: function moveTo() { [native code] }
 function: resizeBy: function resizeBy() { [native code] }
 function: resizeTo: function resizeTo() { [native code] }
 function: matchMedia: function matchMedia() { [native code] }
 function: getComputedStyle: function getComputedStyle() { [native code] }
 function: getMatchedCSSRules: function getMatchedCSSRules() { [native code] }
 function: webkitConvertPointFromPageToNode: function webkitConvertPointFromPageToNode() { [native code] }
 function: webkitConvertPointFromNodeToPage: function webkitConvertPointFromNodeToPage() { [native code] }
 function: requestAnimationFrame: function requestAnimationFrame() { [native code] }
 function: cancelAnimationFrame: function cancelAnimationFrame() { [native code] }
 function: webkitRequestAnimationFrame: function webkitRequestAnimationFrame() { [native code] }
 function: webkitCancelAnimationFrame: function webkitCancelAnimationFrame() { [native code] }
 function: webkitCancelRequestAnimationFrame: function webkitCancelRequestAnimationFrame() { [native code] }
 function: captureEvents: function captureEvents() { [native code] }
 function: releaseEvents: function releaseEvents() { [native code] }
 function: btoa: function btoa() { [native code] }
 function: atob: function atob() { [native code] }
 function: setTimeout: function setTimeout() { [native code] }
 function: clearTimeout: function clearTimeout() { [native code] }
 function: setInterval: function setInterval() { [native code] }
 function: clearInterval: function clearInterval() { [native code] }
 function: webkitRequestFileSystem: function webkitRequestFileSystem() { [native code] }
 function: webkitResolveLocalFileSystemURL: function webkitResolveLocalFileSystemURL() { [native code] }
 function: openDatabase: function openDatabase() { [native code] }
 function: addEventListener: function addEventListener() { [native code] }
 function: removeEventListener: function removeEventListener() { [native code] }
 function: dispatchEvent: function dispatchEvent() { [native code] }


 propriedades
 object: top
 object: window
 object: location
 object: external
 object: chrome
 object: document
 object: speechSynthesis
 object: localStorage
 object: sessionStorage
 object: applicationCache
 object: webkitStorageInfo
 object: indexedDB
 object: webkitIndexedDB
 object: crypto
 object: CSS
 object: performance
 object: console
 number: devicePixelRatio
 object: styleMedia
 object: parent
 object: opener
 object: frames
 object: self
 string: defaultstatus
 string: defaultStatus
 string: status
 string: name
 number: length
 boolean: closed
 number: pageYOffset
 number: pageXOffset
 number: scrollY
 number: scrollX
 number: screenTop
 number: screenLeft
 number: screenY
 number: screenX
 number: innerWidth
 number: innerHeight
 number: outerWidth
 number: outerHeight
 boolean: offscreenBuffering
 object: frameElement
 object: clientInformation
 object: navigator
 object: toolbar
 object: statusbar
 object: scrollbars
 object: personalbar
 object: menubar
 object: locationbar
 object: history
 object: screen
 object: onautocompleteerror
 object: onautocomplete
 object: ondeviceorientation
 object: ondevicemotion
 object: onunload
 object: onstorage
 object: onpopstate
 object: onpageshow
 object: onpagehide
 object: ononline
 object: onoffline
 object: onmessage
 object: onlanguagechange
 object: onhashchange
 object: onbeforeunload
 object: onwaiting
 object: onvolumechange
 object: ontoggle
 object: ontimeupdate
 object: onsuspend
 object: onsubmit
 object: onstalled
 object: onshow
 object: onselect
 object: onseeking
 object: onseeked
 object: onscroll
 object: onresize
 object: onreset
 object: onratechange
 object: onprogress
 object: onplaying
 object: onplay
 object: onpause
 object: onmousewheel
 object: onmouseup
 object: onmouseover
 object: onmouseout
 object: onmousemove
 object: onmouseleave
 object: onmouseenter
 object: onmousedown
 object: onloadstart
 object: onloadedmetadata
 object: onloadeddata
 object: onload
 object: onkeyup
 object: onkeypress
 object: onkeydown
 object: oninvalid
 object: oninput
 object: onfocus
 object: onerror
 object: onended
 object: onemptied
 object: ondurationchange
 object: ondrop
 object: ondragstart
 object: ondragover
 object: ondragleave
 object: ondragenter
 object: ondragend
 object: ondrag
 object: ondblclick
 object: oncuechange
 object: oncontextmenu
 object: onclose
 object: onclick
 object: onchange
 object: oncanplaythrough
 object: oncanplay
 object: oncancel
 object: onblur
 object: onabort
 object: onwheel
 object: onwebkittransitionend
 object: onwebkitanimationstart
 object: onwebkitanimationiteration
 object: onwebkitanimationend
 object: ontransitionend
 object: onsearch
 number: TEMPORARY
 number: PERSISTENT
Agora imagine um objeto json, por exemplo pessoa:
   var pessoa = {
    nome:"Vitor",
    sobrenome:"Rubio",
    email:"vitorrubio@gmail.com",
    idade:30,
    dataCadastro:"14/10/2014",
    dependente:null,
    hobby:undefined};
Com a saída:
 metodos


 propriedades
 string: nome: Vitor
 string: sobrenome: Rubio
 string: email: vitorrubio@gmail.com
 number: idade: 30
 string: dataCadastro: 14/10/2014
 object: dependente: null
 undefined: hobby: undefined
Repare também que foi necessário colocar uma excessão quanto ao método "serialize" para ele não serializar a si mesmo. Se um método for nativo do browser ele é serializado como [native code], mas se for um método seu então todo o source do método é serializado. Veja o poder disso: nas linguagens compiladas como C ou Delphi, ou nas linguagens com compilação Just In Time como C# e Java, os objetos podem ter suas propriedades e valores varridos, serializando-os para xml, json e outros formatos. Mas apenas seus campos e propriedades. NUNCA SEUS MÉTODOS. Então nas aplicações, não importa se você usa um banco de dados relacional ou orientado a objeto, você nunca poderia armazenar o código executável (métodos) no banco de dados. Apenas os dados poderiam ser armazenados. Isso significaria ter que tratar os dados com functions e stored procedures no banco de dados ou reconstituir o objeto e tratar na aplicação. Com javascript é diferente. Você pode salvar o SOURCE de métodos e argumentos em um banco de dados, trazêlos de volta a vida com eval(), JSON.parse ou jQuery.parseJSON e executar as mesmas funções que eles já possuiam. Não é possível fazer isso com JSON.parse(), apenas com eval, pois entende-se que o JSON é apenas para transferência de DADOS. Mesmo assim você pode embutir o eval como mostrado nesse link
O método abaixo faz o mesmo que o serialize, mas joga o resultado em uma string.
   ///o mesmo que serialize só que retornando uma string
   function SerializeObjToString(obj)
   {
    var metodos = [];
    var propriedades = [];
    var resultado = "";
    
    for (i in obj) 
    {
     //tirada a restrição do "serialize". Deixa trazer tudo. 
      if (typeof(obj[i]) == "function") 
      {
       metodos.push(typeof(obj[i]) + ": " + i + ": " + ObjToString(obj[i]) );
      }
      else
      {
       propriedades.push(typeof(obj[i]) + ": " + i + ": " + ObjToString(obj[i]) );
      }
    }
    
    //a string já é formatada
    resultado+="metodos
";
    for (m in metodos) 
    {
     resultado+=metodos[m]+"
";
    }    
    resultado+="

propriedades
";
    for (p in propriedades) 
    {
     resultado+=propriedades[p]+"
";
    } 

    return resultado;
   }
   
O método abaixo transforma o objeto javascript em uma string JSON.
   ///gera uma string json para ser interpretada com eval ou json.parse
   ///não é segura quanto à recursividade, referência cíclica
   function SerializeObjToJSON(obj)
   {
    var metodos = [];
    var propriedades = [];
    var resultado = "";
    
    for (i in obj) 
    {
     //tirada a restrição do "serialize". Deixa trazer tudo. 
      
      //verifica primeiro se o objeto é undefined, para retornar undefined 
      if(typeof (obj[i]) === "undefined")
      {       
       metodos.push("\""+i+"\"" + ":" + "\"undefined\"" );
      }
      //se for uma function retorna o corpo da mesma com ObjToString sem aspas
      else if (typeof(obj[i]) == "function") 
      {
       metodos.push("\""+i+"\""  + ":"  + ObjToString(obj[i]) );
      }
      //se for um número usa ObjToString para retornar sem aspas
      else if (typeof(obj[i]) == "number") 
      {
       metodos.push("\""+i+"\""  + ":" + ObjToString(obj[i]) );
      }
      //se for objeto verifica se é nulo ou não
      else if (typeof(obj[i]) == "object") 
      {
       //se for nulo simplesmente concatena null
       if(obj[i] === null)
       {
        metodos.push("\""+i+"\""  + ":" + "\"null\"" );
       }
       //senão chama a propria função e serializa recursivamente
       else
       {
        metodos.push("\""+i+"\""  + ":" + SerializeObjToJSON(obj[i]) );
       }
      }
      else
      {
       //senão assume que é string e poe entre aspas. Se tiver errado é fácil alterar. 
       propriedades.push("\""+i+"\""  + ":\"" + ObjToString(obj[i])+"\"" );
      }
    }
    
    //começa e termina entre chaves
    resultado+="{";
    for (var m = 0; m 0)
      {
       resultado+=",";
      }
     }
     else
     {
      resultado+=",";
     }
     resultado+="\r\n";
    }    
    
    for (var p = 0; p < propriedades.length; p++) 
    {
     resultado+=propriedades[p];
     
     if(p < propriedades.length-1)
     {
      resultado+=",";
     }
     
     resultado+="\r\n";
     
    }
    resultado+="}";  

    return resultado;
   }
Que pode ser transformada de volta em objeto da seguinte maneira.
    function evalJson()
    { try
     {
      //poe a string entre parenteses antes do eval      
      var txt = SerializeObjToJSON(pessoa);
      alert(txt);
      //var obj = eval("(" + txt + ")"); //para usar eval
      var obj = JSON.parse(txt); //para usar JSON.parse nativo
      //var obj = JSON && JSON.parse(txt) || $.parseJSON(txt); //para decidir de acordo com o browser se vai usar JSON nativo ou a versão do jQuery
      alert(obj.email);
     }
     catch(e)
     {
      alert(e.message);
     }
    }
Bom, por enquanto é isso. Acredito que esse é o meu maior post sobre javascript. Pretendo postar amanhã uma galeria com jQuery e dar uma organizada nesse blog.
Baixar o Exemplo
Have fun :)

segunda-feira, 8 de setembro de 2014

Consultando apenas os registros "sujos" em uma tabela com Microsoft SQL Server

Lidando com bancos de dados podemos controlar as transações e a maneira como podemos acessar os dados em transações concorrentes.

Os níveis de isolamento de transação e os comandos para tais serão discutidos em um próximo post, porém, resumidamente, temos (Serializável, snapshot, repeatable read, read commited, read uncommited).

Dirty read (read uncommited ou nolock) nos permitem visualizar os dados de uma tabela mesmo sendo sujos (alterados em outras transações). Isso garante, com certo nível de inconsistência se as transações forem desprezadas e o  rollback usado, uma leitura o mais rápido possível dos dados no seu estado atual quase que em tempo real.

No entanto, como separar os dados "sujos" daqueles que já estavam gravados e commitados?
Imagine que você tem um ambiente onde um sistema legado permite que a aplicação seja fechada, após uma exception ou queda de rede, sem dar commit ou rollback nas transações correntes. Você rapidamente ficaria com várias transações em aberto, utilizando recursos do servidor e dando table locks ou situações de deadlock. Como saber quais são os dados não commitados?

Em um cenário real, como saber quais dados podem não ser consistentes porque estão sendo alterados neste momento, em uma transação ainda não finalizada?

O comando / table hint nolock depois do nome da tabela nos permite fazer uma dirty read e visualizar tanto os dados antigos (commitados) como os não commitados.  Já o comando readpast faz uma leitura sem lock (somente leitura) da tabela trazendo apenas os dados commitados. Então, para sabermos os dados não comitados basta usar not exist ou except.

Dado o seguinte cenário:

create table teste
(
 valor varchar(50)
)

insert into teste values ('comitado')
select * from teste



begin transaction  --rollback
insert into teste values ('nao comitado')




Not Exist

select * from teste(nolock) where valor not in (select valor from teste (READPAST ))




Except

O Except funciona como not in ou not exists, mas sem especificar uma coluna ou pk: você simplesmente especifica a query com os resultados que você NÃO quer que apareça.
SELECT * FROM teste WITH (nolock)
EXCEPT
SELECT * FROM teste WITH (READPAST)


Com esses comandos você pode verificar quais são os comandos "sujos" de transações ainda não "commitadas" do seu banco de dados. O except ainda te ajudará com relatórios mais elaborados onde você deverá especificar com um filtro complexo um conjunto de resultados que você não quer trazer na sua consulta mas não tem uma chave primária ou não tem exatamente um campo único para comparar.




photo credit: tec_estromberg via photopin cc

domingo, 13 de abril de 2014

Como ordenar o conteúdo de um Memo com regras personalizadas

Recentemente um colega delpheiro me perguntou como ordenar um memo não pelo seu conteúdo, mas por alguma regra especial.

No caso dele, ele tinha no memo várias strings separadas por ":" com números do outro lado. Algo assim:

A:3
B:5
C:1

Devendo ficar:

C:1
A:3
B:5

Para isso podemos usar a função CustomSort do memo, passando como argumento uma função. Essa é uma função especial que aceita uma lista como parâmetro, dois índices da lista e retorna um integer. Assim ela pode comparar dois itens da lista e retornar 1 se o primeiro for maior, 0 se os itens forem iguais ou -1 se o segundo for maior.
Essa função é que fará todo o trabalho de comparação, portanto você decide como os itens serão comparados. A função CustomSort saberá que deve chamar a sua função para decidir, na varredura, qual item é maior porque você passará o nome da sua função, como se fosse um delegate ou ponteiro para função, como argumento para  a função customsort.

Os memos podem ser usados como dictionaries, ou como arquivos ini, eles podem ter pares de namve/value, e para isso você define a propriedade NameValueSeparator. Definindo esta propriedade como ":" os valores são automaticamente separados em duas partes.

O código ficou como abaixo:

//crio uma function inline que compara só dois valores e diz qual é o maior
//ela é usada automaticamente para comparar os valores 2 a 2 dentro da função
//customsort do stringlist e ordenar a parada toda
function compara(List: TStringList; Index1, Index2: Integer): Integer;
var val1: integer;
val2: integer;
begin

  //defino que o separador e chave / valor NameValueSeparator é ':', a string separadora.
  List.NameValueSeparator := ':';
  val1 :=  StrToInt( List.ValueFromIndex[Index1]);
  val2 := StrToInt( List.ValueFromIndex[Index2]);

  //quando você coloca o delimitador de um tstringlist  você pode separar ele em key e value
  //essa função pode ser anonima
  //se o valor do indice 1 for maior que o 2 retorna 1
  if(val1 > val2) then
    Result := 1
  //se for menor retorna -1
  else if(val2 > val1) then
    Result := -1
  else
    //se for igual retorna zero
    Result := 0;

  //esses resultados -1, 0 e  1 são o padrão de todos os comparers
end;



procedure TForm1.Button1Click(Sender: TObject);
  //crio um stringlist temporário para guardar o conteudo ordenado
  var str: TStringList;

begin
  str := TStringList.Create;

  //mando  o conteudo do memo para o stringlist
  str.AddStrings(Memo1.Lines);

  //mando ordenar passando como parâmetro a função ordenadora (existem
  //funções que aceitam outras funções como parâmetro, não o retorno dela, mas seu escopo, ela será executada internamente)
  str.CustomSort( compara );

  //limpa  o  memo1
  Memo1.Clear;

  //adiciona o valor ja ordenado
  Memo1.Lines.AddStrings(str);

  //destroi o stringlist
  str.Free;
end;


Have fun :)

quarta-feira, 2 de abril de 2014

Validação e conversão de datas

Dentre os problemas mais recorrentes em sistemas podemos citar os relacionados à cultura: charset, datas e formatação de números.

Datas são especialmente problemáticas porque podem ser ambíguas dependendo da cultura, por exemplo 02/01/1990 pode ser dois de janeiro no Brasil ou primeiro de fevereiro nos EUA.

Ainda mais agravante é o fato de nem sempre podermos validar as datas nos imputs, pois as mesmas podem vir de arquivos de integração, outros sistemas, CSV, TXT, Excel ou webservices.

Pensando nisso eu fiz uma pequea demonstração do uso dos comandos cast e convert para converter varchar para datas e datas para varchar, e mostrando onde estão os erros mais comuns.



/*******************************************************************************************************
******************************** DEMONSTRAÇÃO DE CONVERSÃO DE DATAS ************************************
********************************************************************************************************/

--imagine que tipo de coisa pode vir em um campo de "data" que não é datetime, ou um arquivo xml, txt, csv mal convertido
--imagine também o problema de gravar a data em uma linguagem, formatar para exibição em outra, e os problemas que sempre acontecem ao se converter string de um campo de texto para datetime

/*************************************** teste do convert *********************************/

--qual é a data  zero
select convert (datetime, 0, 103)
--R: é a data 1900-01-01 00:00:00.000

--e se a data for null?
select convert (datetime, null, 103)
--R: null

--E se a data for uma string vazia?
select convert (datetime, '', 103)
--R: é a mesma coisa que a data zero 1900-01-01 00:00:00.000 (aqui eu acho que deveria dar um erro)

--mesma coisa só que usando o padrão americano
select convert (datetime, 0, 101) 
--1900-01-01 00:00:00.000
select convert (datetime, null, 101)
--null
select convert (datetime, '', 101)
--1900-01-01 00:00:00.000


/*************************************** teste do cast *********************************/
select cast (null as datetime)
--como esperado, null
select cast (0 as datetime)
--como esperado, a data zero 1900-01-01 00:00:00.000
select cast ('' as datetime)
--1900-01-01 00:00:00.000


--agora, se em um arquivo de texto ou xml você tem uma data 0 (zero) ou '' (string vazia), isso é essencialmente um erro. Não quer dizer que a data real seja em primeiro de janeiro de 1900, mas sim que o campo não foi preenchido, logo, certos literais deveriam ser convertidos para null
select CAST(nullif(/*campo_que_pode_ser_string_vazia*/'','') as datetime)
select CAST(nullif(/*campo_que_pode_ser_zero*/0,0) as datetime)

--para todos os casos
select CAST(
  coalesce(
   nullif(/*campo_que_pode_ser_string_vazia*/'',''), --se for null tenta o proximo ...
   nullif(/*campo_que_pode_ser_zero*/0,0), --- que se for null de novo vai null mesmo
   null) as datetime)
   
   
   
/***********************************************************************************************************************
******************************************** exemplos de problemas de conversão de datas *******************************
***********************************************************************************************************************/

--fique atento quando as datas podem vir de outros bancos de dados, de sistemas que formatam de uma forma não canônica e quando você está usando o cast em vez do convert com formato específico
--troque 2014 pelo ano corrente e não faça esse teste em 1º de janeiro

--o que é maior, a data de hoje ou 2014-12-01 ?
select ( cast ('2014-12-1' as datetime)), getdate(), case when cast ('2014-12-1' as datetime) > getdate() then 'Maior' else 'Menor' end
--como estamos em março, primeiro de dezembro é maior

--o que é maior, a data de hoje ou 2014.12.1 ?
select (cast ('2014.12.1' as datetime)), getdate(), case when cast ('2014.12.1' as datetime) > getdate() then 'Maior' else 'Menor' end


--padrão Brasileiro  
--o que é maior, a data de hoje ou 2014-12-01 ?
select ( convert (datetime,'2014-12-1', 103)), getdate(), case when convert (datetime,'2014-12-1', 103) > getdate() then 'Maior' else 'Menor' end
--como estamos em março, primeiro de dezembro é maior

--o que é maior, a data de hoje ou 2014.12.1 ?
select (convert (datetime,'2014.12.1', 103)), getdate(), case when convert (datetime,'2014.12.1', 103) > getdate() then 'Maior' else 'Menor' end

   
   
--padrão americano   
--o que é maior, a data de hoje ou 2014-12-01 ?
select ( convert (datetime,'2014-12-1', 101)), getdate(), case when convert (datetime,'2014-12-1', 101) > getdate() then 'Maior' else 'Menor' end
--como estamos em março, primeiro de dezembro é maior

--o que é maior, a data de hoje ou 2014.12.1 ?
select (convert (datetime,'2014.12.1', 101)), getdate(), case when convert (datetime,'2014.12.1', 101) > getdate() then 'Maior' else 'Menor' end

     
--isso mostra que o formato AAAA-MM-DD ou AAAA.MM.DD, nessa ordem, sempre é interpretado corretamente pelo SQL, independente da linguagem. Agora coisas estranhas podem ocorrer se você obtiver uma data da qual não sabe o formato e tentar converter para datetime forçando uma formatação brasileira ou americana     
--lembre-se também que o CAST depende da data do sistema. 
--Faça de tudo para, tanto para datas como para formatos numéricos, nunca depender das informações de localização e linguagem do servidor de SQL mas depender somente do servidor WEB e do .net framework, ou, na melhor das hipóteses, o que o usuário informar em suas preferências. 

--padrão Brasileiro  
select ( convert (datetime,'01/12/2014', 103)), getdate(), case when convert (datetime,'01/12/2014', 103) > getdate() then 'Maior' else 'Menor' end
--beleza
select (convert (datetime,'01.12.2014', 103)), getdate(), case when convert (datetime,'01.12.2014', 103) > getdate() then 'Maior' else 'Menor' end
--beleza
   
   
--padrão americano   
select ( convert (datetime,'01/12/2014', 101)), getdate(), case when convert (datetime,'01/12/2014', 101) > getdate() then 'Maior' else 'Menor' end
--epa!
select (convert (datetime,'01.12.2014', 101)), getdate(), case when convert (datetime,'01.12.2014', 101) > getdate() then 'Maior' else 'Menor' end
--epa!

Porém não é apenas nas rotinas de banco de dados que devemos nos preocupar com as datas. Em sistemas data centric ou orientados a dados, ou com muitas stored procedures, as dicas acima são uma mão na roda, mas em sistemas orientados a objetos onde o problema das datas é resolvido no domínio da aplicação, via código, é bom conhecer as ferramentas disponibilizadas pelo seu ambiente.

No caso do C# temos Culture, e as ferramentas para identificar a culture corrente, sugerir uma padrão ou modificar a Culture.

Um outro problema relacionado são as datas no nosso domínio que podem ser nulas, mas que devemos em algum momento converter para string dependendo da aplicação para sua exibição. Nesses casos provavelmente você tem no seu banco de dados um campo null, e na aplicação um DateTime? nulável. Soma-se a isso o fato de a data mínima do banco de dados ser muito maior do que a data mínima na aplicação. Isso gera muito código repetitivo onde se verifica se uma data é nula, depois verifica-se se ela é a data mínima para só então fazer a conversão.
Isso pode ser feito via extension method de DateTime ou um método de uma classe estática, mais ou menos como abaixo, e poupar muito código repetitivo:

        public static string DateToString(DateTime? data)
        {
            //se for nula retorna string vazia
            if (data == null)
            {
                return "";
            }

            //não sei se é possível não ser nula e não ter um valor, acho que essas três linhas são inúteis, mas são uma alternativa às três linhas de cima
            if (!data.HasValue)
            {
                return "";
            }

            //se a data for menor ou igual a data mínima do banco de dados é porque era para ser originalmente nula e foi erroneamente gravada ou exportada de forma incorreta (para um arquivo texto com a data mínima em vez de branco, por exemplo).
            if (data.Value <= new DateTime(1900, 1, 1))
            {
                return "";
            }
            
            //...
            //código para obter a culture corrente ou escolhida pelo usuário
            //...
            
            //devolvendo a data como string
            //em um formato padrão
            //return data.Value.ToString("dd/MM/yyyy");
            //ou dependente da culture
            return data.Value.ToString(CultureEncontrada);
        }

Uma dica simples, em breve mais dicas sobre cultura, encoding e afins.

quarta-feira, 19 de março de 2014

Criando um identificador único no excel

Para criar um identificador único no exel, tanto para identificar uma linha sem repetições como para fazer scripts para exportar os dados do excel para um banco de dados é necessário abrir o editor de código VBA do excel, na aba Developer.
Caso a guia não esteja aparecendo para você clique em file -> options -> customize ribbon e marque a opção developer à direita.

Na aba developer abra o editor do visual basic, crie um novo módulo e adicione o código.

Public Function NewId() As String
    NewId = Mid$(CreateObject("Scriptlet.TypeLib").Guid, 2, 36)
End Function

Public Function NewGuid() As String
    NewGuid = CreateObject("Scriptlet.TypeLib").Guid
End Function




A função NewId é igual a newid() do sql server e cria uma Guid sem as chaves no início e fim. A NewGuid está aí só para demonstração e apresenta guids com as chaves.
Ambas usam o objeto COM Scriptlet.TypeLib e chamam o método Guid.
Eu uso isso quando eu tenho que importar dados de uma planilha excel para um banco de dados qualquer sem usar DTS ou algum programa para isso: faço uma formuleta excel que concatena strings em uma linha fazendo um script de inserts, concateno os valores das células que quero e uso uma guid gerada por essa função como chave primária.
Você pode fazer download dessa planilha aqui guid.xlsm
Have Fun

Essa é pra quem curte Duna

Pra quem conhece, vai entender de primeira.

Na boa, sem fazer spoil, esse livro é um dos melhores que eu já li, tem espaço especial dedicado a ele na minha estante. 
Ele simplesmente mistura à ficção científica elementos de religião, filosofia, política e ecologia. Muito avançado para o seu tempo, e praticamente profético se levarmos em conta o caminho que estamos trilhando rumo ao esgotamento da água potável do planeta. 

Leitura recomendada. 

Postagens populares

Marcadores

delphi (60) C# (31) poo (21) Lazarus (19) Site aos Pedaços (15) sql (13) Reflexões (10) .Net (9) Humor (9) javascript (9) ASp.Net (8) api (8) Básico (6) Programação (6) ms sql server (5) Web (4) banco de dados (4) HTML (3) PHP (3) Python (3) design patterns (3) jQuery (3) livros (3) metaprogramação (3) Ajax (2) Debug (2) Dicas Básicas Windows (2) Pascal (2) games (2) linguagem (2) música (2) singleton (2) tecnologia (2) Anime (1) Api do Windows (1) Assembly (1) Eventos (1) Experts (1) GNU (1) Inglês (1) JSON (1) SO (1) datas (1) developers (1) dicas (1) easter egg (1) firebird (1) interfaces (1) introspecção (1) memo (1) oracle (1) reflexão (1)