tI have a Player model in my rails app. 2 columns I am evaluating are highestLevel and highestScore. This is a stats tracking for a single player across multiple profiles, so there is the possibility that either of these values coming in could be lower than the current value in the db. Therefore I only wish it update a particular column IF the incoming posted value is greater than the one in the DB. Reading up on some of the built in validation options, I was not able to get any to work as I intended, however, I was able to write my own validations which work, but at the cost of calling Player.find(id) within the model. Is there a way around this so that my Player.update() does not result in both and UPDATE and SELECT?
Here is my model:
class Player < ActiveRecord::Base
#validates_numericality_of :highestLevel, greater_than: Proc.new { |r| r.highestLevel }
#validates_numericality_of :highestScore, greater_than: Proc.new { |r| r.highestScore }
before_update :player_record, :eval_highestLevel, :eval_highestScore
# TODO: Find a more effective way to handle update evaluations with less SQL overhead
private
def eval_highestLevel
# if highestLevel in DB has higher value , update the value
if @p.highestLevel > self.highestLevel
self.highestLevel = @p.highestLevel
end
end
def eval_highestScore
# if record in DB has higher value , update the value
if @p.highestScore > self.highestScore
self.highestScore = @p.highestScore
end
end
def player_record
@p = Player.find(id)
end
end
Any ideas on how to make this more efficient, or should I leave it alone? I'm always looking for the bigger and better mouse trap for Rails 4.x
Rails automatically defines helpers to get the previous value of an attribute when the attribute has changed but the record hasn't been persisted yet. They're named e.g.
attribute name_was
, so in this casePlayer#highestLevel_was
andhighestScore_was
:This is documented in ActiveModel::Dirty. A number of other useful methods are defined, for example:
attribute_name_changed?
returnstrue
if the attribute has changed.attribute_name_change
returns an array with two elements, the old value and the new value.Armed with this knowledge we can actually simplify your callbacks a lot:
Since
self.highestScore_change
will be an array of two numbers, we can callmax
to get the higher one. We useArray#compact
because if either the old value or the new value isnil
we'd get an error ([nil,1].max # => ArgumentError: comparison of NilClass with 1 failed
).compact
removes anynil
s from the array first.Or even more succinctly: