terça-feira, 7 de dezembro de 2010

Degub de queries que não mostram erro no Doctrine 1

Se você estiver tendo problemas com uma query especifica que morre sem deixar vestigios, sem imprimir nada na tela, pode se beneficiar de mais informações registrando um listener generico para imprimir os eventos e também melhorando o flush para ele realmente mostrar algo. antes de fazer a query, registre o listener na conexão:

$conn = Doctrine_Manager::connection();
$conn->setListener(new MyDebugger());
$result = $query->execute();

E também declare ele e o flush melhorado:

class MyDebugger implements Doctrine_Overloadable
{
public function __call($methodName, $args)
{
echo $methodName . ' called !';
flush2();
}
}

function flush2 (){
echo(str_repeat(' ',256));
// check that buffer is actually set before flushing
if (ob_get_length()){
@ob_flush();
@flush();
@ob_end_flush();
}
@ob_start();
}

quinta-feira, 11 de novembro de 2010

Refactoring - é bom mesmo quando vira costume!

Neste artigo vou falar do refactoring, explicar porque fazer e dizer algumas regras fundamentais que aprendi com a experiência.

Quase todo bom costume que queremos colocar na vida da gente, no ínicio é uma peleja para conquistar. Mas também depois que vira um costume mesmo, aí fica difícil é de deixar de fazer. Uma coisa que desejo para todo programador é que ponha no seu dia a dia o custume de fazer faxina no código, o famoso "refactoring".

Como funciona isso? É simples, você vê um código bagunçado e arruma ele. Claro que não da pra arrumar todo o código bagunçado do mundo, mas ao menos podemos ir arrumando o código no qual vamos precisar trabalhar. A idéia é: Já que vamos mexer na sujeira, que seja para limpar logo de vez.

Se vamos começar a mexer em um código, seja quem foi que programou ele, até mesmo nós em um dia que estavamos com dor de barriga, precisamos entender aquele código. Se for um código confuso, daquelas "belezuras" com variáveis misteriosas e diversas estruturas aninhadas com breaks, continues e switchs, temos geralmente duas opções:

Opção #1 -
  • perder muito tempo tentando entender o código
  • perder muito tempo para cada modificação que queremos fazer
  • deixar uma bagunça maior para o próximo que for mexer naquele código.

Assim temos a seguinte equação:

muito tempo para entender +
muito tempo para mexer * número de modificações
----------------------------------------------------
muito tempo gasto, um código ainda pior e insatisfação


Opção #2 -
  • perder muito tempo tentando entender e já fazendo um refactoring
  • gastar pouco tempo para cada modificação
  • deixar um código bom para o próximo que for mexer (que pode ser inclusive eu mesmo).

Assim temos:

muito tempo para entender e fazer o refactoring
+ tempo razoável para mexer * número de modificações
--------------------------------------------------------
muito tempo gasto, código limpo e satisfação


O fato é que sempre que pegamos um código medonho, teremos que gastar muito tempo nele. E esse tempo geralmente aumenta proporcionalmente à idade daquele código. Quanto mais antigo, mais WTF, mais tempo gasto. Assim se vamos gastar mesmo muito tempo nele, façamos logo um refactoring nele.

O progra
mador preguiçoso, ou espertalhão, muitas vezes vê um código desses e acredita que vai fazer apenas uma gambiarrazinha, vai consertar o problema, e ainda vai fazer uma média com o gerente. Conheço esse tipo. Coitado, um dia a casa cai e ele descobre que não sabe programar, pq ficou sempre dando uma de esperto e não teve tempo para aprender a programar.

Bom, voltando ao refactoring, ele vale para todos os tipos de código. Não só os muito feios. Mesmo que o código seja razoável, ou bom, podemos ir melhorando ele para que fique cada vez mais simples e rápido de dar manutenção. Claro que tem hora que temos que pesar o preço daquela manutenção, mas isso depende de cada caso. Na maioria das vezes a merda fica quando pensamos em fazer algo apenas provisório, "só um protótipozinho", e aquele código fica em produção por muitos anos.

Mas na maior parte dos casos vale muito o esforço de fazer um refactoring. E com certeza isso não só contribui para o código como também para o desenvolvimento profissional do próprio programador. Como eu falei no início, a idéia é que isso vire um costume, uma virtude. A idéia é que seus dedos fiquem coçando para fazer um refactoring quando você olhar para qualquer código. Existem vários casos onde podemos fazer refactoring e até livros inteiros a respeito do assunto, não vou tratar tudo isso aqui, porque não cabe.

Mas algumas regras importantes sempre se aplicam e vou concluir este artigo com elas:

1 - Sempre é bom fazer refactoring antes e depois de mexer no código. Nunca durante. Ou em outras palavras: ta fazendo refactoring, não muda o que o código faz. Essa regra, o espertão sempre acha que pode burlar, pegar um atalho. Muito cuidado! Há atalhos que dão um trabalho enorme para encontrar o caminho de volta.

2 - Escolha blocos pequenos para fazer refactoring. Teste para saber o que o código faz. Faça o refactoring e teste novamente. Dê passos pequenos. Cada coisa de uma vez. Por mais tentador que possa parecer alterar logo tudo que você sabe que precisa, isso geralmente da merda. Assim quando bater o impulso de alterar logo tudo, conte até dez e lembre-se: uma coisa de cada vez.

