Paginação AJAX com Grails e lowpro :D


Bom, apresentei hoje um tutorial sobre Grails[bb] (os slides são os mesmos utilizados para a apresentação na FACENSA no ano passado sobre Grails também), o Exemplo de código pronto eu vou colocar aqui durante a semana pois vou mudar o servidor do blog e ja copiei todos os arquivos para o server novo :D

Mas o último exemplo que eu quis fazer não funcionou, era um exemplo de como fazer Paginação via AJAX no grails utilizando Javascript não com o lowpro, parecido com o que eu comentei no post sobre lowpro para o rails.

Cheguei em casa e fui tentar descobrir por que a coisa não funcionou, e foi mais fácil do que eu esperava.
Não funcionou por que eu esqueci que os templates padrão do Grails não utilizam IDs nas DIVs, por tanto o AjaxUpdater criado não funcionava …

Para fazer o exemplo funcionar é só fazer o seguinte:
copiar o lowpro.js para a pasta web-app/js
editar o template padrão (grails-app/views/layouts/main.gsp) e fazer ele ficar parecido com o seguinte:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<html>
    <head>
        <title><g:layoutTitle default="Grails" /></title>
        <link rel="stylesheet" href="${createLinkTo(dir:'css',file:'main.css')}" />
        <link rel="shortcut icon" href="${createLinkTo(dir:'images',file:'favicon.ico')}" type="image/x-icon" />
        <g:layoutHead />
        <g:javascript library="prototype/prototype" />
        <g:javascript library="lowpro" />
        <g:javascript library="application" />
    </head>
    <body>
        <div id="spinner" class="spinner" style="display:none;">
            <img src="${createLinkTo(dir:'images',file:'spinner.gif')}" alt="Spinner" />
        </div>	
        <div class="logo"><img src="${createLinkTo(dir:'images',file:'grails_logo.jpg')}" alt="Grails" /></div>	
	<div id="content">
        <g:layoutBody />
	<div>
    </body>	
</html>

Basicamente as alterações são:
Nas linhas 7 e 8 onde adicionei as bibliotecas lowpro e prototype[bb]
e mais importante, nas linhas 16 e 18 onde coloquei uma DIV com ID “content” em volta do conteúdo da página (isto vocês podem ajustar para envolver apenas o conteúdo que vocês quiserem).

Depois editando o application.js (web-app/js/application.js)

1
2
3
4
Event.addBehavior.reassignAfterAjax = true;
Event.addBehavior({
  'a.step':Remote.Link({update:'content'})
})

Apenas adicionei estas linhas no final do arquivo, a primeira configura o lowpro para reaplicar os comportamentos no final das chamadas AJAX, e as outras adicionam o comportamento de link AJAX em todas as tags “A” com a classe “step” que é o que o grails utiliza na paginaçao.

Agora a parte chata, no rails ele ja entende isto automaticamente, mas no grails eu tive que fazer na unha mesmo (nem é tanto trabalho assim :D )
Vamos editar o metodo “list” do controller de clientes (ou qualquer controller do grails que vocês quiserem :D )

1
2
3
4
5
6
       if(!params.max) params.max = 10
	if(request.getHeader("X-Requested-With")=="XMLHttpRequest"){
		render template:'listcontent', model:[clienteList: Cliente.list( params )]
	}else{
	        return [ clienteList: Cliente.list( params ) ]
	}

A diferença básica é que se o cabeçalho X-Requested-With disser que é uma requisição AJAX eu vou retornar apenas a parte da página que me interessa (OK, isto eu teria que fazer no rails também, mas o rails iria me retornar toda a página se eu não fizesse isto, o grails não retorna nada ) :D
e para isto funcionar precisamos editar a view também …
eu copiei todo o conteúdo da tag body da view “list.gsp” para o arquivo “_listcontent.gsp” (precisei criar o arquivo), e na list.gsp eu coloquei no body apenas a linha: ${render(template:’listcontent’)}

Ou seja, substitui todo o conteúdo do body por esta linha, assim posso renderizar tudo em um request padrão, e apenas o conteúdo da página em um request AJAX.

Pronto, agora a paginação do Grails esta funcionando via AJAX :D

esta semana eu atualizo este post com o link para o zip do exemplo completo, mas apenas com isto vocês ja devem conseguir fazer a paginação via AJAX :D

PS.: não esqueçam para utilizar todos os recursos do Grails é interessante saber bem groovy[bb] (a linguagem utilizada), spring[bb], GORM (hibernate[bb]) e sitemesh que são os frameworks utilizados por traz dos panos.

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!

Tutorial de Grails do RSJUG - Neste sábado

Anuncio :D

Tutorial Desenvolvimento rápido de aplicações com Grails

Durante o tutorial será desenvolvida uma aplicação completa utilizando
o framework Grails[bb], contendo não apenas um CRUD como pode ser
encontrado na internet com facilidade, mas implementando lógica de
negócio no lugar correto, utilizando plugins do grails para facilitar
o trabalho, escrevendo testes para a aplicação, e o melhor de tudo,
qualquer participante com um notebook podera acompanhar o tutorial
passo a passo e aproveitar muito mais a oportunidade de descobrir como
desenvolver uma aplicação pode ser rápido e divertido.

Depois da aplicação básica desenvolvida, serão adicionados recursos
AJAX utilizando apenas Javascript não intrusivo, garantindo assim que
a aplicação tera recursos modernos para os browsers que suportarem
isto, e continuara funcionando para os usuários que não possuem
javascript habilitado, e tudo isto em apenas uma manha!

Data: 23/02/2008
Horário: 8:00
Custo: R$15 público em geral e R$10 associados SUCESU ou SEPRORGS
Local: Auditório do SEPRORGS.
Rua Felipe Camarão 690/404 - Bom Fim - Porto Alegre-RS

Inscrições : www.rsjug.org/eventos

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

Resolvi tentar me divorciar da samanta

Não é isto que vocês estão pensando, o nome da minha esposa é Aline e estamos muito bem obrigado :D
A samanta do titulo é essa manta de gordura que tenho na barriga :D


E como a bastante tempo o único exercicio que eu fazia era levantamento de copo, resolvi tentar uma luta que é divertido e queima bastante calorias :D
Eu e a aline fizemos hoje a primeira aula de Boxe :D
Vamos ver no que da, se eu conseguir mandar parte da samanta embora ja ta valendo :D

PS.: nem só de computador vivem os nerds :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!