Blog do Urubatan
msgbartop
Desenvolvedor, Palestrante, Escritor, Nerd Assumido e Pai do Marcus :D
msgbarbottom

05 Jul 10 Algoritmos básicos são importantes, você consegue melhorar este?

Bom, este post é mais minha opinião do que qualquer outra coisa, mas o que motivou a escrita dele foi este post no dzone: Will the real programmers please stand up? automated edition, onde o autor diz que apenas 1 em 20 entrevistados por ele conseguiam resolver de forma satisfatória testes simples como:

Escreva em C um método que inverta uma lista encadeada.

Claro que algumas restrições eram aplicadas, a assinatura do método não deveria ser alterada e a estrutura que armazenaria a lista encadeada também não deveria ser alterada. Elas eram apresentadas como o código abaixo:

1
2
3
4
5
6
typedef struct nodetype {
  int value;
  struct nodetype *next;
} node;
 
node* reverselist(node*)

Tentando automatizar um pouco o processo, ele tentou apresentar o código pronto com um bug, para que o bug fosse encontrado, mas o candidato sempre poderia escolher reescrever o código do zero se seguisse as regras acima.
Bom, olhei o código e achei que a recursividade utilizada estava só piorando o algoritmo, ele precisa de duas iterações para inverter a lista, então eu pensei, por que não escrever uma versão sem recursividade e que faça o processo todo em apenas uma iteração?
Bom, a minha resposta para o problema foi esta que esta abaixo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
	static class LinkedItem {
		int value;
		LinkedItem next;
 
		public LinkedItem(int value) {
			super();
			this.value = value;
		}
	}
 
	private static LinkedItem reverseLinkedList(LinkedItem currentHead) {
		LinkedItem nextHead = null, previousHead = null;
		while (currentHead != null) {
			nextHead = currentHead;
			currentHead = nextHead.next;
			nextHead.next = previousHead;
			previousHead = nextHead;
		}
		return nextHead;
	}

Eu escrevi em java, mas não faz diferença pois para o algoritmo que escrevi a unica diferença seria a sintaxe.
Para testar isto escrevi um programa simples que inicializa uma lista encadeada com números de 1 a 10 e imprime a lista antes e depois desta ser invertida. O código esta abaixo, se alguem quiser fazer uma versão melhor deste algoritmo por favor poste a sua resposta nos comentários.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package test;
 
public class Main {
	static class LinkedItem {
		int value;
		LinkedItem next;
 
		public LinkedItem(int value) {
			super();
			this.value = value;
		}
	}
 
	private static LinkedItem reverseLinkedList(LinkedItem currentHead) {
		LinkedItem nextHead = null, previousHead = null;
		while (currentHead != null) {
			nextHead = currentHead;
			currentHead = nextHead.next;
			nextHead.next = previousHead;
			previousHead = nextHead;
		}
		return nextHead;
	}
 
	public static void main(String[] args) {
		LinkedItem head = initializeSampleList();
		printLinkedList(head);
		head = reverseLinkedList(head);
		printLinkedList(head);
	}
 
	private static LinkedItem initializeSampleList() {
		LinkedItem it = new LinkedItem(1), head = it;
		for (int i = 2; i < 10; i++) {
			LinkedItem it2 = new LinkedItem(i);
			it.next = it2;
			it = it2;
		}
		return head;
	}
 
	private static void printLinkedList(LinkedItem it) {
		while (it != null) {
			System.out.printf("%d ", it.value);
			it = it.next;
		}
		System.out.println();
	}
}

Eu achei espetacular a idéia deles de criar um site para automatizar este tipo de smoke test para entrevista de emprego de programadores, se o cara não consegue resolver isto deveria estudar um pouco mais antes de querer trabalhar com programação mesmo.
Mas se a idéia for testar a habilidade com algoritmos acredito que o candidato deveria poder escolher em que linguagem implementar, caso contrario, em alguns casos a habilidade com a linguagem estaria sendo testada, e acho que estas duas coisas deveriam ser testadas em separado.

E vocês o que acham sobre isto? Sobre este tipo de testes?
Já fizeram algum teste parecido com este em alguma entrevista de emprego?
E se você é o entrevistador, gostou da idéia deste tipo de teste? Acha util ou desnecessário?
Utiliza alguma técnica alternativa?
Olha o código produzido pelo candidato em projetos open source?

Bom, acho que por enquanto é isto :D

PS.: me decepcionei com a minha demora para implementar este algoritmo simples, levei uns 10 minutos, quando comecei a escrever achei que seria algo natural e funcionaria de primeira. Isto deve ter sido causado pela facilidade que temos hoje, como um Collections.reverse do java ou um [].reverse do ruby por exemplo. Raramente temos que lidar diretamente com este tipo de algoritmo básico, mas saber implementa-los é extremamente importante, pelo menos na minha opinião :D

Tags: , , , , ,

25 Jun 10 Material da minha palestra do Agile Brazil 2010

Para quem não sabe, eu apresentei hoje mais cedo, no Agile Brazil 2010, a palestra “Transformando os pepinos do cliente no código de testes da aplicação com Cucumber”.
O conteúdo da palestra ficou muito bom na minha opinião, mas a forma como eu apresentei, eu, durante a palestra comecei a achar chato …
Bom, espero que quem assistiu a paletra tenha aproveitado, e vou melhorar a forma de apresentar isto para o FISL, mas como eu sempre faço, o material da paletra esta sendo disponibilizado por aqui!
Os slides eu coloquei no slideshare:

Os videos da execução do cucumber estão aqui para quem quiser pegar:
Aplicação ASP.NET MVC 2 e Aplicação Java

E para quem quiser pegar o código dos exemplos, os links estão aqui:
Aplicação ASP.NET, Aplicação Java e Aplicação Ruby on Rails

A idéia que eu queria passar era que o cucumber pode ser utilizado para testar diversos tipos de aplicação, e acho que isto ficou claro :D

Espero que este material seja útil, e quem quiser comentar, sugerir, criticar, xingar, …
Pode usar o campo de comentários aqui do blog para isto.

Tags: , , , , , , , ,

18 Jun 10 Brincadeirinha com Kanban Board Online

Depois de ter algumas idéias mirabolantes, que acabei deixando de lado, resolvi brincar um pouquinho com javascript e fazer um mini protótipo de  um “Kanban Board”, sem usar nenhuma imagem, onde as tarefas fossem parecidas com aqueles alertas tipo “buble”, bom, o protótipo ficou beem feinho, mas deu pra tirar uma febre do meu CSS.
Para a parte Javascript usei o Jquery, a idéia básica é o seguinte:
Tenho 3 divs que representam 3 estados que qualquer tarefa pode estar: Backlog, Work In Progress e Done
Estas tem que estar uma ao lado da outra.
Dentro destas, quero ter blocos representando cada tarefa, e preciso conseguir mover estas tarefas entre os estados, para isto o JQuery facilita bastante a nossa vida, adicionei o jquery ui, e tudo o que eu precisei fazer foi o seguinte:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.2/jquery-ui.min.js"></script>
<script>
$(document).ready(function(){
    $("#backlog,#wip,#done").sortable({
        connectWith: '.connectedSortable',
        receive: function(event, ui) { 
            /*alert($(ui.item).attr('data-id') + ' -> ' + $(this).attr('id'));*/
            //Do something with the recently dropped item here
        }
    }).disableSelection();
 
});
</script>

Onde #backlog, #wip e #done são os IDs das divs principais …
Para posicionar estas divs coloquei elas dentro de um container de ID bublecontainers, e usei o seguinte CSS:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
            #bublecontainers {
                background-color: gray;
                width: 600px;
                margin: 0 0 0 0;
                padding: 0 0 0 0;
            }
            #backlog, #wip, #done {
                width: 190px;
                margin-left: 5px;
                height: 340px;
                display: inline-block;
                float:left;
                border: solid 1px;
            }

E agora a parte importante, para fazer as tarefas parecerem bolhas, usei a seguinte estrutura HTML:

1
2
3
4
5
6
<div class="buble">
  <div class="buble-content">
Texto da task aqui
  </div>
  <div class="buble-arrow"></div>
</div>

E me aproveitei de como as bordas do HTML são montadas via CSS, para transformar isto em uma bolha, só precisei do CSS abaixo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
.buble-content {
                width: 100%;
                top: 0;
                margin: 0 0 0 0;
                padding: 8px 8px 8px 8px;
                text-align:justify;
                overflow: hidden;
                border-radius:10px;
                -moz-border-radius:10px;
                -webkit-border-radius:10px;
                background-color:#FFF;
                border-style: outset;
                text-indent: 10px;
 
 
            }
            .buble
            {
                display:inline-block;
                width:90%;
            }
 
            .buble-arrow
            {
                border-color:#FFF transparent transparent transparent;
                border-style:solid;
                border-width:17px;
                margin: -4 0 0 0;
                height:0;
                width:0;
                position: relative;
                left: 30%;
            }

As bordas arredondadas só vão aparecer em browsers mais modernos que tenham algum suporte a CSS3 rounded corners.

O experimento até que foi divertido, mas o código tem diversas falhas ainda, serviu para brincar um pouco :D

E o resultado vocês podem ver nesta coisa horrenda aqui de baixo :D (deem uma folga, fiz isto em 20 minutos ais ou menos só pra brincar e ver se a minha idéia era viável :D )

Back Log
Work In Progress
Done
Create a virtual Kanban Board