3 - Ao fazer refactoring, faça pensando na lógica do cliente, na linguagem do cliente. Vai nomear um método, pense como o cliente pensa, na linguagem dele. Vai nomear uma variável, pense na linguagem dele (na verdade o ideal é estabelecer um glossário e uma terminologia única entre cliente e equipe de desenvolvimento). Você deve estar pensando que em baixo nível, mexendo na API, isso não importa pq o cliente não conhece isso. Porém em baixo nível, o cliente não é o cliente do software, mas é o cliente da API: o desenvolvedor. Pense nele e se cabível, mantenha também a linguagem única do cliente do software.

Tem gente que me procura para saber como trabalhar com arquitetura de software, com componentes, herança, injeção de dependência e todas essas coisas. O fato é que a estrada começa sempre nestes pontos que falei neste artigo. Se um programador não desenvolve esses hábitos, dificilmente ele se tornará algo mais.

Em todas as áreas do desenvolvimento humano, é bom procurar as virtudes a serem desenvolvidas, e no desenvolvimento de software, fazer refactoring certamente é uma delas.

sexta-feira, 22 de outubro de 2010

PhpDescribe agora com mais documentação.

O PhpDescribe, ferramenta para escrever especificações ativas de software em uma filosofia semelhante ao do BDD, lançou seu novo site com informações a respeito da ferramenta de teste de software.

É uma ferramenta muito promissora para quem quer unir a escrita de especificação com a escrita dos testes de software. Além disso é um exemplo de código usando novas funcionalidades do php 5.3.

quarta-feira, 22 de setembro de 2010

Trees, forests and applications... please let´s turn tests in real specifications.

To understand the tree, you need to look at the forest. To understand a forest you need to look at the trees. Clients normaly want to see the forest, but developers like to develop trees. A big step taken by the agile process is to start moving the developers to see the forest, not only the trees that compose that forest.

When I was trying to learn how to draw, the good teachers kept telling me to do things from the outer to the inner. TDD and BDD brought a broader vision to this. But I still have a feeling that we need to start from a little bit before and we also need to have a beeter picture of the tree.

The standart bdd tools, using a strict grammar is nice. But, how do we see the results? Are they results at all?

No, they are the specifications. For tests we have results and for specification we have what? I think in examples and these can be working or not workin. So, how do I take a good look at the specifications and see what parts are working, incomplete, not working....

I have not seen yet this being addressed by any tool. Colored test results from a little piece of specification, as most tools will produce, won´t give me the forest view. Forest have leaves, flowers, stems and other important things to be noticed to understand the forest. I think we should think more in writing specs to be read and understood by humans than by continuous integration tools. They should be extremely easy to navigate, search and write. Images are essential in some place, they need to have a way to be added.

So I think there is a gap to be filled.... that´s why I´ve started PSpec.

terça-feira, 20 de julho de 2010

A simple symfony application deploy using shell script.

This is something that can be used to deploy an app to an staging environment. After that you can sync it somehow, but I prefer to export again to prod to keep the fidelity from the release launched with the repo release.

It´s nice to see how many things can be done with shell scritp without the need to configure those weird xmls (IMO, of course). It´s just more.... simple.

#!/bin/bash
die () {
echo >&2 "$@"
exit 1
}

// validate the parameters count....

[ "$#" -eq 1 ] || die "Voce precisa informar o nome da release para ser o nome do diretorio gerado dentro de prod_online"

// exports the code from a local repo. (if you need from a foreign with ssh, ask that I can publish some info also)

svn export --force file:///data/svn/reuni-on/trunk /data/web/reuni/prod_online/$1

// override the config file with one specific to prod environment.

cp /data/web/reuni/deploy_script/testesSincronia.conf.tmp /data/web/reuni/prod_online/$1/web/testesSincronia/conf.php

// create dirs that are not so good to be on the repo.

mkdir /data/web/reuni/prod_online/$1/cache
mkdir /data/web/reuni/prod_online/$1/log
mkdir /data/web/reuni/prod_online/$1/data/offlineParaDownload

//allow to write into this dirs ( here you will probably want a more restrictive permission)
chmod 777 -R /data/web/reuni/prod_online/$1/cache
chmod 777 -R /data/web/reuni/prod_online/$1/log
chmod 777 -R /data/web/reuni/prod_online/$1/data/offlineParaDownload

// clear symfony cache
php /data/web/reuni/prod_online/$1/symfony cc

quarta-feira, 14 de julho de 2010

Robots.txt url without trailing slashes. - ALERT -

To leave a url in the robots.txt without a trailing slash can steal your sleep sometime. This makes the seach engine think you are blocking any match that starts with that url and it can disallow or allow unwanted urls, read these to understand better:

http://www.google.com/support/webmasters/bin/answer.py?hl=br&answer=156449

http://www.webmasterworld.com/forum93/892.htm

quinta-feira, 1 de julho de 2010

Imagens e outros downloads com acesso restrito em PHP... como mostrar?

Quem sempre mostrou imagens usando simplesmente a URL da mesma em um tag img, quando precisa restringir o acesso para elas, muitas vezes fica confuso em como fazer isso. A receita é bem simples:

1 - coloque a imagem fora do diretório que é servido pelo apache.

2 - crie um script que faz o controle de acesso

3 - se o usuário estiver autorizado, então devemos ler a imagem e mandar direto pro usuário, para isso podemos usar o método fpassthru() que já vai jogando a imagem direto pro buffer de saída, gerando um ganho de performance.

4 - depois disso é só fazer uma referência para aquele script, algo como < src="'mostraImagem.php?id="444'">

simples né? olhaí um exemplo de como fazer isso:


