2009-05-19 4 views
40

Ruby on Rails 애플리케이션에 업적 시스템을 구현하려는 시도가 잘못되었습니다.RoR에서 업적 시스템을 구현하는 방법

내가 확인하고 싶은 업적에 대한 긴 목록이 있습니다. 모두는 다양한 제어기에서 일부 작성 조치에 의해 트리거됩니다.

내가 컨트롤러와 반응하는 작업을 포함하는 업적 모델을 가질 것이라는 생각이 들었습니다. 그런 다음 작성하기 전에 필터를 수행하고 적용 가능한 업적을 확인하십시오. 업적을 실제로 정의/실행하는 데는 어려움이 있습니다. 각 업적에는 다른 데이터가 필요할 수 있습니다. 예를 들어 사용자가 몇 개의 질문에 답변했는지, 몇 개의 댓글을 작성했는지, 그리고 3 번째로 얼마나 많은 사람들이 초대했는지 알고 싶을 것입니다.

실제로 필요한 모든 루비 코드를 DB에 직접 삽입하는 것이 가장 좋습니다. 모든 활성 레코드 찾기 등을 수행하고 true/false를 반환하는 자체 포함 된 블록을 볼 수 있습니다. 미리 설정이 무엇인지 (예 : current_user 등) 알기위한 몇 가지 문제가 있습니다.

더러운 느낌이 들지 않는 합리적인 모범 사례가 있습니까? 정책/규칙 엔진에 대한 전체 내용을 하나의 경로로 볼 수 있지만 계획보다 많은 것을 두려워 할 수도 있습니다.

감사합니다. 오렌지

답변

52

나는 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 모델의 모든 서브 클래스가 있으며, 그들은 단지 하나 개의 테이블에 저장되도록 단일 테이블 상속을 사용하여 다음과 같은 모델을 가질 수있다. 하위 클래스에는 각 업적에 필요한 모든 논리가 포함될 수 있습니다. 업적을 수상한 날짜와 같은 추가 정보를이 모델에 저장할 수도 있습니다. 데이터베이스가 중복 업적을 거부하는지 확인하려면 typeuser_id 열에 UNIQUE 색인을 생성 할 수 있습니다.

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 

을 위, 물론 다른 사람이있을 수 있습니다 그것을 수행하는 방법의 한 생각입니다. 코드는 단지 예일뿐입니다. 실제로 테스트하지는 않았습니다. 바라건대 당신에게 도움이 되길 바랍니다.

+0

나는 뭔가 다른 것을 생각하고 있었고 아마도 어리 석을 수도 있습니다. 업적을 DB에 저장하도록 모든 업적에 대해 새 코드를 적용하지 않으려 고합니다. 그런 다음 액션 및 컨트롤러를 통해 적용 가능한 레코드에 대한 업적을 쿼리하고 발견 된 각 결과에 대해 몇 가지 "로직"을 반복합니다. 트릭은 더러운 느낌의 'eval'ing입니다. – teich

+1

평가판은 보안 위험이 너무 큽니다. 누군가가 업적 테이블에 데이터를 입력 할 수 있다고 가정하면 다음 논리를 가진 업적을 추가 할 수 있습니다. 'system ("rm -rf /")';-) 아마 논리를 사전 정의 된 집합으로 변환 할 수 있습니다. 옵션? 예를 들어 사용자 모델에 대한 연관의 이름을 포함하는 업적 유형 테이블과 트리거 될 때의 양을 나타내는 필드를 만듭니다. 이 방법으로 데이터베이스에 응용 프로그램 코드를 저장하지 않고도 유연성을 가질 수 있습니다. – molf

+0

그것으로 잠을 자면 DB에있는 모든 것이 어쩌면 멍청한 생각이라는 것을 알게 될 것입니다. 합리적으로 테스트하는 것은 불가능합니다. 포인터 주셔서 감사! – teich

7

저는이 작업을 위해 Rails 3 gem을 작성했습니다.이 작업은 배지, 점수 및 순위에 적용됩니다. 소스 코드는 https://github.com/tute/merit에서 찾을 수 있습니다.

+0

그냥 보석을 설치했습니다. 그것을 지금 검사 할 것이다 =) – Sasha

관련 문제