Подтвердить что ты не робот

Как реализовать систему достижений в RoR

Я пытаюсь, плохо, внедрить систему достижений в свое приложение Ruby on Rails.

У меня есть длинный список достижений, которые я бы хотел проверить. Все инициируются некоторыми действиями создания в разных контроллерах.

У меня была идея, что у меня будет модель достижений, которая включает в себя контроллер и действие, на которое он отвечает. Затем сделайте фильтр перед созданием и проверьте применимые достижения. Я застреваю, когда дело доходит до фактического определения/выполнения достижений. Для каждого достижения могут потребоваться разные данные. Например, вы захотите узнать, сколько вопросов ответили пользователю, еще одно количество комментариев, которые они сделали, и третье число тех, кого пригласил пользователь.

Лучше всего сделать, чтобы на самом деле просто вставить весь необходимый рубиновый код прямо в БД? Я мог видеть, что делает самостоятельный блок, который делает все активные записи, и т.д., И возвращает true/false, хотя там все еще есть некоторые проблемы, связанные с тем, что нужно заранее знать (то есть current_user и т.д.).

Какие-нибудь разумные лучшие практики, которые не заставляют меня чувствовать себя грязным? Я мог видеть, что полный механизм политики/правил является одним из путей, хотя это может напугать меня больше, чем планировать.

спасибо! Орен

4b9b3361

Ответ 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

Выше всего лишь одна идея, как это сделать, конечно, могут быть и другие. Код просто пример, я его не протестировал. Надеюсь, это поможет вам.

Ответ 2

Действительно приятное решение, molf.

Я включил это в плагин/драгоценный камень с генераторами для новых достижений:

http://github.com/paulca/paths_of_glory

Счастливое достижение!

Ответ 3

Я написал для этой задачи камень Rails 3, который работает для значков, очков и ранжирования. Исходный код можно найти в https://github.com/tute/merit.