Arquivos para posts com tag: opensource

Nos dias 13 e 14 de outubro de 2009 acontecerá em São Paulo o Rails Summit Latin America: o maior evento sobre Ruby e Rails da América Latina, criado pela Locaweb. Esta será a segunda edição do Rails Summit, e com certeza vai bombar! A primeira edição contou com mais de 550 participantes, e para esta edição espera-se um número ainda maior.

Os palestrantes do evento são nada mais que os caras mais feras do mundo Ruby e Rails. Não vou citar nomes aqui, pois a lista é grande =).

As palestras serão dos mais variados assuntos relacionados a Ruby, Rails, metaprogramação, testes, agilidade, banco de dados, entre outros. Nem todas as palestras estão definidas ainda, para dar uma conferida no que já está disponível acesse aqui e fique ligado nos updates.

Para conhecer mais sobre o evento, a programação, os palestrantes, e é claro, fazer sua inscrição, clique no banner abaixo:

Rails Summit 2009

E não se esqueça de seguir o @railssummit no twitter.

Nos vemos lá!

Recentemente foi adicionado ao Rails o método touch, disponível tanto no Rails 2.3 quanto no Edge (em direção ao 3.0). O touch atualiza o campo updated_at/updated_on do model e então salva o registro. Ele também permite a passagem de um parâmetro, que identifica outro campo data/hora para ser atualizado no lugar de updated_at/updated_on.  Touch quer dizer exatamente isto: tocar o registro, ou atualizar um determinado campo com a data/hora atual.

Utilizando o método:

class Project < ActiveRecord::Base
  validates_presence_of :name
end

# No console
project = Project.create(:name => 'Meu primeiro projeto')
#<Project id: 1, name: "Meu primeiro projeto",
#  created_at: "2009-04-21 05:35:22", updated_at: "2009-04-21 05:35:22">
# Atualizando o campo updated_at no momento que for necessário
project.touch
# true
project
#<Project id: 1, name: "Meu primeiro projeto",
#  created_at: "2009-04-21 05:35:22", updated_at: "2009-04-21 05:40:49">

Preste atenção ao campo updated_at e verifique que ele foi atualizado com sucesso. Podemos também passar o campo que desejamos “tocar”:

project.touch(:created_at)
# true
project
#<Project id: 1, name: "Meu primeiro projeto",
#  created_at: "2009-04-21 05:42:31", updated_at: "2009-04-21 05:42:31">

Veja que agora foi o campo created_at que foi atualizado (nesse caso o updated_at também foi atualizado por ser uma funcionalidade interna do Rails).

Agora vem o mais interessante: essa funcionalidade foi também adicionada a associação belongs_to, onde é possível passar a opção :touch => true para atualizar o campo updated_at/updated_on do model master automaticamente, ou então passar um símbolo, como :touch => :campo_a_atualizar, para “tocar” este campo específico ao invés do padrão.

Vejamos como fica:

class Project < ActiveRecord::Base
  has_many :tasks
  validates_presence_of :name
end

class Task < ActiveRecord::Base
  belongs_to :project, :touch => true
end

# No console
project = Project.first
#<Project id: 1, name: "Meu primeiro projeto",
#  created_at: "2009-04-21 05:42:31", updated_at: "2009-04-21 05:42:31">
# Checagem de sanidade
project.updated_at
# Tue, 21 Apr 2009 05:42:31 UTC +00:00
# Criando uma tarefa
project.tasks.create(:name => 'Primeira tarefa')
#<Task id: 1, name: "Primeira tarefa", project_id: 1,
#  created_at: "2009-04-21 05:49:41", updated_at: "2009-04-21 05:49:41">
project.reload.updated_at
# Tue, 21 Apr 2009 05:49:41 UTC +00:00

Novamente atenção ao campo updated_at do model Project: atualizado =).

É isso, uma mudança simples que facilita bastante o dia-a-dia.

Lembrando que para testar os método touch é necessário ter a última versão do rails 2.3 disponível no git.

Ontem a Comunidade Rails brasileira recebeu uma maravilhosa notícia: o lançamento oficial dos Rails Guides na versão traduzida.

Não vou me extender falando sobre o projeto, pois isso já foi feito pelo Cássio Marques,  o Daniel Lopes, o Fábio Akita e também no site do Ruby Inside Brasil (e provavelmente em mais alguns blogs que não tive tempo de olhar ainda…).

Parabéns a toda a equipe responsável pelo projeto.

Confira os guias aqui.

Uma nova funcionalidade adicionada ao Rails 2.3 é a possibilidade de atualizar os atributos em modelos aninhados diretamente.

Para mostrar essa funcionalidade vamos trabalhar numa aplicação de gerenciamento de contatos, permitindo cadastrar pessoas, contatos e cidades. A estrutura é bastante simples: uma pessoa possui muitos contatos e pertence a uma cidade. O código foi baseado no exemplo de gerenciamento de formulários complexos criado pelo Eloy Duran.

Durante o desenvolvimento da aplicação vou criar também alguns testes, contudo eles não serão disponibilizados aqui para não estender demais o post. Quem tiver interesse poderá conferir o código fonte no github. Já aviso a todos que não sou um dos maiores peritos em testes, estou aprendendo, portanto qualquer dica sempre será bem-vinda =).

Bom, vamos lá! Utilizando o terminal, navegue até o diretório onde deseja criar a aplicação e execute os comandos:

rails nested_attributes
cd nested_attributes

Já sabemos que vamos precisar de um modelo pessoa, cidade e contato, então vamos criá-los utilizando os generators do Rails:

script/generate model City name:string
script/generate scaffold Person name:string city:references
script/generate model Contact kind:string description:string person:references

Olhos mais atentos devem ter percebido que o modelo de pessoa foi gerado com scaffold: apenas para facilitar a nossa vida já criando o controller e as views necessárias para a manutenção.

Agora vamos criar e migrar nossa base de dados:

rake db:create
rake db:migrate

Vamos trabalhar inicialmente nos modelos, criando as validações e relacionamentos. Primeiro o modelo de cidades (app/models/city.rb) deve ter muitas pessoas, e deve validar a presença do nome.

class City < ActiveRecord::Base
  has_many :people

  validates_presence_of :name
end

Continuando, nosso modelo de pessoas (app/models/person.rb) deve requerer o nome, possuir muitos contatos e pertencer a uma cidade.

class Person < ActiveRecord::Base
  belongs_to :city
  has_many :contacts

  validates_presence_of :name
end

E por fim temos o modelo de contatos (app/models/contact.rb), que além de pertencer a uma pessoa deve validar a presença do tipo e da descrição.

class Contact < ActiveRecord::Base
  KINDS = %w(home_phone work_phone email url)
  belongs_to :person

  validates_presence_of :kind, :description
  validates_inclusion_of :kind, :in => KINDS
end

Também criamos aqui uma constante para facilitar o trabalho de gerenciar os tipos de contatos possíveis: telefone residencial, telefone comercial, email e url, respectivamente. Se você desejar mais algum tipo de contato é só adicionar nesta lista.

Neste ponto a estrutura de nossos modelos está pronta. Todas as validações e relacionamentos necessários foram criados. Podemos então partir para o que realmente nos interessa: atributos aninhados.

Atributos Aninhados: Os Modelos

Para que um modelo possa criar e salvar dados de modelos relacionados, devemos dar esta permissão a ele explicitamente através do comando accepts_nested_attributes_for. Este comando permite dois parâmetros adicionais: allow_destroy, que quando verdadeiro permitirá a exclusão de registros aninhados através de uma flag :_delete (veja mais abaixo); e reject_if, uma proc que deve validar se um registro aninhado será ou não criado. Vamos fazer isto no nosso modelo de pessoas, adicionando as seguintes linhas:

  accepts_nested_attributes_for :contacts, :allow_destroy => true,
                                :reject_if => proc { |contact| contact['description'].blank? }

  accepts_nested_attributes_for :city,
                                :reject_if => proc { |city| city['name'].blank? }

Para o modelo de contatos estamos permitindo excluir registros, e também ignoramos registros sem o campo descrição. Para a cidade a opção :allow_destroy não foi informada, pois não pretendemos permitir a exclusão de uma cidade a partir da pessoa. Também ignoramos a cidade se o nome estiver em branco.

Para fazer alguns testes com essas novas opções abra um console no terminal (script/console) e vá digitando os comandos abaixo e analisando os resultados:


# City
person = Person.new(:name => 'Carlos')
#<Person id: nil, name: "Carlos", city_id: nil, created_at: nil, updated_at: nil>
person.city_attributes = { :name => 'Rio do Sul' }
#{:name=>"Rio do Sul"}
person.city
#<City id: nil, name: "Rio do Sul", created_at: nil, updated_at: nil>
person.city.new_record?
#true
person.save
#true
person.city.new_record?
#false

# Contacts
person.contacts_attributes = { 'new_1' => {
   :kind => 'email', :description => 'teste@example.com' }}
#{"new_1"=>{:kind=>"email", :description=>"teste@example.com"}}
person.contacts_attributes = { 'new_2' => {
  :kind => 'email', :description => 'teste2@example.com' }}
