RSpec Stories - Ruby gerado a partir do texto!

Não, eu não quero que vocês gerem o código dos testes, isto iria apenas criar testes inúteis!
Mas eu acho muito chato ter certeza de que todos os possíveis passos de um arquivo txt com a user story estão presentes nos passos definidos no arquivo .rb
Claro que isto não esta considerando reutilização de passos através de Mixins ou outras técnicas semelhantes, mas este script me ajudou bastante ja, então resolvi compartilhar ele para quem estiver interessado :D

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
name=ARGV[0]
all = []
last = ""
f = File.new(name)
f.each_line do |l|
  l.strip!
  l,command,params = *(/^(When|Then|Given|And) (.*)/.match l)
  if l
    command = last if command == "And"
    last = command
    params = params.gsub /"/, '\"'
    all << "#{last} \"#{params}\" do\n    pending\n  end"
  end
end
all.uniq!.sort!
sym_name = name[File.dirname(name).length+1..-5]
puts %Q{require 'stories/helper'
steps_for(:#{sym_name}) do
}
all.each { |l| puts "  #{l}"}
puts %Q{end 
 
with_steps_for :#{sym_name} do
  run "\#{dir = File.dirname(__FILE__)}/#{sym_name}.txt"
end
}

eu salvei este código em um arquivo txt_to_steps.rb, e para utilizar basta executar:
ruby txt_to_steps.rb

Um exemplo de user story (utilizando o formato do RSpec) seria este texto:

Story: new user
  As a company employee
  I want to register in the CRM
  So that I can see and manage company contacts

Scenario: user with no access to the system
  Given the username user1
  And the password mypassword
  When the login form is submited
  Then the login form should be shown again

Scenario: user registration
  Given the username user1
  And the password mypassword
  And the email user1@company.com
  When the registration form is submited
  And there is no other user with the same e-mail or email
  Then the registration should be OK
  And the user should be redirected to /

Scenario: repeated user registration
  Given the username user1
  And the password mypassword
  And the email user1@company.com
  When the registration form is submited
  And there is already another user with the same name or email
  Then the registration should fail
  And the registration form should apear again

Scenario: existing user login
  Given the username user1
  And the password mypassword
  When the login form is submited
  Then the user should be redirected to /


O txt_to_steps pode ser melhorado para tentar identificar alguns padrões, mas como esta agora ja me poupou bastante trabalho :D
Sei que não é o código ruby mais limpo que vocês ja leram, mas para algo escrito em 5 minutos até que ficou legal :D

Se ajudar mais alguem, a única exigência é deixar um comentário aqui dizendo o que poderia ser melhorado no script :D

PS.: sei que o blog anda meio parado demais, mas é por um bom motivo, acho que daqui a um mes aproximadamente volta tudo ao normal e eu posso contar aqui o motivo deste tempo quase sem posts :D

Se você gostou deste post, lembre-se de assinar o RSS feed do blog, para ser notificado de novos posts!

Orientação a objetos é fácil, as pessoas é que complicam

A idéia de escrever este post veio desta discussão no GUJ, que começou falando de testes unitários e terminou sobre orientação a objetos.
Então seguem as minhas opiniões.


As linguagens de programação mais modernas, ou pelo menos as mais utilizadas de hoje em dia são Orientadas a Objeto, as famosas linguagens OO, mas mesmo assim, a grande maior parte dos desenvolvedores parece não conseguir entender direito a tal da Orientação a Objeto.
Resolvi escrever este este post porque do alto do meu ego inflado, acredito conseguir entender corretamente OO e poder ajudar um pouco.
Então, leiam o post até o final, e depois se quiserem tirar dúvidas ou me xingar, é só deixar um comentário …

O primeiro erro que todos cometem ao iniciar em OO é achar que Orientação a Objetos é sobre classes, isto não faz nenhum sentido, Orientação a Objetos como o próprio nome diz, é sobre Objetos, então, qualquer coisa que não seja um objeto não é realmente importante quando se fala em Orientação a Objetos.
OK, mas por que todos caem no mesmo engano então? porque a grande maior parte das linguagens de programação orientadas a objeto, utilizam classes como moldes para objetos, e durante o desenvolvimento se cria os moldes, ja que os objetos só existem em tempo de execução nestas linguagens.
Quais linguagens não funcionam assim? Bom, agora me vem a cabeça SmallTalk, IO e Ruby, mas devem ter outras.
SmallTalk porque não existe realmente distinção entre “tempo de desenvolvimento” e “tempo de execução” em SmallTalk, tudo esta sempre executando.
IO porque não utiliza o conceiro de classes (a não ser que eu tenha entendido a linguagem errado) e sim objetos template.
E Ruby porque as classes em Ruby são objetos também, mas das três é a que mais se aproxima do “padrão” que citei antes.

Outro erro bastante comum, desta vez quando se utiliza alguma tecnologia de mapeamento Objeto/Relacional, é achar que objetos são tabelas, mapeamento Objeto/Relacional serve para salvar o estado de um objeto em um banco de dados relacional, apenas isto, um objeto do tipo Usuario por exemplo, não possui um atributo “grupo_id”, mas pode possuir um relacionamento com um objeto do tipo Grupo. Percebam que eu escrevi atributo e não campo, pois tabelas tem campos e linhas, objetos possuem atributos e métodos.

Uma coisa bastante comum, ainda utilizando mapeamento O/R é achar que um objeto possui os mesmos atributos que a tabela correspondente, mas neste caso até os nomes são diferentes, infelizmente nestes casos, um objeto precisa ter um atributo “id” que mapeia para a chave primária da tabela, mas um objeto não tem uma chave primária, ele tem apenas atributos.

Então, como solucionar estes problemas?
Bom, no geral, um objeto tem comportamento, e alguns atributos também, mas o importante em um objeto é o que ele faz e como ele faz.
Claro que existem objetos que não tem comportamento, objetos em um sistema são a representação binária de alguma coisa do mundo real, e existem coisas no mundo real que não tem comportamento, tem apenas atributos, como por exemplo mesas e cadeiras, mas exceto se você estiver mapeando um destes objetos no seu sistema, todos os objetos tem um comportamento.
Por exemplo, se você criar um objeto do tipo Usuário, se ele não possuir nenhum comportamento, com certeza o seu sistema esta construído de forma errada, pois pelo menos eu, não conheço nenhum usuário que fique parado como uma estátua o tempo todo, e se este for o caso, com certeza ele não é usuário de sistema nenhum, portanto ele não deveria existir no seu sistema.
A mesma coisa se aplica a um carro, você conhece algum carro que não se move? Que não tenha comportamento algum? A única desculpa para um carro que não tem comportamento algum é um sistema para um museu de carros, onde só importa a carcaça do carro e os atributos dele, principalmente o ano de fabricação.

Outra coisa que todos entendem errado é o padrão MVC (Model, View, Controller).
Na definição do padrão, o controlador esta la apenas como ponto de entrada, ele passa parâmetros para um método de um Model, pega o resultado disto e passa para uma View, se ele estiver fazendo mais do que isto, ele possui parte do código que deveria estar dentro do Model, por tanto, o seu Controller esta errado!

Beleza, mas se eu estou dizendo que todos cometem os mesmos erros, como eu posso dizer que OO é fácil então?
Porque a maioria aprende primeiro a programar de forma estrutural, e depois passa para orientação a objetos, e como as pessoas aprendem coisas novas utilizando como base suas experiências anteriores, utilizam os paradigmas procedurais para tentar programar orientado a objetos, esta é basicamente a raiz do problema.
Para tornar a vida de todos mais fácil, o primeiro contato com programação nos cursos e faculdades deveria ser com SmallTalk e não com portugol ou pascal.
Se você aprender primeiro orientação a objetos, conseguira passar para programação procedural com uma facilidade muito maior do que alguem que aprende primeiro programação procedural tentando aprender orientação a objetos.

O pior é que as linguagens de programação que não são 100% OO contribuem ainda mais para aumentar este problema, vamos pegar o Java como exemplo de uma linguagem não 100% OO.
“Mas me disseram que java era uma linguagem de programação OO”
Bom isto também é verdade, eu disse não 100% OO, não disse que não era OO.
Exemplos de coisas que não são OO em Java: tipos primitivos e métodos estáticos
Basicamente eu acho que na linguagem é só isto que não é OO, mas isto ja causa problemas o suficiente confundindo a cabeça de muita gente.
“Perai, métodos estáticos ficam dentro de classes, como podem não ser OO?”
Mérodos estáticos são uma forma porca de programação procedural dentro de uma linguagem OO que teve uma linguagem procedural como origem, como por exemplo o Java e o C++.
Métodos estáticos não fazem parte de uma classe, utilizam a classe apenas como namespace, o que é diferente de fazer parte.
Em um método estático, não é possível nem saber a qual classe ele pertence, como é possível tanto em object pascal quanto em Ruby, ja que estas não possuem métodos estáticos e sim métodos de classe.
E os tipos primitivos estes sim, não tem relação nenhuma com orientação a objetos, só estão la porque quando a linguagem foi criada não era possível fazer uma classe ter a mesma performance do que um tipo mapeado diretamente do processador da máquina, eles existem em Java por problemas de performance e não por serem Orientados a Objeto.

Alguns design patterns foram criados apenas para contornar limitações da pataforma (Exemplo prático, com um banco de dados OO decente não seria necessário criar o pattern DTO), da mesma forma algumas das regras sobre a utilização de OO foram criadas apenas porque a maioria das pessoas não entende OO, como a regra que diz favoreça composição em vez de herança.
Este é um engano terrível cometido por muitas pessoas que eu não consigo entender.
Muita gente acha que estender uma classe é uma forma de compartilhar código, por exemplo:
Existe uma entidade no sistema de nome Cliente, e em algum momento é necessário criar um ClienteDependente que vai possuir todos os mesmos atributos de cliente mais um que aponta para o responsável, não faz sentido algum fazer ClienteDependente estender Cliente para compartilhar este código, porque o comportamento deles não é o mesmo, por exemplo, ClienteDependente não paga a conta, mas Cliente sim, neste caso seria melhor refatorar o sistema e criar uma entidade que pudesse realizar compras (Ou criar objetos do tipo Compra se o sistema foi bem modelado), e fazer com que Cliente e ClienteDependente estendessem desta, pois neste caso elas estariam realmente compartilhando o comportamento e não apenas evitando algum código aparentemente repetido.
Estender uma classe é estender o comportamento, ou seja, só faz sentido estender o comportamento se a classe “filha” tiver todo o comportamento da classe “pai”, ou seja, é um relacionamento o tipo “é um”. Mas como quase ninguém consegue entender que estender uma classe estende o comportamento (não me parece uma coisa tão complexa assim para se entender), foi criada esta regra que diz: Não use herança, use composição. Simplesmente para evitar que pessoas façam porcaria por não entender conceitos que são estupidamente simples, mas que todos tem mania de complicar.

Outro erro bastante comum é tentar colocar em uma classe apenas o comportamento de todo o sistema, se for para fazer isto, utilize programação procedural, pois você vai estar utilizando a classe apenas como namespace mesmo, não vai nem lembrar que em tempo de execução estara trabalhando com diversos objetos criados a partir daquela forma.

Orientação a objetos é simples, é um paradigma de desenvolvimento que modela um sistema transformando conceitos do mundo real como objetos que colaboram entre si para atingir um objetivo.
Acho que o nome Objeto pode ajudar a confundir boa parte das pessoas, pois em um sistema OO, Compra pode ser um tipo de Objetos, mas no mundo real, compra não é um objeto. Mas para resolver isto eu simplesmente assumo que qualquer um que queira ser um programador precisa ter capacidade para trabalhar com abstrações, não tenho uma explicação melhor para alguem que não consegue entender que Compra pode ser um tipo de Objeto em um sistema OO, assim como Viajem também pode ser um tipo de Objeto (estou utilizando “Tipo de Objeto” em vez de classe porque o que importa em um sistema OO são as instâncias e não as classes) dependendo do problema a ser resolvido.

Acho que são estes os erros mais comuns na orientação a objetos, lendo isto você não concorda comigo? OO é simples, as pessoas é que complicam!
Você lembra de algum outro erro muito comum em OO? deixe um comentário.
Se eu lembrar de mais coisas simples que as pessoas tem mania de complicar eu escrevo outro post.

Se você gostou deste post, lembre-se de assinar o RSS feed do blog, para ser notificado de novos posts!

mod_rails revolucionando as hospedagens Rails compartilhadas, inclusive no Brasil

Já existem alguns provedores internacionais disponibilizando o mod_rails como opção para hosting compartilhado de aplicações Ruby On Rails, como por examplo o HostingRails.com (se tiverem mais para adicionar a lista avisem nos comentários que atualizo o post com a lista de provedores disponíveis) , e hoje, vi a noticia que a Locaweb esta começando a disponibilizar hosting compartilhado de aplicações Ruby On Rails utilizando o mod_rails também.
E melhor que isto, como podem ver no quote abaixo, os primeiros a testar o brinquedo, ganham 3 meses de gratis :D

Trial de Ruby on Rails Powered by mod_rails

Você já deve ter ouvido, até mesmo nesse blog, sobre o poder do Ruby
on Rails e suas vantagens para programadores iniciantes e experientes.
Mas provavelmente você também deve conhecer os problemas de
performance de uma aplicação Ruby rodando com Apache e CGI.

A Locaweb inova mais uma vez disponibilizando uma plataforma mais
robusta para Ruby on Rails usando Passenger, também conhecido como
mod_rails. Com mod_rails você pode publicar sua aplicação RoR de
maneira simples em um ambiente estável e de melhor desempenho.

Verifique você mesmo. Estamos oferecendo um período de Trial de 3
meses aos primeiros interessados em experimentar esse novo ambiente.
Para ter um Plano de Hospedagem Profissional I em plataforma Linux
rodando Ruby on Rails com Passenger basta preencher o formulário
abaixo e aguardar nosso contato.

Fonte: http://blog.locaweb.com.br/archives/263

PS.: se se cadastrarem na Locaweb, informem que o cadastro de cliente foi indicação de “usiinformatica”, assim eu ganho bônus :D

Se você gostou deste post, lembre-se de assinar o RSS feed do blog, para ser notificado de novos posts!

Melhorando o “try()” do Ruby, a maneira “Groovy” :D

Bom, desta vez não vou traduzir o meu post, então deem uma olhadinha no meu blog em ingles: A better “try()” for Ruby, why not do the Groovy way?

Mas um resumo rápido:
Acho que todos concordam que é bem chato nas views por exemplo em uma aplicação Ruby escrever:

<%= @person.name if @person %>

Fazer isto um monte de vezes, ou usar o operador ternario, começa a ficar chato com o tempo …
então algumas pessoas sugeriram utilizar:

<%= @person.try(:name) %>

Eu acho que isto fica mais estranho do que o if.
Então resolvi escrever este post: A better “try()” for Ruby, why not do the Groovy way?

Se você gostou deste post, lembre-se de assinar o RSS feed do blog, para ser notificado de novos posts!

Javascript não intrusivo com Lowpro e Prototype

Como eu disse que iria fazer, e fui cobrado no último rails podcads brasil, segue um post sobre o lowpro.
Isto não vai ser exatamente um tutorial, apenas uma coleção de dicas sobre esta excelente extensão para o prototype[bb], o Lowpro.
O Lowpro ja foi até transformado em um plugin para o rails, o UJS Rails Plugin, mas sinceramente, eu prefiro adicionar a meia duzia de linhas de javascript no meu application.js mesmo do que usar outro plugin só pra isto :D

Mas seguindo o baile, tudo o que vou escrever neste post, vocês podem usar com ou sem o rails, só sera necessário o prototype e o lowpro, inclusive o lowpro ja tem até uma versão que funciona junto com o jQuery[bb] para os que não gostam do prototype :D

Mas seguindo com o assunto deste post, o que vocês acham de adicionar alguns recursos Javascript e até mesmo AJAX[bb] em uma página pronta sem mexer no HTML gerado? nem para adicionar Javascript nos onclick, onblur, onXXX …
Na verdade, sera necessário adicionar pelo menos 3 linhas:

1
2
3
<script src="prototype.js" type="text/javascript" charset="utf-8"></script>
<script src="lowpro.js" type="text/javascript" charset="utf-8"></script>
<script src="meuarquivodescripts.js" type="text/javascript" charset="utf-8"></script>

A última linha, ou seja o “meuarquivodescripts.js” define o arquivo onde vamos trabalhar :D

Um dos recursos que eu acho mais espetaculares no lowpro, é o suporte a comportamentos …
Como assim?
imagine a seguinte situação:
Tenho uma página pronta, é uma página de listagem, e no final da tabela tem um link para a criação de um novo item, hoje toda a página é atualizada quando clico neste link, mas eu quero que apenas a parte da tablea seja substituida pelo formulário, em vez de recarregar a tela toda …
Bom isto é muito fácil de fazer :D
E não vamos precisar de praticamente nenhum código …
Claro, precisamos de alguma forma de identificar o link de adicionar novo objeto na tabela, então, consideremos que este e todos os links que quisermos que sejam abertos por ajax no lugar da tabela, tenham a classe “my_ajax_link”, e consideremos que a tabela esta dentro de uma DIV com ID ‘minha_tabela’…
Se isto for verdade, podemos fazer o seguinte:

1
Event.addBehavior({ 'a.my_ajax_link' : Remote.Link({update:'minha_tabela'}) });

Com isto, todos os links que tenham a classe “my_ajax_link” quando clicados vão ser executados via ajax, e vão substituir o conteúdo do elemento de id “minha_tabela” …

OK, mas é só isto que eu posso fazer com o tal de lowpro? E estes tais de comportamentos?

Na verdade o negócio é bem mais flexivel que isto :D
Vamos ver mais alguns exemplos …
Se você quiser executar algum código no momento em que a página aparecer para o usuário (não é no onload, o onload só é executado depois de toda a página carregada :D )
Basta fazer algo assim:

1
2
3
Event.onReady(function() {
  $$('h1').first().innerHTML += ' (Version ' + LowPro.Version + ')'; 
});

Isto vai mostrar a versão do Lowpro dentro da primeira tag H1 da página :D
O Event.onReady executa um código qualquer no momento em que o DOM da página estiver carregado, o que é bem antes de carregas as imagens, por exemplo, e o onload só sera executado depois de tudo carregado …

Outro exemplo bem simples:
Eu quero que quando o usuário colocar o mouse sobre um link ele fique verde, quando tirar o mouse ele volte a cor padrão, e quando clicar o link incremente um contador dizendo quantas vezes ele ja foi clicado …

Para isto precisamos criar um comportamento …

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var LinkBehavior = Behavior.create({
  initialize : function() {
    this.count = 0;
  },
  onmouseover : function() {
    this.element.addClassName('goo');
  },
  onmouseout : function() {
    this.element.removeClassName('goo');
  },
  onclick : function() {
    this.element.innerHTML = 'Clicks: ' + ++this.count;
    return false;
   }
});

O que vai fazer os nossos links mudarem de cor é a classe “goo”, por tanto para que as cores mudem como eu disse antes, é preciso criar a classe “goo” em algum arquivo css carregado pela página.

Mas só este código apenas cria uma classe “comportamento” que define que o objeto que tiver este comportamento vai receber a classe “goo” no evento onmouseover, tera esta classe removida no evento onmouseout e vai ter o conteúdo alterado quando clicado.

Para que isto funcione com todos os links da página, precisamos adicionar este comportamento aos links da página …

1
Event.addBehavior({ 'a' : LinkBehavior });

Pronto, agora todos os links vão ter o comportamento especificado pela classe.

Claro que alterar assim todos os links de uma página não é a coisa mais útil do mundo, mas se compararmos este último exemplo com o anterior, podemos ver que o lowpro, utiliza css selectors para identificar onde os behaviors devem ser aplicados …
Como assim?

  • a - todas as tags a
  • a.meulink - todas as tags a que tenham a classe “meulink”
  • a#link - a tag a com ID link
  • #qualquerid - qualquer tag mas que tenha o ID “qualquerid”
  • .qualquerclasse - qualquer tag que tenha a classe “qualquerclasse”

Tudo bem, esta melhorando, mas eu não curti esta idéia de ter que criar uma classe para definir o que eu quero que aconteça quando o usuário clicar em um link, não tem como fazer tudo isto com uma linha de código só?
Na verdade tem :D

1
Event.addBehavior({'a#bung:click' : function () {alert('Você Clicou no link');return false;}})

O que estamos fazendo agora é ligar uma função javascript ao evento onclick da tag a com ID bung.

E como é possível ver (talver não tão fácil por que a letra do código aqui no blog é meio pequena, o parâmetro apra a função addBehavior é um objeto, esta sendo utilizada a sintaxe de objetos JSON para passar os parâmetros, por tanto, isto quer dizer que podemos fazer algo parecido com isto:

1
2
3
4
5
Event.addBehavior({
  'a#bung:click' : function () {alert('Você Clicou no link');return false;},
  'a.ajax_link' : Remote.Link({update:'qualquerid'}),
  '.mudacoreconta': LinkBehavior 
})

Ou seja, passar diversos itens a cada chamada do Event.addBehavior.
Outra coisa interessante, é que novas chamadas, vão empilhar métodos na fila se chamada, ou seja, este código:

1
2
3
Event.addBehavior({'a#bung:click' : function () {alert('Você Clicou no link');return false;}})
Event.addBehavior({'a#bung:click' : function () {alert('Você Clicou no link novamente');return false;}})
Event.addBehavior({'a#bung:click' : function () {alert('Você Clicou no link');return false;}})

Vai mostrar 3 mensagens quando o link for clicado.

Uma outra coisa interessante do lowpro é que ele pode aplicar os comportamentos em objetos que forem carregados por uma chamada ajax também, para isto precisamos de mais uma linha de código (pode ser executada no inicio da aplicação)

1
Event.addBehavior.reassignAfterAjax = true;

Isto configura o lowpro para reaplicar os eventos após cada chamada AJAX.

Claro que podemos misturar isto com outras bibliotecas Javascript, por exemplo no meu pet project de planejamento doméstico, eu tenho o seguinte código no application.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var WindowedForm = Behavior.create({
   onclick : function() {
    if (remote_window){
      remote_window.destroy();
    }
    remote_window = new UI.Window({ theme: 'leopard', width: 600, height: 300, focus : true, minimize : false });
    remote_window.setAjaxContent(this.element.href, {method: 'get'});
    remote_window.center();
    remote_window.activate();
    remote_window.show(true);
    return false;
  } 
});
remote_window = false;
 
Event.addBehavior({ 'a.action_form' : WindowedForm });

Isto faz com que todos os links que tenham a classe action_form sejam abertos em uma janela modal criada pela biblioteca “prototype-ui”.

Ahh, e para finalizar com chave de ouro, uma dica para quem usa rails e will_paginate para a paginação, o código que vou mostrar agora utiliza a lowpro para fazer com que a paginação da will_paginate passe a funcionar via AJAX :D

1
2
3
Event.addBehavior({
  'div.pagination a' : Remote.Link({update:'nomedadiv'})
})

Pronto, só isto, agora é só fazer a action correspondente retornar apenas a tabela e não a página toda quando for uma requisição AJAX, e colocar uma div com ID “nomedadiv” na volta da tabela :D

E por último mas não menos importante, não esqueça de alterar o código no servidor para retornar ou Javascript para ser executado, ou a parte do HTML desejado, mas sem as tags html,head,body, …

O lowpro facilita muito o trabalho no client, mas as respostas as chamadas AJAX continuam necessitando ser implementadas adequadamente.

Bom, espero que este post ajude alguem :D
Quaisquer dúvidas podem perguntar por aqui mesmo :D

Se você gostou deste post, lembre-se de assinar o RSS feed do blog, para ser notificado de novos posts!

Novo “pet project” em Rails (planejamento)

A idéia básica do projeto é um sistema simples para orçamento doméstico onde seja possível cadastrar as receitas previstas, os débitos previstos, realizar estas previsões e logo depois ter relatórios de previsão de saldo no final do mes, final do ano, e coisas do gênero :D

Bom, eu sei que existe o Fincanças on Rails do Maximiliano Guzenski, mas ele não funcionava exatamente como eu queria, e eu fiquei com preguiça de alterar ele.
Sim, seria mais vantajoso para a comunidade Open Source se eu colaborasse com ele em vez de criar outro, mas eu achei mais divertido pegar a planilha excel que a minha esposa usa para controlar as finanças de casa e transformar em um sisteminha :D
E com isto naceu o projeto “planejamento”, claro o nome tinha que ser algo completamente sem criatividade :D
Por enquanto a única coisa que esta pronta é o cadastro de créditos e débitos previstos e a realização destas previsões.
O projeto é todo feito com javascript não instrusivo e funciona se não houver suporte para javascript também.
Ainda não escrevi os testes, e a parte de relatórios que é a parte mais importante do sistema não esta pronta …

Estou utilizando alguns plugins bem legais do rails:

  • mydry - um plugin que eu criei que se assemelha ao scaffold antigo, mas gera models ja com algum código de relacionamento e validações que podem ser inferidas a partir do banco de dados, e controllers restfull para estes models, ele gera o código utilizando o will_paginate para paginação, eu criei este plugin por que eu achava muito chato criar as migrations, definir ali os campos not null, definir os campos de relacionamento, definir tamanhos máximos para strings e assemelhados e depois ter que definir tudo novamente no model. Por enquanto este plugin só existe dentro deste projeto, mas se mais alguem se interessar em usar ele eu crio um projeto separado :D
  • annotate_models - um plugin show de bola que coloca o schema da tabela como comentários nos arquivos dos models, facilita bastante a minha vida
  • will_paginate - o melhor plugin para paginação no RoR que eu conheço :D
  • calendar_date_select - um plugin que adiciona um helper para criar um input com um icone de calendário e um calendário popup para seleção de datas, bem mais bonito do que o padrão do rails :D
  • client_side_validation - achei este plugin espetacular, ele cria javascript para validação client side a partir das validações definidas nos models
  • validation_reflection - dependencia do client_side_validation

E estou utilizando também duas bibliotecas Javascript espetaculares:

  • lowpro - extensão da prototype que adiciona um suporte extremamente prático e fácil de usar para javascript não instrusivo (vou escrever um post sobre isto assim que eu me animar :D )
  • prototype-ui - outra extensão da prototype que adiciona alguns widgets, por enquanto apenas janelas popup e carrousel, estou utilizando apenas as janelas popup, de forma totalmente não intrusiva para os links de formulários, utilizando esta biblioteca em combinação com a lowpro

O próximo passo é escrever testes, adicionar os relatórios, adicionar suporte a tags e disponibilizar uma versão online para vocês brincarem :D

Estou utilizando o GIT para controle de versões, e quem quiser dar uma olhada no código fonte, ele esta hospedado no gitorious.

Se ninguem mais se interessar pelo projeto, ele vai evoluir pelo menos até atender todas as minhas necessidades, mas todos são bem vindos a dar sugestões e se alguem quiser colaborar eu posso dar direito de escrita no projeto no gitorious, ou então posso buscar os patches que forem criados a partir de qualquer repositório git que vocês estiverem usando :D.

Para baixar o código fonte é só rodar o comando:
git clone git@gitorious.org:planejamento/mainline.git se você ja tiver uma conta no gitorious
ou
git clone git://gitorious.org/planejamento/mainline.git se você não tiver uma conta por la :D

Se você gostou deste post, lembre-se de assinar o RSS feed do blog, para ser notificado de novos posts!

Escrevendo plugins para o Rails - dicas e truques - plugins com generators

Seguindo a seqüencia que eu iniciei aqui, mas alterando a ordem proposta, Vamos conversar um pouco sobre os Geradores de código do Rails.

Generators são uma das coisas mais legais do Rails!
Sim, você ja viu eles, lembra do primeiro screencast sobre rails que você viu? Sim, aquele mesmo em que um cara cria um CRUD em 1 minuto …

Generators são a “forma rails” de gerar código[bb] :D

Mas o código gerado não precisa ser apenas aquele CRUD, ele pode ser o que você quiser, e o Rails ja tem um suporte excelente para isto, pois este mesmo suporte ja é utilizado na base do Rails.

Beleza, mas por que eu iria querer escrever um gerador de código?
Pense sobre o projeto que você estaria começando hoje, lembre de todos aqueles formulários para CRUD que você precisara criar, ou aquela parte quase igual de todas as páginas da aplicação …
Agora pense em todas as aplicações da empresa que você trabalho …
Entendeu agora?

Sim, eu concordo que utilizar o “scaffold” padrão não é realmente útil, mas se esta geração de código inicial for feita seguindo os padrões da sua empresa, ela pode aumentar muito a velocidade de desenvolvimento inicial das aplicações …

Agora eu imagino que você esteja pensando: Legal, generators são show de bola, mas para de falar besteira e me mostre o código!

Então, vamos brincar um pouco[bb]
Vamos iniciar criando um novo projeto rails: rails plugins102
agora de dentro do diretório do projeto execute: script\generate plugin my_generator
(Se você esta pensando que você ja viu isto antes, provavelmente você leu o post anterior sobre escrita de plugins para o rails :D )

Agora que você a criou a estrutura para um novo plugin, vamos escrever algum código!
Para criar um novo generator, você precisa criar um diretório de nome “generators” dentro do diretório do plugin, um diretório com o nome do seu generator dentro deste, e um diretório de nome “templates” dentro do diretório do seu generator, por exemplo a minha estrutura ficou assim:

  • my_generator
    • lib
    • tasks
    • test
    • generators
      • test_gen
        • templates

Na verdade, você não precisa de um plugin para criar um generator, você pode colocar o seu novo generator dentro de qualquer um dos seguintes diretórios:

  • RAILS_ROOT/lib/generators
  • RAILS_ROOT/vendor/generators
  • RAILS_ROOT/vendor/plugins/plugin_name/generators
  • USER_HOME/.rails/generators
  • gems com nome terminado em _generator

Mas eu acho que um plugin é a orma mais fácil de começar, e vai ser mais fácil de utilizar nas suas aplicações também, mas claro que se você for utilizar isto em diversas aplicações um GEM seria melhor pois você teria apenas uma cópia do código para todo o servidor, mas como eu nunca criei um GEM vou continuar com o plugin pelo menos para este exemplo …

para o código do generator nos vamos criar um arquivo no diretório do generator de nome [nome_do_generator]_generator.rb, no meu caso o nome do arquivo ficou: my_generator/generators/test_gen/test_gen_generator.rb

Agora dentro deste arquivo precisamos criar a classe que define o generator, esta classe deve ter o nome de acordo com o nome do arquivo (seguindo os padrões do ruby), no meu caso o nome da classe precisa ser TestGenGenerator, e ela precisa extender uma das classes padrão para generators do rails (na verdade eu poderia não extender nada e implementar um monte de métodos, mas assim é bem mais fácil :D ), estas classes são:
Rails::Generator::Base ou Rails::Generator::NamedBase, eu vou utilizar NamedBase (A base para o controller generator do Rails), e vou criar um modelo de generator para uma migration apenas para mostrar como isto funciona …

NamedBase é uma ótima base para geradores que esperam parametros no formato: Nome [parametro1] [parametro2] …
Para todos os outros Base é uma melhor pedida …

O nosso código inicial é este:

1
2
3
4
5
6
7
class TestGenGenerator < Rails::Generator::NamedBase
  def manifest
    record do |m|
 
    end
  end
end

Nesta classe precisamos configurar o manifesto do generator e configurar quaisquer variáveis locais que utilizemos nos templates.

Mas com o código que nos temos, você ja pode executar: ruby script\generate test_gen asdas_dasda asd:ash (letras aleatórias como parâmetros por hora).
NamedBase ja vai configurar algumas variáveis para nós quando executarmos este comando:

  • class_name -> AsdasDasda
  • class_nesting ->
  • class_nesting_depth -> 0
  • class_path ->
  • file_path -> asdas_dasda
  • name -> asdas_dasda
  • plural_name -> asdas_dasdas
  • singular_name -> asdas_dasda
  • table_name -> asdas_dasdas
  • attributes -> #<Rails::Generator::GeneratedAttribute:0×3716418>
  • args -> asd:ash

Claro que por enquanto não estamos gerando absolutamente nada, por que o manifesto do gerador esta em branco, então vamos criar um exemplo simples para ver como isto realmente funciona …
Vamos criar um diretório de nome “dummy” dentro do diretório “template” e um arquivo em branco de nome “log.log” dentro deste, e vamos fazer as seguintes alterações no manifesto:

1
2
3
4
5
  def manifest
    record do |m|
      m.file 'dummy/log.log', "log/#{file_path}.log"
    end
  end

Este código vai dizer para o gerador copiar o nosso arquivo em branco para $APP_ROOT/log/asdas_dasda.log se executarmos o gerador com os mesmos parâmetros que antes …
Mas apenas copiar arquivos de um lugar para outro não é uma coisa muito divertida, então vamos brincar um pouco com o ERB, e vamos criar uma migration para o nosso plugin, para isto vamos alterar novamente o manifesto como no seguinte exemplo:

1
2
3
4
5
6
7
8
  def manifest
    @migration_name = "Create#{class_name}"
    @migration_action = "add"
    record do |m|
      m.file 'dummy/log.log', "log/#{file_path}.log"
      m.migration_template 'lib/mymigration.rb',"db/migrate", :migration_file_name => "create_#{file_path}"
    end
  end

Como você pode ver no código, estou informando ao gerador que eu tenho um arquivo de nome mymigration.rb dentro do diretório templates/lib, este template vai ser processado pelo ERB e o código gerado vai ser colocado no diretório “db/migrate”e vai ser chamado XXX_create_asdas_dasda.rb (XXX vai ser o número da migration gerado automaticamente pelo rails)

O conteúdo do arquivo mymigration.rb é o seguinte:

1
2
3
4
5
6
7
8
9
10
11
class <%= migration_name.underscore.camelize %> < ActiveRecord::Migration
  def self.up<% attributes.each do |attribute| %>
    <%= migration_action %>_column :<%= table_name %>, :<%= attribute.name %><% if migration_action == 'add' %>, :<%= attribute.type %><% end -%>
  <%- end %>
  end
 
  def self.down<% attributes.reverse.each do |attribute| %>
    <%= migration_action == 'add' ? 'remove' : 'add' %>_column :<%= table_name %>, :<%= attribute.name %><% if migration_action == 'remove' %>, :<%= attribute.type %><% end -%>
  <%- end %>
  end
end

este é um template ERB que vai gerar código ruby.

Agora que ja temos um plugin razoavelmente útil , vamos ver o que mais é possível fazer …
para responder esta pergunta, vou copiar parte da documentação da classe Rails::Generator::Commands::Create, ou seja, todas as possibilidades de comandos para o manifesto do gerador:

  • class_collisions - Check whether the given class names are already taken by Ruby or Rails. In the future, expand to check other namespaces such as the rest of the user‘s app.
  • directory - Create a directory including any missing parent directories. Always directories which exist.
  • file - Copy a file from source to destination with collision checking.
  • identical? - Checks if the source and the destination file are identical. If passed a block then the source file is a template that needs to first be evaluated before being compared to the destination.
  • migration_template - When creating a migration, it knows to find the first available file in db/migrate and use the migration.rb template.
  • readme - Display a README.
  • route_resources - add a route to the routes.rb
  • template - Generate a file for a Rails application using an ERuby template. Looks up and evaluates a template by name and writes the result.

Acho que era isto, você pode fazer perguntas nos comentários se quiser, vou tentar responder todas (se é que alguem vai perguntar :D )

Para mais documentação sobre generators do rails siga os seguintes links:
http://wiki.rubyonrails.org/rails/pages/UnderstandingGenerators
http://www.aidanf.net/node/33
http://api.rubyonrails.org/classes/Rails/Generator/Base.html
http://api.rubyonrails.org/classes/Rails/Generator/NamedBase.html
http://api.rubyonrails.org/classes/Rails/Generator/Commands/Create.html

Espero que este passo a passo ajude alguem!
O próximo vai ser sobre testar o seu plugin! e o quarto eu ainda não pensei sobre o que vai ser, sugestões são bem vindas :D

Se você gostou deste post, lembre-se de assinar o RSS feed do blog, para ser notificado de novos posts!

É nois na fita e os preiboy no DVD :D

Legal, falaram do meu blog no Rails Podcast Brasil 2,
ainda não ouvi, mas ja copiei para o celular para ouvir no caminho pra casa hoje :D

Se você gostou deste post, lembre-se de assinar o RSS feed do blog, para ser notificado de novos posts!

Escrevendo plugins para o Rails - dicas e truques - plugins com view helpers

Uma das coisas mais legais no Rails[bb] é o suporte a Plugins …
O Ruby On Rails em si já é um excelente framework, mas a combinação dos Plugins do Rails com as classes abertas do Ruby é uma combinação explosiva!
Esta combinação é o que permite a criação de “tags” customizadas para utilizar nas suas aplicações RoR nas views e nos layouts.
E é realmente fácil de criar estes “view helpers” no Rails.

A forma padrão (sem plugins) de se criar estas “tags” é simplesmente criar um método em uma das classes Helper (as que ficam em app/helpers), por exemplo, se todos os forms na sua aplicação ficam dentro de tabelas, com uma coluna para o label e uma para o campo real, você pode criar um método helper para diminuir bastante a quantidade de código a ser digitada seguindo estes passos:

  1. rails plugins101
  2. cd plugins101
  3. ./script/generate scaffold example name:string url:string

O formulário padrão, bastante simples e feio, para o model “Example” que criamos, nesta aplicação seria algo parecido com:

1
2
3
4
5
6
7
<% form_for(@example) do |f| %>
  <table>
    <tr><td><label for="example_name">Name</label></td><td><%= f.text_field :name %></td></tr>
    <tr><td><label for="example_name">Url</label></td><td><%= f.text_field :url %></td></tr>
    <tr><td colspan="2"><%= f.submit "Update" %></td></tr>
  </table>
<% end %>

mas se editarmos o arquivo app/helpers/application_helper.rb e adicionarmos o seguinte método:

1
2
3
   def textfield label, object, property, options = {}
    %Q{<tr><td><label for="#{object.to_s}_#{property.to_s}">#{label}</label></td><td>#{text_field object, property, options}</td></tr>}
  end

o código da view ficaria muito mais simples como podemos ver abaixo.

1
2
3
4
5
6
7
<% form_for(@example) do |f| %>
  <table>
    <%= textfield "Name", :example, :name %>
    <%= textfield "Url", :example, :url %>
    <tr><td colspan="2"><%= f.submit "Update" %></td></tr>
  </table>
<% end %>

Se você pensar apenas neste pequeno formulário, pode parecer muita complicação para pouca coisa, mas pensando na aplicação inteira isto poupa bastante trabalho :D

Agora pensando um pouco maior, imagine que este padrão de layout (bem feio por sinal) que eu defini para esta aplicação, seja o padrão de todas as aplicações de toda a empresa!

e todos os desenvolvedores estão trabalhando da primeira forma que eu descrevi, codificando o HTML direto em todas as páginas.
Um belo dia, um designer novo, contratado pelo dono da empresa, diz que em todos os TR de todos os forms, é necessário adicionar uma classe CSS.
Nesta situação, o negócio é sentar e chorar …

Mas se você escolheu trabalhar da segunda forma, utilizando um helper, no máximo você precisara alterar uma linha de código por aplicação!
Você pode se considerar um herói! Certo?

Mas considere a opção de você ser ainda mais esperto do que isto! Que tal criar um plugin que vai conter estas tags para facilitar o trabalho, e utilizar este plugin em todas as aplicações da empresa?
Desta forma você precisaria alterar apenas uma linha de código, testar apenas uma vez, e todas as aplicações da empresa ja estariam corrigidas!

E como sempre, com o Rails, esta é uma tarefa muito fácil de ser completada :D
Apenas siga estes passos simples:

  1. ./script/generate plugin life_saver
  2. Edite o arquivo vendor/plugins/life_saver/lib/life_saver.rb e adicione o seguinte código nele:
    1
    2
    3
    4
    5
    6
    
    # LifeSaver
    module LifeSaver
      def textfield label, object, property, options = {}
        %Q{<tr><td><label for=#{object.to_s}_#{property.to_s}>#{label}</label></td><td>#{text_field object, property, options}</td></tr>}
      end
    end
  3. Edite o arquivo vendor/plugins/life_saver/init.rb e inclua o seguinte código:
    1
    2
    
    # Include hook code here
    ActionView::Base.send :include, LifeSaver
  4. Remova o método helper que adicionamos no arquivo app/helpers/application_helper.rb

Ok, você acabou de criar o seu primeiro plugin para o Rails[bb]!
E sim, o código é exatamente o mesmo utilizado no application_helper.rb, o único truque esta no init.rb, aquela linha de código, inclui todos os métodos do module “LifeSaver” na classe base de todas as views do Rails, a ActionView::Base.

Agora, se você escolheu esta terceira opção, va falar com o seu chefe, conte uma historia parecida com a que eu contei aqui, e peça um aumento, por que você merece, você acabou de poupar diversas horas de trabalho de umas 3 pessoas pelo menos :D

Espero que este pequeno passo a passo ajude alguem :D

Este é o primeiro post de uma série sobre escrita de plugins para RoR, o próximo vai ser sobre testes unitários para o código dos seus plugins, o terceiro vai falar de plugins com generators, e o quarto vocês vão ter que voltar aqui para descobrir sobre o que vai ser :D

Se você gostou deste post, lembre-se de assinar o RSS feed do blog, para ser notificado de novos posts!

Rails For Kids - Ajax com Rails, interatividade e usabilidade ao alcance de todos

Sábado passado ocorreu um evento beneficente virtual chamado Rails for Kids, o pouco que pude participar do evento estava muito bom mesmo …
Este post é apenas para disponibilizar, como prometi durante a minha palestra, o código fonte e os slides do exemplo feito durante o evento …

A proposta da palestra era a seguinte:

  • Criar passo a passo um CRUD para uma tabela utilizando Ruby on Rails
  • Utilizando apenas uma página
  • Sem refresh de página inteira
  • Avisando a todos os usuários simultaneamente das edições
  • Sem esrever uma linha de Javascript!

E todos os objetivos foram cumpridos, passo a passo durante a palestra :D

Seguem os arquivos …
Código fonte da aplicação
Slides da palestra

Espero que façam bom proveito :D
Qualquer duvida é só perguntar por aqui mesmo …

Se você gostou deste post, lembre-se de assinar o RSS feed do blog, para ser notificado de novos posts!

Mais dois cursos abertos da Tech Office

A Tech Office esta com mais dois cursos abertos, um agora em dezembro e um em janeiro.

Spring Framework 2.0 | 4ª. edição

Atendendo a pedidos, a Tech Office It promove no dia 15 de dezembro, em Porto Alegre, a quarta edição do curso express Spring Framework 2.0, voltado a programadores Java. Esta edição, servirá como introdução a uma abordagem mais detalhada de como utilizar este conjunto de componentes para facilitar o desenvolvimento de aplicações JavaEE. Durante o curso, será desenvolvida uma livraria virtual que poderá ser utilizada posteriormente como exemplo ou como base para o desenvolvimento de uma nova aplicação. Com carga horária de 8 horas/aula, o treinamento será 100% prático e ministrado em laboratório pelo consultor Mauro do Valle.

Ruby On Rails | 2ª. Turma

E para entrar o ano de 2008 com todo o gás, a Tech Office It promove de 14 a 18 de janeiro, a segunda turma do curso Ruby on Rails com 20 horas/aula presenciais, voltado a desenvolvedores Ruby que desejam conhecer mais sobre Rails e desenvolvedores de outras linguagens que tenham interesse em ingressar no RoR. O curso objetiva auxiliar o aluno a entender o Ruby on Rails e possibilitar a criação de aplicações o utilizando, além de expor suas vantagens e limitações. Como todos os cursos presenciais da Tech Office It, com alunos desenvolverão exemplos que servirão como base para aplicações futuras.

Para informações e inscrições sobre os cursos, acesse www.techoffice.com.br ou envie um e-mail para cursos@techoffice.com.br, além do telefone 51.3012.8241.

Mas se vocês quiserem se inscrever para o curso de Spring Framework é bom correr, por que o curso vai ser em uma semana :D

Se você gostou deste post, lembre-se de assinar o RSS feed do blog, para ser notificado de novos posts!

Ruby On Rails 2.0!

Acaba de ser oficialmente liberado o Ruby On Rails 2.0.
Se você executar exatamente agora: gem install -y rails
O Rails 2.0 sera instalado e você ja pode começar a brincar com os novos recursos.

Se bem que não são tantos novos recursos assim :D

  • O suporte a named routes para rest melhorou
  • A principio REST é o modelo sugerido de desenvolvimento agora.
  • Foi adicionado suporte a multiplas engines de renderização (antes era apenas o ERB)
  • Melhora no gerenciamento de excessões
  • As sessions agora são por padrão armazenadas em cookies (o que reforça a boa pratica de não colocar muitos dados na sessão do usuário)
  • Boa parte do sexy_migrations foi integrado ao core do rails

Bom, tem melhoras em todas as areas :D

Deem uma lida no post oficial do DHH e comecem a brincar :D

Se você gostou deste post, lembre-se de assinar o RSS feed do blog, para ser notificado de novos posts!

Um exemplo de chat com Ruby On Rails e Juggernaut (utilizando AJAX Push)

English Version here
Antes de iniciar a implementação do chat, nos precisamos instalar as dependências do juggernaut, ele precisa das gems json e eventmachine instaladas, para instalar ambas basta executar o seguinte comando:

  • gem install -y json eventmachine

Agora estamos prontos para começar a brincar …
Primeiro, vamos criar uma aplicação rails e instalar o plugin juggernaut com os seguitnes comandos:

  • rails -d sqlite3 chattest
  • cd chattest
  • script/plugin install svn://rubyforge.org//var/svn/juggernaut/trunk/juggernaut

O Juggernaut usa um servidor externo implementando Flash XML Push, e precisamos configurar este servidor, então vamos editar o arquivo: config/juggernaut.yml
Coloquei aqui no blog apenas as configurações que precisei alterar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
PUSH_PORT: 8080
...
DEFAULT_CHANNELS: 
- "chat"
...
PUSH_HELPER_HOST: "localhost"
...
SECRET: "481516232342edededededed"
...
LOGIN_GET_URL: "http://localhost:3000/session/login"
LOGOUT_GET_URL: "http://localhost:3000/session/logout"
...
SESSION_ID: "_chattest_session_id"
...
BASE64: true

Precisei alterar PUSH_PORT Por que no linux apenas o root pode ouvir em portas inferiores a 1024, e mesmo assim eu não acho que a porta 443 padrão seja uma boa escolha, ja que esta é a porta padrão do protocolo HTTPS.
A linhe com PUSH_HELPER_HOST precisa ser alterada para o mesmo nome do servidor que for utilizado para a acessar a aplicação, em desenvolvimento, localhost deve resolver, mas não esqueça de alterar esta configuração quando for fazer o deploy do seu chat.
LOGIN_GET_URL e LOGOUT_GET_URL são utilizadas para notificar a aplicação quando um usuário conecta ou desconecta, apenas o desconectar é realmente importante para nós.
A SESSION_ID precisa ser igual ao nome do cookie utilizado como marcador de sessão da aplicação, no rails 1.2.x fica no application controller, no rails 2.0 esta configuração vai para o enviroment.rb
e BASE64 é a configuração mágica que habilita o uso dos helpers do rails para geração do Javascript que vai ser utilizado.

Agora vamos começar a criar o layout da aplicação.
Crie um arquivo de nome pubic/stylesheets/public.css com o seguinte conteúdo:

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
body {
  background-color: white;
}
#users {
  float: left;
  width: 200px;
  height: 400px;
  border-style: inset;
  overflow: auto;
  color: white;
  background-color: gray;
}
#dasd {
  height: 400px;
  margin-left: 5px;
  border-style: inset;
  overflow: auto;
  color: white;
  background-color: gray;
}
#controls {
  clear: both;
  padding: 0 0 0 0;
  height: 55px;
  vertical-align: top;
  border-style: inset;
  overflow: auto;
  color: white;
  background-color: gray;
}

e o arquivo: app/views/layouts/application.rhtml com o seguinte conteúdo.

1
2
3
4
5
6
7
8
9
10
<html>
  <head>
    <title>Chat Test</title>
    <%= stylesheet_link_tag 'public' %>
    <%= javascript_include_tag :defaults %>
  </head>
  <body>
    <%= yield %>
  </body>
</html>

Com o layout pronto (sim, esta bem feio, mas eu não sou designer eu sou um desenvolvedor, então solicite ao designer da sua equipe a criação de um novo layout para este chat :D ) vamos criar o código fonte necessário e o banco de dados com estes 4 comandos:

  • script/generate model online_user username:string session_id:string last_seen:date online:boolean
  • script/generate controller session
  • script/generate controller chat index
  • rake db:migrate

Layout pronto, arquivos criados, agora só precisamos escrever o código da aplicação :D
Vamos começar editando o model OnlineUser (app/model/online_user.rb) para adicionar algumas validações, elas não são realmente necessárias, são resquícios da primeira tentativa de implementação do chat, mas mesmo assim mantive elas no código :D

1
2
3
4
class OnlineUser < ActiveRecord::Base
	validates_presence_of :username, :session_id, :last_seen
	validates_uniqueness_of :username, :if => Proc.new {|user| user.online }
end

Agora a view principal da aplicação no arquivo:app/views/chat/index.rhtml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- Register with Juggernault -->
<%= listen_to_juggernaut_channels [:generic],session.session_id %>
<!-- The Users List -->
<div id="users">
  <ul id="users_list"></ul>
</div>
<!-- The messages pane -->
<div id="dasd"></div>
<!-- The controls pane (login and send messages) -->
<div id="controls"><%= render :partial => 'login' %></div>
<!-- An util javascript to scroll the messages window -->
<script type="text/javascript">
  function scrollMessages(){
    $('dasd').scrollTop = $('dasd').scrollHeight;
  }
</script>

Como podem ver é bem simples, apenas 3 DIVs, um javascript para fazer o scroll da tela de mensagens e um comando para inicializar o juggernaut no canal “generic” e utilizando o session_id como identificador individual do usuário.
A DIV de mensagens esta com o id dasd por que eu estava testando algumas possibilidades de conflito e esqueci de mudar o ID depois :D

Como pode ser visto na página index, precisaremos de um partial de nome “login” e vamos também precisar de um de nome “controls”, então vamos começar codificando o partial “controls”: app/views/chat/_controls.rhtml

1
2
3
4
5
6
<% form_remote_tag(
      :url => { :action => :say },
      :complete => "$('message').value = '';$('message').focus();" ) do %>
      <%= text_field_tag( 'message', '', { :size => 90, :id => 'message'} ) %>
      <%= submit_tag "Send" %>
  <% end %>

Bastante simples, possui apenas uma tag remote_form_tag que fara um post para a action “say”, um campo de texto para a mensagem e um botão de submit, quando o formulário é enviado o foco volta para o campo message para que o usuário possa digitar a próxima mensagem …

E o partial de login: app/views/chat/_login.rhtml

1
2
3
4
5
6
7
<%= "#{@message}<br/>" if @message %><% form_remote_tag(
      :url => { :action => :login },
      :complete => "$('username').value = ''",
      :after => "$('login').disabled = true" ) do %>