$targetPath = $caminhoDaMinhaImagem . '.jpg'; // presumindo que tem esta extensão, é claro.
$fp = fopen($targetPath, 'rb'); // abre para ler um binário
header("Content-Type: image/jpg"); // manda um header dizendo que é um jpg
header("Content-Length: " . filesize($targetPath)); // da a dica do tamanho da img pro browser
fpassthru($fp); // manda
// se você tiver dentro de algum framework, você tem que dar um jeito de não mandar mais nada, um método "bruto", mas prático, é simplesmente die();


Dê uma olhada melhor na documentação dessa função, la tem alguns exemplos muito úteis:

http://php.net/manual/en/function.fpassthru.php

quarta-feira, 30 de junho de 2010

Doctrine - Operações em lote ou "batch updates" - algums dicas

O Doctrine não é uma ferramenta feita para operações em lote, ele é um ORM. Mas tem alguns momentos que já temos o método na nossa camada de negócio e queremos chama-lo um monte de vezes para executar alguma manutenção no banco de dados ou algo assim.

Nestes momentos nem sempre é necessário reescrever tudo em um sql otimizado, em casos mais simples ou não tão críticos, podemos usar o que já temos. Porém, fazer esse tipo de operação no Doctrine requer alguns cuidados que temos aprendido com o tempo. Coloco aqui algumas dicas para quem quiser pular a fase do aprendizado pelo erro. Mais abaixo também coloco um exemplo de código para o tão famigerado e entretanto tão usado copy and paste.

Dicas:

1 - Faça um looping iniciando e "commitando" a transação. Isso aumenta em muito a velocidade de insert/update no banco. A quantidade de registros atualizados a cada iteração vai ter que ser definida na tentativa e erro, mas quanto mais simples a operação, maior a quantidade de itens que podem ser incluidos. Vale perceber que mesmo que a transação funcione, se forem muitos itens, o resultado final pode ficar mais lento mesmo assim.

2 - Sempre chame o método free() passando o parâmetro true para seus objetos instanciados pelo doctrine para que ele libere a memória usada por aquele objeto. Eu chamo também um unset() logo em seguida.

3 - Se você usar uma action no symfony (isso é, se é que você usa o symfony), ele tem uma mania de querer repetir a operação depois que termina. Por algum motivo que ainda não descobri, ele faz um redirect ou forward para a mesma action, então eu coloco aquele die() no final. Sei que ideal seria uma task rodando do shell, mas faço assim pq acho mais simples, principalmente para operações que vão rodar uma vez só. Se for algo que vai ser ativado pelo cron, aí não tem como fugir das tasks.

Para recortar e colar (divirta-se):


function executeRecalculateAllRatings() {
set_time_limit(3600);
$min = 0;
$increment = 1000;
$last = 50000;
$count = 0;
while ($min <= $last) { echo "<>NEW TRANSACTION - $min <>";
Doctrine_Manager::connection()->beginTransaction();
$comments = CommentTable::build()->findByIdRange($min, $min + $increment);
foreach($comments as $comment) {
$res = RatingLogTable::build()->calculateCommentRatingTotal($comment['id']);
$comment->setRating($res[0],$res[1]);
$comment->save();
$comment->free(true);
unset($comment);
echo $count++ . ' - ';
}
$min = $min + $increment;
Doctrine_Manager::connection()->commit();
}
die();
}

quarta-feira, 23 de junho de 2010

Git Submodules - um recurso para gerenciar dependências no lugar de svn externals.

Submodules & GIT - usando bibliotecas externas.

Quem está acostumado em gerenciar suas bibliotecas em svn usando o recurso externals, quando considera usar o GIT, geralmente tem dúvida a respeito de como trabalhar. Já escrevi aqui um artivo a respeito de svn externals e agora que estou migrando pro git então escrevo este aqui também para quem quiser me acompanhar.

Dois recursos normalmente surgem quando pesquisamos a respeito do assunto:
  • submodules
  • subtree merge strategy
Vamos tratar aqui do primeiro, porém quem quiser pesquisar o segundo, veja a comparação entre submodules e subtree merge strategy na documentação.

O que são submodules?

Na documentação diz que submodules ou submódulos permitem repositórios externos serem adicionados dentro de um subdiretório dedicado da arvore do nosso projeto, sempre apontando para um commit específico.

Bom... uma parte de cada vez:

repositórios externos
são outros repositórios em git.

diretórios dedicado
o diretório onde o submódulo fica tem sua arvore específica, inclusive a pasta .git com suas próprias configurações. Não pode ser feito merge com a arvore do repositório pai.

commit específico
quando adicionamos um submódulo a uma arvore, estamos adicionando um commit específico. Isso quer dizer que atrelamos ele a uma versão (um SHA). Toda vez que alguém baixar este projeto e pedir o submódulo ele vai receber esta versão mesmo que o repositório original esteja bem mais adiantado.

Isso é legal. Quero um pra mim. Como eu faço para criar um submódulo?

você deve usar um comando semelhante à este aqui (nessa hora o melhor mesmo é consultar o manual do git):

git submodule add [URL do repositório] [pasta de destino]
git submodule add http://github.com/brunoreis/pspec.git extlibs/pspec


com isso ele vai criar um repositório para o pspec na pasta extlibs/pspec. Vale a pena dar uma olhada nesta pasta, dentro do diretório .git, e ver o que tem nos arquivos texto de configuração. Com certeza vai auxiliar a encaixar as peças do quebra-cabeças. Ah, e também olhe o .git da raiz do seu projeto.

