2

I have a gamification app that has four types of points, and the sum of all these kinds is the total points for a user, I want to be able to do sum and scopes on that column, so I think I should have it as a column in the DB.

scope :points_rank, -> { order(points: :desc) }

I was using a before_save for adding all four point types and storing it in points, but now I'm using a gem that does increment to these types of points, so when it updates those values, the before_save is not called, hence not updating the points value as expected.

What is the correct ActiveRecord callback to be using instead of before_save, or what else could I be doing to keep the column updated.

chamini2
  • 2,820
  • 2
  • 24
  • 37

2 Answers2

1

Try using the after_touch callback instead.

after_touch callback is triggered whenever an object is touched.

So, whenever point type changes, it should update the points.

K M Rakibul Islam
  • 33,760
  • 12
  • 89
  • 110
0

First of all, counter_culture seems to be a way to enhance the counter_cache functionality of rails...

Used to cache the number of belonging objects on associations. For example, a comments_count column in a Post class that has many instances of Comment will cache the number of existent comments for each post.

It might not be exactly what you want, judging from your question.

Okay I get it. You're using points in your User model to create a "cached" column which can be used for wider application functionality. Okay that's cool...

--

Your setup, then, will look something like this (you were manually setting the counter_cache column, and now the gem handles it):

#app/models/user.rb
class User < ActiveRecord::Base
   counter_cache :points
end

#app/models/point.rb
class Point < ActiveRecord::Base
   belongs_to :user, counter_cache: true
end

The question is then that when you update the points model, you need to be able to update the "cached" column in the users model, now without any callbacks.


What is the correct ActiveRecord callback to be using instead of before_save

I'm presuming you're calling before_save on your User model (IE adding the associated data and putting the points column?

If so, you should try using a callback on the Point model, perhaps something like this:

#app/models/user.rb
class User < ActiveRecord::Base
   has_many :points
end

#app/models/point.rb
class Point < ActiveRecord::Base
   belongs_to :user, inverse_of: :points
   after_commit :update_user

   private

   def update_user
      if user?
          user.update(points: x + y + z)
      end
   end
end

--

Oberservers

If you have real problems, you could look at ActiveRecord observers.

Here's an answer I wrote about it: Ruby On Rails Updating Heroku Dynamic Routes

Whether this will trigger without any callbacks is another matter, but what I can say is that it will work to give you functionality you may not have had access to otherwise:

#config/application.rb (can be placed into dev or prod files if required)
config.active_record.observers = :point_observer

#app/models/point_observer.rb
class PointObserver < ActiveRecord::Observer
  def before_save(point)
    #logic here
  end
end

A good way to test this would be to use it (you'll have to use the rails-observers gem) with different methods. IE:

 #app/models/point_observer.rb
class PointObserver < ActiveRecord::Observer
  def initialize(point)
     #if this fires, happy days
  end
end
Community
  • 1
  • 1
Richard Peck
  • 76,116
  • 9
  • 93
  • 147