Make this VKB usable

write a blog post about it

make it using no images, just CSS

Se quiserem o código completo, segue abaixo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
<html>
    <head>
        <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
        <script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.2/jquery-ui.min.js"></script>
        <style>
            #bublecontainers {
                background-color: gray;
                width: 600px;
                margin: 0 0 0 0;
                padding: 0 0 0 0;
            }
            .buble-content {
                width: 100%;
                top: 0;
                margin: 0 0 0 0;
                padding: 8px 8px 8px 8px;
                text-align:justify;
                overflow: hidden;
                border-radius:10px;
                -moz-border-radius:10px;
                -webkit-border-radius:10px;
                background-color:#FFF;
                border-style: outset;
                text-indent: 10px;
 
 
            }
            .buble
            {
                display:inline-block;
                width:90%;
            }
 
            .buble-arrow
            {
                border-color:#FFF transparent transparent transparent;
                border-style:solid;
                border-width:17px;
                margin: -4 0 0 0;
                height:0;
                width:0;
                position: relative;
                left: 30%;
            }
            #backlog, #wip, #done {
                width: 190px;
                margin-left: 5px;
                height: 340px;
                display: inline-block;
                float:left;
                border: solid 1px;
            }
            #bltitle, #wiptitle, #donetitle {
                height: 15px;
                text-align: center;
                width: 190px;
                display: inline-block;
                font-size: 20;
                font-height: bold;
            }
        </style>
        <script>
            $(document).ready(function(){
                $("#backlog,#wip,#done").sortable({
                    connectWith: '.connectedSortable',
                    receive: function(event, ui) { 
                        /*alert($(ui.item).attr('data-id') + ' -> ' + $(this).attr('id'));*/
                        //Do something with the recently dropped item here
                    }
                }).disableSelection();
 
            });
        </script>
    </head>
    <body>
        <div id="bublecontainers">
            <div id="bltitle">Back Log</div>
            <div id="wiptitle">Work In Progress</div>
            <div id="donetitle">Done</div>
            <div id="backlog" class="connectedSortable">
                <div class="buble" data-id="buble1">
                    <div class="buble-content">
                        Create a virtual Kanban Board
                    </div>
                    <div class="buble-arrow"></div>
                </div>
                <div class="buble" data-id="buble2">
                    <div class="buble-content">
                        Make this VKB usable
                    </div>
                    <div class="buble-arrow"></div>
                </div>
                <div class="buble" data-id="buble2">
                    <div class="buble-content">
                        write a blog post about it
                    </div>
                    <div class="buble-arrow"></div>
                </div>
                <div class="buble" data-id="buble2">
                    <div class="buble-content">
                        make it using no images, just CSS
                    </div>
                    <div class="buble-arrow"></div>
                </div>
            </div>
            <div id="wip" class="connectedSortable">
            </div>
            <div id="done" class="connectedSortable">
            </div>
            <div style="clear:both"></div>
        </div>
    </body>
</html>

Tags: , ,

18 May 10 HTTP e URL – Dois ilustres pouco conhecidos

Este post foi extraido de uma apostila de um curso que ministrei em 2007, eu queria passar este texto para um colega novo do trabalho e não tinha o texto disponível, então resolvi publicar isto aqui no blog que vai ser útil para mim e pode ajudar mais alguem também :D


Para que seja possível acessar qualquer página na internet, alem de outros recursos, como FTP, SSH e diversos outros serviços, foi definido um padrão de endereçamento chamado URL (Uniform Resource Locator), que possui o seguinte formato:

PROTOCOLO://[USUARIO[:SENHA]@]ENDERECO_SERVIDOR/CAMINHO_NO_SERVIDOR?parametro=valor&outro_parametro=outro_valor#ancoraNaPagina

Por exemplo:

http://www.google.com/search?q=ruby+on+rails

onde:

  • http é o protocolo
  • www.google.com é o endereço do host
  • /search é o caminho dentro do servidor
  • q=ruby+on+rails é o parâmetro de nome “q” com o valor “ruby on rails”

Como uma URL não pode conter espaços os espaços são substituídos pelo sinal “+”, isto se chama codificar o parâmetro.

Como uma URL não aceita nenhum caractere com código ASCII maior que 127, qualquer caractere nesta categoria precisa ser codificado. Esta codificação consiste em substituir os caracteres por um “%” mais 2 dígitos numéricos representando o código ASCII em hexadecimal do caractere desejado, como na tabela a baixo:

