How to implement an achievement system in RoR

后端 未结 3 1184
执念已碎
执念已碎 2020-11-30 16:59

I\'m attempting, poorly, to implement an achievement system into my Ruby on Rails application.

I have a long list of achievements I\'d like to check for. All are tr

相关标签:
3条回答
  • 2020-11-30 17:25

    Really nice solution, molf.

    I rolled this in to a plugin / gem with generators for new achievements:

    http://github.com/paulca/paths_of_glory

    Happy achieving!

    0 讨论(0)
  • 2020-11-30 17:40

    I agree with your idea to use an Achievement model.

    You should probably not implement the triggers in your controllers, though. Imagine that you have two ways to post a comment; you will inevitably get code duplication. This sort of behaviour belongs in a model.

    Suppose you want to track the number of comments that a user makes, and award an achievement for 100 comments. You could have the following models:

    class User < ActiveRecord::Base
      has_many :comments
      has_many :achievements
    
      def award(achievement)
        achievements << achievement.new
      end
    
      def awarded?(achievement)
        achievements.count(:conditions => { :type => achievement }) > 0
      end
    end
    
    class Achievement < ActiveRecord::Base
      belongs_to :user
    end
    
    class Comment < ActiveRecord::Base
      belongs_to :user
    end
    
    class CommentAchievement < Achievement
      def self.check_conditions_for(user)
        # Check if achievement is already awarded before doing possibly expensive
        # operations to see if the achievement conditions are met.
        if !user.awarded?(self) and user.comments.size > 100
          user.award(self)
        end
      end
    end
    

    The different achievements are all subclasses of Achievement model, and use single table inheritance so that they are stored in just one table. The subclasses can contain all logic required for each individual achievement. You can also store additional information in this model, such as the date on which the achievement was awarded. To make sure that the database rejects duplicate achievements, you could create a UNIQUE index on the type and user_id columns.

    CommentAchievement.check_conditions_for(user) can be called whenever you wish to. You may create a background job that runs every now and then, or you could create an observer:

    # app/models/comment_achievement_observer.rb
    class CommentAchievementObserver < ActiveRecord::Observer
      observe :comment
    
      def after_create(comment)
        CommentAchievement.check_conditions_for(comment.user)
      end
    end
    
    # config/environment.rb
    config.active_record.observers = :comment_achievement_observer
    

    The above is just one idea of how to do it, of course there may be others. The code is just an example, I haven't actually tested it. Hopefully it's of some help to you.

    0 讨论(0)
  • 2020-11-30 17:50

    I wrote a Rails 3 gem for this task, which works for badges, points and rankings. You can find the source code in https://github.com/tute/merit.

    0 讨论(0)
提交回复
热议问题