Valider un modèle qui ne descend pas de ActiveRecord::Base sans plugin
Par Bounga le lundi, 21 décembre 2009, 15:19 - Trucs et astuces - Lien permanent
Après bien longtemps sans le moindre signe de vie, voici un petit billet rapide pour vous faire savoir que je suis toujours présent et que je continu à développer en Ruby !
Vous est-il déjà arrivé d'avoir besoin, en Rails, d'une classe qui se comporte comme un modèle ActiveRecord mais qui n'en ai pas un ? Typiquement, un modèle auquel aucune table n'est associée en base.
Le cas le plus simple où l'on peut avoir besoin de ce genre de classe serait par exemple un modèle qui gère les prises de contact (formulaire de contact) depuis le site.
Votre formulaire de contact doit valider les infos qui lui sont passées (est-ce que les champs obligatoires sont remplis, est-ce que l'email semble valide, …) avant d'envoyer par mail cette prise de contact à l'admin mais vous n'avez absolument pas besoin d'enregister cette prise de contact en base et donc aucune envie de créer une table dédiée pour simplement satisfaire ActiveRecord !
Il existe des plugins qui font ça me direz-vous. Certes oui, c'est vrai. Mais j'aime comprendre comment fonctionne les choses et j'aime autant me passer d'un plugin pour une chose qui me paraît aussi simple.
On pourrait penser qu'il suffit d'inclure le module de validation dans notre classe, comme ceci :
class Contact include ActiveRecord::Validations attr_accessor :name, :email, :message validates_presence_of :email, :message end
mais ce n'est pas le cas, les validations dépendent de plusieurs méthodes d'instance de votre classe et vous serez donc obligé de les définir si vous voulez avoir accès aux validations dans votre modèle.
class Contact include ActiveRecord::Validations attr_accessor :name, :email, :message def initialize(attrs = {}) attrs.each do |key, value| update_attribute(key, value) end @new_record = true end def save # Ici votre logique de sauvegarde # Dans notre cas, un envoi d'email ... @new_record = false return true # Si notre mail a pu être envoyé end alias :save! :save def update_attribute(key, value) send "#{key}=", value end def new_record?() @new_record end # Et enfin nos validations validates_presence_of :email, :message end
Nous pouvons maintenant créer un objet Contact :
contact = Contact.new(:name => "Nico", :email => "nico@test.com") # => #<Contact:0x132d3a0 @new_record=true, @name="Nico", @email="nico@test.com"> contact.valid? # => false
Nos validations fonctionnent, notre objet n'est pas valide.
contact = Contact.new(:name => "Nico", :email => "nico@test.com", :message => "Hey !") # => #<Contact:0x134d3c3 @new_record=true, @name="Nico", @email="nico@test.com", @message="Hey !"> contact.valid? # => true
L'objet est maintenant valide.
Vous avez bien évidemment accès à toutes les méthodes mises à disposition par le module de validation, vous êtes donc en mesure de parcourir les erreurs pour savoir ce qui est à l'origine d'un objet non-valide.
Il serait très simple, à partir de ce code, de créer une classe de base dont hériteraient vos modèles non-ActiveRecord qui ont besoin des validations. Il est également très facile d'en extraire un plugin pour encore faciliter la mise en place de cette architecture.