Bom, então é só isso? Meu submódulo está criado, posso mandar meu projeto para o github e ficar famoso porque uso submódulos e o pspec para especificar e testar meu software?

Muita hora nessa calma! Ainda temos algumas coisas a fazer antes. Ao criar o submodule, como já dissemos antes, atrelamos ele a um SHA específico. Os arquivos do repositório, a princípio, não ficam dentro do nosso repositório, mas o SHA sim. Temos então que adicionar o subdiretório criado à arvore original para que ele possa ser integrado ao nosso repositório.

Fazemos assim:
git add extlibs/pspec
git commig -m "agora sim com pspec"

Agora está lá... e se quiser subir para o repo de origem, não esqueça de fazer um push.

Isso quer dizer que quem fizer checkout no meu projeto já vai baixar o submódulo?

Não. Como já falei antes, você mandou apenas o SHA. Assim quando você fizer checkout vai trazer apenas uma referência à aquele submodulo, sem baixar o conteúdo do mesmo. Então após fazer checkout do seu repo, você ainda vai ter que fazer o seguinte:
git submodules init
git submodules update

Com isso ele vai trazer o conteúdo do(s) submódulos de acordo com o SHA de cada um.

E como eu faço pra saber se um projeto que baixei tem submódulos?

essa é fácil, é só rodar
git submodules

ele mostra os submódulos e também o SHA de cada um.

Beleza, mas agora eu sei que tem algo novo que eu preciso em um submódulo, como eu atualizo ele?

Simples, como o submódulo tem lá sua arvore e seu .git, etc.. porém este repositório está atrelado a um commit (sha) específico. Então precisamos fazer um checkout do branch que você quer, ex:

git checkout master

depois disso você pode usar

git pull

para atualizar.

Ah, e lembre-se de que depois que você fez isso, você tem que adicionar novamente o submodule ao repositório e fazer um commit (da atualização do SHA) para que os outros possam ter essa atualização.

Ao fazer essa atualização não coloque uma barra depois do nome do submodule. Se você fizer isso vai bagunçar tudo. Ou seja, use:
submodule add extlibs/pspec


mas NÃO USE

submodule add extlibs/pspec/ <--------------- Essa barra faz @#$@%@!!


Ta bom, ta bom....Hum.... e....

Se eu quiser atualizar todos de uma vez?

É só fazer assim:

git submodule foreach "git pull"

Esse comando eu achei muito interessante. Ele roda por todos os submódulos e executa o comando que for passado para ele, no noso caso o pull. Não esqueça de mandar esta alteração adicionando os submodules novamente à arvore principal.

Beleza, mas agora que estou no meu projeto, vi que tem um bug no repositório do submódulo. Tem como eu arrumar isso sem fazer checkout do sub em outro diretório?

Sim. Você pode entrar no dir do submódulo, fazer um checkout, alterar, fazer um commit e mandar novamente para o repo original.

Se você fizer isso e encontrar um erro dizendo que o original não aceita commits deste, leia este post a respeito do uso de "bare repositories".

Ta Bom.

Espero que você tenha sucesso usando os submódulos. Eles me parecem bem mais simples e dinâmicos que os externals do svn. Ainda estou aprendendo o git, então deve ter algo aqui que pode ser feito de uma maneira melhor ou então algo que você não entendeu. Peço que você acrescente seu comentário que se eu puder, respondo. Assim agregamos mais informações para os próximos não terem que ficar pesquisando as mesmas coisas que nós.

Sucesso!

--------------
Ah...

Algumas referências citadas e usadas aqui:

http://www.kernel.org/pub/software/scm/git/docs/git-submodule.html

http://book.git-scm.com/5_submodules.html

https://git.wiki.kernel.org/index.php/GitSubmoduleTutorial

http://stackoverflow.com/questions/804545/what-is-this-git-warning-message-when-pushing-changes-to-a-remote-repository

sexta-feira, 18 de junho de 2010

PHP - Para fazer refactoring faz diferença se um método é públicos ou privados?

Para muitos o fato de um atributo ou método ser público ou privado pode nada ter a ver com refactoring de código. Afinal de contas isso tem mais a ver com o fato dessa classe poder ser acessada de fora ou não do objeto em questão. Então, a princípio, é melhor deixar aquele método público (padrão do php), afinal de contas deixando ele público, ele poderá ser usado por muitas outras classes, correto?

ERRADO!

Toda vez que você cria um método e declara ele como sendo público você está aumentando a interface do seu objeto. Com isso o seu sistema fica mais complexo.

E o que isso tem a ver com refactoring?

Para quem refatora código normalmente (todos fazem isso, correto? ), isso é bem fácil de entender. Se eu tenho um método público, estou dizendo que este método pode estar sendo chamado de qualquer lugar no sistema. Ou seja, se eu precisar alterar o meu método, vou precisar revisar o sistema inteiro para saber se alguém usa aquele método. E para uma linguagem dinâmica como o php, isso não é nenhum pouco fácil de fazer usando uma IDE.

Tudo que é dificil de fazer parece que atrai aquela conhecida de muitos programadores: preguiça! E a preguiça gera "ctrl+c ctrl+v". Resultado: código duplicado, dificuldade de fazer refactoring, problemas na implementação de novas regras. Mais erros, mais custo de manutenção, etc... Você deve conhecer esta novela!

