2010-11-04 2 views
1

Предположим, у вас есть объект «Автор», который имеет несколько книг, и вы хотите создать некоторые методы в модели. Ваша основная установка выглядит примерно так:Комплексные комплекты выбора, Rails?

class Author 
    def book_count(fiction = nil, genre = nil, published = nil) 
    end 
end 

Для каждого аргумента, у вас есть несколько способов, которыми Вы хотите работать:

fiction = true #retrieve all fiction books 
fiction = false #retrieve all nonfiction 
fiction = nil #retrieve books, not accounting for type 

genre = nil #retrieve books, not accounting for genre 
genre = some_num #retrieve books with a specific genre id 

published = true #retrieve all published 
published = false #retrieve all unpublished 
published = nil #retrieve books, not accounting for published 

Теперь я написал основную отборное заявление для некоторых из этого, вдоль линий:

if published == true 
    return self.books.select{ |b| b.published == true }.size 
elsif published == false 
    return self.books.select{ |b| b.published == false}.size 
else 
    return self.books.size 
end 

Когда я только имел один или два аргумента, это было громоздким, но достаточно легко. Однако, поскольку клиент запрашивает дополнительные условия для метода, становится все более утомительно писать.

Каким будет лучший способ «рельсов»?

Спасибо!

+0

Являются ли эти объекты ActiveRecord, с которыми мы работаем, или простые классы Ruby? Кроме того, какая версия Rails? –

+0

Это рельсы 2.3, и они являются объектами ActiveRecord. Причина, по которой я решил использовать select, а не find, заключается в том, что в большинстве случаев я использую этот метод, я уже вытащил больший выбор книг. –

ответ

0

Если вы готовы загружены books, то вы можете попробовать это:

def book_count(options = {}) 
    books.select{|b| options.all?{|k, v| v.nil? || b.send(key) == v} }.size 
end 

Теперь вы можете делать вызовы, такие как

author.books.book_count(:genre => "foo", :fiction => true) 

Исключить атрибуты из хэша параметров, когда вы хотите удалить атрибут из критериев фильтрации. В приведенном выше примере :published исключается из критериев фильтра, поскольку он отсутствует в хэш-параметре. Я добавил дополнительный nil чек, чтобы удовлетворить сценарии, где значение атрибута действительно нулевое.

Если список books не загружен, используйте подход named_scope, предложенный Olives.

0
if published.nil? 
    return books.size 
else 
    return books.count{ |b| b.published == published } 
end 

или

if published.nil? 
    return books.size 
else 
    return books.map(&:published).count published 
end 

или

return books.count{ |b| published.nil? || b.published == published } 

или

return published.nil? ? books.size : books.map(&:published).count(published) 

или

return published.nil? ? books.size : books.count{ |b| b.published == published } 
6

области, (или «named_scopes», если вы используете Rails < 3), вероятно, лучший способ сделать это.

Ниже приведено описание для рельсов 3, но это может быть сделано с незначительными настройками синтаксиса Вы можете создать кучу областей в своей модели. То есть

scope :with_genre, lambda {|genre| where(:genre => genre) unless genre.nil?} 
scope :published, lambda{|published| where(:published => published) unless published.nil?} 
scope :fiction,, lambda{|fiction| where(:fiction => fiction) unless fiction.nil?} 

и т.д.

Затем, когда вам нужно получить доступ к ним вы можете делать такие вещи, как

def book_count(..) 
    self.books.with_genre(genre).published(published).fiction(fiction).size 
end 

Кроме того, вы можете просто сделать параметр book_count хэш, то вы можете иметь любое количество параметры, которые вы хотите, без создания функции, имеют множество параметров.

+0

Теперь это очень аккуратно :) Это то, что я люблю о ТАК, даже когда я думаю, что знаю ответ на вопрос, у кого-то часто есть лучший способ: –

1

Во-первых, вы можете захотеть book_count взять хэш options={} и определить ключевые значения по умолчанию в самом методе. Таким образом, поскольку клиенту требуется больше опций (или решает удалить некоторые из них), вам не нужно преследовать все вызовы в вашем проекте и соответствующим образом изменять их. Я предпочитаю делать так, но вы также можете использовать *arguments.

Одним из преимуществ прохождения в качестве опции хэша является то, что вы просто не передать ключи, если значения nil, то вы можете просто найти количество книг, которые соответствуют вашим критериям поиска, следующим образом:

return self.books.find(:all, :conditions => options).count 

Это должно работать нормально и позволить добавлять дополнительные спецификации позже.Просто убедитесь, что ключи в шаге options соответствуют вашим атрибутам модели.

0

Чем больше Rails-y будет использовать встроенные методы поиска ActiveRecord, тем лучше их вывести из базы данных, а не фильтровать в Ruby. Это будет быстрее, и код будет более чистым. Метод where может принимать хеш атрибутов и значений. (см. ActiveRecord guide to querying для получения дополнительной информации, это хорошее введение)

Используете ли вы Rails 3? В этом случае ActiveRecord становится еще проще в использовании.

Нечто подобное могло бы работать (хотя я и не имеют доступа к рельсам прямо сейчас, так это, вероятно, содержит ошибки):

class Author 
    def book_count(filter) 
     Book.find_by_author(self).where(filter).count 
    end 
end 

Это должно найти все книги этого автора (если у вас есть модель ассоциация между Автором и Книгой), где указаны все условия, которые вы указали. Возможно, вам придется отфильтровать все нибы. filter будет хешем таких условий, как { :genre => 'Horror', :published => true }.

Обратите внимание, что я использую count, а не size. count использует функцию подсчета SQL, а не возвращает данные, а затем подсчитывает их в рубине.

Надеюсь, что помогает.

Смежные вопросы