2012-06-05 15 views
5

Estoy usando Ruby on Rails 3.2.2 y me gustaría recuperar/objetos asociados al alcance "especificando"/"filtrando" un valor de atributo en esos objetos asociados. Es decir, en este momento estoy usando el siguiente código:¿Cómo SECAR los métodos de alcance utilizados en dos clases diferentes?

class Article < ActiveRecord::Base 
    def self.search_by_title(search) 
    where('articles.title LIKE ?', "%#{search}%") 
    end 
end 

class ArticleAssociation < ActiveRecord::Base 
    def self.search_by_article_title(search) 
    joins(:article).where('articles.title LIKE ?', "%#{search}%") 
    end 
end 

En el código anterior la cláusula where('articles.title LIKE ?', "%#{search}%") se repite dos veces y así que pensé que puede ser mejorada con el principio DRY: es posible utilizar elArticle.search_by_titlemétodo directamente en elArticleAssociation.search_by_article_titlemétodo?


casos de uso típicos son:

  • ArticleAssociation.search_by_article_title("Sample string")
  • Article.search_by_title("Sample string")
+0

Me enfrenté a esta misma situación en un proyecto (4 modelos relacionados) y creé un módulo para contener los métodos de búsqueda comunes. No es exactamente lo que pides, pero es una solución típica. – tokland

+0

El código compartido entre clases generalmente termina en un módulo – apneadiving

+0

por cierto, los tamices de squeel se pueden usar para esto, pero usar squeel en lugar de AR es un gran cambio. Https://github.com/ernie/squeel#sifters – tokland

Respuesta

2

a menos que cambie la estructura del código completo, no.

Podrías hacer algo de piratería con lambdas, pero eso sería más código que el código que estás SECANDO. Existe una buena refacturación, y una mala refactorización. A menos que se use una pieza de muy código complejo o largo en 2 o más lugares, entonces puede preocuparse por la refactorización. Las convenciones de los códigos son importantes, pero para los pequeños llamados de un solo método es un desperdicio y probablemente hará que su código sea más críptico.

Aunque, sé que es molesto cuando la gente no responder a su pregunta, por lo que aquí:

class Article < ActiveRecord::Base 
    SEARCH_BY_TITLE=lambda {|obj, search| obj.where('articles.title LIKE ?', "%#{search}%")} 
    def self.search_by_title(search) 
    SEARCH_BY_TITLE.call(self, search) 
    end 
end 

class ArticleAssociation < ActiveRecord::Base 
    def self.search_by_article_title(search) 
    Article::SEARCH_BY_TITLE.call(joins(:article),search) 
    end 
end 

Eso sólo hace que una lambda como una constante que realiza la llamada where en un objeto especificado. Ambos métodos simplemente envuelven esa lambda.

Nota: Aunque esto se puede considerar más elegante, disminuirá mucho el rendimiento, ya que las lambdas, los cierres y la llamada adicional son caros en un lenguaje dinámico como Ruby. Pero no creo que sea un problema para ti.

+0

@ Linux_iOS.rb.cpp.c.lisp.n - Gracias por su respuesta. Sin embargo, tengo * muchos lugares donde necesito usar el código * 'where ('articles.title LIKE?',"% # {Search}% ")' e, incluso si dijiste "por un método pequeño" -llamar cosas así es un desperdicio y probablemente hará que tu código sea más críptico ", * ¿es malo repetir ese código en todas partes de mis clases? * – Backo

+1

@Backo: Si usas mucho el código, la lambda probablemente sea una mejor idea. Si lo usa solo dos veces, como en el ejemplo, es mejor que lo repita. – Linuxios

1

Según la petición OP, he puesto el código que escribí para una búsqueda 3-módulo con un módulo:

module Listable 
    extend ActiveSupport::Concern 

    module ClassMethods 
    # Search a listable module search in properties (or related) tables 
    def search_from_properties(string) 
     return where({}) if string.blank? 
     associations = self.reflect_on_all_associations.map(&:name) & 
     [:property, :properties, :supplier, :suppliers, :address] 
     s = "%#{string}%" 
     associations.inject(self, :includes).where(
     ((Address[:base] =~ s) | (Address[:city] =~ s)) | 
     ((Property[:owner] =~ s) | (Property[:cif] =~ s)) | 
     ((Supplier[:cups] =~ s) | (Supplier[:contract] =~ s)) 
    ) 
    end 
    end 
end 

Ahora basta con incluir este módulo en las clases relacionadas:

class Property < ActiveRecord::Base 
    include Listable 
end 

Nota: Todos los modelos tienen asociaciones definidas para alcanzar a los demás (es por eso que el trabajo joins). Además, usa this wrapper over AR.

Cuestiones relacionadas