Как мне найти .max значения атрибута среди группы разных моделей? - PullRequest
0 голосов
/ 16 мая 2010

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

У меня есть для каждого контакта кампания, в которой есть_модели трех типов: электронная почта, вызов и письмо.

Когда электронное письмо (звонок или письмо) было выполнено для определенного контакта, у меня есть Contact_Email (_or_Call_or_Letter), который принадлежит как контакту, так и модели (Email_or_Call_or_Letter).

Каждое сообщение Contact_Email для примера, например, имеет атрибут: date_sent. Как и каждый Contact_Call и Contact_Letter.

Как мне найти последние из них?

Вот код, который я написал, который может найти последнюю электронную почту, и я обнаружил, что повторно набрал похожий код для Call и Letter, но затем остановился на том, как сделать .max для всех из них:

  def last_email(contact)
    #get campaign the contact belongs to
    @campaign = Campaign.find_by_id(contact.campaign_id)

    @last_email = ContactEmail.find(:last, 
                        :conditions => "contact_id = #{contact.id}",
                        :order => "date_sent DESC")

    @last_call = ContactCall.find(:last, 
                        :conditions => "contact_id = #{contact.id}",
                        :order => "date_sent DESC")

    @last_letter = ContactLetter.find(:last, 
                        :conditions => "contact_id = #{contact.id}",
                        :order => "date_sent DESC")

    # how do I get the latest of all of these to display?

    @email_template = Email.find_by_id(@last_email.email_id)

    if @last_email.nil?
      return "no email sent"
    else
      return @last_email.date_sent.to_s(:long) + link_to('email was sent', @email_template)
    end
  end

Вопрос 1: Имея то, что у меня есть, как мне эффективно найти @last_event, если я могу найти последний E-mail, последний звонок и последнее письмо для каждого контакта?

Вопрос 2: Как я могу удалить повторяющийся код, который мне нужно написать для каждой модели?

Ответы [ 2 ]

1 голос
/ 16 мая 2010

Есть ли у вас has_many настройки ассоциаций в Contact со ссылкой на другие модели? Что-то вроде:

class Contact < ActiveRecord::Base
  has_many :contact_emails
  has_many :contact_calls
  has_many :contact_letters
end

Если это так, вы можете создать метод last_event для модели Contact:

def latest_event
  [contact_emails, contact_calls, contact_letters].map do |assoc|
    assoc.first(:order => 'date_sent DESC')
  end.compact.sort_by { |e| e.date_sent }.last
end

Обработка nil

При использовании метода latest_event вы получите nil, если нет связанных записей. Есть несколько способов обойти это. Во-первых, сначала нужно проверить на ноль что-то вроде:

contact.latest_event && contact.latest_event.date_sent

В поздних версиях Rails / Ruby вы также можете использовать Object#try, который будет вызывать метод, если он существует:

contact.latest_event.try(:date_sent)

Я предпочитаю не использовать это, поскольку он не проверяет nil, но только если объект может ответить на метод. Это может привести к некоторым интересным ошибкам, если вы ожидаете nil, если объект nil, но вызывает метод, на который nil сам отвечает.

Наконец, мой предпочтительный метод для простого случая - использовать камень andand, который обеспечивает Object#andand. Это значительно сокращает указанный выше безопасный случай и несколько раз сохраняет вызовы latest_event:

contact.latest_event.andand.date_sent

date_sent, nil и Вы.

Для вашего примера использования звонка to_s(:long), вы можете использовать && или andand:

contact.latest_event.andand.date_sent.andand.to_s(:long)
* * Или тысяча сорок-девять
contact.latest_event && contact.latest_event.date_sent.to_s(:long)

Первое безопаснее, если date_sent само по себе может быть nil. Без использования andand это можно записать как:

contact.latest_event &&
  contact.latest_event.date_sent &&
  contact.latest_event.date_sent.to_s(:long)

что довольно сложно и громоздко на мой взгляд. Я бы порекомендовал посмотреть andand

0 голосов
/ 16 мая 2010

На вопрос 1:

Просто сделай

@last_event = [@last_letter, @last_email, @last_call].sort_by{|m| m.date_sent}.first

На вопрос 2:

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

...