everyone: I am also open to just straight-up refactoring what I'm finding to be pretty repetitive, but to give a baseline of how it's working....
I have for every contact a Campaign, which has_many of three types of Models: Email, Call, and Letter.
When an Email (Call or Letter) has been executed for a specific contact, I have a Contact_Email(_or_Call_or_Letter) which belongs to both the Contact and the Model (Email_or_Call_or_Letter).
Each Contact_Email for example pairing has a :date_sent attribute. So does each Contact_Call and Contact_Letter.
How do I find the latest of all of them?
Here is the code I wrote that can find the latest Email and my finding retyping similar code for Call and Letter, but then stuck on how to do a .max on all of them:
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 DES开发者_StackOverflow社区C")
# 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
Question 1: With what I have, how can I find effectively @last_event given I can find the last Email, last Call, and last Letter for every contact?
Question 2: How can I remove the repetitive code that I have to write for each Model?
Do you have has_many
associations setup in Contact
referring to the other models? Something like:
class Contact < ActiveRecord::Base
has_many :contact_emails
has_many :contact_calls
has_many :contact_letters
end
If so, you can then create a last_event
method on the Contact
model:
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
Handling nil
When using the latest_event
method you will get nil
if there are no associated records. There are a couple of ways you can workaround this. The first is to check for nil first with something like:
contact.latest_event && contact.latest_event.date_sent
On late versions of Rails/Ruby you can also use Object#try
which will call the method if it exists:
contact.latest_event.try(:date_sent)
I prefer not to use this as it doesn't check for nil
but only if the object can respond to a method. This has cause some interesting errors if you expect nil
if the object is nil
but are calling a method which nil
itself responds to.
Finally, my preferred method for the simple case is to use the andand
gem which provides Object#andand
. This greatly shortens the safe case above and saves calling of latest_event
multiple times:
contact.latest_event.andand.date_sent
date_sent
, nil
and You.
For your example usage of calling to_s(:long)
, you could either use &&
or andand
:
contact.latest_event.andand.date_sent.andand.to_s(:long)
or
contact.latest_event && contact.latest_event.date_sent.to_s(:long)
The first is safer if date_sent
itself may be nil
. Without using andand
this could be written as:
contact.latest_event &&
contact.latest_event.date_sent &&
contact.latest_event.date_sent.to_s(:long)
which is rather complex and unwieldily in my opinion. I would recommend looking into andand
For question 1:
Just do
@last_event = [@last_letter, @last_email, @last_call].sort_by{|m| m.date_sent}.first
For question 2:
Well this is more interesting. This kind of depends on how exactly do your models look, but you might want to consider Single Table Inheritance for this type of scenario.
精彩评论