Как внедрить систему достижений в RoR - PullRequest
40 голосов
/ 20 мая 2009

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

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

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

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

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

спасибо! Oren

Ответы [ 3 ]

52 голосов
/ 20 мая 2009

Я согласен с вашей идеей использовать модель 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

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

18 голосов
/ 20 мая 2010

Действительно хорошее решение, мольф.

Я добавил это в плагин / гем с генераторами для новых достижений:

http://github.com/paulca/paths_of_glory

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

7 голосов
/ 01 марта 2012

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...