Question

Je souhaite consigner les actions de l'utilisateur dans mon application Ruby on Rails.

Jusqu'à présent, j'ai un observateur de modèle qui insère des journaux dans la base de données après les mises à jour et les crée. Afin de pouvoir enregistrer quel utilisateur a effectué l'action consignée, j'ai besoin d'accéder à la session, mais cela pose problème.

Tout d’abord, cela casse le modèle MVC. Deuxièmement, les techniques vont du hackish au farfelu, peut-être même en liant la mise en œuvre au serveur Mongrel.

Quelle est la bonne approche à adopter?

Était-ce utile?

La solution

Je trouve que cette question est très intéressante. Je vais penser à voix haute ici un instant ...

En fin de compte, nous sommes confrontés à la décision de violer une pratique acceptable du modèle de conception afin de réaliser un ensemble spécifique de fonctionnalités. Donc, nous devons nous demander

1) Quelles sont les solutions possibles qui ne violeraient pas le modèle MVC

2) Quelles sont les solutions possibles pour violer le modèle MVC

?

3) Quelle est la meilleure option? Je considère que les modèles de conception et les pratiques standard sont très importants, mais en même temps, s’ils tiennent à rendre votre code plus complexe, la bonne solution peut très bien consister à enfreindre cette pratique. Certaines personnes pourraient être en désaccord avec moi à ce sujet.

Commençons par considérer n ° 1.

De mémoire, je penserais aux solutions possibles suivantes

A) Si vous souhaitez vraiment savoir qui exécute ces actions, ces données doivent-elles être stockées dans le modèle? Cela mettrait ces informations à la disposition de votre observateur. Cela signifie également que tous les autres appelants de votre classe ActiveRecord bénéficient des mêmes fonctionnalités.

B) Si vous n'êtes pas vraiment intéressé à comprendre qui a créé une entrée, mais plus intéressé à consigner les actions Web elles-mêmes, vous pouvez alors envisager "d'observer". les actions du contrôleur. Cela fait longtemps que je n’ai pas fouillé dans les sources de Rails, je ne sais donc pas qui est leur agent ActiveRecord :: Observer " observe " le modèle, mais vous pourrez peut-être l'adapter à un observateur de contrôleur. En ce sens, vous n'observez plus le modèle et il est logique de transmettre à cet observateur des informations de données de session et autres types de contrôleurs. C) La solution la plus simple, avec le moins de "structure", consiste simplement à supprimer votre code de journalisation à la fin des méthodes d'action que vous regardez.

Envisagez maintenant l’option n ° 2, qui casse les pratiques de MVC.

A) Comme vous le proposez, vous pouvez trouver le moyen de faire en sorte que votre modèle Observer ait accès aux données de la session. Vous avez associé votre modèle à votre logique métier.

B) Je ne peux en penser à aucun autre ici:)

Mon penchant personnel, sans connaître davantage de détails sur votre projet, est soit 1A, si je veux lier des personnes à des enregistrements, soit 1C, si cela ne m'intéresse que dans quelques endroits. Si vous souhaitez réellement une solution de journalisation robuste pour tous vos contrôleurs et actions, vous pouvez envisager 1B.

Demander à votre observateur de modèle de trouver des données de session est un peu "puant", et risquerait de se rompre si vous tentiez d’utiliser votre modèle dans un autre projet / situation / contexte.

Autres conseils

Hm, c'est une situation délicate. Vous devez pratiquement violer MVC pour que cela fonctionne correctement.

Je ferais quelque chose comme ça:

class MyObserverClass < ActiveRecord::Observer
  cattr_accessor :current_user # GLOBAL VARIABLE. RELIES ON RAILS BEING SINGLE THREADED

  # other logging code goes here
end

class ApplicationController
  before_filter :set_current_user_for_observer

  def set_current_user_for_observer
    MyObserverClass.current_user = session[:user]
  end
end

C'est un peu hacky, mais ce n'est pas plus hacky que beaucoup d'autres choses que j'ai vues sur les rails.

Tout ce que vous devez faire pour le rendre threadsafe (cela ne compte que si vous utilisez jruby de toute façon) est de changer le paramètre cattr_accessor en une méthode appropriée et de le faire stocker ses données dans un stockage thread-local

Vous avez raison de dire que cela brise MVC. Je suggérerais d’utiliser des rappels dans vos contrôleurs, principalement parce qu’il existe des situations (comme un modèle qui enregistre est appelé mais échoue à la validation) et dans lequel vous ne souhaitez pas qu'un observateur enregistre quoi que ce soit.

J'ai trouvé un moyen propre de faire ce que suggère la réponse que j'ai choisie.

http://pjkh.com / articles / 2009/02/02 / créer-un-audit-de-connexion-à-l'audit

Cette solution utilise un modèle AuditLog ainsi qu'un module TrackChanges pour ajouter une fonctionnalité de suivi à tout modèle. Vous devez toujours ajouter une ligne au contrôleur lorsque vous mettez à jour ou créez bien.

Dans le passé, lorsque je faisais quelque chose comme cela, j'avais tendance à étendre la classe de modèles User pour inclure l'idée de "l'utilisateur actuel"

En regardant les réponses précédentes, je vois des suggestions pour stocker l’utilisateur actif de l’enregistrement actif dans la session. Cela présente plusieurs inconvénients.

  • Il stocke un objet éventuellement volumineux dans la base de données de session
  • Cela signifie que la copie de l'utilisateur est "mise en cache" pour toujours (ou jusqu'à ce que la déconnexion soit forcée). Cela signifie que tout changement de statut de cet utilisateur ne sera pas reconnu jusqu'à ce que l'utilisateur se déconnecte et se reconnecte. Cela signifie par exemple que toute tentative de désactivation de l'utilisateur l'attendra avant de se déconnecter puis de se reconnecter. Ce n'est probablement pas le comportement que vous souhaitez.

Pour qu'au début d'une requête (dans un filtre), vous preniez l'identifiant de l'utilisateur dans la session et lisiez l'utilisateur, définissant User.current_user.

Quelque chose comme ça ...

class User
  cattr_accessor :current_user
end

class Application
  before_filter :retrieve_user

  def retrieve_user
    if session[:user_id].nil?
      User.current_user = nil
    else
      User.current_user = User.find(session[:user_id])
    end
  end
end

À partir de là, cela devrait être trivial.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top