2010-10-20 5 views
1

J'ai un modèle qui ressemble à ceci:Rails: Ajout de méthodes aux modèles pour effectuer des contrôles basés sur current_user?

class Comment < ActiveRecord::Base 
    ... 
    #allow editing comment if it is moderated and the user passed-in 
    #is the one that owns the comment 
    def can_edit?(user) 
    moderated? and user.Type == User and user.id == self.user_id 
    end 
    ... 
end 

Et un appel en vue:

<%= link_to 'Show Comment', @comment if @comment.can_show?(current_user) %> 

Je dois écrire beaucoup de ces méthodes dans de nombreux modèles différents - sorte de contrôles de validation voir si current_user est autorisé à faire quelque chose sur un modèle.

Mais il semble lourd - en particulier la nécessité de vérifier que le user transmis est en effet un objet de type User.

Qu'est-ce qu'une façon propre et pratique de faire ce genre de chose? Suis-je sur la bonne voie? (C.-à-Dois-je ajouter de telles méthodes à un modèle ou un autre)

Remarque

Je suis en utilisant des requêtes scope pour obtenir les commentaires et les autres modèles, mais dans certains cas, je ne peux pas la portée de la requête donc je dois utiliser les méthodes can_xxxx?

Ps. Est-ce que ce que je fais est considéré comme un «gros modèle»?

Répondre

1

Créer un module contenant toutes les méthodes d'autorisation et include le module à toutes les classes nécessitant une autorisation. Ajouter un fichier appelé authorization.rb au répertoire app/models.

module Authorization 

    def can_edit?(user) 
    moderated? and user.is_a?(User) and user.id == self.user_id 
    end 

    def self.included(base) 
    base.send(:extend, ClassMethods) 
    end 

    module ClassMethods 
    # add your class methods here. 
    end 
end 

Ajouter un fichier appelé authorization.rb au répertoire config/initializers.

%w(
Comment 
Post 
).each do |klass| 
    klass.constantize.include(Authorization) 
end 

Maintenant modèles Comment et Post auront toutes les méthodes d'autorisation.

Une autre approche consiste à utiliser votre current_scope actuel.

class Post 
    named_scope :accessible, lambda { |user| 
    { 
     :conditions => { :user_id => user.id, :moderated => true} 
    } 
    } 
end 

Post actions contrôleur

class PostsController 

    def index 
    @posts = Post.acessible(current_user) 
    # process data 
    end 

    def show 
    # throws record not found when the record is not accessible. 
    @post = Post.acessible(current_user).find(params[:id]) 
    # process data 
    end 

end 

J'aime cette approche car elle utilise la même logique pour accéder à un tableau d'objets ou d'un seul objet.

Vous pouvez ajouter le named_scope au module afin d'éviter les définitions répétées:

module Authorization 
    def self.included(base) 
    base.named_scope :accessible, lambda { |user| 
     { 
     :conditions => { :user_id => user.id, :moderated => true} 
     } 
    } 
    end 

    module ClassMethods 
    # add your class methods here. 
    end 
end 

Assurez-vous d'inclure le module dans les classes requises comme l'a suggéré plus tôt.

+0

Merci beaucoup *. Cette approche semble plus propre. Je suppose que vous avez maintenant beaucoup de compréhension en profondeur à faire au sujet de votre technique! – Zabba

1

Je ne pense pas que ce que vous faites est nécessairement faux. Je vois trois façons de simplifier, cependant:

1) piste self.user ainsi que self.user_id. Ensuite, vous pouvez dire:

def can_show?(user) 
    moderated ? and user == self.user 
end 

Remarque, cela pourrait ajouter les frais généraux, soit avec DB temps de consultation et/ou l'empreinte mémoire.

2) Utilisez #is_a? afin de vérifier l'ascendance et non seulement l'égalité des classes:

def can_show?(user) 
    moderated ? and user.is_a?(User) and user.id == self.user_id 
end 

3) Si le passage dans un non-utilisateur est incorrecte, vous voudrez peut-être soulever une erreur lorsque cela se produit:

def can_show?(user) 
    raise "expected User, not #{ user.class.to_s }" unless user.is_a?(User) 
    moderated ? and user.id == self.user_id 
end 

En ce qui concerne Q2, je n'ai pas entendu la terminologie "gros modèle". Est-ce que c'est référencé n'importe où en particulier?

+0

J'ai lu sur Internet quelques bonnes pratiques mentionnant «un gros modèle et un contrôleur maigre» – Zabba

+0

Comment suivre l'utilisateur dans le modèle de commentaire? Il a un champ user_id. – Zabba

1

Re: modèle graisse et contrôleur maigre

Telle est l'idée de la logique poussant dans le modèle plutôt que dans le contrôleur (ou pire, le point de vue).

Un grand avantage est d'aider avec les tests; aussi l'accent de placer plus de logique dans le modèle plutôt que dans le contrôleur. Rappelez-vous qu'il n'est pas rare que les contrôleurs fonctionnent avec plusieurs modèles. Mettre la logique dans un modèle plutôt que dans un contrôleur signifie souvent que les règles métier sont intégrées dans le modèle, ce qui correspond exactement à leur appartenance.Un inconvénient possible est que toute information disponible au contrôleur qui n'est pas disponible dans le modèle doit être explicitement transmise dans les méthodes du modèle ou «set» en utilisant les variables d'instance d'un modèle.

Votre exemple de nécessité de passer l'utilisateur actuel dans le modèle illustre le problème. Dans l'ensemble cependant, moi et beaucoup d'autres avons constaté que les modèles de graisse ont tendance à mieux fonctionner que non.

Questions connexes