2009-05-19 9 views
40

Sto tentando, malamente, di implementare un sistema di risultati nell'applicazione Ruby on Rails.Come implementare un sistema di risultati in RoR

Ho una lunga lista di risultati che mi piacerebbe controllare. Tutti sono attivati ​​da alcuni creano azioni in vari controller.

Ho avuto l'idea che avrò un modello di realizzazione, che include il controller e l'azione a cui risponde. Quindi fai un filtro precedente per la creazione e verifica i risultati applicabili. Mi blocco quando si tratta di definire/eseguire realmente i risultati. Ogni risultato può richiedere dati diversi. Ad esempio si vorrà sapere quante domande ha risposto un utente, un altro quanti commenti hanno fatto e un terzo quante persone l'utente invitato ha risposto.

È la cosa migliore da fare per incorporare semplicemente tutto il codice ruby ​​necessario direttamente nel DB? Potrei vedere fare un blocco autonomo che fa tutto il rilevamento di record attivo, ecc. E restituisce vero/falso, anche se ci sono ancora alcuni problemi nel sapere cosa è stato impostato in anticipo (cioè, utente corrente, ecc.).

Qualsiasi ragionevole buona pratica là fuori che non mi faccia sentire sporco? Potrei vedere un intero motore di regole/regole come un unico percorso, anche se ciò potrebbe spaventarmi di più del piano a.

grazie! Oren

risposta

52

Sono d'accordo con la tua idea di utilizzare un modello Achievement.

Probabilmente non dovresti implementare i trigger nei tuoi controller, però. Immagina di avere due modi per pubblicare un commento; otterrete inevitabilmente la duplicazione del codice. Questo tipo di comportamento appartiene a un modello.

Supponiamo di voler tenere traccia del numero di commenti che un utente fa e di assegnare un risultato per 100 commenti. Si potrebbe avere i seguenti modelli:

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 

Le diverse realizzazioni sono tutte le sottoclassi di Achievement modello e usare l'ereditarietà singola tabella in modo che essi sono memorizzati in un solo tavolo. Le sottoclassi possono contenere tutte le logiche richieste per ogni singolo risultato. È inoltre possibile memorizzare ulteriori informazioni in questo modello, ad esempio la data in cui è stato assegnato il raggiungimento. Per assicurarsi che il database rifiuti risultati duplicati, è possibile creare un indice UNIQUE sulle colonne type e user_id.

CommentAchievement.check_conditions_for(user) può essere chiamato ogni volta che si desidera. È possibile creare un processo in background che viene eseguito ogni tanto, o si potrebbe creare un osservatore:

# 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 

Quanto sopra è solo un'idea di come farlo, naturalmente ci possono essere altri. Il codice è solo un esempio, non l'ho ancora testato. Spero che ti sia di aiuto.

+0

Stavo pensando a qualcosa di diverso, e forse stupido. Preferirei non dover spingere un nuovo codice per ogni risultato, quindi lo stavo progettando in modo che i risultati fossero memorizzati nel DB. Quindi interrogherò il risultato per i record applicabili (tramite azione e controller) e itererò attraverso qualche "logica" per ciascuno dei risultati trovati. Il trucco è, questo è un sacco di "valutazione", che si sente sporco. – teich

+1

Eval è anche un enorme rischio per la sicurezza. Immaginiamo che se qualcuno è riuscito a inserire dati nella tabella dei risultati, potrebbe aggiungere un risultato con la seguente logica: 'system ("rm -rf /")' ;-) Forse è possibile tradurre la logica in un gruppo predefinito di opzioni? Ad esempio, creare una tabella dei tipi di risultati che contiene il nome di un'associazione nel modello Utente e un campo che indica l'importo al momento dell'attivazione. In questo modo hai ancora una certa flessibilità senza memorizzare il codice dell'applicazione nel tuo database. – molf

+0

Dormire su di esso, e mi rendo conto che qualsiasi cosa nel DB è probabilmente un'idea stupida - rende impossibile testare ragionevolmente. Grazie per il puntatore! – teich

7

Ho scritto una gemma Rails 3 per questa attività, che funziona per badge, punti e classifiche. È possibile trovare il codice sorgente in https://github.com/tute/merit.

+0

ha appena fatto un'installazione gemma per questo. lo verificherò ora =) – Sasha

Problemi correlati