Desenvolvimento

Screencast – Buscas e Rails primeira parte

Olá a todos. Já estava na hora de lançar este screencast (a quase um mês venho prometendo).

Efetuar buscas em modelos é algo que com ActiveRecord se tornou super fácil mas relacionar objetos e buscar em vários locais é um problema para quem está começando. Tentarei em três episódios mostrar como fazer buscas em seus conteúdos gravados no banco de dados de maneira simples.

O primeiro episódio será baseado em consultas no banco de dados, o que é uma solução um tanto quanto antiga, mas resolve para pequenas aplicações sem grande complexidade. A idéia em si pode ser utilizada de outras maneiras por isto achei interessante expor a mesma.

Código-fonte com aplicação pronta

Código-fonte com aplicação em fase inicial

Resumo em texto:

Criar model de buscas

rails g model Busca texto:string objeto_id:integer objeto_classe:string
rake db:migrate

Configurar observers

# config/application.rb

config.active_record.observers = :buscas_observer

Callbacks dos observers

# app/observers/buscas_observer.rb

class BuscasObserver < ActiveRecord::Observer
  observe :post, :foto
  def after_create(objeto)
    criar_busca(objeto)
  end

  def after_update(objeto)
    if Busca.where(hash_comum(objeto)).exists?
      busca = Busca.where(hash_comum(objeto)).first
      busca.update_attribute(:texto, unir_texto(objeto))
    else
      criar_busca(objeto)
    end
  end

  def after_destroy(objeto)
    Busca.where(hash_comum(objeto)).delete
  end

  def hash_comum(objeto)
    {:objeto_id => objeto.id, :objeto_classe => objeto.class.to_s}
  end

  def unir_texto(objeto)
    objeto.attributes.find_all{|t,v| v.is_a?(String)}.collect{|t,v| v}.join(' ')
  end

  def criar_busca(objeto)
    Busca.create!(hash_comum(objeto).merge(:texto => unir_texto(objeto)))
  end
end

Gerar novamente o índice para models existentes

Foto.all.each{|t| t.update_attribute(:updated_at, Time.now)}
Post.all.each{|t| t.update_attribute(:updated_at, Time.now)}

Criar o método de cast

# app/models/busca.rb

default_scope :order => "created_at DESC"
def cast
  Kernel.const_get(objeto_classe).find(objeto_id)
end

Ajustar controller de busca

# app/controllers/buscas_controller.rb

def index
  busca = params[:busca].to_s
  @resultados = Busca.where("texto LIKE ?","%#{busca}%")
end

Ajustar index de buscas

# app/views/buscas/index.html.erb

<% @resultados.each do |resultado|%> <% objeto = resultado.cast%>

<%=objeto.titulo%>

<%=truncate objeto.texto, :length => 200%> <%=link_to 'ver', objeto%> <% end %>