Usando OpenStruct em formulários de busca no Rails

Fazer formulários que não tenha um Model diretamente associado sempre foi um problema no Rails. Isso se deve ao “Convention over configuration” e não vejo grandes problemas nisso, pois 90% dos formulários são para criação/edição de dados no Banco de Dados e, por conseqüência, utilizam um Model.

Mas e os outros 10%?

Vi artigo no blog da Plataforma sobre como resolver esse problema, porém o código parou de funcionar com o Rails 3. Para descobrir o por que disso, fui investigar no código do Rails (ahh esse mundo OpenSource) e descobri que quando você inclui por exemplo um text_field é retornado um novo objeto da classe InstanceTag e executa o método to_input_field_tag.

Neste método to_input_field_tag, que está o problema. Passou-se a verificar se na classe que está sendo chamado o form existe o método do text_field que queremos e nesse caso não existe, gerando um erro.

Para contornar isso é nessesário fazer um Monkey Patch na classe OpenStruct modificando o método respond_to?:

class OpenStruct
  def respond_to?(symbol, include_private = false)
    !ActiveRecord::Base.instance_methods.include? symbol.to_s
  end
end

Assim, ele responderá a qualquer coisa menos ao aos métodos de instância que o ActiveRecord, evitando que nos passemos totalmente por um model, o que causaria outros problemas.

Além disso modifiquei o nome do helper, já que não utilizava isso apenas para buscas, e primeiro é verificado se uma variável de instância com o nome do objeto existe (@contacts por exemplo) e caso não exista é buscado da variável params:

require 'ostruct'
module OpenFormHelper
  def open_form_for(object_name, options={}, &block)
    options[:html] = {:method => :get}.update(options[:html] || {})
    options[:as] = object_name
    object = OpenStruct.new(instance_variable_get("@#{object_name}") || params[object_name])
    form_for(object, options, &block)
  end
end

Fiz a modificação da variável pois em algumas buscas queria que algumas opções viessem marcadas por padrão, ou seja teria que adicioná-las a uma variável e escrever no params não é recomendável.

Lendo o código do Rails

Uma dica final que eu gostaria de deixar é que quando problemas como esse ocorrem é a melhor hora para investigar o código do Rails e tentar descobrir o que está acontecendo, ficando muito mais fácil entendermos como o Framework funciona.