Ответ 1
Я согласен с вашей идеей использовать модель 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
Выше всего лишь одна идея, как это сделать, конечно, могут быть и другие. Код просто пример, я его не протестировал. Надеюсь, это поможет вам.