Topic: How do I create a single helper to work on different models?

I am probably going to need to refactor in two steps since I'm still developing the project and learning the use-cases as I go along since it is to scratch my own itch.  I have three models:  Letters, Calls, Emails.  They have some similarilty, but I anticipate they also will have some different attributes as you can tell from their description.

Ideally I could refactor them as Events, with a type as Letters, Calls, Emails, but didn't know how to extend subclasses.

My immediate need is this:  I have a helper which checks on the status of whether an email (for example) was sent to a specific contact:

   def show_email_status(contact, email)

    @contact_email = ContactEmail.find(:first, :conditions => {:contact_id => contact.id, :email_id => email.id })
    if ! @contact_email.nil?
      return @contact_email.status
    end
  end

I realized that I, of course, want to know the status for whether a call was made to a contact as well, so I wrote:

  def show_call_status(contact, call)

    @contact_call = ContactCall.find(:first, :conditions => {:contact_id => contact.id, :call_id => call.id })
    if ! @contact_call.nil?
      return @contact_call.status
    end
  end

I would love to be able to just have a single helper show_status where I can say show_status(contact,call) or show_status(contact,email) and it would know whether to look for the object @contact_call or @contact_email.

Yes, it would be easier if it were just @contact_event, but I want to do a small refactoring while I get the program up and running, and this would make the ability to do a history for a given contact much easier.

Thanks!

Re: How do I create a single helper to work on different models?

I would use an if statement and compare the .class attribute/method.

def show_status(contact, contact_type)

    @contact = nil
    if contact_type.class == Email
      @contact = ContactEmail.find(:first, :conditions => {:contact_id => contact.id, :email_id => contact_type.id })
    elsif contact_type.class == Call
      @contact = ContactCall.find(:first, :conditions => {:contact_id => contact.id, :call_id => conctact_type.id })
    end
    unless @contact.nil?
      return @contact.status
    end
  end

Last edited by agm_ultimatex (2010-05-01 22:25:04)

Re: How do I create a single helper to work on different models?

While agm_ulimatex's code does exactly what you asked, I'm not sure if the logic you're applying should really go in a helper. Also, are you using associations, because it looks likes you ought to be from the description of your problem.

If Contact


has_many :emails

And Email

belongs_to :contact

You can simply do in your view

<%= @email.status if @email.contact == @contact %>

Or, for readability, I might be tempted to move the logic further into the model with a simple method like this:

Class Email < ActiveRecord::Base
  belongs_to :contact

  def was_sent_to?(contact)
     self.contact == contact
  end
end


giving you:

<%= @email.status if @email.was_sent_to? @contact %>

To answer the question about how to make a common method for Emails, Calls, Letters, you just use inheritance, which is really simple in Ruby.

When you see:


Class Email < ActiveRecord::Base

That means that Email is inheriting all the methods and attributes from the ActiveRecord::Base class. But you can make another step in the chain. For example you can make a new model like this:

Class ContactEvent < ActiveRecord::Base
  def was_sent_to?(contact)
     self.contact == contact
  end
end

Then change:

Class Email < ActiveRecord::Base

To

Class Email < ContactEvent

Doing the same for Letters and Calls. Then all 3 will inherit the methods from ContactEvent, which in turn inherits from ActiveRecord::Base. So all 3 will have access to all the normal ActiveRecord methods, as well as the was_sent_to? method.

I hope that all makes sense,

Alex