2009-05-19 10 views
40

Estoy intentando, pobremente, implementar un sistema de logros en mi aplicación Ruby on Rails.Cómo implementar un sistema de logros en RoR

Tengo una larga lista de logros que me gustaría comprobar. Todos son activados por alguna acción de creación en varios controladores.

He tenido la idea de que tendré un modelo de logros, que incluye el controlador y la acción a los que responde. Luego haga un filtro antes de la creación y verifique los logros aplicables. Me quedo atascado cuando se trata de definir/ejecutar los logros. Cada logro puede requerir datos diferentes. Por ejemplo, uno querrá saber cuántas preguntas ha respondido un usuario, cuántos comentarios han realizado y, en tercer lugar, cuántas personas ha respondido el usuario.

¿Es lo mejor que puedes hacer para insertar todo el código Ruby necesario directamente en la base de datos? Pude ver un bloque autocontenido que hace todo el registro activo, etc. y devuelve verdadero/falso, aunque todavía tenemos algunos problemas para saber lo que se configura por adelantado (es decir, usuario_actual, etc.).

¿Alguna de las mejores prácticas razonables que no me hacen sentir sucio? Pude ver un motor de políticas/reglas completo en un solo camino, aunque eso puede asustarme más que planificar a.

gracias! Oren

Respuesta

52

Estoy de acuerdo con su idea de utilizar un modelo Achievement.

Sin embargo, probablemente no deberías implementar los disparadores en tus controladores. Imagine que tiene dos formas de publicar un comentario; inevitablemente obtendrás duplicación de código. Este tipo de comportamiento pertenece a un modelo.

Supongamos que desea hacer un seguimiento de la cantidad de comentarios que realiza un usuario y otorgar un logro por 100 comentarios. Usted podría tener los siguientes modelos:

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 

Los diferentes logros son todas las subclases de Achievement modelo, y el uso de la herencia simple mesa para que se almacenan en una sola mesa. Las subclases pueden contener toda la lógica requerida para cada logro individual. También puede almacenar información adicional en este modelo, como la fecha en que se otorgó el logro. Para asegurarse de que la base de datos rechace los logros duplicados, puede crear un índice UNIQUE en las columnas type y user_id.

CommentAchievement.check_conditions_for(user) se puede llamar siempre que lo desee. Es posible crear una tarea de fondo que va de vez en cuando, o se podría crear un observador:

# 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 

Lo anterior es sólo una idea de cómo hacerlo, por supuesto, puede haber otros. El código es solo un ejemplo, en realidad no lo he probado. Espero que sea de alguna ayuda para ti.

+0

Estaba pensando en algo diferente, y posiblemente estúpido. Preferiría no tener que presionar un código nuevo para cada logro, así que lo estaba diseñando para que los logros se almacenaran en el DB. A continuación, consultaría el logro de los registros aplicables (por acción y controlador) y repetiría una "lógica" para cada uno de los resultados encontrados. El truco es que esto es mucha 'evaluación', que se siente sucio. – teich

+1

Eva también es un gran riesgo de seguridad. Imagine que si alguien lograra ingresar datos en su tabla de logros, podría agregar un logro que tuviera la siguiente lógica: 'sistema ("rm -rf /")' ;-) Quizás pueda traducir la lógica a un conjunto predefinido de opciones? Por ejemplo, cree una tabla de tipos de logro que contenga el nombre de una asociación en el modelo de Usuario y un campo que indique la cantidad cuando se active. De esta forma, aún tiene cierta flexibilidad sin almacenar el código de la aplicación en su base de datos. – molf

+0

Dormir en él, y me doy cuenta de que cualquier cosa en el DB es probablemente una idea tonta, hace que sea imposible realizar pruebas de manera razonable. Gracias por el puntero! – teich

7

Escribí una joya de Rails 3 para esta tarea, que funciona para insignias, puntos y clasificaciones. Puede encontrar el código fuente en https://github.com/tute/merit.

+0

acaba de hacer una instalación gema para ello. lo verificará ahora =) – Sasha

Cuestiones relacionadas