I’ve got 2 tables in my DB. users and trackers.
Each Tracker has a user_id relating to the users table as you’d expect.
The context of this app is that it’s part of a team based time tracking solution. As part of this I need to sort users by the total amount of time they’ve tracked to date and also display this total on screen.
So I figured that whenever a Tracker is saved I fire a before_save callback that grabs the start_time and end_time values from the Tracker object and does a simple (end_time - start_time).to_i to get the time of the tracker in seconds. The outcome is saved to Tracker.tracked_time. This part works and I can see the calculated value in the DB.
I now need to save a total result of all Tracker instances’ tracked_time value per user and then I’d like to store this value to the users table in the tracked_time column there so that I can pull this out from the database to sort results by (think: most active user, least active user, top 10).
However when I try calling this before_save :calculate_tracked_time, if: :finished?, unless: :admin? I simply get a ActiveRecord::RecordNotSaved error.
It appears that I cannot save a value on a User model while inside the Tracker module… but I have no clue as to why or how else to do what I need.
Any thoughts on how to make this work (or work better perhaps) would be much appreciated.
Oh and the :tracked_time on the User model is listed in the attr_accessible so it can be written to.
Below is the Tracker model, I can also post the User model if needed.
class Tracker < ActiveRecord::Base
attr_accessible :running, :start_time, :end_time, :tracked_time, :intention, :achievement
belongs_to :user
before_save :calculate_tracked_time, if: :finished?, unless: :admin?
validates :user_id, presence: true
validates :intention, presence: true,
length: { minimum: 5 }
default_scope order: 'trackers.created_at DESC'
private
def finished?
# Only save the total amount of time elapsed if we have
# a start_time AND end_time for this object.
!self.end_time.nil?
end
def admin?
# Don't calculate totals for the admin user.
User.find_by_id( self.user_id ).admin?
end
def calculate_tracked_time
# write the tracker's tracked time value in seconds
self.tracked_time = ( self.end_time - self.start_time ).to_i
# update the user's tracked time total
user = User.find_by_id( self.user_id )
# calculate time from all trackers this user has
total_time = 0
user.trackers.each { |t| total_time += t.tracked_time }
# this log correctly prints the correct figures.
p "Total Time for user (#{self.user_id}): #{total_time}"
# store it to the user's :tracked_time db column
user.tracked_time = total_time
# save !!! Errors out.
user.save
end
end
Thanks for reading.
Instead of updating the field and calling save, try using update_column:
update_column doesn’t call any callbacks (or validations, so you do need to be careful). Callbacks can prevent saving when you’re already in a callback.