Измените метод поиска с параметрами на ассоциацию
Как мне превратить это в ассоциацию has_one?
(Возможно has_one + именованная область для размера.)
class User < ActiveRecord::Base
has_many :assets, :foreign_key => 'creator_id'
def avatar_asset size = :thumb
# The LIKE is because it might be a .jpg, .png, or .gif.
# More efficient methods that can handle that are OK. ;)
self.assets.find :first, :conditions =>
["thumbnail = '#{size}' and filename LIKE ?", self.login + "_#{size}.%"]
end
end
РЕДАКТИРОВАТЬ: Cuing из AnalogHole на Freenode #rubyonrails, мы можем сделать это:
has_many :assets, :foreign_key => 'creator_id' do
def avatar size = :thumb
find :first, :conditions => ["thumbnail = ? and filename LIKE ?",
size.to_s, proxy_owner.login + "_#{size}.%"]
end
end
... что довольно круто и, по крайней мере, делает синтаксис немного лучше.
Тем не менее, это все еще не ведет себя так, как я хотел бы. В частности, он не учитывает дальнейшее хорошее построение цепочки поиска (такое, что он не выполняет эту находку, пока не получит все свои условия).
Что еще более важно, он не позволяет использовать в: include. В идеале я хочу сделать что-то вроде этого:
PostsController
def show
post = Post.get_cache(params[:id]) {
Post.find(params[:id],
:include => {:comments => {:users => {:avatar_asset => :thumb}} }
...
end
... так что я могу кэшировать активы вместе с постом. Или вообще кешировать их - например, get_cache(user_id){User.find(user_id, :include => :avatar_assets)}
будет хорошим первым проходом.
Это на самом деле не работает (self == Пользователь), но правильно по духу:
has_many :avatar_assets, :foreign_key => 'creator_id',
:class_name => 'Asset', :conditions => ["filename LIKE ?", self.login + "_%"]
(Также размещено на Refactor My Code.)
1 ответ
Так как на самом деле есть несколько avatar_assets
(по одному для каждого размера), вы должны сохранить его как has_many
ассоциация.
class User < AR::B
has_many :avatar_assets, :conditions => ['filename like ?' '%avatar%'], :class_name => 'Asset'
named_scope :avatar_size, lambda { |size|
{ :conditions => [ "thumbnail = ?", size ] }
}
end
Альтернативой было бы поместить всю работу в именованную область:
class User < AR::B
named_scope :avatar_for, lambda { |user, options|
if options[:size]
{ :conditions => [ "filename like ? AND thumbnail = ?", user.login, options[:size] ] }
else
{ :conditions => [ "filename like ?", user.login ] }
end
}
end
это позволяет вам сказать
Asset.avatar_for(current_user, :size => :medium)
но менее круто, когда вы говорите
current_user.avatar_for( current_user, :size => :medium )
Вы могли бы добавить немного :avatar
, :avatar?
и т. д. методы для пользователя, чтобы очистить это.
Лично я советую вам проверить плагин Paperclip и полностью избежать этих проблем.
РЕДАКТИРОВАТЬ:
В соответствии с вашим комментарием, чтобы создать условие типа "показывать мне комментарии пользователей, имеющих аватар", я не уверен, что это будет сделано. Вы могли бы сделать отношения так:
class Comment
named_scope :with_avatars, :include => { :user => :avatar_assets }, :conditions => [ 'assets.thumbnail = ?', :thumb ]
end
РЕДАКТИРОВАТЬ:
Поскольку вас интересует только кэширование, а не условия, мы можем удалить массив условий:
named_scope :with_avatars, :include => { :user => :avatar_assets }
Я пересмотрел код выше, чтобы быть более работоспособным. Главное отличие состоит в том, чтобы сделать "аватарность" активов легко запрашиваемой. Если вы можете обновить свои существующие значения avatar_assets, добавив в них имя файла, включая шаблон "avatar-[login]", вы можете сделать набор условий статическим, что намного чище, чем постоянный поиск аватара на основе имени пользователя. Расширения ассоциации - это еще один способ решения этой проблемы, однако я не думаю, что вы сможете их связать или объединить с именованными областями.