#{"new_2"=>{:kind=>"email", :description=>"teste2@example.com"}}
person.contacts.size
#2
person.contacts
#[#<Contact id: nil, kind: "email", description: "teste@example.com", person_id: nil, created_at: nil, updated_at: nil>, #<Contact id: nil, kind: "email", description: "teste2@example.com", person_id: nil, created_at: nil, updated_at: nil>]
person.save
#true
person.contacts
#[#<Contact id: 5, kind: "email", description: "teste@example.com", person_id: 6, 
#    created_at: "2009-03-28 00:36:49", updated_at: "2009-03-28 00:36:49">, 
#   <Contact id: 6, kind: "email", description: "teste2@example.com", person_id: 6, 
#    created_at: "2009-03-28 00:36:49", updated_at: "2009-03-28 00:36:49">]

# Deleting a contact
contact_id = person.contacts.first.id
#5
person.contacts_attributes = { contact_id.to_s => { :id => contact_id.to_s, :_delete => true } }
#{"5"=>{:id=>"5", :_delete=>true}}
person.contacts.first.marked_for_destruction?
#true
person.save
#true
person.reload.contacts.size
#1
person.contacts
#[#<Contact id: 6, kind: "email", description: "teste2@example.com", person_id: 6, 
#    created_at: "2009-03-28 00:36:49", updated_at: "2009-03-28 00:36:49">]

Se desejar ver mais exemplos de utilização das estruturas aninhadas dê uma olhada nos testes do projeto no github.

Apenas ativar a opção accepts_nested_attributes_for no modelo nos dá uma série de funcionalidades “de graça”: o Rails agora irá salvar automaticamente os registros atribuídos através desta opção. Além disso, o modelo verifica se existe erros nos modelos filhos e carrega quaisquer mensagens de erros para ele, também para facilitar a visualização de forma amigável na view. E o melhor é que tudo acontece dentro de uma transação única, o que significa que se algum dos registros gerar um erro no momento de salvar ou de algum callback, toda a transação será cancelada.

Atributos Aninhados: As Views

Toda essa funcionalidade que o Active Record disponibiliza não seria de tanta ajuda se não tivéssemos facilitadores para gerenciar isso nos formulários. Para isso ganhamos novas funcionalidades dentro de views: tudo acontece utilizando o método fields_for, de uma maneira um pouquinho diferente de como já o utilizávamos.

A primeira modificação que precisamos fazer é criar um partial com o formulário para inclusão/edição de pessoas. Como geramos esse modelo com o scaffold, a estrutura html inicial já está pronta, então crie um partial com o nome _form.html.erb dentro de app/views/people/ com o código abaixo:

<% form_for(@person) do |f| %>
  <%= f.error_messages %>
  <p>
    <%= f.label :name %><br />
    <%= f.text_field :name %>
  </p>
  <p>
    <%= f.submit 'Submit' %>
  </p>
<% end %>

Percebam que removi a parte gerada para o campo city, pois trabalharemos nele mais tarde. Outra mudança apenas estética: o submit ganhou um caption mais “genérico”, ao invés de “Create” ou “Update” como é gerado nos templates new e edit.

Devemos agora alterar estes últimos dois para gerar nosso partial:

# new.html.erb
<h1>New person</h1>
<%= render :partial => 'form' %>
<%= link_to 'Back', people_path %>

# edit.html.erb
<h1>Editing person</h1>
<%= render :partial => 'form' %>
<%= link_to 'Show', @person %> |
<%= link_to 'Back', people_path %>

Vamos rodar nossa aplicação para vermos como está ficando. Primeiro remova o arquivo index.html que está dentro da pasta public. Depois, abra o arquivo config/routes.rb e modifique-o conforme abaixo:

ActionController::Routing::Routes.draw do |map|
  map.resources :people
  map.root :people
end

Isto configura o root de nossa aplicação para a listagem de pessoas. Agora vá para o terminal e rode o servidor (script/server), depois acesse no navegador http://localhost:3000 e deverá ver a listagem com as pessoas que cadastrou anteriormente. Acesse a tela de edição e de inclusão de nova pessoa para verificar se tudo correu bem com nosso partial.

Pronto. Com tudo funcionando podemos nos concentrar no gerenciamento de contatos para a pessoa no mesmo formulário.

Contatos

A criação de um formulário aninhado utiliza o método fields_for, sendo chamado a partir da instância do formulário que está sendo criado, ou formulário “pai”, e identificando qual o modelo aninhado que será gerado. Acho que ficou um pouco complicado de entender não é? Para simplificar vamos olhar como fica nosso partial _form.html.erb:

<% form_for(@person) do |f| %>
  ........
  <fieldset id="contacts">
    <legend>Contacts</legend>
    <% f.fields_for :contacts do |contacts_form| -%>
      <%= render :partial => 'contact', :locals => { :f => contacts_form }  %>
    <% end -%>
  </fieldset>
  <p>
    <%= f.submit 'Submit' %>
  </p>
<% end %>

Adicionamos um fieldset para agrupar os contatos, e utilizamos o método fields_for a partir da instância do form pessoa (f.fields_for), informando que estamos gerenciando os contatos (:contacts). Para cada contato da pessoa será gerado o partial _contact.html.erb, que iremos criar agora. Perceba também que estamos passando para o partial a instância do formulário atual de contato (contacts_form). Crie um partial chamado _contact.html.erb em app/views/people/ e coloque o seguinte código (lembre-se que a variável f aqui se refere a instância contacts_form):

<div class="contact">
  <p>
    <%= f.label :kind %>
    <%= f.select :kind, Contact::KINDS.map { |kind| [kind.humanize, kind] } %>
    <%= f.label :description %>
    <%= f.text_field :description %>
    <% unless f.object.new_record? -%>
      <%= f.check_box :_delete %><%= f.label :_delete, 'Delete' %>
    <% end -%>
  </p>
</div>

Criamos aqui um select com opções do tipo de contato que estamos criando, e um input para o usuário entrar com o telefone ou e-mail por exemplo, além de uma checkbox para excluir um contato se o mesmo não for um novo registro (f.object nos dá acesso ao objeto ActiveRecord do formulário que está sendo processado, no nosso caso um contato). Isso já facilita bastante a manutenção: para excluir um contato é só marcar a checkbox e enviar o formulário, se tudo correr bem o contato deixará de existir.

Para testar essa funcionalidade crie algumas pessoas e contatos no console e abra a tela de edição: você verá os contatos no mesmo formulário para alterá-los, excluí-los, etc. Para permitir a inclusão de mais contatos nessa mesma tela, altere os métodos new e edit do controller e adicione a seguinte linha para criar alguns contatos em memória, gerando assim alguns formulários de contatos:

3.times { @person.contacts.build }

Até aqui tudo bem. Contudo esbarramos num pequeno problema: a melhor maneira de utilizar essa funcionalidade é permitir que o usuário possa criar quantos contatos ele queira, sem ter que incomodar o servidor a cada momento apenas para gerar uma nova linha no formulário. Da maneira que programamos até aqui, o usuário poderá criar apenas 3 contatos por vez, ou quantos nós decidirmos que seja ideal. Para conseguir isto de forma dinâmica temos que utilizar um pouco de javascript e alguns helpers.

Primeiro temos que modificar o nosso layout people.html.erb em app/views/layouts para que carregue os javascripts padrão, e também vamos adicionar um método para poder escrever algum código javascript manualmente a partir dos templates que usam esse layout. Localize a linha que adiciona o stylesheet do scaffold e coloque o seguinte código acima dela:

  <%= javascript_include_tag :defaults %>
  <% javascript_tag do %>
    <%= yield(:javascript) %>
  <% end -%>

Vamos adicionar um link para criar novos contatos, e teremos que programar este link para gerar um novo formulário com javascript.

  <fieldset id="contacts">
    ......
    <p><%= link_to 'Add contact', '#contacts', :class => 'add' %></p>
  </fieldset>

Antes de fazermos com que o o link adicione o formulário, vamos apenas programá-lo para adicionar algum texto no local onde o mesmo deveria ser posicionado: desta forma só precisamos gerar um novo formulário e colocá-lo no lugar do texto, e tudo estará funcionando. A classe 'add' será usada para identificarmos o link e manipularmos a função click dele. O segundo parâmetro do link_to, que seria o caminho para onde o link apontaria, vai ser utilizado mais tarde para identificar o template correto a ser renderizado (por enquanto vamos renderizar apenas um texto, ok?). Abra o application.js e adicione o seguinte código nele:

var NestedAttributesJs = {
  add : function(e) {
    element = Event.findElement(e);
    element.insert( { before: 'AQUI VAI O NOSSO FORMULARIO<br/>' } )
  }
}

Event.observe(window, 'load', function(){
  $$('.add').each(function(link){
    link.observe('click', NestedAttributesJs.add);
  });
});

