0f8736dfd93c21c75ad591770a14bdb4

Store has many Promos. Promos belong to Store. But I also want default Promos (without a Store) to belong to each Store and be included in the association's find. I wrote a custom 'for_store' finder-type method and overrode the Store.promos reader with it. Is there a cleaner way to do this in Rails?

class Store < ActiveRecord::Base
  has_many :promos
  def promos
    Promo.for_store(self)
  end
  def promos?
    promos.count > 0
  end
end
class Promo < ActiveRecord::Base
  belongs_to :store
  def self.for_store(store=nil, limit=4)
    return find(:all, :conditions => ["promos.store_id IS NULL"], :limit => limit) if store.nil?
    find(:all, :conditions => ["promos.store_id = ? or promos.store_id IS NULL", store.id], :limit => limit)
  end
end

Refactorings

No refactoring yet !

7855792dbc5f3b4c365344314e2b1ad6

arvanasse

June 8, 2010, June 08, 2010 14:51, permalink

1 rating. Login to rate!

Your code does not seem to match your description. As coded, #promos (instance method override) will always call #for_store with an instance of store so you will only get the promos directly assigned to the store. The main question to understand is how to choose between 'default' promos that belong to every store and the promos assigned to a specific store. Should they be mixed? That is, should you ask for promos and get both 'default' and assigned promos? Should you only get 'default' promos when there are no assigned ones?

Here's an idea or two based on moving the logic of the custom method into (chainable) named_scopes.

class Store < ActiveRecord::Base
  has_many :promos

  # returns a mix of default and assigned promos
  def current_promos
    promos.recent(4).for_store(self)
  end

  # returns either default or assigned promos
  def active_promos
    promos? ? promos : Promo.recent(4).default_promos
  end

  def promos?
    promos.count > 0
  end
end
class Promo < ActiveRecord::Base
  belongs_to :store

  named_scope :recent, lambda{|limit| { :order => 'id desc', :limit => limit} }
  named_scope :for_store, lambda{|store| {:conditions => {:store_id => [nil, store.id]} } }
  named_scope :default_promos, :conditions => {:store_id => nil}
end

Your refactoring





Format Copy from initial code

or Cancel