Я согласен с вашей идеей использовать модель Achievement
.
Вы, вероятно, не должны реализовывать триггеры в своих контроллерах. Представьте, что у вас есть два способа оставить комментарий; вы неизбежно получите дублирование кода. Такое поведение относится к модели.
Предположим, вы хотите отследить количество комментариев, оставленных пользователем, и присудить достижение за 100 комментариев. Вы можете иметь следующие модели:
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
Различные достижения являются подклассами модели Achievement
и используют наследование одной таблицы, поэтому они хранятся в одной таблице. Подклассы могут содержать всю логику, необходимую для каждого отдельного достижения. В этой модели также можно сохранить дополнительную информацию, например, дату, когда достижение было присуждено. Чтобы убедиться, что база данных отклоняет повторяющиеся достижения, вы можете создать индекс UNIQUE
для столбцов type
и user_id
.
CommentAchievement.check_conditions_for(user)
можно вызывать в любое время. Вы можете создать фоновое задание, которое запускается время от времени, или вы можете создать наблюдателя:
# 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
Вышесказанное - только одна идея, как это сделать, конечно, могут быть и другие. Код является лишь примером, я на самом деле не проверял его. Надеюсь, это поможет вам.