O que acontece aqui: após todo o DOM da página ser carregado, usamos o método $$('.add') para encontrar todos os elementos com a classe add (lembram do nosso link?), percorrendo então o array de elementos encontrados (apenas um por enquanto) com o método each e adicionando um observador para o evento click. Em resumo, quando cada elemento com essa classe for clicado, irá disparar a função NestedAttributesJs.add. Dentro desta função buscamos o elemento atual passado como parâmetro (nesse caso o nosso link que está sendo clicado) e inserimos antes dele um texto e uma quebra de linha html. É exatamente no lugar deste texto que vamos renderizar nosso formulário, mas para fazermos isto temos que ter um template do formulário já gerado em alguma variável javascript, para apenas mudar o nosso texto temporário por esta variável. Vamos criar alguns helpers para nos ajudar nesta tarefa, mas antes faça alguns testes no seu navegador clicando no link e vendo onde o texto está sendo inserido. Após, abra o PeopleHelper e adicione o seguinte código nele:

  def nested_attributes_for(form_builder, *args)
    content_for :javascript do
      content = ""
      args.each do |association|
        content << "\nvar #{association}_template='#{generate_template(form_builder, association.to_sym)}';"
      end
      content
    end
  end

  def generate_html(form_builder, method, options = {})
    options[:object] ||= form_builder.object.class.reflect_on_association(method).klass.new
    options[:partial] ||= method.to_s.singularize
    options[:form_builder_local] ||= :f

    form_builder.fields_for(method, options[:object], :child_index => 'NEW_RECORD') do |f|
      render(:partial => options[:partial], :locals => { options[:form_builder_local] => f })
    end
  end

  def generate_template(form_builder, method, options = {})
    escape_javascript generate_html(form_builder, method, options = {})
  end

São três métodos para estudarmos aqui:

  • generate_html: cria um novo objeto da classe que estamos gerando (o mesmo que Contact.new no nosso caso), e o partial para esse novo objeto utilizando o fields_for. O resultado é um partial vazio para incluir um registro aninhado. Um pouco de atenção ao parâmetro :child_index => 'NEW_RECORD': ele será bastante importante. Este parâmetro diz ao fields_for para utilizar a string NEW_RECORD como índice do registro ao gerar este formulário, ao invés de utilizar um contador, como estávamos fazendo manualmente ao criar novos contatos aninhados ('new_1', 'new_2'). Se tiver dúvidas quanto a esta parte, volte um pouco até onde falamos sobre os testes no console e verifique como estávamos criando novos contatos;
  • generate_template: dispara o método generate_html, escapando caracteres javascript (lembre-se: tudo isto irá parar em uma variável javascript);
  • nested_attributes_for: cria uma ou mais variáveis javascript, cada uma contendo o respectivo template gerado para formulário, jogando o conteúdo no cabeçalho do documento (adicionamos esta possibilidade anteriormente com yield(:javascript) no layout).

Com estes novos métodos helpers podemos gerar agora nossa variável javascript contendo o formulário vazio para a criação de novos contatos. Vale lembrar que estes métodos funcionarão posteriormente em qualquer formulário aninhado que você precise, seguindo as mesmas convenções de nomes que seguimos aqui. Adicione ao partial _form.html.erb logo abaixo do form_for:

  <% nested_attributes_for f, :contacts -%>

Ao atualizar a página agora e visualizar o código fonte, poderá ver que no cabeçalho será criada uma variável javascript chamada contacts_template que possui o html necessário para gerar o novo formulário. Aproveite para analisar também como a string NEW_RECORD foi posicionada dentro do nosso template gerado.

  <script type="text/javascript">
//<![CDATA[
var contacts_template='<div class=\"contact\">\n  <p>\n    <label for=\"person_contacts_attributes_NEW_RECORD_kind\">Kind<\/label>\n    <select id=\"person_contacts_attributes_NEW_RECORD_kind\" name=\"person[contacts_attributes][NEW_RECORD][kind]\"><option value=\"home_phone\">Home phone<\/option>\n<option value=\"work_phone\">Work phone<\/option>\n<option value=\"email\">Email<\/option>\n<option value=\"url\">Url<\/option><\/select>\n    <label for=\"person_contacts_attributes_NEW_RECORD_description\">Description<\/label>\n    <input id=\"person_contacts_attributes_NEW_RECORD_description\" name=\"person[contacts_attributes][NEW_RECORD][description]\" size=\"30\" type=\"text\" />\n\n      <\/p>\n<\/div>\n\n';
//]]>
</script>

Vamos alterar agora nosso javascript para utilizar o template. Você se lembra quando criamos o link para adicionar um novo contato, passamos o parâmetro #contacts como se fosse o caminho para o link? Vamos utilizar a propriedade href do link, que possui esse texto, remover o # e obter nosso template que se chama contacts_template. Por isso a importância da convenção =). Substitua o código do application.js pelo abaixo:

