Topic: Help me with DRY my model

I have this model called Contact.

And this model has 3 class methods:

self.show_all(sort_by)
self.show(page, sort_by)
self.search(search, sort_by, page)

And all of these class methods have the same lines at the top:
 sort_by = 'group' if !sort_by || sort_by.empty?
      can_sort_by = ['first_name', 'last_name', 'organisation', 'profession', 'address',
         'postcode', 'country', 'birthday', 'telephone', 'email', 'group']
      sorts = {
         :first_name => 'contacts.first_name',
         :last_name => 'contacts.last_name',
         :organisation => 'contacts.organisation',
         :profession => 'contacts.profession',
         :address => 'contacts.address',
         :postcode => 'contacts.postcode',
         :country => 'countries.name',
         :birthday => 'contacts.birthday',
         :telephone => 'contacts.telephone',
         :email => 'contacts.email',
         :group => 'groups.name'
      }
      unless can_sort_by.include?(sort_by)
         return []
      end

I am thinking about moving this into a private method but I have a gut feeling, there's another way, rails way. Isn't it?

Last edited by HappyCoder (2007-10-25 09:38:49)

Re: Help me with DRY my model

Hmm... let's see. There's definitely a couple of things you could do. First of all, you could make your can_sort_by array a constant at the top of your model.

class TheSorter < ActiveRecord::Base

  CAN_SORT_BY = [ ... ]

  def self.show_all(sort_by)
    # whatever
  end

  ... 

end


And instead of the way you're setting sort_by, do it like this:

sort_by = 'group' if sort_by.blank?

And you could do something like this for the hash:

private

def array_to_hash(items)
  returning h = {} do
    items.each do |can_sort_by|
      case can_sort_by
        when /^(country|group)$/ then prefix = can_sort_by.pluralize
        else prefix = 'contacts'
      end
      h[can_sort_by.to_sym] = "#{prefix}.#{can_sort_by}"
    end
  end
end


If this is something you find yourself doing a lot, you may want to think about generalizing it in a way that would let you add a method to the Array class. Something like this (untested):

class Array
  def to_hash(prefix = 'contacts')
    returning h = {} do
      self.each_with_index { |item, i| h[item] = "#{prefix}.#{self[i]} }
    end
  end
end

Then you could simply call sorts = CAN_SORT_BY.to_hash.

Anyway, after all of that, it should look something like this:

return [] unless CAN_SORT_BY.include? sort_by  # do this first, so if it's not included
                                               # you avoid the overhead of the other processing
sort_by = 'group' if sort_by.blank?            # .blank? checks for nil? and empty?
sorts   = array_to_hash(CAN_SORT_BY)           # pass the constant created at the top of your model