Já se a pessoa que declarou o método colocou uma simples palavra antes dele (protected ou private), aí fica bem mais fácil de saber se o método está sendo usado no sistema, afinal de contas ele só poderá ser usado naquela classe ou filhas. Assim a preguiça fica no canto dela, o método é refatorado e o código fica limpo, fácil de mover, etc... espero que conheça esta história também! Se não conhece, a hora é agora. Sucesso!

sábado, 29 de maio de 2010

Que tal uma validação de dados "universal" para a comunidade php?

Validação de dados é um tema que sempre gera alguma controvérsia. Alguns acham que ela tem que ser feita na action, outros em uma camada de serviço ou de aplicação e outros no modelo. A maioria das pessoas também concorda que ela deva ser feita no browser. Neste texto pretendo examinar um pouco este assunto e sugerir a necessidade de se ter um componente de validação de dados para php como um projeto independente de qualquer framework ou camada.

Esses dias li um artigo interessante que diz que ao mesmo tempo que a comunidade ruby está caminhando para um único padrão forte de framework com muita convenção sobre conficuração, a comunidade php está caminhando para retirar a "mágica" dos códigos focando em padrões de projeto e OO, ter mais de um framework robusto e buscar ter interoperabilidade entre os frameworks. Achei isso muito bom. Pela minha experiência vejo que esses comportamentos "mágicos" dos frameworks costumam embaralhar muito a cabeça dos programadores mais jóvens.

Nesse rumo também vemos que algumas ferramentas estão amadurecendo muito e buscando encontrar o seu foco principal, removendo tudo aquilo que foge a esse foco. O Doctrine 2, por exemplo, que é um projeto que venho estudando, não vem com componente de validação integrado na versão 2. No início fiquei bem chateado com isso pois gostava muito de definir já algumas restrições no yml e já ter essa validação prontinha pra mim. Mais depois consegui perceber que o foco de um ORM não é esse e acho que se cada ferramente se manter no seu foco ela tem mais sucesso de prosperar.

Além disso se examinarmos um pouco mais, como já falei antes, dependendo da arquitetura uns acham melhor ter a validação no modelo, outros no JS, outros na action, etc... Não quero nesse texto ficar discutindo o que é melhor. Eu mesmo acho que isso varia de sistema pra sistema. Mas se percebemos bem essas necessidades vemos que a comunidade se beneficiará muito de ter uma ferramenta de validação de dados como sendo uma biblioteca completamente isolada. Assim ela poderá ser utilizada com qualquer framework e em qualquer camada. Claro que podem ser definidos adaptadores para integrar essa ferramenta em um lugar ou outro, mas o cerne dela deve ser "puro".

O meu pensamento é que se tivermos um arquivo de definição de dados, ele possa ser lido e usado para validar tanto um entidade na camada de modelo quanto um formulário no javascript. Com isso criamos um padrão de formato de dados que pode ser uma ótima contribuição nessa filosofia de ter ferramentas que podem ser usados nos diversos frameworks e camadas.

Algumas coisas eu acho interessante que essa ferramenta tenha:
  • Extensibilidade. Esse é um dos pontos principais. Deve ser muito fácil acrescentar um novo tipo de dados e também a classe ou método para validar este tipo. Além disso pode ser útil ter um sistema para registrar listeners que recebem eventos durante o processo de validação extendendo ou sobreescrevendo algum comportamento.
  • Facilidade de configuração. Geralmente as mensagens de erro lançados por estas ferramentas já atendem nos casos mais simples. Então é necessário poder trocar as mensagens de erro de cada validação e também poder capturar um erro e sobreescrever a mensagem para casos específicos.
  • Trabalhar com objetos/arrays multidimensionais. Esse é um dos pontos onde a maioria das ferramentas do "mercado de software livre" não atende. Acho importanto poder dizer que dentro do dado que eu quero validar pode ter um array e que cada item "nome" desse array pode ter no máximo 15 caracteres.
  • Ter um formato que pode ser compartilhado entre servidor e browser. É um fato que o servidor tem que ter validação para termos os dados íntegros. Também é um fato que uma validação no JS melhora a experiência do usuário e diminui o trafego entre cliente e servidor. E muitas vezes a validação nos dois pontos é a mesma. Por isso é importante que a definição da validação possa ser usada pelo browser em uma api semelhante à usada no servidor.
São poucos formatos que atendem a todas essas demandas. Um formato que achei bem interessante para o que já desenvolvi até agora é o Json Schema:

http://groups.google.com/group/json-schema/web/json-schema-proposal-working-draft

http://tools.ietf.org/html/draft-zyp-json-schema-02

Mas com certeza existem outros. O que quero ressaltar aqui é exatamente a necessidade da existência de uma ferramenta como essa. Tendo algo assim separado de frameworks, a comunidade pode juntar esforços para contribuir melhorando cada vez mais os algorítmos de validação e ampliando cada vez mais os tipos de dados disponíveis.

quarta-feira, 19 de maio de 2010

Alterando uma informação automáticamente antes do update no doctrine 2. (com annotations)

1 - Definimos que a classe ouve os callbacks:

/**
* @Entity
* @HasLifecycleCallbacks
*/

2 - Definimos um método para ser atualizado antes do update.

/**
* @PrePersist @PreUpdate
*/

function updateUpdatedAt() {
$this->updatedAt = new DateTime();
}


3 - Lembrar de mandar construir os modelos novamente.

sábado, 3 de abril de 2010

Data Repositories - low coupling with persistence layer.

Someday I used to think a method is good if it can be very flexible. But thins article tells a diferent story:

http://codebetter.com/blogs/gregyoung/archive/2009/01/16/ddd-the-generic-repository.aspx

