Bahaïka
Bahaïka

Reputation: 725

Best way to sum attributes with Ruby on Rails

In a web application I'm currently creating, I have to calculate a score for each user. I'm currently calculating the score this way :

class Opinion < ActiveRecord::Base
  belongs_to :hero
  def score_value
    self.note * (self.end_date - self.start_date)
  end
end

class User < ActiveRecord::Base
  has_many :opinions
  def update_score
    self.score = 0
    self.opinions.each { |opinion| self.score += opinion.score_value }
    self.save
  end
end

But I have the feeling that's not the best way to do it. Is there any better way to do it ?

Upvotes: 1

Views: 70

Answers (2)

Mike Szyndel
Mike Szyndel

Reputation: 10592

There are multiple improvements you could make here, what I would go for is

Recalculate the score when attributes are changes

class Opinion
  before_save :update_score

  def update_score
    return unless note_changed? || end_date_changed? || start_date_changed?
    self.note * (self.end_date - self.start_date)
  end
end

Update how score is calculated in User model

class User
  def update_score
    self.score = self.opinions.sum(:score)
  end
end

Upvotes: 0

nsave
nsave

Reputation: 984

Well, I can suggest using all ruby power in #update_score:

class User < ActiveRecord::Base
  has_many :opinions
  def update_score
    self.score = self.opinions.map(&:score_value).inject(0, &:+)
    self.save
  end
end

Another thing you could do is to move some calculation to database:

class User < ActiveRecord::Base
  has_many :opinions
  def update_score
    self.score = opinions.pluck(:note, 'DATEDIFF(end_date, start_date)').map do |note, days|
      note * days 
    end.inject(0, &:+)
    self.save
  end
end

This is just a perfomance improvement and I don't think its elegant.

Upvotes: 1

Related Questions