var NestedAttributesJs = {
  add : function(e) {
    element = Event.findElement(e);
    template = eval(element.href.replace(/.*#/, '') + '_template');
    element.insert( { before: NestedAttributesJs.replace_ids(template) } );
  },
  replace_ids : function(template){
    var new_id = new Date().getTime();
    return template.replace(/NEW_RECORD/g, new_id);
  }
}

Event.observe(window, 'load', function(){
  $$('.add').each(function(link){
    link.observe('click', NestedAttributesJs.add);
  });
});

O que mudou: dentro do método NestedAttributesJs.add estamos atribuindo à variável template o conteúdo da nossa variável contacts_template através do método eval. Agora um ponto importante: adicionamos um método chamado replace_ids, que substitui no nosso template todas as ocorrências de NEW_RECORD pelo número de milisegundos desde a meia-noite de primeiro de janeiro de 1970, gerados pela função Date().getTime(). A idéia é que seja sempre gerado um número novo a cada registro adicionado ao formulário. Se você verificar o código gerado através da extensão Web Developer do Firefox, verá que ao invés de person[contacts_attributes][NEW_RECORD] temos algo como person[contacts_attributes][1238198708416] nos campos do novo formulário. Desta forma o Rails saberá identificar que este é um novo registro. Caso isto não seja feito, se você criar 3 contatos no seu formulário e enviá-lo para o servidor, o Rails receberá três contatos com NEW_RECORD e salvará apenas um deles.

Bom, neste ponto já devemos ter nossa estrutura funcionando para adicionar e remover contatos aninhados. Lembre-se que estamos ignorando contatos sem descrição, portanto você pode criar 10 contatos no formulário sem preencher sua descrição, que ao postar o Rails ignorará esses contatos.

Uma outra funcionalidade interessante seria a possibilidade de excluir um contato adicionado, mas não temos como fazer isto através de checkboxes pois o formulário está sendo criado dinamicante. Vamos criar um link para remover o html gerado para o novo registro. Para facilitar vamos mover o código que cria ou um link ou uma checkbox para remoção do registro aninhado, se o registro for ou não novo, permitindo assim reutilizar este código em outros templates posteriormente. Adicione ao PeopleHelper:

  def remove_link_unless_new_record(form_builder)
    unless form_builder.object.new_record?
      form_builder.check_box(:_delete) + form_builder.label(:_delete, 'Delete')
    else
      link_to_function('Delete', "$(this).up('.#{form_builder.object.class.name.underscore}').remove();")
    end
  end

E altere o partial _contact.html.erb conforme abaixo:

    # remova estas linhas
    #<% unless f.object.new_record? -%>
    #  <%= f.check_box :_delete %><%= f.label :_delete, 'Delete' %>
    #<% end -%>

    # e adicione esta no lugar
    <%= remove_link_unless_new_record(f) %>

Isto já deve ser suficiente para utilizarmos este cadastro incluindo e excluindo contatos dinamicamente. Podemos partir para a seleção de uma cidade para a pessoa.

Cidade

Quando trabalhamos com este tipo de relação entre pessoa e cidade, normalmente criamos um select com as cidades disponíveis para que o usuário selecione qual a cidade que a pessoa pertence. Muitas vezes gostaríamos de permitir que uma cidade possa ser criada junto com a pessoa, caso ela não exista na lista. E o Rails agora permite fazer isto de maneira simples utilizando atributos aninhados. Para que isto funcione precisamos apenas fazer uma alteração na view, adicionando abaixo do campo nome:

  <p>
    <%= f.label :city_id %>
    <%= f.collection_select :city_id, City.all(:order => 'name'), :id, :name %>
    or
    <% f.fields_for :city, City.new do |city_form| -%>
      <%= city_form.label :name, 'Create a new' %><%= city_form.text_field :name %>
    <% end -%>
  </p>

O código acima cria um select com as cidades disponíveis ordenadas por nome (ok eu sei, a chamada ao City.all(:order => 'name') não deveria estar aqui, mas estou apenas simplificando pois o post já está bastante extenso), e também um input para a criação de uma nova cidade. Desta forma, o usuário tem a possibilidade de ou selecionar uma cidade pelo select ou entrar com o nome de uma nova cidade.

Para terminar

Ufa! Foi um pouco extensivo mas é isso aí. Após um pouco de trabalho temos uma manutenção totalmente funcional que trabalha com atributos aninhados.

O projeto que criamos aqui está disponível no github.

Espero ter ajudado a quem estava com dúvidas sobre a utilização desta nova funcionalidade.

Qualquer dúvida, dificuldade ou crítica por favor poste um comentário que terei prazer em responder assim que possível.

Após algum tempo trabalhando na tradução do Rails 2.3 Release Notes para o projeto de tradução dos Rails Guides, finalmente temos uma versão 100% (faltando alguma revisão, mas vamos lá).

Peço a todos que lerem as notas de lançamento, se encontrarem alguma sentença mal formada, erro de português ou mesmo erro na tradução, por favor postem um comentário informando o problema que terei prazer em analisar e corrigir o que for necessário tanto aqui no post quanto na tradução oficial.

Espero que esta tradução ajude a esclarecer as novidades que o Rails 2.3 está trazendo.

Boa leitura.

Notas de Lançamento do Ruby on Rails 2.3

O Rails 2.3 traz uma variedade de funcionalidades novas e melhoradas, incluindo integração pervasiva com Rack, atualização do suporte aos Rails Engines, transações aninhadas para o Active Record, escopos dinâmicos e padrões, roteamento mais eficiente, templates de aplicação, e backtraces silenciosos. Esta lista abrange as maiores atualizações, mas não inclui cada pequena correção de bug e alteração. Se você quiser visualizar tudo, verifique a lista de commits no repositório principal do Rails no GitHub ou revise os arquivos CHANGELOG para os componentes individuais do Rails.

Arquitetura da Aplicação

Existem duas grandes mudanças na arquitetura das aplicações Rails: integração completa da interface modular para servidores web Rack, e suporte renovado para os Rails Engines.

Integração com o Rack

O Rails agora separou-se do seu passado com CGI, e utiliza o Rack em toda parte. Isto exigiu e resultou em um tremendo número de modificações internas (mas se você usa CGI, não se preocupe; o Rails agora suporta CGI através de uma interface proxy). Ainda assim, esta é uma mudança importante para o Rails internamente. Após atualizar para o 2.3, você deve testar em seu ambiente local e em seu ambiente de produção. Algumas coisas para testar:

  • Sessões
  • Cookies
  • Upload de arquivos
  • APIs JSON/XML

Segue um resumo das mudanças relacionadas ao rack:

  • script/server foi mudado para usar Rack, o que significa que suporta qualquer servidor compatível com Rack. script/server também irá carregar um arquivo de configuração rackup se existir. Por padrão, ele irá procurar um arquivo config.ru, mas você pode sobrescrever isto com o modificador -c.
  • O gerenciador FCGI passa pelo Rack.
  • ActionController::Dispatcher mantém sua própria pilha de middleware padrão. Middlewares podem ser injetados, reordenados, e removidos. A pilha é compilada dentro da cadeia no boot. Você pode configurar a pilha de middleware no environment.rb.
  • A tarefa rake middleware foi adicionada para inspecionar a pilha de middleware. Isto é útil para depurar a ordem da pilha de middleware.
  • O executador de testes de integração foi modificado para executar as pilhas de middleware e da aplicação inteiras. Isto faz os testes de integração perfeitos para testar o middleware Rack.
  • ActionController::CGIHandler é um wrapper para compatibilidade CGI em torno do Rack. O CGIHandler destina-se a pegar um antigo objeto CGI e converter sua informação de ambiente em um formato compatível com Rack.
  • CgiRequest e CgiResponse foram removidos.
  • Sessões agora são carregadas sob demanda (lazy-load). Se você nunca acessar o objeto da sessão durante uma requisição, ele nunca tentará carregar os dados da sessão (analisar cookies, carregar os dados do memcache, ou buscar um objeto Active Record).
  • Você não precisa mais usar CGI::Cookie.new no seus testes para definir um valor para um cookie. Agora associar um valor String para request.cookies["foo"] seta o cookie como esperado.
  • CGI::Session::CookieStore foi substituído pelo ActionController::Session::CookieStore.
  • CGI::Session::MemCacheStore foi substituído pelo ActionController::Session::MemCacheStore.
  • CGI::Session::ActiveRecordStore foi substituído pelo ActiveRecord::SessionStore.
  • Você ainda pode mudar seu método de armazenamento de sessão com ActionController::Base.session_store = :active_record_store.
  • As opções padrão das sessões ainda são definidas com ActionController::Base.session = { :key => "…" }.
  • O mutex que normalmente envolve toda a requisição foi movido para um middleware, ActionController::Lock.
  • ActionController::AbstractRequest e ActionController::Request foram unificados. O novo ActionController::Request herda de Rack::Request. Isto afeta o acesso ao response.headers['type'] nas requisições de teste. Use no lugar response.content_type.
  • O middleware ActiveRecord::QueryCache é automaticamente inserido dentro da pilha de middleware se o ActiveRecord foi carregado. Este middleware monta e limpa a cache de consultas do Active Record a cada requisição.
  • As classes router e controller do Rails seguem a especificação do Rack. Você pode chamar um controller diretamente com SomeController.call(env). O router armazena os parâmetros de roteamento em rack.routing_args.
  • ActionController::Request herda de Rack::Request.
  • Ao invés de config.action_controller.session = { :session_key => 'foo', … utilize config.action_controller.session = { :key => 'foo', ….
  • Usando o middleware ParamsParser quaisquer requisições XML, JSON, ou YAML são pré-processadas, podendo então ser normalmente lidas com qualquer objeto Rack::Request após isto.
Suporte renovado para Rails Engines

Após algumas versões sem atualização, o Rails 2.3 traz algumas novas funcionalidades para Rails Engines (aplicações Rails que podem ser embutidas dentro de outras aplicações). Primeiro, arquivos de roteamento nos engines são automaticamente carregados e recarregados agora, assim como seu arquivo routes.rb (isto também se aplica a arquivos de roteamento em outros plugins). Segundo, se seu plugin possui um diretório app, então app/[models|controllers|helpers] serão automaticamente adicionados ao load path do Rails. Engines também suportam agora a adição de caminhos para views, e o Action Mailer assim como o Action View usará views dos engines e outros plugins.

Documentação

O projeto Ruby on Rails guides tem publicado vários guias adicionais para o Rails 2.3. Além disso, um site separado mantém cópias atualizadas dos Guias para o Rails Edge. Outros esforços de documentação incluem o relançamento do wiki Rails e o planejamento inicial para um livro de Rails.

Suporte para o Ruby 1.9.1

O Rails 2.3 deve passar por todos os seus próprios testes se você está rodando o Ruby 1.8 ou o recentemente liberado Ruby 1.9.1. No entanto, você deve estar ciente que mudar para o 1.9.1 acarreta checar todos os adaptadores de dados, plugins, e outros códigos que você depende para compatibilidade com o Ruby 1.9.1, assim como o core do Rails.

Active Record

O Active Record recebe um número considerável de novas funcionalidades e correções de bug no Rails 2.3. Os destaques incluem atributos aninhados (nested attributes), transações aninhadas (nested transactions), escopos dinâmicos (dynamic scopes) e padrões (default scopes), e processamento em lote (batch processing).

Atributos Aninhados

O Active Record pode agora atualizar os atributos em modelos aninhados diretamente, desde que você o diga para fazê-lo:

class Book < ActiveRecord::Base
  has_one :author
  has_many :pages

  accepts_nested_attributes_for :author, :pages
end

Ligar os atributos aninhados ativa uma séria de coisas: salvamento automático (e atômico) de um registro juntamente com seus filhos associados, conhecimento das validações nos filhos, e suporte para formulários aninhados (discutidos mais tarde).

Você também pode especificar requerimentos para qualquer novo registro que for adicionado através de atributos aninhados usando a opção :reject_if

accepts_nested_attributes_for :author,
  :reject_if => proc { |attributes| attributes['name'].blank? }
Transações Aninhadas

O Active Record suporta agora transações aninhadas, uma funcionalidade muito requisitada. Agora você pode escrever código como este:

User.transaction do
    User.create(:username => 'Admin')
    User.transaction(:requires_new => true) do
      User.create(:username => 'Regular')
      raise ActiveRecord::Rollback
    end
  end

  User.find(:all)  # => Retorna somente Admin

Transações aninhadas permitem que você cancele uma transação interna sem afetar o estado da transação externa. Se você deseja que uma transação seja aninhada, precisa adicionar explicitamente a opção :requires_new; caso contrário, a transação aninhada simplesmente será parte da transação pai (assim como é atualmente no Rails 2.2). Por baixo dos panos, transações aninhadas estão usando savepoints, portanto elas são suportadas mesmo em bancos de dados que não possuem transações aninhadas reais. Existe também um pouco de mágica acontecendo para fazer com que estas transações funcionem bem com fixtures transacionais durantes os testes.

Escopos Dinâmicos

Você conhece sobre os finders dinâmicos no Rails (que permitem que você concatene métodos como find_by_color_and_flavor dinamicamente). Bem, agora você pode ter métodos de escopo dinâmicos. A idéia é unir sintaxes que permitam filtragem dinâmica e encadeamento de métodos. Por exemplo:

Order.scoped_by_customer_id(12)
Order.scoped_by_customer_id(12).find(:all,
  :conditions => "status = 'open'")
Order.scoped_by_customer_id(12).scoped_by_status("open")

Não há nada para definir o uso de escopos dinâmicos: eles apenas funcionam.

Escopos Padrão

O Rails 2.3 introduzirá a noção de escopos padrão similares aos escopos nomeados (named scopes), mas aplicados a todos os escopos nomeados ou métodos find dentro do modelo. Por exemplo, você pode escrever default_scope :order => 'name ASC' e a qualquer hora que você recuperar registros desse modelo eles retornarão ordenados pelo nome (a menos que você sobrescreva a opção, é claro).

Processamento em Lote

Agora você pode processar um grande número de registros de um model ActiveRecord com menos pressão sobre a memória usando find_in_batches:

Customer.find_in_batches(:conditions => {:active => true}) do |customer_group|
  customer_group.each { |customer| customer.update_account_balance! }
end

Você pode passar a maioria das opções do find para o find_in_batches. Entretanto, você não pode especificar uma ordem para os registros serem retornados (eles sempre retornarão em ordem crescente pela chave primária, que precisa ser um inteiro), ou usar a opção :limit. Ao invés, utilize a opção :batch_size, que tem o padrão de 1000, para definir o número de registros que serão retornados em cada lote.

O novo método find_each proporciona um wrapper em torno do find_in_batches que retorna registros individuais, com o próprio find sendo executado em lotes (de 1000 por padrão):

Customer.find_each do |customer|
  customer.update_account_balance!
end

Note que você só deve utilizar este método para processamento em lote: para números de registros pequenos (menores que 1000), você deve usar só os métodos find regulares com seu próprio loop.

Condições Múltiplas para Callbacks

Quando se usa os callbacks do Active Record, você pode agora combinar opções :if e :unless no mesmo callback, e fornecer condições múltiplas como um array:

before_save :update_credit_rating, :if => :active,
  :unless => [:admin, :cash_only]
  • Contribuidor Líder: L. Caviola
Find com Having

O Rails agora tem uma opção :having no find (assim como nas associações has_many e has_and_belongs_to_many) para filtragem de registros em finds agrupados. Tal como aqueles com backgrounds SQL pesados sabem, isto permite filtros com base em resultados agrupados:

developers =  Developer.find(:all, :group => "salary",
  :having => "sum(salary) >  10000", :select => "salary")
Reconectando Conexões MySQL

O MySQL suporta a flag de reconectar em suas conexões – se definida como verdadeiro, então o cliente tentará reconectar ao servidor antes de desistir no caso de uma conexão perdida. Você pode agora setar reconnect = true para suas conexões MySQL no database.yml para obter este comportamento em uma aplicação Rails. O padrão é false, assim o comportamento das aplicações existentes não muda.

Outras Mundaças no Active Record
  • Um AS extra foi removido do SQL gerado pelo carregamento do has_and_belongs_to_many, melhorando o funcionamento para alguns bancos de dados.
  • ActiveRecord::Base#new_record? agora retorna false ao contrário de nil quando confrontado com um registro existente.
  • Um bug com o nome de tabelas com aspas em algumas associações has_many :through foi corrigido.
  • Você pode agora especificar um timestamp particular para o updated_at: cust = Customer.create(:name => "ABC Industries", :updated_at => 1.day.ago).
  • Melhores mensagens de erro em falhas nos métodos find_by_attribute!.
  • O suporte ao to_xml do Active Record fica um pouco mais flexível com a adição da opção :camelize.
  • Um bug no cancelamento de callbacks a partir do before_update ou before_create foi corrigido.
  • Tarefas rake para bases de teste via JDBC fora adicionadas.
  • validates_length_of usará a mensagem de erro customizada com a opção :in ou :within (se for fornecida).
  • Counts em selects com escopos (scoped selects) agora funcionam corretamente, assim você pode fazer coisas como Account.scoped(:select => "DISTINCT credit_limit").count.
  • ActiveRecord::Base#invalid? agora funciona como o oposto de ActiveRecord::Base#valid?.

Action Controller

O Action Controller lança algumas mudanças significantes à renderização, bem como melhoramentos no roteamento e em outras áreas, nesta versão.

Renderização Unificada

ActionController::Base#render está muito mais esperto na hora de decidir o que renderizar. Agora você pode apenas dizer para ele o que renderizar e esperar obter os resultados corretos. Em versões antigas do Rails, muitas vezes você precisava fornecer a informação a ser renderizada explicitamente:

render :file => '/tmp/random_file.erb'
render :template => 'other_controller/action'
render :action => 'show'

Agora no Rails 2.3, você pode apenas fornecer o que quer renderizar:

render '/tmp/random_file.erb'
render 'other_controller/action'
render 'show'
render :show

O Rails escolhe entre arquivo, template, e ação dependendo se há uma barra no início, uma barra no meio, ou nenhuma barra em todo o conteúdo que é para ser renderizado. Note que você também pode utilizar um símbolo em vez de uma string quando renderizar uma ação. Outros estilos de renderização (:inline, :text, :update, :nothing, :json, :xml, :js) ainda requerem uma opção explícita.

Application Controller Renomeado

Se você é uma das pessoas que sempre se aborreceu pelo caso especial de nomenclatura do application.rb, alegre-se! Ele foi reformulado para application_controller.rb no Rails 2.3. Além disso, há uma nova tarefa rake, rake rails:update:application_controller para fazer isto automaticamente para você – e ela rodará como parte do processo normal do rake rails:update.

Suporte a Autenticação HTTP Digest

O Rails agora possui suporte embutido a autenticação HTTP digest. Para usá-la, você chama authenticate_or_request_with_http_digest com um bloco que retorne a senha do usuário (a qual é então transformada em hash e comparada contra as credenciais transmitidas):

class PostsController < ApplicationController
  Users = {"dhh" => "secret"}
  before_filter :authenticate

  def secret
    render :text => "Password Required!"
  end

  private
  def authenticate
    realm = "Application"
    authenticate_or_request_with_http_digest(realm) do |name|
      Users[name]
    end
  end
end
Roteamento Mais Eficiente

Existe um par de mundaças significativas no roteamento do Rails 2.3. Os helpers para rotas formatted_ sumiram, em favor de passar apenas :format como uma opção. Isto reduz o processo de geração de rotas em 50% para qualquer recurso – e pode salvar uma quantia substancial de memória (até 100MB em grandes aplicações). Se o seu código utiliza os helpers formatted_, ele ainda funcionará atualmente – mas este comportamento está deprecado depreciado e sua aplicação será mais eficiente se você reescrever estas rotas usando o novo padrão. Outra granda mudança é que o Rails agora suporta vários arquivos de roteamento, não só o routes.rb. Você pode utilizar RouteSet#add_configuration_file para importar mais rotas a qualquer momento – sem limpar as rotas carregadas atualmente. Embora esta mudança é mais útil para os Engines, você pode utilizá-la em qualquer aplicação que precise carregar rotas em lote.

Sessões com Lazy-load Baseadas no Rack

Uma grande mudança empurrou as camadas baixas de armazenamento de sessão do Action Controller para o nível do Rack. Isto envolveu uma grande quantidade de trabalho no código, apesar de que deve ser completamente transparente para suas aplicações Rails (como um bônus, alguns patches desagradáveis em torno do antigo gerenciador de sessão CGI foram removidos). Isto ainda é significativo, entretanto, por uma simples razão: aplicações Rails não baseadas no Rack têm acesso aos mesmos gerenciadores de armazenamento de sessão (e por conseqüência a mesma sessão) assim como sua aplicação Rails. Além disso, as sessões agora são carregadas sob demanda (lazy load) (em conjunto com os melhoramentos de carga do resto do framework). Isto significa que você não precisa mais desabilitar as sessões explicitamente se você não as quer; somente não se refira a elas e elas não serão carregadas.

Mundanças no Tratamento de Tipos MIME

Há um par de mudanças no código para tratamento de tipos MIME no Rails. Primeiro, agora o MIME::Type implementa o operador =~, tornando as coisas muito mais limpas quando você precisa checar pela presença de um tipo que possui sinônimos:

if content_type && Mime::JS =~ content_type
  # faça algo legal
end

Mime::JS =~ "text/javascript"        => true
Mime::JS =~ "application/javascript" => true

A outra mudança é que agora o framework utiliza o Mime::JS quando checa por javascript em vários lugares, tornando o tratamento dessas alternativas limpo.

Otimização do respond_to

Em alguns dos primeiros frutos da união entre o Rails e o Merb, o Rails 2.3 inclui algumas otimizações para o método respond_to, que é certamente bastante utilizado em muitas aplicações Rails para permitir que seu controller formate resultados diferentemente baseado no tipo MIME da requisição de entrada. Após eliminar uma chamada ao method_missing e alguns recortes e aprimoramentos, nós estamos vendo uma melhoria de 8% no número de requisições por segundo servidas por um simples respond_to que altera entre três formatos. A melhor parte? Nenhuma mudança é requerida em todo o código de sua aplicação para obter vantagem deste ganho.

Melhoria na Performance do Caching

O Rails agora mantém um cache local a cada requisição de leitura a partir dos armazenamentos remotos de cache, reduzindo leituras desnecessárias e melhorando o desempenho do site. Embora este trabalho era originalmente limitado ao MemCacheStore, ele está disponível para qualquer armazenamento remoto que implemente os métodos necessários.

Views Localizadas

O Rails agora pode fornecer views localizadas, dependendo do locale que você tiver definido. Por exemplo, suponha que você tem um controller Posts com uma ação show. Por padrão, isto irá renderizar app/views/posts/show.html.erb. Mas se você setar I18n.locale = :da, ele irá renderizar app/views/posts/show.da.html.erb. Se o template localizado não existe, a versão normal será usada. O Rails inclui também I18n#available_locales e I18n::SimpleBackend#available_locales, que retornam um array das traduções disponíveis no projeto Rails atual.

Além disso, você pode utilizar o mesmo esquema para localizar os arquivos de erro no diretório public: public/500.da.html ou public/404.en.html funcionam, por exemplo.

Escopo Parcial para Traduções

Uma mudança na API de tradução torna as coisas mais fáceis e menos repetitivas para escrever traduções de chaves dentro de partials. Se você chamar translate(".foo") a partir do template people/index.html.erb, na verdade você estará chamando I18n.translate("people.index.foo"). Se você não iniciar a chave com um ponto, então a API não usará o escopo, assim como antes.

Outras Mudanças do Action Controller
  • A manipulação de ETag foi um pouco organizada: O Rails agora pula o envio de um cabeçalho ETag quando não há corpo para a resposta ou quando enviar arquivos com send_file.
  • O fato do Rails checar por IP spoofing pode ser um incômodo para sites com tráfego pesado de telefones celulares, porque os proxies deles geralmente não ajustam as coisas de maneira correta. Se você é um desses, pode agora definir ActionController::Base.ip_spoofing_check = false para desabilitar esta checagem por completo.
  • O ActionController::Dispatcher implementa agora sua própria pilha de middleware, que você pode ver executando rake middleware.
  • Sessões em cookies possuem agora identificadores de sessão persistentes, com compatibilidade de API com o armazenamento no lado servidor.
  • As opções :only e :except para o map.resources não são mais herdadas por recursos aninhados.
  • Agora você pode utilizar símbolos para as opções :type do send_file e send_data, como por exemplo: send_file("fabulous.png", :type => :png).
  • O pacote memcached client foi atualizado para a versão 1.6.4.99.
  • Os métodos expires_in, stale?, e fresh_when aceitam agora uma opção :public para fazê-las funcionar bem com proxy caching.
  • A opção :requirements agora funciona corretamente com rotas RESTful member adicionais.
  • Rotas shallow agora respeitam namespaces de maneira correta.
  • polymorphic_url faz um trabalho melhor ao tratar objetos com nomes irregulares no plural.

Action View

O Action View no Rails 2.3 traz formulários com modelos aninhados, melhorias no render, prompts mais flexíveis para os helpers de seleção de data, e um ganho no cache de assets, dentre outras coisas.

Formulários com Objetos Aninhados

Contanto que o model pai aceite atributos aninhados para os objetos filhos (como discutido na seção do Active Record), você pode criar formulários aninhados usando form_for e field_for. Estes formulários podem ser aninhados em profundidade arbitrária, permitindo que você edite hierarquias complexas de objetos em uma única view sem código excessivo. Por exemplo, dado este model:

class Customer < ActiveRecord::Base
  has_many :orders

  accepts_nested_attributes_for :orders, :allow_destroy => true
end

Você pode escrever esta view no Rails 2.3:

<% form_for @customer do |customer_form| %>
<div>
    <%= customer_form.label :name, 'Customer Name:' %>
    <%= customer_form.text_field :name %></div>
<!-- Aqui nós chamamos fields_for na instância do construtor
   customer_form. O bloco é chamado para cada membro da coleção orders. -->
  <% customer_form.fields_for :orders do |order_form| %>
<div>
        <%= order_form.label :number, 'Order Number:' %>
        <%= order_form.text_field :number %></div>
<!-- A opção allow_destroy no model habilita a exclusão dos
   registros filhos. -->
      <% unless order_form.object.new_record? %>
<div>
          <%= order_form.label :_delete, 'Remove:' %>
          <%= order_form.check_box :_delete %></div>
<% end %>

  <% end %>

  <%= customer_form.submit %>
<% end %>
Renderização Inteligente de Partials

O método render vem se tornando mais inteligente ao longo dos anos, e ele está ainda mais inteligente agora. Se você tem um objeto ou uma coleção e um partial apropriado, e os nomes casam, agora você pode renderizar somente o objeto e as coisas irão funcionar. Por exemplo, no Rails 2.3, estas chamadas ao render funcionarão na sua view (supondo uma nomeação sensata):

# Equivalente de render :partial => 'articles/_article',
# :object => @article
render @article

# Equivalente de render :partial => 'articles/_article',
# :collection => @articles
render @articles
Prompts para Helpers de Seleção de Datas

No Rails 2.3, você pode fornecer prompts customizados para os vários helpers de seleção de data (date_select, time_select, e datetime_select), do mesmo modo que pode com helpers de seleção de coleções. Você pode fornecer uma string ou um hash de strings individuais para os vários componentes. Você também pode definir apenas o :prompt para true para usar o prompt genérico:

select_datetime(DateTime.now, :prompt => true)

select_datetime(DateTime.now, :prompt => "Choose date and time")

select_datetime(DateTime.now, :prompt =>
  {:day => 'Choose day', :month => 'Choose month',
   :year => 'Choose year', :hour => 'Choose hour',
   :minute => 'Choose minute'})
Caching de Assets com Timestamps

É provável que você conheca a prática do Rails de adicionar timestamps para caminhos estáticos de assets como um “cache buster”. Isto ajuda a garantir que cópias antigas de coisas como imagens e stylesheets não fiquem desatualizadas no cache do navegador dos usuários quando você os modifica no servidor. Agora você pode modificar este comportamento com a opção de configuração cache_asset_timestamps para o Action View. Se você ativar a cache, o Rails calculará o timestamp uma vez quando ele servir um asset a primeira vez, e salvará este valor. Isto significa menos (custosas) chamadas ao sistema de arquivos para servir assets estáticos – mas também significa que você não pode modificar nenhum dos assets enquanto o servidor está executando e esperar que as mudanças sejam obtidas pelos clientes.

Hosts de Asset como Objetos

Hosts de assets tornam-se mais flexíveis no Rails edge com a habilidade de declarar um host de assets como um objeto específico que responda a uma chamada. Isto permite que você implemente qualquer lógica complexa que precisar no seu hosting de asset.

Método Helper grouped_options_for_select

O Action View já possui um monte de helpers para auxiliar na geração de controles select, mas agora existe mais um: grouped_options_for_select. Este aceita um array ou hash de strings, e os converte em uma string de tags option dentro de tags optgroup. Por exemplo:

grouped_options_for_select([["Hats", ["Baseball Cap","Cowboy Hat"]]],
  "Cowboy Hat", "Escolha um produto...")

retorna

<option value="">Escolha um produto...</option>
<optgroup label="Hats">
  <option value="Baseball Cap">Baseball Cap</option>
  <option selected="selected" value="Cowboy Hat">Cowboy Hat</option>
</optgroup>
Tags com Opção Disabled para Helpers Select de Formulários

Os helpers select de formulários (tais como select e options_for_select) agora suportam uma opção :disabled, que pode levar um único valor ou um array de valores para disabilitar as tags resultantes:

select(:post, :category, Post::CATEGORIES, :disabled => 'private')

retorna

<select name="post[category]">
<option>story</option>
<option>joke</option>
<option>poem</option>
<option disabled="disabled">private</option>
</select>

Você também pode utilizar uma função anônima para determinar em tempo de execução quais opções das coleções serão selecionadas e/ou desabilitadas:

options_from_collection_for_select(@product.sizes, :name, :id, :disabled => lambda{|size| size.out_of_stock?})
Uma Nota sobre Carregamento de Template

O Rails 2.3 inclui a capacidade de ativar ou desativar cache de templates para qualquer ambiente específico. Cache de templates impulsionam a velocidade porque eles não checam por um novo arquivo de template quando são renderizados – mas também significam que você não pode substituir um template “on the fly” sem reiniciar o servidor.

Na maioria dos casos, você desejará o cache de templates habilitado em produção, o que pode ser feito através de uma configuração no seu arquivo production.rb:

config.action_view.cache_template_loading = true

Esta linha será gerada para você por padrão em uma nova aplicação com Rails 2.3. Se você tiver atualizado de uma versão mais antiga do Rails, ele irá por padrão fazer o cache de templates em produção e teste mas não em desenvolvimento.

Outras Mudanças do Action View
  • Geração de token para proteção CSRF foi simplificada; agora o Rails utiliza uma string aleatória simples gerada pelo ActiveSupport::SecureRandom ao invés de gastar tempo com IDs de sessão.
  • auto_link agora aplica corretamente opções (tais como :target e :class) para os links de e-mail gerados.
  • O helper autolink foi refatorado para torná-lo um pouco menos bagunçado e mais intuitivo.
  • current_page? agora funciona corretamente mesmo quando existem vários parâmetros na URL.

Active Support

O Active Support possui algumas mudanças interessantes, incluindo a introdução do Object#try.

Object#try

Muita gente adotou a idéia de usar try() para tentar executar operações em objetos. Isto é especialmente útil em views onde você pode evitar a checagem de nil escrevendo código como <%= @person.try(:name) %>. Bem, agora isto está incorporado direto no Rails. Como implementado no Rails, ele gera um NoMethodError em métodos privados e sempre retorna nil se o objeto é nil.

  • Mais Informações: try.
Backport do Object#tap

Object#tap é uma adição ao Ruby 1.9 e 1.8.7 que é similar ao método returning que o Rails tinha até então: ele retorna para um bloco, e então retorna o objeto que foi passado. Agora o Rails inclui código para disponibilizar isto também em versões mais antigas do Ruby.

Parsers Configuráveis para XMLmini

O suporte para parsing XML no ActiveSupport tornou-se mais flexível permitindo que você troque entre diferentes parsers. Por padrão, ele utiliza a implementação REXML padrão, mas você pode facilmente especificar as implementações mais rápidas LibXML ou Nokogiri para suas próprias aplicações, contanto que você possua as gems apropriadas instaladas:

XmlMini.backend = 'LibXML'
Segundos Fracionários para TimeWithZone

As classes Time e TimeWithZone incluem um método xmlschema que retorna o tempo em uma string XML amigável. A partir do Rails 2.3, TimeWithZone suporta o mesmo argumento para especificar o número de dígitos na parte fracionária do segundo para a string retornada, assim como o Time faz:

>> Time.zone.now.xmlschema(6)
=> "2009-01-16T13:00:06.13653Z"
Chaves JSON com Aspas

Se você verificar a especificação no site “json.org”, descobrirá que todas as chaves em uma estrutura JSON devem ser string, e elas devem estar dentro de aspas duplas. Começando com o Rails 2.3, nós fazemos a coisa certa aqui, mesmo com chaves numéricas.

Outras Mudanças do Active Support
  • Você pode usar Enumerable#none? para verificar se nenhum dos elementos batem com o bloco fornecido.
  • Se você está usando delegates do Active Support, a nova opção :allow_nil permite que você retorne nil em vez de gerar uma exceção quando o objeto alvo é nil.
  • Agora o ActiveSupport::OrderedHash: implementa each_key e each_value.
  • ActiveSupport::MessageEncryptor fornece uma maneira simples para encriptar informações para armazenamento em uma localização não confiável (como cookies).
  • O método from_xml do Active Support não depende mais do XmlSimple. Em vez disso, o Rails inclui agora sua própria implementação XmlMini, somente com a funcionalidade que ele precisa. Isto permite que o Rails se livre da cópia embutida do XmlSimple que ele tem carregado.
  • Se você memoizar um método privado, o resultado agora será privado.
  • String#parameterize aceita um separador opcional: "Quick Brown Fox".parameterize('_') => "quick_brown_fox".
  • number_to_phone agora aceita números de telefone com 7 dígitos.
  • ActiveSupport::Json.decode agora trata seqüências de estilo escapadas \u0000.

Railties

Além das modificações do Rack cobridas acima, Railties (o código principal do próprio Rails) ostenta uma quantidade de mudanças significante, incluinto Rails Metal, templates de aplicação, e backtraces silenciosos.

Rails Metal

Rails Metal é um novo mecanismo que proporciona endpoints super rápidos dentro de suas aplicações Rails. As classes Metal passam por cima do roteamento e do Action Controller para lhe dar velocidade pura (ao custo de todas as coisas no Action Controller, claro). Isto baseia-se em todo o trabalho recente para tornar o Rails uma aplicação Rack com uma pilha de middleware exposta. Endpoints Metal podem ser carregados a partir de sua aplicação ou de plugins.

Templates de Aplicação

O Rails 2.3 incorpora o gerador de aplicações rg do autor Jeremy McAnally. O que isto significa é que temos agora geração de aplicações com base em templates diretamente no Rails; se você tem um conjunto de plugins que inclui em cada aplicação (entre muitos outros casos de uso), pode apenas criar um template uma vez e usá-lo quantas vezes quiser quando executar o comando rails. Também há uma tarefa rake para aplicar um template em uma aplicação existente:

rake rails:template LOCATION=~/template.rb

Isto irá jogar as modificações do template em cima de qualquer código que o projeto já contenha.

Backtraces Silenciosos

Criado com base no plugin Quiet Backtrace da Thoughtbot, que permite que você remova linhas seletivamente dos backtraces do Test::Unit, o Rails 2.3 implementa no core ActiveSupport::BacktraceCleaner e Rails::BacktraceCleaner. Isto suporta ambos filtros (para realizar substituições com expressões regulares em linhas do backtrace) e silenciadores (para remover por completo linhas do backtrace). O Rails adiciona silenciadores automaticamente para livrar-se do barulho mais comum em uma nova aplicação, e cria um arquivo config/backtrace_silencers.rb para armazenar as suas próprias adições. Esta funcionalidade também ativa uma impressão melhor a partir de qualquer gem no backtrace.

Tempo de Boot Mais Rápido em Modo de Desenvolvimento com Lazy Loading/Autoload

Um pouco de trabalho foi feito para se certificar que partes do Rails (e suas dependências) somente são carregadas em memória quando são efetivamente necessárias. Agora os frameworks core – Active Support, Active Record, Action Controller, Action Mailer e Action View – estão usando autoload para carregar suas classes individuais quando necessário (lazy-load). Este trabalho deve ajudar a manter o uso de memória baixo e melhorar a performance geral do Rails.

Você também pode especificar (usando a nova opção preload_frameworks) se as bibliotecas do core devem ser carregadas de forma automática na inicialização. Esta opção tem como padrão false para que o Rails se auto-carregue peça por peça, mas existem algumas circunstâncias onde você ainda precisa carregar tudo de uma vez – ambos Passenger e JRuby precisam ter o Rails carregado por completo.

Reescrita da Tarefa rake gem

Várias tarefas rake gem foram substancialmente revisadas internamente, fazendo com que o sistema trabalhe melhor em uma variedade de casos. O sistema de gems agora sabe a diferença entre dependências de desenvolvimento e execução, tem um sistema de desempacotamento mais robusto, traz melhores informações quando procurar pelo status de gems, e é menos propenso a problemas de dependência do tipo “galinha e ovo” quando você está começando com as coisas do zero. Existem também correções para a utilização de comandos gem com JRuby e para dependências que tentam importar cópias externas de gems que já estão vendoziradas.

Outras Mudanças no Rails
  • As instruções para atualizar um servidor CI para construir o Rails foram atualizadas e ampliadas.
  • Os testes internos do Rails foram trocados do Test::Unit::TestCase para o ActiveSupport::TestCase, e o core Rails requer Mocha para os testes.
  • O arquivo padrão environment.rb foi reorganizado.
  • O script dbconsole permite que você use uma senha totalmente numérica sem falhar.
  • Agora o Rails.root retorna um objeto Pathname, o que significa que você pode usá-lo diretamente com o método join para limpar o código existente que usa File.join.
  • Vários arquivos no /public que tratam com dispatch CGI e FCGI não são mais gerados em toda aplicação Rails por padrão (você ainda pode obtê-los se precisar adicionando —with-dispatches quando executar o comando rails, ou adicioná-los mais tarde com rake rails:generate_dispatchers).
  • Rails Guides foram convertidos da marcação AsciiDoc para Textile.
  • Controllers e Views geradas com o scaffold foram um pouco limpos.
  • script/server aceita agora um argumento —path para subir uma aplicação Rails a partir de um caminho específico.
  • Se algumas gems configuradas estão faltando, as tarefas rake de gems irão pular a carga da maior parte do ambiente, Isto deve resolver muitos dos problemas do tipo “galinha e ovo” onde o rake gems:install não poderia rodar porque as gems estavam faltando.
  • As gems agora são desempacotadas exatamente uma vez. Isto corrige problemas com gems (hoe, por exemplo) que são empacotadas com permissões somente-leitura nos arquivos.

Deprecado Depreciado

Alguns pedaços de código antigo foram deprecados depreciados nesta versão:

  • Se você é um dos (bastante raros) desenvolvedores Rails que faz deploy em uma modo que depende dos scripts inspector, reaper, e spawner, precisa saber que estes scripts não estão mais inclusos no core Rails. Se você precisa deles, pode obter cópias através do plugin irs_process_scripts.
  • render_component vai de “deprecado” “depreciado” para “naoexistente” no Rails 2.3. Se você ainda precisa dele, pode instalar o plugin render_component.
  • O suporte para componentes Rails foi removido.
  • Se você é uma das pessoas que costumava executar script/performance/request para verificar a performance com base nos testes de integração, precisa aprender um novo truque: este script agora foi removido do core Rails. Existe um novo plugin request_profiler que você pode instalar para obter a mesma funcionalidade de volta.
  • ActionController::Base#session_enabled? está deprecado depreciado porque as sessões são agora carregadas sob demanda (lazy-load).
  • As opções :digest e :secret para o protect_from_forgery estão deprecadas depreciadas e não tem efeito.
  • Alguns helpers de testes de integração foram removidos. response.headers["Status"] e headers["Status"] não retornarão mais nada. O Rack não permite “Status” nos seus headers de retorno. Contudo você ainda pode usar os helpers status e status_message. response.headers["cookie"] e headers["cookie"] não retornarão nenhum cookie CGI. Você pode inspecionar headers["Set-Cookie"] para ver o header do cookie sem ser processado ou utilizar o helper cookies para obter um hash dos cookies enviados para o cliente.
  • formatted_polymorphic_url está deprecado depreciado. Utilize no lugar polymorphic_url com :format.
  • A opção :http_only no ActionController::Response#set_cookie foi renomeada para :httponly.
  • As opções :connector e :skip_last_comma do método to_sentence foram substituídas pelas opções :words_connnector, :two_words_connector, e :last_word_connector.
  • Postar um form multipart com um controle file_field vazio costumava enviar uma string vazia para o controller. Agora ele envia nil, devido a diferenças entre o parser multipart do Rack e o antigo parser do Rails.

Créditos

Notas de lançamento compiladas por Mike Gunderloy. Esta versão das notas de lançamento do Rails 2.3 foi compilada com base no RC2 do Rails 2.3.

Tradução para o português das notas de lançamento do Rails 2.3 por Carlos A. da Silva.

O Rails 2.3 trouxe uma nova funcionalidade bastante interessante para quem trabalha com uma grande quantidade de informações e precisa efetuar rotinas de processamento em todos os registros (ou em um subset deles) de uma única vez: processamento em lote.

O funcionamento é simplificado, são dois métodos que fazem todo o trabalho:

  • find_in_batches: retorna grupos de 1000 registros (por padrão) para serem processados;
  • find_each: se utiliza do find_in_batches, carregando os registros em lote da mesma maneira, porém retornando cada registro para ser processado.

Veremos como isso funciona na prática. Para os exemplos abaixo considere que temos o seguinte modelo:

class Person < ActiveRecord::Base
  named_scope :all_active, :conditions => { :active => true }

  def process_something
    # Aqui podemos criar nosso método para efetuar o processamento desejado.
  end
end

Considere também que temos em torno de 10.000 registros de pessoas no banco de dados. Mais abaixo veremos o porquê.

find_in_batches

# Processando todas as pessoas ativas no banco de dados
Person.find_in_batches(:conditions => { :active => true }) do |people_batch|
  people_batch.each { |person| person.process_something  }
end

Aqui estamos processando somente as pessoas ativas, em grupos de 1.000, conforme já falamos.

O método find_in_batches funciona da seguinte maneira: ele carrega do banco de dados todos os registros que tenham o id maior ou igual a 0 (por padrão), com um limite máximo de 1.000, ou seja, os primeiros 1.000 registros, e retorna esse lote. Após ser processado, ele busca os próximos 1.000, contando a partir do id do último registro processado anteriormente, e assim sucessivamente até não haver mais registros.

Para que o método funcione corretamente é necessário que a chave primária da tabela, no caso do padrão do Rails o campo id, seja do tipo inteiro. Isto é obrigatório pois o find_in_batches usa o valor da chave para contar os registros a cada lote. Ele também sempre ordena pela chave primária em ordem crescente, para se utilizar do índice da chave e otimizar a performance, significando que não podemos definir a ordenação. Outro parâmetro que não pode ser definido é o limite de registros, pois ele é usado internamente pelo método para controlar o tamanho dos lotes.

Todas as demais opções que são utilizadas no método find comum funcionam também para o find_in_batches, como as condições passadas no exemplo. Também é possível utilizar o método com named_scopes:

Person.all_active.find_in_batches(:batch_size => 500) do |people_batch|
  people_batch.each { |person| person.process_something  }
end

Notem também que foi passado um parâmetro diferente: batch_size. Este parâmetro identifica o número de registros por lote, e é usado internamente pelo método para montar a cláusula limit.

Ainda é possível definir uma opção para iniciar a contagem de registros: start. Essa opção define a partir de qual registro deve ser retornado, por exemplo, com o parâmetro :start => 50, o método irá retornar todos os registros a partir do id 50.

find_each

O método find_each funciona como um wrapper sobre o find_in_batches, retornando cada registro retornado pelos lotes. Vamos ver o mesmo exemplo que utilizamos anteriormente, agora com o find_each:

Person.find_each(:conditions => { :active => true }) do |person|
  person.process_something
end

Da mesma forma que o find_in_batches, este método funciona com named_scopes:

Person.all_active.find_each do |person|
  person.process_something
end

Finalizando

Os métodos para processamento em lote são bastante úteis para efetuar rotinas em grandes números de registros, contudo não devem ser utilizados quando você está trabalhando com poucos registros. O objetivo é carregar da forma mais rápida possível todos os registros, mas em grupos, para não carregar todos de uma vez em memória. Por isso quando se está trabalhando com poucos registros não há porque utilizar essa funcionalidade. Neste caso, pode-se utilizar o find comum.

Só uma nota rápida: acaba de ser liberado o que parece ser o último RC do Ruby on Rails 2.3.

Para instalar digite no console:

gem install rails --source http://gems.rubyonrails.org

O pessoal do Rails está pedindo para que todos testem o quando puderem este RC, pois pretendem lançar logo a versão final do 2.3 caso nenhum problema maior seja reportado. Procure testar o RC2 em sua máquina de desenvolvimento e também em produção para ver se está tudo ok.

O Release Notes do RC2 pode ser encontrado aqui (em inglês).

ps.: O pessoal que está traduzindo o Rails Guides está trabalhando na tradução deste Release Notes para o lançamento da versão final do 2.3. A tradução deve ficar pronta logo logo =). Dê uma olhada na página do projeto de tradução no github.

Hoje vi uma dica bastante interessante para instalar as nossas gems de uma forma bem mais rápida.

Para fazer isto é bastante simples: desligue a instalação do ri e rdoc quando instalar uma gem.

Essas duas instalações consomem boa parte do tempo de instalação de uma gem, portanto desligar essas opções, caso você não as use, vai agilizar bastante o processo. Você pode fazer isso passando as opções --no-ri --no-rdoc da seguinte maneira:

sudo gem install gem_a_ser_instalada --no-ri --no-rdoc

Para facilitar ainda mais, você pode definir essas opções como padrão para quando for instalar qualquer gem. Para isso, altere o arquivo ~/.gemrc, adicionando a seguinte linha:

gem: --no-ri --no-rdoc

Isto fará com que todas as gems sejam instaladas sem o ri e o rdoc, melhorando bastante a velocidade de instalação.

Post original por Brian Danton, desenvolvedor da FiveRuns, aqui, e comentários no Ruby Inside, por Peter Cooper, aqui.

Olá pessoal…

Depois de algum tempo sem postar, estou de volta a ativa, e com bastante novidades aguardando…

Para hoje a novidade é a nossa gem brazilian-rails, que acabou de lançar a versão 2.1.3 contendo suporte ao I18n do Rails. Basicamente, a gem possui um arquivo YAML com as traduções em pt-br, e inclui esse arquivo para ser carregado pelo load path do I18n, definindo também o default_locale como ‘pt-BR’.

A idéia é bastante simples, porém muito útil para que você não tenha que ficar configurando isto em todos os seus projetos, podendo apenas adicionar ao seu config/environment.rb a linha:

config.gem “brazilian-rails”

se desejar utilizar as demais funcionalidades do brazilian-rails, ou apenas:

config.gem “brI18n”

para utilizar esta funcionalidade do I18n.

O arquivo contém as traduções padrão do Rails, desde a parte de formatação de datas, horas e números até as mensagens de erro do ActiveRecord.

Mais informações aqui.

Ao final do dia 21 foi liberada a versão final do Rails 2.2: finalmente! Acho que muita gente (assim como eu =]) estava aguardando ansiosa essa nova versão.

Confira informações sobre o lançamento e as novidades nos links que seguem:

Weblog oficial do Rails: aqui.

Série Edge Rails do Carlos Brando: aqui.

Livro “Ruby on Rails 2.2 – O que há de novo?”: aqui.

Release Notes do Rails 2.2 traduzido Fábio Akita: aqui, ou o original (em inglês) aqui.

Como instalar:

O Rails 2.2 pode ser instalado através do RubyGems. Para isso primeiro é necessário que você tenha a versão 1.3.1 requerida, você pode obtê-la rodando o comando:

gem update --system

A partir daí você pode instalar o Rails:

gem install rails

E para atualizar uma aplicação já existente, navegue até o diretório da aplicação e execute:

rake rails:update

Seguir

Obtenha todo post novo entregue na sua caixa de entrada.