Question

I'm trying to chain some scoping methods (instead of using an actual scope), but I have a problem combining them. I now have the code below working, but it's not completely DRY. It would be way nicer if I could write something like

Project.from_advertisers(advertiser_ids).active

and

Project.from_advertisers(advertiser_ids).finished

instead of having to use the separate methods 'active_from_advertisers' and 'finished_from_advertisers' I created, as you can see in my code below. These separate methods work, but the preferred chained methods like above give me the error that the first is an array...

Is it possible to accomplish this some way inside the model?

Thanks for any help!

#### campaign.rb
# datetime   :start_on
# datetime   :end_on
# integer    :advertiser_id
class Campaign < ActiveRecord::Base
  belongs_to :project
  belongs_to :advertiser

  def self.active
    self.select{|c| c.end_on >= Time.now && c.start_on <= Time.now }
  end

  def self.finished
    self.select{|c| c.end_on <= Time.now }
  end
end

#### project.rb
class Project < ActiveRecord::Base
  has_many :campaigns

  def self.active
    includes(:campaigns).joins(:campaigns).select{|p| p.end_on >= Time.now && p.start_on <= Time.now }
  end

  def self.finished
    includes(:campaigns).joins(:campaigns).select{|p| p.end_on && p.end_on <= Time.now }
  end

  def self.from_advertisers(advertiser_ids)
    includes(:campaigns).joins(:campaigns).select{|p| advertiser_ids.include?(p.advertiser_id)}
  end

  def self.active_from_advertisers(advertiser_ids)
    includes(:campaigns).select{|p| advertiser_ids.include?(p.advertiser_id) && p.end_on >= Time.now && p.start_on <= Time.now }
  end

  def self.finished_from_advertisers(advertiser_ids)
    includes(:campaigns).select{|p| advertiser_ids.include?(p.advertiser_id) && p.end_on && p.end_on <= Time.now }
  end

  def advertiser_id
    campaigns.first.try(:advertiser_id)
  end

  def start_on
    campaigns.map(&:start_on).min
  end

  def end_on
    campaigns.map(&:end_on).max
  end
end
Was it helpful?

Solution

Using .select will always return an array.

You might consider changing your queries using the where method.

def self.active
    where("end_on >= #{Time.now} && start_on <= #{Time.now}")
end
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top