quarta-feira, 24 de março de 2010

Concordion - parece algo de qualidade para BDD

Tem tempo que procuro uma ferramenta útil que una testes unitários e escrita de especificação em uma coisa só. Ou seja, uma ferramenta boa de BDD. Recebi este link de um colega de trabalho e gostei. Esta pode ser uma boa ferramenta para se ter um port para php:

http://www.concordion.org/

quinta-feira, 18 de março de 2010

Refactoring e Clareza de Código - Negação da Negação

Este faz parte de uma série de pequenos posts com os tags código limpo e refactoring que vou estar publicando na medida que vou limpando alguns códigos por aqui.

A negação da negação geralmente atrapalha a leitura:
if(!$userAllowed) {
//x
} // aqui estamos negando a negação do if acima...
else {
//y
}
ou seja, se o sistema não tiver acesso, faça x e se ele não não tiver (que é o mesmo que ele ter, mas da um trabalho para ler) faça y. Sei que isso parece simples, principalmente em um código pequeno, mas tudo que diminui a velocidade de leitura deve ser evitado, até mesmo porque em alguns métodos fica difícil não ter um código extenso.

Normalmente quando um if tem um else, é mais claro se não começamos negando. Porque então o else vai ser a negação da negação.

Ou então algo assim:

if(!$accesForbidden) {//a} else {//b}

parece normal, mas da um trabalhinho pra ler. É bem mais fácil ler algo como:

if($hasAccess) {//a} else {//b}

ou no máximo:

if($accesForbidden) {//b} else {//a}
apesar de que este segundo caso só acho aceitável quando a variável accessForbiden já existe e tem esse nome justificado em outro contexto.

quarta-feira, 17 de março de 2010

Desacoplando para Testar - Injeção de dependência, inversão de controle e outras palavras bonitas.

Objetivo: Ensinar uma maneira de se estruturar uma aplicação para ela ser mais fácil de testar. Reduzir o tempo dos testes. Simplificar os testes usando mocks de maneiras não intrusivas.

Uns dos pontos principais para o teste de software é:
  • testar algo coeso, não atrapalhando um teste com funcionalidades de outras bibliotecas
  • ser rápido
Para isso geralmente ouvimos falar muito de mocks e stubs. Neste artigo vamos abordar não só uma maneira de usa-los como também como inserir estas ferramentas de maneira "transparente" no ambiente de testes, deixando o nosso código limpo. Vou começar de uma maneira simples e ir aprofundando no assunto para atender também a quem não está acostumado ao uso de mocks e stubs.

Não vou entrar de jeito nenhum na discussão do que é mock e o que é um stub. Vou melhorar este termo e traduzir. Digamos que podemos usar um "orêia". Por exemplo, se temos alguma parte do software que acessa um serviço externo, provavelmente isso não vai acontecer em menos de 0,1 segundo e isso é MUITO demorado para um teste. Você não acha? Imagine que ao final de um projeto pequeno para médio você terá em torno de 1000 testes.

Bom, então como fazemos para o nosso teste não demorar tanto? Usamos um mock, quer dizer stub, ou melhor: um "orêia". O nosso orêia vai ser usado no lugar do serviço original e fingir que executa o serviço. Ele pode até retornar algo estático ou que esteja em memória para simular o componente que ele está substituindo.

Por exemplo, digamos que no método inserirTexto() usamos o serviço do yahoo de extrair palavras chaves. Para isso usamos uma classe nossa onde encapsulamos a chamada a esse serviço: a classe PalavraChaveExtractorTabajaras. então teremos algo assim:
function inserirTexto($texto) {
$this->palavrasChaves = PalavraChaveExtractorTabajaras::extrair($texto);
$this->gravarNoBanco();
}
Beleza, então esse método extrai as palavras chaves e grava tudo no banco. Hum... e agora como vamos fazer para chamar esse método de um teste? Imaginem que temos o seguinte teste:
function testDeveInserirTextoNoBanco() {
$texto = $this->getTextoFixture();
$conteudo = FabricaDoConteudo::criaUmNovoAiPorFavor();
$conteudo->inserirTexto($texto);
$conteudoNoBanco = RepositorioDoConteudo::lerPeloTexto($texto);
$this->assertTrue((boolean)$conteudoNoBanco,"O conteúdo deve ser gravado no banco.");
}

Mas como vamos fazer no nosso método inserirTexto para poder dizer que queremos usar o ôreia (Mock)?

Temos que ter um jeito de fazer com que o sistema use o "PalavraChaveExtractorTabajaras" em um ambiente de produção e use o mock nos testes. Já sei, que tal...

if($ambiente == 'test') {
$this->palavrasChaves = PalavraChaveExtractorTabajarasMock::extrair($texto);
}
else {
$this->palavrasChaves = PalavraChaveExtractorTabajaras::extrair($texto);
}
Funcionar funciona, mas ainda não é algo muito bonito. Se eu quiser colocar outro ambiente vou ter que adicionar mais um if. Além disso, neste meu método, o ideal é que eu escreva o código de maneira fluente sem ter que me preocupar com testes. Já pensou ter que colocar estes ifs em todo canto. Vai ficar muito sujo.

Bom, uma outra alternativa é criar um builder para o extractor assim:

class PalavraChaveExtractorTabajaras {
static function build() {
// retorna de acordo com o ambiente
}
Eu teria que alterar a declaração do método "extrair", de estática para dinâmica, mas funciona melhorzinho, mas ainda assim eu acrescento código a minha classe que não tem a ver com ela. A minha classe não deve saber em que ambiente eu estou. Isso deve ficar para as classes das camadas mais altas, da aplicação ou do framework que eu estiver usando.

E então?

Então o melhor que já encontrei até agora para poder ter estes serviços variando de acordo com ambiente e não precisar saber de nada disso no código que usa o serviço é passar esta responsabilidade para uma outra classe. Esta classe vai gerenciar esse processo de instanciar e entregar os "serviços" de acordo com o ambiente que estamos. Essa classe geralmente é chamada de serviceContainer e já existem algumas implementações interessantes para ela em php, como por exemplo esta:

http://components.symfony-project.org/dependency-injection/trunk/book/03-Service-Container

Apesar de ser muito boa esta implementação, para um projeto que fizemos agora eu preferi escrever algo mais simples e que funcionou tão bem quanto, inclusive com algum ganho de performance, economizando em torno de 1m a cada requisição.

Porque o nome ServiceContainer ?

Porque a classe é um container que contém serviços. Por serviços podemos entender os objetos que implementam serviços como o de palavras chave do nosso exemplo, ou serviços de envio de email, ou até mesmo um repositório de objetos que se responsabiliza por cuidar da persistência de um determinado objeto.

Assim no nosso código podemos ter algo como:
function inserirTexto($texto) {
$extrator = SimpleServiceContainer::getInstance()->getService('keywords');
$this->palavrasChaves = $extrator->extrair($texto);
$this->gravarNoBanco();
}

O código fica um pouco mais extenso, mas continua claro. Estou pegando o serviço responsável por processar 'keywords'. Não sei qual serviço ele é, mas tudo bem, polimorfismo é isso aí.

Desta maneira apenas o container centraliza a responsabilidade de definir em qual ambiente estamos. Também ele vai instanciar o objeto de acordo com o ambiente. Um outro benefício que ganhamos disso é que se precisarmos trocar serviço que usamos poderemos intanciar ele e configurar também de outra maneira que não seja a que usavamos antes.

O SimpleServiceContainer

Com base na mesma interface do container do symfony, eu criei esse aí mais simplezinho:

class SimpleServiceContainer {

public $parameters;
static $containers = array();
protected $services = array();

protected $env;

/**
* @return SimpleServiceContainer
*/
static function getContainer($env) {
if(!array_key_exists($env,self::$containers)) {
$class = 'SimpleServiceContainer_'.$env;
if(!class_exists($class)) {
$class = 'SimpleServiceContainer_all';
}
self::$containers[$env] = new $class($env);
}
return self::$containers[$env];
}

static function getInstance() {
return self::getContainer(ConfigSystem::get('environment'))
}

function init() {}

protected function __construct($env) {
$this->env = $env;
$this->init();
}

/**
* @return SimpleServiceContainer
*/
function setParameter($name,$value) {
$this->parameters[$name] = $value;
return $this;
}

function getParameter($name) {
return $this->parameters[$name];
}

function getService($serviceName) {
if(!isset($this->services[$serviceName])) {
$this->createService($serviceName);
}
return $this->services[$serviceName];
}

private function createService($serviceName) {
$methodName = 'createService_'.$serviceName;
if(!method_exists($this, $methodName)) {
throw new ServiceBuilderNotConfiguredException(
'this container does not have a builder for the service called "'
.$serviceName."', the method should be called ".$methodName
);
}
$this->services[$serviceName] = $this->$methodName();
}

public function getInstantiatedServiceNames() {
return array_keys($this->services);
}
}

Por padrão ele usa as definições do SimpleServiceContainer_all. Esse eu tenho que criar obrigatóriamente. Depois eu poso extender dele de acordo com o ambiente que eu quiser. Por exemplo, se eu chamo o ambiente de desenvolvimento de test eu posso criar as minhas classe da seguinte maneira:


class SimpleServiceContainer_all extends SimpleServiceContainer {

protected function createService_keywords() {
return PalavraChaveExtractorTabajaras::build();
}

}

class SimpleServiceContainer_test extends SimpleServiceContainer_all {

protected function createService_keywords() {
return PalavraChaveExtractorTabajarasMock::build();
}

}


Assim desta maneira, quando eu crio o meu serviço com:


$extrator = SimpleServiceContainer::getInstance()->getService('keywords');


Eu vou receber o mock para o ambiente de testes e o normal para quando eu não estiver rodando os meus testes. Pode parecer simples, mas faz toda a diferença.

Veja:
  1. No meu código eu não digo como instanciar o serviço, pois o container funciona como uma factory. Assim qualquer outro componente que tenha a mesma interface pode ser substituido por ambiente eu até em um refactoring futuro sem impacto nas camadas que o usam.
  2. Isso diminui o acoplamento das classes que usam o serviço com o serviço em sí.
  3. Quando eu rodo meus testes o sistema automáticamente instancia o serviço de acordo com o ambiente de maneira transparente e me manda um componente que eu posso usar só sabendo a sua interface.
  4. Isso permite eu usar mocks transparentemente nos testes e também em outros ambientes e até substituí-los futuramente por outras classes quando estas forem desenvolvidas.
  5. Eu gosto desta implementação pois também instancio os objetos em puro php, sem a necessidade de um xml ou algo assim.
  6. Os objetos são instanciados apenas quando necessito deles.
  7. Os objetos dos serviços são por padrão instanciados uma vez só e usados em diferentes partes do sistema. Claro que isso pode não ser o comportamento desejado, mas pela simplicidade da implementação é fácil trocar este comportamento.

quinta-feira, 25 de fevereiro de 2010

Diferentes JS em cada ambiente do Symfony.

Quem estiver querendo colocar um site em produção vai provavelmente se deparar com a necessidade de "minificar" os arquivos de javascript. Porém é interessante manter os arquivos não minificados no projeto também para ficar mais fácil de editar e debugar. Porém o view.yml do symfony não permite configurações sensíveis ao ambiente. Assim achei uma solução bem simples em http://shout.setfive.com/2009/06/09/loading-different-javascript-css-files-in-different-environments/ que é a seguinte:

1 - Crie as configurações (um array) para cada ambiente no app.yml com o nome de "javascript_files".

2 - no view.yml faça referência a estas configurações usando:

javascripts: [< ? php echo implode(',',sfConfig::get('app_javascript_files');) ? >]


Depois me disseram que o view.yml está "deprecated", então fiz o seguinte:

Incluí uma partial em todos meus leiautes e nessa parcial usei:


< ?php if(sfConfig::get('sf_debug')): ? >
< ?php use_javascript('x') ?>
< ?php use_javascript('y') ?>
< ?php use_javascript('z') ?>
< ? else: ? >
< ?php use_javascript('xyz.min.js') ?>
< ? endif; ? >

quarta-feira, 24 de fevereiro de 2010

Tratando exceções no ambiente de produção.

* ver o primeiro comentário para ajustar um bug de loop eterno na homepage

Objetivo: Logar qualquer exceção não tratada no código, mandar um email para a equipe, gravar um log e mostrar uma mensagem para o usuário do tipo: "Estamos em manutenção para melhor servir." enquanto a equipe corre pra consertar o erro.

Como Fazer

1 Primeiro conectamos um listener ao evento de exception. Inicialmente pensei em capturar isso com um filtro, mas vi que existem algumas exceções que são capturadas que atrapalham o bom funcionamento do framework, como por exemplo a ReflectionException que vai gerar um 404 mais na frente.

Usando um listener para o evento, capturamos apenas aquilo que o symfony não trata:


class frontendConfiguration extends sfApplicationConfiguration {
public function configure() {
$this->dispatcher->connect(
'application.throw_exception',
array('MyExceptionHandler', 'listenToApplicationException')
);
}
...


2 Depois criamos a classe que ser chamada, contendo o método que vai "ouvir" o evento, conforme determinamos acima:


class MyExceptionHandler extends sfFilter {
static public function listenToApplicationException($event)
{
// pegamos a exception original
$e = $event->getSubject();
// para passar um link na mensagem mostrada ao usuário
sfLoader::loadHelpers(array('Url'));
try {
// manda email e grava um log
self::notify($e);
}
catch(Exception $e) {
// "Engulindo" propositadamente os
// erros quando envia email de log
// para não impedir que a mensagem
// seja mostrada para o usuário
}
if(self::isAjax()) {
// lembre dos requests de ajax...
// tratar de acordo com o framework do freguês.
}
else {
sfContext::getInstance()->getUser()->setFlash(
'feedback',
sprintf(
Messages::UNEXPECTED_ERROR,
url_for('@support')
)
);
sfContext::getInstance()->getController()->redirect('@homepage');
}
// este evento específico chama cada listener até que um responda que pôde tratar o evento. Por isso esse retorno.
return true;
}

static function notify(Exception $e) {
// cria a mensagem...manda email e registra um log.
self::log($mensagem);
}

static function log($message) {
$exceptionLogLocation = sfConfig::get('sf_log_dir') . '/exceptions.log';
$fileHandle = fopen($exceptionLogLocation, "a+");
fwrite($fileHandle, $message."\n");
}

static function isAjax(){
return sfContext::getInstance()->getRequest()->isXmlHttpRequest() ||
sfContext::getInstance()->getRequest()->hasParameter('iframeAjaxUpload');

}
}


3 Feito. Fica aqui mais uma dica de algumas coisas que você pode capturar e colocar na mensagem para facilitar a vida depois:



$username = sfContext::getInstance()->getUser()->getService()->getUsername();
$referer = sfContext::getInstance()->getRequest()->getReferer();
$uri = sfContext::getInstance()->getRequest()->getUri();
$ajax = self::isAjax() ? 'Yes' : 'No';
'time' => date('Y-m-d H:i:s'),
'exception_class' => get_class($e),
'exception_message' => $e->getMessage(),
'trace' => $e->__toString()

quarta-feira, 13 de janeiro de 2010

Método para Gerar Índice para HTML com JS


var htmlMenu = {
generate : function htmlMenu(id) {
var headers = {};
var stack = [];
var lastLevel = 0;
var text,level;
var levels = {'H1':0,'H2':1,'H3':2,'H4':3,'H5':4}
var menu = '';
$('#' + id + ' :header').each(function(a){
actualLevel = levels[this.nodeName];
if(actualLevel < lastLevel) {
for(var r = actualLevel+1; r <= lastLevel; r++) {
delete stack[r];
}
}
text = $(this).html();
if(stack[actualLevel] == undefined) {
stack[actualLevel] = 1;
}
else {
stack[actualLevel]++;
}
var index = '';
var first = true;
for(i in stack) {
index += (first ? '' : '.') + stack[i];
first = false;
}
headers[index] = text;
$(this).append('<a name =" '">');
menu += "<a href="'#">" + index + " - " + text + "</a><br/>";
lastLevel = actualLevel;
})
return menu;
}
};