views:

60

answers:

2

I have a controller with two different actions, but both need this same code, which is a little long, how can I allow them access to this same behavior but keep it DRY?

   @list = Contact.find :all,
      :select => "companies.name AS co_name, 
                  companies.id AS comp_id, 
                  COUNT(contact_emails.id) AS email_count, 
                  COUNT(contact_calls.id) AS call_count, 
                  COUNT(contact_letters.id) AS letter_count, 
                  COUNT(contact_postalcards.id) AS postalcard_count",

      :conditions => ['contact_emails.date_sent < ? and contact_emails.date_sent > ?', 
                      report_end_date, report_start_date],

      :joins => [
        "LEFT JOIN companies ON companies.id = contacts.company_id",
        "LEFT JOIN contact_emails ON contact_emails.contact_id = contacts.id",
        "LEFT JOIN contact_letters ON contact_letters.contact_id = contacts.id",
        "LEFT JOIN contact_postalcards ON contact_postalcards.contact_id = contacts.id",
        "LEFT JOIN contact_calls ON contact_calls.contact_id = contacts.id"
      ],
      #:group => "companies.id"
       :group => "companies.name"
    puts @list[0].attributes.inspect
+6  A: 

You should move this code to model:

# Contatct model

def self.get_list(report_start_date, report_end_date)
  self.find :all,
    :select => "companies.name AS co_name, 
              companies.id AS comp_id, 
              COUNT(contact_emails.id) AS email_count, 
              COUNT(contact_calls.id) AS call_count, 
              COUNT(contact_letters.id) AS letter_count, 
              COUNT(contact_postalcards.id) AS postalcard_count",

    :conditions => ['contact_emails.date_sent < ? and contact_emails.date_sent > ?', 
                  report_end_date, report_start_date],

    :joins => [
      "LEFT JOIN companies ON companies.id = contacts.company_id",
      "LEFT JOIN contact_emails ON contact_emails.contact_id = contacts.id",
      "LEFT JOIN contact_letters ON contact_letters.contact_id = contacts.id",
      "LEFT JOIN contact_postalcards ON contact_postalcards.contact_id = contacts.id",
      "LEFT JOIN contact_calls ON contact_calls.contact_id = contacts.id"
    ],
    #:group => "companies.id"
    :group => "companies.name"
end

Then you can use it in controllers:

@list = Contact.get_list(report_start_date, report_end_date)

Probably you can also split it to smaller parts and use scopes and defined associations instead of writing all of it on your own.

klew
Most definitely. This code screams, "Get me out of the controller!" Pushing it to the model cleans up your code, and allows you to access the method from a rake task or more easily from the console if you wanted to pull up that list for a one-off report.
jdl
does this code look right, I'd love to clean it up, but I couldn't find a way....
Angela
Joins are usually ugly, the important things is that it is DRY: you only have to define it once in the model, but you can call it many times is various controllers, as klew pointed out.
Tim
+1  A: 

I would add a function for generating the count and join sql:

class Contact < ActiveRecord::Base
  def  self.get_list(report_start_date,    report_end_date)
    all(:select      => "companies.name AS co_name,
                        companies.id AS comp_id,
                        #{table_count_col(
                          :contact_emails, 
                          :contact_calls, 
                          :contact_letters, 
                          :contact_postalcards
                         )}",
        :conditions => ['contact_emails.date_sent < ?  AND
                         contact_emails.date_sent > ?',
                         report_end_date, report_start_date],
        :joins      => join_table(
                    :companies, 
                    :contact_emails, 
                    :contact_letters, 
                    :contact_postalcards, 
                    :contact_calls
                    ),
       )
  end
end

Where table_count_col and table_join are static methods inside Contact class:

  def self.table_count_col(*args)    
    args.collect do |table| 
      count_col = "#{table.to_s.gsub(/^contact_/,  '').singularize}_count"
      "COUNT(#{table}.id) AS #{count_col}"    
    end.join(",")
  end

  def self.table_join(*args)    
    args.collect do |table|
     "LEFT JOIN #{table} ON #{table}.id = contacts.company_id"
    end.join(",")
  end       
KandadaBoggu
table_count_col is a method on Contacts as is table_join?
Angela
Yes, they are static methods on Contact class.
KandadaBoggu
I like it +1 for helping simplify -- may need to implement in phases
Angela