Caractere Código Hexadecimal
$ 24
& 26
+ 2B
, 2C
/ 2F
: 3A
; 3B
= 3D
? 3F
@ 40
Espaço 20
22
< 3C
> 3E
# 23
% 25
{ 7B
} 7D
| 7C
\ 5C
^ 5E
~ 7E
[ 5B
] 5D
` 60

Alguns destes caracteres são aceitáveis em uma URL, mas em algumas situações precisam ser codificados, como por exemplo o # que pode ser utilizado no final de uma URL para indicar ao browser qual parte da página mostrar, quando não for utilizado para este fim, deve ser codificado para evitar confusões.

Cabeçalhos HTTP

Cabeçalhos HTTP são uma forma de passar parâmetros extras, além da URL do browser para o servidor. Normalmente, estes parâmetros são utilizados para informar ao servidor as capacidades do browser e preferências do usuário, mas podem também ser utilizados para trocar dados adicionais além dos informados pelo usuário (que podem ser passados pela URL).

Os cabeçalhos HTTP são enviados com o seguinte formato, logo depois da URL e antes da primeira linha em branco na requisição:

nome: valor

O valor dos cabeçalhos deve ser codificados utilizando a mesma tabela de conversão das URLs.

Exemplo de Requisião HTTP

O usuário digita no browser a URL: http://www.google.com/search?q=ruby+on+rails

O browser abre uma conexão TCP/IP na porta 80 (padrão) do servidor www.google.com e envia o seguinte texto:

GET http://www.google.com/search?q=ruby+rails HTTP/1.0
User-Agent: Mozilla/4.73 [en] (X11; U; Linux 2.0.35  i686)
<Linha em branco no final>

O Servidor Responde:

HTTP/1.0 200 OK
Cache-Control: private
Content-Type: text/html; charset=ISO-8859-1
Set-Cookie: PREF=ID=059bd3c506c56f82:TM=1189888548:LM=1189888548:S=hV0hCZi26b1SuRY7; expires=Mon, 14-Sep-2009 20:35:48 GMT; path=/; domain=.google.com
Server: gws
Date: Sat, 15 Sep 2007 20:35:48 GMT
Connection: Close

<html><head>
<Resto do HTML da página>

Explicando o que foi enviado:

  • GET http://www.google.com/search?q=ruby+rails HTTP/1.0
    • GET é o método sendo utilizado
    • http://www.google.com/search?q=ruby+rails é a URL solicitada no servidor
    • HTTP/1.0 é a versão do protocolo sendo utilizada
  • User-Agent: Mozilla/4.73 [en] (X11; U; Linux 2.0.35  i686)
    • User-Agent é um cabeçalho enviado que informa ao servidor qual browser esta fazendo a requisição
  • Linha em branco marcando o final da transmissão.

Explicando a resposta:

  • HTTP/1.0 200 OK
    • HTTP/1.0 é a versão do protocolo
    • 200 é o código da resposta que informa que o processamento esta OK
    • OK é um texto livre escrevendo o status, cada servidor pode responder com uma descrição diferente relativa ao código
  • Nas linhas seguintes, antes do espaço, tem diversos HEADERS que podem ser utilizados pelo browser
  • Após a linha em branco, há o conteúdo da URL solicitada

Métodos HTTP

GET

O método GET é utilizado para buscar o conteúdo de uma URL e o conteúdo retornado para a mesma URL deve ser sempre o mesmo. Isso facilita que o browser possa guardar páginas em cache por algum tempo para melhorar a experiência do usuário.

POST

O método POST é utilizado para enviar dados para o servidor. Esse método não possui a obrigatoriedade de ter sempre o mesmo resultado para os mesmos dados enviados.

PUT

O método PUT é utilizado para criar recursos no servidor. Ele possui a característica do método GET, de ter a mesma resposta para o mesmo conteúdo enviado.

DELETE

Este método é utilizado para remover recursos do servidor.

HEAD

O método HEAD serve para solicitar ao servidor apenas o cabeçalho do conteúdo com os headers, para que o cliente decida se precisa requisitar o conteúdo completo ou não.

Os métodos mais utilizados pela maioria das aplicações são o GET para buscar dados e o POST para enviar dados. Os métodos PUT e DELETE raramente são suportados por uma aplicação, mas são bastante úteis quando se deseja implementar Web Services seguindo o padrão REST. Alguns outros protocolos utilizam métodos adicionais e até definem alguns novos, como por exemplo o protocolo WebDav.

Códigos de resposta HTTP

Os códigos de resposta HTTP possuem 4 famílias básicas que serão apresentadas abaixo. Mais informações sobre estes códigos de resposta podem ser encontradas nesta URL: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html

  • 1XX – Códigos de informação, muito pouco utilizados
  • 2XX – Sucesso: o código mais utilizado desta família é o 200 que significa OK
  • 3XX – Redirecionamento: os códigos mais utilizados desta família são: 301 redirecionamento permanente e 307 redirecionamento temporário
  • 4XX – Erro do cliente: o código mais conhecido desta família é o 404 que significa recurso não encontrado
  • 5XX – Erro do servidor: o código mais conhecido desta família é o 500 que significa erro interno do servidor.

Cabeçalhos comuns

Como o protocolo HTTP não mantém estado portanto, cada request é independente. Para solucionar este problema os cookies podem informar ao servidor quem é o cliente que está acessando o recurso.

Segue uma lista de alguns cabeçalhos bastante utilizados.

  • Location: utilizado junto com os códigos 301 e 307 informando o novo endereço do recurso
  • Content-Type: informa o tipo MIME do recurso retornado ou enviado ao servidor
  • Set-Cookie: grava um cookie no browser
  • Cookie: envia os cookies armazenados no servidor
  • Cache-Control: informa se o browser pode ou não armazenar o recurso em cache
  • Content-Length: informa o tamanho do conteúdo enviado ou da resposta
  • Last-Modified: informa ao cliente quando foi a ultima alteração do recurso solicitado
  • Accept: informa ao servidor quais são os tipos de recursos que o cliente sabe processar
  • Allow: informa ao cliente quais as ações disponíveis para um recurso especifico
  • Content-Encoding: informa ao cliente ou ao servidor qual a codificação que está sendo utilizada para o conteúdo enviado ou recebido
  • Host: informa ao servidor qual o nome do host utilizado no request. Bastante útil para possibilitar a configuração de hosts virtuais no mesmo endereço IP

Bom, acho que era isto, claro que isto não é tudo sobre formação de URLs e sobre o protocolo HTTP, mas já é um bom começo.

Tags: , , , ,

27 Apr 10 Os 5 segredos para um sistema altamente escalável

Ontem enquanto caminhava do trabalho até o carro, conversando com um colega sobre infra estrutura para um sistema que ele estava pensando em construir. Qual arquitetura seguir, qual linguagem utilizar, qual framework, se rails era o suficiente para o que ele queria ou não, se devia usar ruby ou java, se ruby escala ou não escala, consegui convencer ele de que a resposta para a pergunta inicial dele é “depende”, mas também lembrei de alguns segredos que se aplicam em todos os casos que consigo lembrar agora para se criar um sistema web altamente escalável,por conseqüência, também são segredos bastante importantes para colocar a sua aplicação na “nuvem” e também são muito importantes para você escrever os serviços que farão parte da sua arquitetura SOA[bb] ou seus serviços REST.
Ou seja, sempre mantenha estas cinco regras em mente se o sistema que você esta desenvolvendo pode crescer um dia.

Sim, eu sei que estou sendo um pouco categórico demais, e que nenhuma regra se aplica a tudo e que existem excessões para o que estou escrevendo.

Stateless

Se você quer um sistema ou um serviço escalável, com certeza você quer que todas as requisições para este serviço sejam stateless.
Mas por que?
Simplesmente por que caso em um futuro próximo você precise rodar a sua aplicação em um cluster, você não prende um cliente a um nó do cluster, cada requisição pode ir para o nó do cluster que estiver com a menór carga naquele momento, fazendo com que o tempo de resposta aquela requisição seja o menor possível, mantendo o nó do cluster que vai atender a esta requisição ocupado o menor tempo possível.

Request Response Time

Acho que já falei um pouco sobre isto, mas imagine a seguinte situação, o sistema que você acabou de publicar bombou e agora em vez de ter que atender a 5 clientes (Você, seu melhor amigo, sua mãe, seu pai, sua namorada), agora o sistema tem que atender a 1k clientes simultâneos, e você pensa: Beleza, só preciso fazer um clusterzinho aqui, coloco mais duas maquinas e tudo beleza.
O problema é que o seu sistema antes estava com um “RRT” médio de 30s ou seja, com uma maquina só, você conseguia responder a duas requisições por minuto (considerando apenas um thread por máquina para facilitar o exemplo), agora com as duas maquinas novas você consegue atender a maravilhosos 6 requests por minuto. Ou seja, você tem um baita fiasco nas mãos.
Por isto o Request Response Time, ou seja, o tempo que a sua aplicação leva para atender a cada requisição tem que ser o mais baixo possível.
Por exemplo, enquanto escrevia este post criei uma aplicação rails com um cadastro de usuários de brinquedo, gerado pelo scaffold e o RRT é de 15.6ms (alto mas a maquina esta bastante carregada agora) o que permite que com uma thread seja possível atender a 64 requisições por segundo. Em um outro sistema que trabalhei, algumas requisições por diversos motivos chegaram a demorar 20s para retornar, mas isto foi resolvido claro :D
Bom, acho que deu para entender o por que o RRT é tão importante. Para clarear mais a idéia, imagine a seguinte situação que você já deve ter visto diversas vezes: Uma loja grande, com fila unica e 4 caixas atendendo. Se cada um dos caixas for muito demorado, você vai passar horas na fila, e não é economicamente viável ter um caixa por usuário. então a solução viável para este problema é otimizar o trabalho dos caixas para que cada caixa seja mais rápido e consiga atender a um maior número de clientes por minuto.

Cache

Cache é uma forma de melhorar muito o RRT, imagine que a sua mega aplicação seja uma página de classificados, em que a página inicial, que todos os clientes acessam ao chegar no site. Tenha uma lista com os produtos mais baratos anunciados.
Esta página precisa de uma consulta no banco de dados para ser renderizada, mas se você pensar bem os dados serão sempre os mesmos até que alguem delete um dos produtos listados ali ou cadastre outro mais barato.
Então faz todo o sentido “guardar uma copia” da página prontinha, e só mostrar aquela cópia para o próximo que acessar a página, e simplesmente deletar esta cópia quando uma das duas situações que pode alterar a página acontecer, ai no próximo acesso a página é renderizada novamente e a copia atualizada é guardada, e novamente ficamos utilizando apenas aquela cópia até que algo aconteça que possa fazer os dados ficarem desatualizados.
Isto é chamado de “cache”, na verdade esta é uma forma bem simples de cache. Estude aos recursos de caching do framework que você esta utilizando, do servidor de aplicações, do sistema operacional, do banco de dados.
Mas não ative tudo ao mesmo tempo, o correto uso de cache vai melhorar muito a performance da sua aplicação, mas o uso incorreto pode causar muitos problemas.

Remote Data

Muito pouca coisa é pior para um cluster do que uma aplicação que guarda dados localmente.
Imagine só o seguinte, naquela aplicação de classificados, você resolveu que as imagens correspondentes a cada anuncio ficariam gravadas na própria maquina do servidor, barbadinha, facilita o upload.
Ai é que mora o perigo.
Quando você precisar colocar isto pra rodar em um cluster, o segundo nó do cluster quando atendendo uma requisição, vai acreditar que as imagens estão naquele nó também, mas ele nem existia quando o anuncio foi criado, o upload foi feito pra outra maquina. Isto vai fazer com que o usuário veja uma página com a imagem quebrada.
Ahh, e como resolver isto?
Bom, existem diversas formas, uma bem simples é criar um servidor de “recursos estáticos”, ou seja, tudo o que é estático no site é servido por um endereço diferente, e quando for feito o upload de uma imagem, ela deve ser salva neste outro servidor. Também é possível utilizar um serviço como o Amazon S3 que vai fazer exatamente a mesma coisa que o seu servidor de recursos estáticos, mas de fora :D
Outra coisa a lembrar é do banco de dados, ele também não deve ficar na mesma maquina que o servidor da aplicação, ou o endereço do banco (seja ele SQL, document driven, …) deve ser configurável. Isto por que em um cluster, o ideal é que todos os nós acessem o mesmo banco de dados, ou no mínimo que todos os bancos do cluster estejam sincronizados.

Proxy Reverso

Um próxi reverso no contexto deste post vai ser utilizado para mascarar algumas das técnicas de caching, para implementar algumas técnicas de caching e principalmente para esconder do usuário que você tem diversos servidores, ele vai ser o “front-end” da sua aplicação, a mágica do cloud só pode acontecer atrás do proxy, e ele também pode esconder do usuário o fato de você usar um “servidor de recursos estáticos” sendo ele interno criado por você mesmo ou você usando um S3 da vida, o proxy reverso pode mascarar o endereço real da aplicação.
Mas nunca esqueça de um detalhe importante. Este proxy mesmo sendo vital para uma arquitetura cloud, ou um cluster menorzinho, ele acaba de adicionar um SPoF (Single Point Of Failure) se o proxy morrer, a sua mega aplicação esta fora do ar.

Bom, acho que era isto, sempre tem alguem perguntando como fazer um sistema escalável, como fazer um sistema robusto, como fazer um sistema para rodar em cluster ou na nuvem.
Este post não é a resposta final para todos os problemas, este post não responde estas perguntas para todos os casos, mas para a maior parte dos casos que me lembro agora, as dicas deste post são pontos imprescindíveis para qualquer aplicação ou arquitetura que pretenda ter um mínimo de escalabilidade.
Espero que este post seja útil para alguem, e se não concordarem com alguma coisa é só deixar um comentário.

Tags: ,

22 Jan 10 As 8 Falácias da programação distribuida


Encontrei esta lista no blog do James Gosling.

Essencialmente todos, ao desenvolver a primeira aplicação distribuída, assumem os 8 itens seguintes como verdade. Todos se provam falsos ao longo do tempo, e estes erros causam problemas graves e aprendizados dolorosos.

  1. A rede é confiável
  2. A latência é zero
  3. A banda é infinita
  4. A rede é segura
  5. A topologia não muda
  6. Existe um administrador de rede
  7. O custo de transporte é zero
  8. A rede é homogênea

Para mais detalhes, leiam o artigo escrito por Arnon Rotem-Gal-Oz.

James Gosling em um link fala o seguinte sobre a origem desta lista:

A verdadeira origem desta lista é um pouco mítica, sendo amplamente baseada na experiência coletiva de muitos hackers nos primórdios da construção de sistemas distribuídos. Peter Deutch foi o primeiro agrupar os itens em uma lista. Ele diz o seguinte em sua página:

Eu publiquei as “8 falácias da programação em rede” internamente enquanto trabalhava para “Sun Microsystem Labs” em 1991-92. (As primeiras 4 foram originalmente listadas por Bill ou Dick Lyon; Eu adicionei as outras 4.)

Achei que vali a tradução já que muita gente comete os mesmos erros até hoje :D

Tags: , , ,

30 Sep 09 Seja comunicativo, diga por que escreveu este código

Bom, no último post eu disse para você não ser repetitivo, e não escrever nos comentários o que o seu código faz. Se você precisa comentar o que o seu código faz, você realmente precisa refatorar o seu código e torna-lo mais legível
Mas comentários no código não são sempre ruins, o ruim é você escrever em português o que o seu código faz em java, mas existem diversas situações que veremos a seguir onde os comentários são ótimos, ou pelo menso são úteis …

Por exemplo, na interface pública de um serviço, um JavaDoc informando para que serve aquele serviço e com exemplos de utilização são sempre muito bem vindos, principalmente se este comentário informar quais são as validações que serão feitas nos parâmetros e quais são as restrições ao uso daquele serviço …
Eu não posso pegar exemplos de projetos em que estou trabalhando por que isto seria motivo para demissão por justa causa por quebra de sigilo :D
Então vamos ficar com alguns exemplos públicos e alguns exemplos mais simples que eu conseguir pensar na hora …

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
 * This is the only interface you need to create and manage system users, but if you need to manage user privileges take a look at the @link{PrivilegesService} interface
 */
public interface UsersService {
   /**
    * This method should be used only if you are already authenticated in the system, do no forget to send the session id header when calling methods that are not public like this one.
    * To create a user you need to provide all the needed information, this method will create  user in the database and then will try to create the user in the backend system, if the creation works the two accouns will be linked otherwise the database reccord will be erased. 
    * @param userName the user name must be unique within the LDAP domain, this means that there can
    * @param password
    * @param email
    */ 
   public User createUser(String userName, String password, String email);
}

Neste exemplo, não há necessidade alguma de colocar um comentário dizendo que o método createUser cria um usuário, mas o javadoc do exemplo informa de algumas pré condições para a execução do método e também diz qual o fluxo de execução e possíveis falhas. Como neste exemplo o UsersService é a interface publica de um serviço, esta documentação é bastante bem vinda, pois vai ajudar os usuários que vão acessar o serviço a saber o que fazer antes de criar um usuário e a saber possíveis motivos para as falhas.

Bom, acho que todos os outros exemplos que eu procurar vão ser parecidos com isto, resumindo este post e o anterior:
Se um comentário diz o que o método faz, o código é um lixo ou o comentário é redundante, o que o método faz tem que ser óbvio pelo nome do método
Se o comentário diz quando o método deve ser utilizado, algumas pré condições, o fluxo de execução, possíveis erros, e principalmente se ele esta em uma interface publica do sistema, que vai ser entregue a outra equipe/programador/empresa. o comentário é útil
Se o comentário esta em algum método ou variável privada, provavelmente tem algum problema por ai.

Acho que com isto eu encerro este assunto de comentários, eu posso ter me expressado errado, ou o mais provável, muitas pessoas leram o título do post e saíram berrando antes de ler o resto, mas a idéia básica é, o seu código tem que ser muito fácil de ler, os nomes das classes, métodos, variáveis, pacotes e quaisquer arquivos envolvidos no sistema devem dizer o que são, para que servem e por que estão ali. Se você esta colocando um comentário que diz que o método “create” cria um usuário, você deveria chamar o método de “createUser”, mas se o comentário esta informando o usuário do método de que o nome de usuário utilizado não precisa ser único mas o email sim, pois é este o campo utilizado como chave, ai você tem um comentário útil (e um sistema extranho :D ).

Não é por que o seu código tem comentários que ele é um lixo, mas tome cuidado por que é muito fácil utilizar comentários como desculpa para código ruim. E neste caso, talvez você precise estudar um pouco de rafactoring.

Tags: , , , ,

26 Feb 09 Dicas e truques: Adobe Flex – MXML é para Layout, ActionScript para o código

Uma das melhores idéias do Flex, e o que o torna bastante produtivo na minha opinião é a existencia de uma “linguagem” para layout de telas, é o tal de MXML.
Claro que eu sei que é apenas XML, mas é XML com acesso direto as classes existentes e recem criadas, e uma mão na roda para separar o código do layout, facilitando bastante a escrita de código MVC compliant.
O maior problema da arquitetura básica do Flex é não ser “óbvio” como escrever o código de tratamento dos eventos gerados sem poluir o MXML com muitas tags “mx:Script” e muito código ActionScript.
Claro que existem alguns frameworks para o desenvolvimento de aplicações Flex como o Cairngorm e o PureMVC, mas algumas vezes estes frameworks mais complicam do que ajudam, e mesmo utilizando um deles, esta dica que vou apresentar agora de como simular o “code behind” do “.NET” em flex pode ser utilizada sem maiores problemas …

A idéia principal é separa o código ActionScript do código XML, como se fosse uma “partial class” do “.NET”, o Flex não suporta Partial Classes, mas suporta herança, e podemos tirar proveito disto …

Vou mostrar um exemplo bastante simplista aqui, apenas para que vocês peguem a idéia, vejam o código da tela abaixo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" width="100%" height="100%">
	<mx:Script>
		<![CDATA[
			import mx.controls.DataGrid;
			import mx.events.ListEvent;
			[Bindable]
			private var current : Object = new Object();
			[Bindable]
			private var all : Array = [];
			private function saveCurrent(nome : String, idade : Number) : void {
				var obj : Object = {nome : nome, idade : idade, id : all.length + 1};
				var temp : Array = all.slice(0,all.length);
				temp.push(obj);
				all  = temp;
				current = obj;
			}
			private function updateCurrent(nome : String, idade : Number) : void {
				current.nome = nome;
				current.idade = idade;
			}
			private function newCurrent() : void {
				current = new Object();
			}
			private function selectionChange(evt : ListEvent) : void {
				current = (evt.target as DataGrid).selectedItem;
			}
		]]>
	</mx:Script>
	<mx:Label fontSize="24" width="100%" textAlign="center" text="Cadastro de Pessoas"/>
	<mx:HBox width="100%">
		<mx:Label text="Nome:" width="100"/>
		<mx:TextInput id="currentNome" text="{current.nome}"/>
	</mx:HBox>
	<mx:HBox width="100%">
		<mx:Label text="Idade:" width="100"/>
		<mx:NumericStepper id="currentIdade" value="{current.idade}" minimum="5" maximum="150"/>
	</mx:HBox>
	<mx:HBox width="100%" horizontalAlign="center">
		<mx:Button label="Salvar" click="saveCurrent(currentNome.text,currentIdade.value)" enabled="{!current.id}"/>
		<mx:Button label="Novo" click="newCurrent()" enabled="{current.id}"/>
		<mx:Button label="Atualizar" click="updateCurrent(currentNome.text,currentIdade.value)" enabled="{current.id}"/>
	</mx:HBox>
	<mx:DataGrid width="100%" dataProvider="{all}" change="selectionChange(event)">
		<mx:columns>
			<mx:DataGridColumn headerText="Id" dataField="id"/>
			<mx:DataGridColumn headerText="Nome" dataField="nome"/>
			<mx:DataGridColumn headerText="Idade" dataField="idade"/>
		</mx:columns>
	</mx:DataGrid>
</mx:VBox>

Apenas para este bastatne simples, já temos quase metade do arquivo contendo ActionScript e metade contendo MXML.
Em um caso simples destes isto não chega a incomodar, mas quando o arquivo começa a crescer, isto atrapalha a leitura do código fonte, o que é bastante ruim. Então a minha sugestão, ou “dica” de como resolver isto, é separar o código ActionScript em um arquivo .as e manter apenas o layout no arquivo MXML, como pode ser visto no seguinte exemplo (considerando que o nome do arquivo mostrado acima seja telaexemplo.mxml)
Criar a classe “exemplo.TelaExemploBase” no diretório correspondente com o seguinte código:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package exemplo
{
	import mx.containers.VBox;
	import mx.controls.DataGrid;
	import mx.events.ListEvent;
 
	public class TelaExemploBase extends VBox
	{
		[Bindable]
		protected var current : Object = new Object();
		[Bindable]
		protected var all : Array = [];
		public function TelaExemploBase()
		{
			super();
		}
		protected function saveCurrent(nome : String, idade : Number) : void {
			var obj : Object = {nome : nome, idade : idade, id : all.length + 1};
			var temp : Array = all.slice(0,all.length);
			temp.push(obj);
			all  = temp;
			current = obj;
		}
		protected function updateCurrent(nome : String, idade : Number) : void {
			current.nome = nome;
			current.idade = idade;
		}
		protected function newCurrent() : void {
			current = new Object();
		}
		protected function selectionChange(evt : ListEvent) : void {
			current = (evt.target as DataGrid).selectedItem;
		}
 
	}
}

Alterar o arquivo MXML para o seguinte código:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="utf-8"?>
<TelaExemploBase xmlns="exemplo.*" xmlns:mx="http://www.adobe.com/2006/mxml" width="100%" height="100%" >
	<mx:Label fontSize="24" width="100%" textAlign="center" text="Cadastro de Pessoas"/>
	<mx:HBox width="100%">
		<mx:Label text="Nome:" width="100"/>
		<mx:TextInput id="currentNome" text="{current.nome}"/>
	</mx:HBox>
	<mx:HBox width="100%">
		<mx:Label text="Idade:" width="100"/>
		<mx:NumericStepper id="currentIdade" value="{current.idade}" minimum="5" maximum="150"/>
	</mx:HBox>
	<mx:HBox width="100%" horizontalAlign="center">
		<mx:Button label="Salvar" click="saveCurrent(currentNome.text,currentIdade.value)" enabled="{!current.id}"/>
		<mx:Button label="Novo" click="newCurrent()" enabled="{current.id}"/>
		<mx:Button label="Atualizar" click="updateCurrent(currentNome.text,currentIdade.value)" enabled="{current.id}"/>
	</mx:HBox>
	<mx:DataGrid width="100%" dataProvider="{all}" change="selectionChange(event)">
		<mx:columns>
			<mx:DataGridColumn headerText="Id" dataField="id"/>
			<mx:DataGridColumn headerText="Nome" dataField="nome"/>
			<mx:DataGridColumn headerText="Idade" dataField="idade"/>
		</mx:columns>
	</mx:DataGrid>
</TelaExemploBase>

A idéia básica é utilizar o MXML para extender a classe ActionScript que contem o código, a diferença é que tudo o que deve ser acessado do MXML na classe criada não pode ser privado, tem que ser no mínimo protected (o que foi utilizado no exemplo).
Outro truque é definir o “xmlns” para o pacote da classe que vai ser extendida como pode ser visto na linha 2.
E para testar isto é necessária uma “aplicação flex”, eu utilizei apenas o código abaixo para o projeto de testes:

1
2
3
4
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:local="*">
	<local:telaexemplo width="100%" height="100%"/>
</mx:Application>

Mas claro que você pode fazer algo mais elaborado :D

Espero que esta dica ajude alguem, ja que eu tive bastante trabalho reorganizando código depois que descobri que isto era muito melhor do que colocar tudo no MXML logo que comecei a trabalhar com Flex.

Tags: , ,

05 Feb 09 Mais um livro sobre Ruby On Rails publicado!!

Capa Livro Ruby On Rails Urubatan

Desta vez é o meu livro que acaba de ser lançado (na verdade ele esta em pré lançamento, mas me disseram que ele chega nas bancas rapidinho :D ) pela editora Novatec (http://www.novatec.com.br).
É um Livro focado em Ruby On Rails, tentando mostrar todo o necessário para ser produtivo com o Rails de uma forma 100% prática …
Por que comprar este livro e não algum outro que ja exista por ai? A tradução de algum livro conhecido?
Bom, este livro foi escrito originalmente em portugues, utiliza uma linguagem bem descontraida.
Tentei escreve-lo como se estivesse conversando com o leitor.
O livro é 100% prático.

Durante o livro são desenvolvidas duas aplicações, que você vai poder utilizar como referência mais tarde.
Depois do capitulo sobre testes, a segunda aplicação é desenvolvida utilizando BDD com RSpec.
Todos os conceitos e recursos do Rails são apresentados utilizando as aplicações desenvolvidas, ou seja, você vai desenvolvendo uma aplicação e aprendendo a trabalhar com Rails.
No livro falo de Ruby (uma breve introdução, não é um curso completo de Ruby)
Rails
Ajax
BDD
TDD
Recursos avançados do Active Record
Recursos avançados do Rails
Boas práticas
CSS
XHTML
Um pouquinho de Javascript
Como escrever menos código
DRY

Mas mesmo assim, o livro é recomendado para iniciante e intermediario no máximo, pois existem alguns tópicos mais avançados que não são tratados, mas nada que faça falta no dia a dia.

Acho que o maior diferencial do livro é ser 100% prático, apresentar todos os conceitos utilizando exemplos desenvolvidos pelo próprio leitor, facilitando o aprendizado.
Garanto que se você ler o livro e fizer todos os exercícios e as duas aplicações propostas no livro, no final você vai conseguir desenvolver as suas aplicações em RoR sem maiores problemas.
Se tiver dúvidas, vou ficar feliz em responde-las e com certeza, se vocês gostarem do livro, as dúvidas vão ajudar a melhorar a próxima versão do livro :D
Então agora é só vocês comprarem o livro e começar a estudar.

Para quem quiser dar uma olhada antes do livro chegar nas bancas, segue o “indice” com o título dos capítulos:

  1. Dedicatória
  2. Agradecimentos
  3. Sobre o autor
  4. Prefácio
  5. Introdução ao Ruby
    • Instalação do Ruby
    • Primeiros passos
    • Variáveis e escopo
    • Tipos básicos do Ruby
    • Blocos de código
    • Procs
    • Números
    • Valores booleanos
    • Strings
    • Constantes
    • Intervalos numéricos
    • Arrays
    • Hashes
    • Símbolos
    • Expressões regulares
    • Classes e métodos
    • Métodos
    • Módulos
    • Operadores condicionais e loops
    • If elsif else end
    • case when else end
    • Operadores de loop
    • while
    • for
    • until
    • begin
    • loop
    • Padrões importantes
      • Nomes de arquivos
      • Classes, e atributos e métodos de acesso
      • Nomenclatura de métodos
      • Dominando o Ruby!
  6. Ambiente de desenvolvimento
    • Multi plataforma
      • Aptana RadRails
      • NetBeans
      • IntelliJ IDEA
      • Vim
    • Windows
      • Notepad++
      • E-TextEditor
    • Linux
      • GEdit
      • Kate
    • Mac
      • TextMate
    • Plug-ins para os editores
    • Escolhendo o seu
  7. Rails básico
    • Criando a primeira aplicação
    • Estrutura de diretórios de uma aplicação
      Rails

      • Scripts padrão do Rails
      • Gerando código, um cadastro instantâneo
      • A primeira Migration
      • O primeiro model
      • O primeiro controlador
      • Métodos básicos do ActiveRecord
      • Recebendo parâmetros nos controladores
    • Respondendo a requisições
    • As primeiras views
    • Precisando de ajuda para limpar o código das
      views?
    • Configurando rotas, um nome bonito para URLs
    • Continuando o desenvolvimento
    • Conferindo as migrations geradas
    • Escrevendo modelos
    • Gerando todo o código
    • Um pouco de segurança na aplicação
    • Um layout menos ruim para a aplicação
    • Associando usuários a projetos
    • Adicionando tipos de tarefa a um projeto
      • Cadastrando as horas trabalhadas
      • Um relatório para a aplicação
      • Limpando um pouco o código e se livrando um pouco do
        inglês
  8. Suporte a testes no Rails
    • RUnit básico
    • Testando modelos
    • Escrevendo fixtures
      • Testando as validações
      • Asserções disponíveis
      • Testes funcionais, testando os controladores e
        helpers
      • Tornando testes mais divertidos e úteis (inclusive
        para o seu chefe)
      • Notificação de testes executados
      • De volta às User Stories
      • Uma olhada nas especificações
      • Utilizando objetos falsos ou mocks
      • Finalizando a definição de comportamento do
        sistema com as User Stories
  9. ActiveRecord avançado
    • Utilizando o ActiveRecord para consultas
      • Métodos de busca dinâmicos por atributos
      • ActiveRecord::Base.find
      • ActiveRecord::Base.find_bysql
    • Associações polimorficas
    • Mais sobre associações
      • Callbacks para associações
    • Outros métodos úteis do ActiveRecord
  10. Use todo o poder do RESTful
    • Introdução ao REST
    • Métodos do protocolo HTTP
    • Princípios do REST
    • RESTful: The Rails way
      • Tema de casa
  11. Paginação
    • Paginação manual
    • Will_paginate
    • Outros plug-ins
      • paginating_find
      • Paginator
  12. Upload de arquivos
    • Attachment_fu
    • File_column
  13. Outros plug-ins úteis
    • Brazilian-rails
    • Exception_logger
    • Exception_notifier
    • JRails
    • Restful_authentication
    • Annotate_models
    • Acts_as_taggable_on_steroids
    • Juggernaut
    • Mydry
    • Calendar_date_select
    • Css_graph
    • Backgroundrb
    • Quais plug-ins utilizar na sua aplicação
  14. AJAX on Rails Quase sem JavaScript
  15. Reutilizando código entre projetos
    • O plug-in biblioteca
    • Plug-ins de geração de código
    • Finalizando o assunto
  16. Colocando tudo isto em produção
    • CGI
    • FastCGI
    • Mongrel
    • Mod_rails
    • JRuby
    • Opções de deployment

Junto com o livro vou colocar no ar o site http://livro.urubatan.com.br onde vou publicar mais exemplos, quaisquer correções que sejam necessárias, e qualquer um que comprar o livro vai poder deixar a opinião e fazer perguntas.

PS.: Este livro não vai te transformar em um ninja fodão que desenvolve qualquer sistema em 2 horas. Mas é um bom começo se você quer aprender Ruby On Rails.

Tags: , , , ,

06 Jan 09 Desenvolvimento visual de aplicações para windows mobile em linux

Este não é o post que diz passo a passo como fazer isto, este vai ser o próximo, este post é para dizer que descobri que o Free Pascal tem um cross compiler para arm-wince, se vocês acessarem http://www.lazarus.freepascal.org/, na parte de downloads para windows existe o download do lazarus+free pascal e também o download do crosscompiler para Windows Mobile.
Não existe o download deste cross compiler para linux, mas depois de perder algum tempo, eu ja montei um .sh que compila todo o free pascal e o cross compiler para windows mobile no linux :D
Só falta agora fazer isto funcionar com o lazarus …
Assim que eu fizer isto funcionar no Lazarus eu posto aqui para vocês um passo a passo de como programar em linux/Object Pascal, de modo visual, criando aplicações para windows mobile :D

O canal seria ter uma forma de desenvolver utilizando o mono para Compact Framework, mas como não encontrei ainda nenhuma forma decente de fazer isto, voltar a brincar de “Delphi” até que vai ser divertido :D

Tags: , , , , , , ,

19 Nov 08 Curso básico de refactoring para quem é pobre e preguiçoso

Continuando com a seqüência de posts com títulos polêmicos que comecei dizendo que “Comentário no código é para os fracos“, segue um curso básico de refactoring para quem é pobre (por que vou utilizar o eclipse que é uma excelente IDE e alem de tudo é “di grátis”), e preguiçoso, por que o eclipse vai fazer quase todo o trabalho para nós.
O ponto de partida vai ser o “exercício” que deixei no final do post sobre comentários no código.
Par quem não quiser ler todo o outro post, o código inicial vai ser este abaixo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package blog;
 
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
 
public class VeryBadlyNamedFile {
	private static final char[] asdfg = new char[] {'I', ' ', 'c', 'a', 'n', ' ', 'd', 'o', ' ', 'v', 'e', 'r', 'y', ' ', 'u', 'g', 'l', 'y', ' ', 'c', 'o', 'd', 'e'};
	private String an;
	private BufferedReader rfsdw;
	private FileReader temp;
 
	public VeryBadlyNamedFile(String an, BufferedReader rfsdw, FileReader temp) {
		super();
		this.an = an;
		this.rfsdw = rfsdw;
		this.temp = temp;
	}
 
	public void doIt() throws IOException {
		ctfiidne();
		startDoing();
		try {
			canIDoAnyThing();
		} catch (RuntimeException yicdet) {
			nowReallyDoIt();
		}
	}
 
	private void nowReallyDoIt() {
		firstDoTheOtherThing();
		reallyDoItInternal();
	}
 
	private void firstDoTheOtherThing() {
		rfsdw = new BufferedReader(temp);
	}
 
	private void reallyDoItInternal() {
		while (true) {
			try {
				imDoingIt();
			} catch (Exception e) {
				break;
			}
		}
	}
 
	private void imDoingIt() throws Exception {
		String s = rfsdw.readLine();
		if (s == null)
			throw new Exception("hahaha, I bet you did not understood the code");
		System.out.println(s);
	}
 
	private void ctfiidne() throws IOException {
		File a = new File(an);
		if (!a.exists()) {
			FileWriter wrfedsd = new FileWriter(a);
			wrfedsd.write(asdfg);
			wrfedsd.close();
		}
	}
 
	private void canIDoAnyThing() {
		if (new File(an).exists() && new File(an).canRead() && new File(an).canWrite())
			throw new RuntimeException();
	}
 
	private void startDoing() throws FileNotFoundException {
		File f = new File(an);
		temp = new FileReader(f);
	}
}

Antes de começar o refactoring, vamos definir o que é refactoring de uma forma bem simples:
Refactoring = Alterar partes do código de uma aplicação sem quebrar outras partes da aplicação que dependam daquele código.
Refactoring é uma forma de melhorar o design de um código existente enquanto ele continua funcionando.
Gosto muito de uma frase espetacular do Fowler, um dos papas do desenvolvimento ágil, que coloco abaixo:

“Any fool can write code that a computer can understand. Good programmers write code that humans can understand.”
-Martin Fowler et al, Refactoring: Improving the Design of Existing Code, 1999

Nos vamos utilizar os recursos de refactoring do Eclipse para transformar este lixo acima em alguma coisa legível, mantendo exatamente o mesmo comportamento, ou seja, sem quebrar o código que já funciona.
Para facilitar o trabalho, este tutorial (se é que pode ser chamado de tutorial), vai ser um simples “passo a passo” que eu utilizei para alterar este código no Eclipse, que é a minha segunda IDE preferida (a melhor de todas na minha opinião é o IntelliJ IDEA, mas se eu utilizasse este, não seria um tutorial para quem é pobre :D )
Siga os passos abaixo:

  1. Primeiro vamos renomear a classe, com o arquivo aberto, o cursos sobre o nome da classe (VeryBaclyNamedFile) pressione ALT+SHIFT+R e substitua o nome da classe por “TextFileToScreenPrinter” depois pressione “Enter”
  2. Agora esta na hora de alterar os nomes de alguns métodos. Repetindo exatamente o mesmo procedimento (ALT+SHIFT+R, altera nome, Enter), faça as seguintes alterações nos nomes de métodos e variáveis:
    • asdfg -> standardContentForNewFiles
    • an -> fileName (neste caso no parâmetro do construtor e na variável de instância)
    • ctfiidne -> ensureFileAlreadyExists
    • canIDoAnyThing -> abortIfCanNotReadOrWriteFile
    • startDoing -> createFileReader
    • temp -> rawFileReader (neste caso no parâmetro do construtor e na variável de instância)
    • rfsdw -> fileLineReader (neste caso no parâmetro do construtor e na variável de instância)
    • nowReallyDoIt -> printEachLineFromFileToConsole
    • yicdet -> e
    • firstDoTheOtherThing -> createLineReaderFromFileReader
    • reallyDoItInternal -> forEachLineInTheFilePrintItOnTheScreen
    • imDoingIt -> printNextLineOfFileToStdOutButFailIfThereAreNoMoreLines
  3. Agora com nomes menos ruins para os métodos vamos começar a organizar um pouco as coisas, dentro do método “abortIfCanNotReadOrWriteFile” selecione o trecho “new File(fileName).exists()”, pressione as teclas ALT+SHIFT+L e de o nome “fileExists” para a variável, agora repita a operação para o bloco “new File(fileName).canRead()” e de o nome “canReadTheFile” para a variável, e por último repita a operação com o bloco “new File(fileName).canWrite()” e nomeie a variável “canWriteToTheFile”.
  4. Agora podemos ler facilmente que a lógica deste método esta invertida, então precisamos negar as variáveis no “if” dentro do método “abortIfCanNotReadOrWriteFile”.
  5. Agora dentro do método “doIt”, podemos remover o try/catch.
  6. Editando o método “createLineReaderFromFileReader”, vamos mudar o tipo sendo utilizado para inicializar a variável “fileLineReader” para um java.util.Scanner, para isto basta substituir o bloco “new BufferedReader(rawFileReader);” por “new Scanner(rawFileReader);”. No momento em que isto for feito, o eclipse vai reclamar que não sabe o que é “Scanner”, então precione CTRL+1 com o cursor sobre a palavra “Scanner” e selecione “import java.util.Scanner”. Agora a reclamação é um “type mismatch”, simplesmente precione CTRL+F1 novamente e selecione a opção “Change the type of fileLineReader to Scanner”. E por último, vamos adicionar a seguinte linha neste método:
    1
    
    fileLineReader.useDelimiter("\n");
  7. Para melhorar um pouco o código, vamos remover os dois parâmetros extras do construtor da clase, o único parâmetro que deve ficar no construtor é o primeiro, que aponta para o arquivo que deve ser lido. Para fazer isto, coloque o cursor sobre o construtor e pressione ALT+SHIFT+C, remova os dois parâmetros extras no dialogo e clique em finish, como esta alteração vai causar um erro no código, será necessário clicar em continue no próximo dialogo. Para corrigir o erro, remova as linhas 19 e 21, o eclipse deve ter sublinhado estas linhas em amarelo.
  8. O código esta começando a parecer um pouco melhor, mas ainda precisa de alterações (no momento ele não deve estar nem compilando se você seguiu todos os passos até agora). Vamos então remover o método “printNextLineOfFileToStdOutButFailIfThereAreNoMoreLines”, e alterar o código do método “forEachLineInTheFilePrintItOnTheScreen” para algo parecido com o bloco abaixo:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    Iterable<String> lineIterator = new Iterable<String>(){
      @Override
      public Iterator<String> iterator() {
        return fileLineReader;
      }
    };
    for(String line : lineIterator){
      System.out.println(line);
    }
  9. Para melhorar o código do método, selecione a criação do iterator toda (deve ser da linha 40 a 45 no arquivo), pressione as teclas ALT+SHIFT+M e digite o nome “initializeLineIteratorFromLineReader” para o novo método.
  10. Agora utilizando o refactoring de rename (ALT+SHIFT+R) altere o nome da variável “lineIterator” para “linesInTheFile”, desta forma o último bloco do método pode ser lido exatamente igual ao nome do método.
  11. Agora no método “ensureFileAlreadyExists” vamos alterar o conteúdo para o seguinte bloco:
    1
    2
    3
    4
    5
    6
    7
    
    File file = new File(fileName);
    boolean fileExists = file.exists();
    if (!fileExists) {
      FileWriter fileWriter = new FileWriter(file);
      fileWriter.write(conteudoPadraoParaNovoArquivo);
      fileWriter.close();
    }
  12. Se você já esta se acostumando com os refactorings, vai perceber que o que foi feito foi um “extract variable” na condição do IF, e dois renames, um na variável que aponta para o arquivo e outro na variável que aponta para o FileWriter.
  13. Agora que quase todas as variáveis e métodos tem nomes decentes, podemos fazer algumas alterações na lógica, para limpar um pouco o código desta classe, como por exemplo, remover o método “createFileReader” e a variável “rawFileReader”, alterar o método “createLineReaderFromFileReader” para passar “new File(fileName)” como parâmetro na criação do Scanner.
  14. Isto vai gerar um erro de compilação, com o CTRL+1 poderemos adicionar a exception que falta no throws do método e tudo vai ficar OK.
  15. Podemos renomear o método “createLineReaderFromFileReader” para apenas “createLineReader”.

Pronto, o Eclipse acabou de nos ajudar a ter um código menos porco na classe do post sobre comentários de código.
Claro que o código ainda não esta nenhum primor, mas a idéia deste post era mostrar que é possível utilizar recursos da IDE para facilitar o refactoring de código porco quando este for encontrado.
E não se iluda, se você estuda para melhorar o seu conhecimento sobre desenvolvimento e ser um profissional cada vez melhor, provavelmente o código que você escreveu a dois meses atrás você ache muito ruim hoje.

Só para finalizar o post, o seu código deve ter ficado parecido com este:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package blog;
 
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Iterator;
import java.util.Scanner;
 
public class TextFileToScreenPrinter {
	private static final char[] standardContentForNewFiles = new char[] {'I', ' ', 'c', 'a', 'n', ' ', 'd', 'o', ' ', 'v', 'e', 'r', 'y', ' ', 'u', 'g', 'l', 'y', ' ', 'c', 'o', 'd', 'e'};
	private String fileName;
	private Scanner fileLineReader;
 
	public TextFileToScreenPrinter(String fileName) {
		super();
		this.fileName = fileName;
	}
 
	public void doIt() throws IOException {
		ensureFileAlreadyExists();
		abortIfCanNotReadOrWriteFile();
		printEachLineFromFileToConsole();
	}
 
	private void printEachLineFromFileToConsole() throws FileNotFoundException {
		createLineReader();
		forEachLineInTheFilePrintItOnTheScreen();
	}
 
	private void createLineReader() throws FileNotFoundException {
		fileLineReader = new Scanner(new File(fileName));
		fileLineReader.useDelimiter("\n");
	}
 
	private void forEachLineInTheFilePrintItOnTheScreen() {
		Iterable<String> linesInTheFile = initializeLineIteratorFromLineReader();
		for(String line : linesInTheFile){
			System.out.println(line);
		}
	}
 
	private Iterable<String> initializeLineIteratorFromLineReader() {
	  Iterable<String> lineIterator = new Iterable<String>(){
			@Override
      public Iterator<String> iterator() {
	      return fileLineReader;
      }
		};
	  return lineIterator;
  }
 
	private void ensureFileAlreadyExists() throws IOException {
		File file = new File(fileName);
		boolean fileExists = file.exists();
		if (!fileExists) {
			FileWriter fileWriter = new FileWriter(file);
			fileWriter.write(standardContentForNewFiles);
			fileWriter.close();
		}
	}
 
	private void abortIfCanNotReadOrWriteFile() {
		boolean fileExists = new File(fileName).exists();
		boolean canReadTheFile = new File(fileName).canRead();
		boolean canWriteToTheFile = new File(fileName).canWrite();
		if (!fileExists && !canReadTheFile && !canWriteToTheFile)
			throw new RuntimeException();
	}
 
}

Atalhos do Eclipse Ganimede que todo preguiçoso inteligente deveria decorar

  • ALT+SHIFT+M -> Extract method
  • ALT+SHIFT+L -> Extract Local Variable
  • ALT+SHIFT+R -> Rename
  • CTRL+1 -> Quick Fix
  • ALT+SHIFT+X,J -> Executa aplicação Java
  • ALT+SHIFT+X,T -> Executa Unit Test
  • ALT+SHIFT+X -> Executar, se o tipo não for especificado, um menu será aberto em 5 segundos no canto inferior direito
  • CTRL+SPACE -> Code completion
  • CTRL+SHIFT+SPACE -> Parameter completion
  • CTRL+3 -> Atalho super mega mágico para qualquer tarefa dentro do eclipse,digite o nome do que quer fazer no dialogo que vai aparecer
  • ALT+SHIFT+S -> menu “source” onde ficam comandos como “Generate getters and setters” e “Override/Implement methods”
  • ALT+SHIFT+T -> Menu de refactoring

Atalhos do Eclipse apra usuáriso de MAC

  • COMMAND+OPTION+M -> Extract method
  • COMMAND+OPTION+L -> Extract Local Variable
  • COMMAND+OPTION+R -> Rename
  • COMMAND+1 -> Quick Fix
  • COMMAND+OPTION+X,J -> Executa aplicação Java
  • COMMAND+OPTION,T -> Executa Unit Test
  • COMMAND+OPTION+X -> Executar, se o tipo não for especificado, um menu será aberto em 5 segundos no canto inferior direito
  • CTRL+SPACE -> Code completion
  • COMMAND+3 -> Atalho super mega mágico para qualquer tarefa dentro do eclipse,digite o nome do que quer fazer no dialogo que vai aparecer
  • COMMAND+OPTION+S -> menu “source” onde ficam comandos como “Generate getters and setters” e “Override/Implement methods”
  • COMMAND+OPTION+T -> Menu de refactoring

E o atalho de teclado mais mágico de todos é:

COMMAND+SHIFT+L (CTRL+SHIFT+L em PCs) -> Lista os atalhos de teclado. :D

Acho que isto já esta bom para começar, se você for realmente um preguiçoso inteligente (o tipo que passa um pouco mais de tempo pensando para ter uma solução melhor agora e trabalhar menos no futuro), provavelmente você vai prestar atenção nos menus do eclipse e vai ir decorando as teclas de atalho com o tempo :D

PS.: Será que alguém vai ficar ofendido com o titulo deste post e vai ficar reclamando que não é pobre ou não é preguiçoso?

Tags: , , ,

13 Nov 08 Comentário no código é para os fracos

Provavelmente serei crucificado por causa deste post, mas se você se der ao trabalho de ler até o final, provavelmente vai concordar comigo que comentários no código são para os fracos, programador hardcore de verdade escreve código legível!
É exatamente isto que eu estou dizendo, por exemplo, o que faz o código abaixo?

1
2
3
4
5
6
7
8
9
10
public String write(StringBuilder fle, StringBuffer con) {
  File f = new File(fle.toString());
  FileReader fr = new FileReader(f);
  BufferedReader br = new BufferedReader(fr);
  String lin;
  while((lin=br.readLine())!=null){
    con.append(lin).append("\n");
  }
  return con.toString();
}

Difícil? E este ainda é um código simples, mas vamos dar uma melhorada nele …

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public String read(StringBuilder fle, StringBuffer con) {
  //Opens the file with the name container in the fle parameter
  File f = new File(fle.toString());
  //Create a file reader, then a buffered reader to make our work easier
  FileReader fr = new FileReader(f);
  BufferedReader br = new BufferedReader(fr);
  String lin;
  //Read each line of the file until it is null
  while((lin=br.readLine())!=null){
    //Put the content read into the buffer pointed by the parameter "con"
    con.append(lin).append("\n");
  }
  //The caller already have the content, because he created the buffer, but I'll return the string anyway
  return con.toString();
}

Mais fácil certo? Bastou ler os comentários, mas o código continua um lixo.
Ou seja, esta é uma gambiarra utilizada por péssimos programadores para contornar a própria limitação de não conseguir escrever um código decente.

Então qual a solução que eu recomendo?
Vamos tentar reescrever este método então:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public String readFileContents(File fileToRead) {
  boolean canReadFile = fileToRead.exists();
  if(!canReadFile)
    return "";
  StringBuilder buffer = new StringBuilder();
  BufferedReader readerForFile = openBufferedReaderForFile(fileToRead);
  readFileContetIntoBuffer(buffer,readerForFile);
  closeFileReader(readerForFile);
  return buffer.toString();
}
private  BufferedReader openBufferedReaderForFile(File fileToRead){
  return new BufferedReader(new FileReader(fileToRead));
}
private void readFileContetIntoBuffer(buffer,readerForFile){
  String line;
  while((line=readerForFile.readLine())!=null){
    buffer.append(line).append("\n");
  }
}
private void closeFileReader(readerForFile){
  readerForFile.close();
}

Agora se você prestar atenção no nome do método “readFileContents” já vai saber o que o método faz, Além disto, o código do método é quase legível em inglês. A leitura dele ficaria mais ou menos assim:

if not can read file, return null
open Buffered Reader For File: fileToRead
read File Contet Into Buffer: buffer, readerForFile
close File Reader: readerForFile
return buffer.toString();

Ou seja, qualquer um que entenda inglês, como qualquer desenvolvedor tem a obrigação de entender, vai ler o método como se fosse um comentário.

E eu já vi gente fazendo pior do que isto, o código tinha comentários, mas parecia com esta coisa ai em baixo:

1
2
3
4
5
public String write(StringBuilder fle, StringBuffer con) {
  File f = new File(fle.toString()); FileReader fr = new FileReader(f);  BufferedReader br = new BufferedReader(fr);
String lin; while((lin=br.readLine())!=null){  con.append(lin).append("\n");  }
  return con.toString();
}

Com certeza tinha muito menos linhas de código do que a minha versão :D
Mas não é uma tarefa fácil entender o código que uma criatura destas escreve :D

Claro que o exemplo que eu apresentei foi um exemplo bem simples, e que escrever código legível requer uma certa prática …
Então, vou fazer uma proposta:
Vou deixar um exemplo de código abaixo, e vocês tentam torna-lo mais legível. Em um ou dois dias eu posto a minha resposta aqui.
Quem quiser pode postar nos comentários o código que escreveu.
Para que o código fique colorido no blog, basta colocar dentro de uma tag <pre lang=”java” line=”1″> … </pre>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package blog;
 
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
 
public class VeryBadlyNamedFile {
	private static final char[] asdfg = new char[] {'I', ' ', 'c', 'a', 'n', ' ', 'd', 'o', ' ', 'v', 'e', 'r', 'y', ' ', 'u', 'g', 'l', 'y', ' ', 'c', 'o', 'd', 'e'};
	private String an;
	private BufferedReader rfsdw;
	private FileReader temp;
 
	public VeryBadlyNamedFile(String an, BufferedReader rfsdw, FileReader temp) {
		super();
		this.an = an;
		this.rfsdw = rfsdw;
		this.temp = temp;
	}
 
	public void doIt() throws IOException {
		ctfiidne();
		startDoing();
		try {
			canIDoAnyThing();
		} catch (RuntimeException yicdet) {
			nowReallyDoIt();
		}
	}
 
	private void nowReallyDoIt() {
		firstDoTheOtherThing();
		reallyDoItInternal();
	}
 
	private void firstDoTheOtherThing() {
		rfsdw = new BufferedReader(temp);
	}
 
	private void reallyDoItInternal() {
		while (true) {
			try {
				imDoingIt();
			} catch (Exception e) {
				break;
			}
		}
	}
 
	private void imDoingIt() throws Exception {
		String s = rfsdw.readLine();
		if (s == null)
			throw new Exception("hahaha, I bet you did not understood the code");
		System.out.println(s);
	}
 
	private void ctfiidne() throws IOException {
		File a = new File(an);
		if (!a.exists()) {
			FileWriter wrfedsd = new FileWriter(a);
			wrfedsd.write(asdfg);
			wrfedsd.close();
		}
	}
 
	private void canIDoAnyThing() {
		if (new File(an).exists() && new File(an).canRead() && new File(an).canWrite())
			throw new RuntimeException();
	}
 
	private void startDoing() throws FileNotFoundException {
		File f = new File(an);
		temp = new FileReader(f);
	}
}

E agora um “main” só para executar o lixão acima.

1
2
3
4
5
6
7
8
9
package blog;
 
import java.io.IOException;
 
public class Main {
	public static void main(String[] args) throws IOException {
		new VeryBadlyNamedFile("c:\\anyFile.any",null,null).doIt();
	}
}

Os exemplos estão em java, mas em qualquer linguagem os comentários para explicar o código servem para mascarar a incapacidade dos programadores escreverem código decente.

PS.: Só para constar, eu não acho de verdade que vocês não devem comentar o código, mas se vocês não escrevem código legível, ou escrevem código que realmente precisa de um comentário para outro programador entender, então vocês não aprenderam a programar ainda!

PS2.: só para constar, o código deste post foi inventado na hora, inspirado em coisas que ja vi em diversos lugares por ai, masescrevi ele direto no blog, então existe uma grande possibilidade de não compilar.

PS3.: acho que preciso de exemplos melhores, mas vocês devem ter entendido a idéia deste post

Tags: , , ,

07 Nov 08 IDE REVIEW: RubyMine – a melhor IDE para Rails até agora

Em todos os meus trabalhos com Ruby On Rails até agora eu não achei necessária uma IDE, pois eu sempre fui mais produtivo com simples editores de texto (tudo bem, não eram editores tão simples assim), As IDEs eram sempre lentas, muito mais lentas que os editores de texto. Mas o pessoal da JetBrains se superou, na verdade não se superou, pois o IntelliJ IDEA ja é expetacular.
Mas o RubyMine é a IDE para Rails [bb]mais rápida até agora, com performance similar ao GEdit e GVim que são os meus editores favoritos (mesmo preferindo o KDE não consigo me acostumar com o Kate).
Eu não sei direito como eles fizeram, mas o code completion para Ruby e Rails esta muito rápido.
A IDE ficou Leve, o que é uma novidade interessante, pois o IntelliJ IDEA é quase tão pesado quanto o Eclipse.
E o melhor de tudo! O RubyMine tem Refactorings para código Ruby, coisa que não encontrei em nenhuma outra IDE (o NetBeans tem algumas tentativas, mas no meu último teste não funcionaram muito bem).
Bom, segue então uma lista do que faz o RubyMine ser uma ótima IDE para desenvolvimento Rails na minha opinião, com alguns screenshots :D


Como o Aptana e o Netbeans o Rubymine tem integração com o “script/generate”, com dialogos bonitinhos para criar cada item, mas isto o Netbeans também tem, e fora os dialogos no GVim com um :Rgen faço a mesma coisa :D


O RubyMine também tem code completion simples, como todas as outras IDEs para Ruby e Rails, o Eclipse tem isto, o Aptana tem, até o GVim tem quando rodando em um Linux (No windows não consegui fazer o Omnicompletion funcionar :D )


Mas aqui a coisa começa a ficar mais divertida, o code completion do RubyMine é inteligente o suficiente para saber quais são os parâmetros possívels para a maioria dos métodos do Rails.
Claro que ele não inferiu isto pelo código, é a integração com o Rails que ja conhece os métodos, mas mesmo assim, este é um recurso que eu não encontrei em nenhuma outra IDE para Ruby ou Rails até agora.


Outra coisa interessante são os refactorings disponíveis.
Este do screenshot é um extract local variable, que substitui uma expressão por uma variável em todo o arquivo.
Alem deste, testei alguns outros refactorings como rename, extract class, …
Só não consegui fazer funcionar o Extract Method que eu realmente queria que funcionasse, seria muito bom ter um Extract Method para código Ruby.

Isto fora os recursos ja conhecidos do IntelliJ IDEA para edição de HTML e Javascript, com refactorings para ambos. E o fato de o RubyMine ser a IDE mais rápida para Ruby que eu encontrei até agora. Fazem ela ser a minha preferida desde ontem também :D

Claro que trabalhei com ela muito pouco tempo, mas mesmo assim, ja gostei bastante do que vi.
Só falta ver como vão ficar os preços, ja que é uma IDE comercial.
Se o preço for bom, por exemplo, próximo ao preço do TextMate, vai valer a pena com certeza!

Tags: , , , , , ,

07 Nov 08 Kubuntu com KDE 4.1 – desktop show de bola e bem rapido :D


Este é o meu desktop em casa rodando o Kubuntu 8.10.
Esta um espetaculo.
Tudo bem que eu sempre gostei mais do KDE do que do Gnome, e que cada um tem um gosto, mas o KDE 4.1 ficou show de bola.
Mas este post não é para mostrar um screenshot do meu desktop, é para dar a dica de como resolver um bug chato no Kubuntu 8.10.
Logo depois que eu instalei o KDE 4.1 a cada 10 segundos aproximadamente a tela fazia um flick ou algo parecido com isto, e eu ja estava quase abandonando o KDE para instalar o Gnome.
Foi quando eu abri as configurações do KDE, em Avançado, serviços e desabilitei o processo KDED.
tudo passou a funcionar perfeitamente!

E só pra constar, ele esta mais rápido do que as versões do KDE3 que eu estava utilizando antes.

Então se estiver com o mesmo problema, é só fazer o mesmo.
PS.: parece que isto faz o KDE não reconhecer monitores conectados ao computador depois dele ligado, mas ai é só ativar e desativar o serviço quando necessário, eu vou testar ligando o note na televisão em casa no findi e atualizo o post dizendo o que aconteceu.

04 Sep 08 Mudanças problemas e novidades

Não sei se alguem chegou a sentir falta, mas o blog ficou um bom tempo meio que jogado as traças, quase sem posts, e ainda por cima passou dois dias fora do ar …
Bom, os dois dias fora do ar foram por pura bocabertice, eu esqueci de pagar a anuidade do dominio para a fapesp …
Ja o tempo quase sem posts no blog tem um motivo mais nobre, este fim de semana estarei finalizando o último capitulo do meu livro sobre Ruby On Rails que eu ja entrego com uma semana de atraso para a editora :D
Mas ele esta pronto! E contando com revisões da editora e o tempo necessário para a publicação, ele deve estar disponível entre o final deste ano e o início do próximo :D

Fora isto também tenho mais novidades …
Não trabalho mais com consultoria há algum tempo já, estou trabalhando para a HP, e acho que é a primeira vez que encontro uma empresa que posso pensar em trabalhar por muitos anos ainda. Ou seja, estou gostando bastante do trabalho :D

Bom, acho que era isto, a partir da próxima semana devo postar aqui com mais frequencia, já que o livro ja vai estar finalizado :D