5 votes

Une façon plus élégante d'ignorer les validations dans Rails ?

Dans mon modèle d'utilisateur, je dispose des éléments habituels tels que l'adresse électronique, le prénom, le nom, le mot de passe, etc.

J'ai plusieurs cas où je dois ignorer tout ou partie des validations.

Actuellement, j'ai une condition d'absence qui ressemble à quelque chose comme :

  validates :first_name, presence: :true, etc..., unless: :skip_first_name_validation?
  validates :last_name, presence: :true, etc..., unless: :skip_last_name_validation?
  validates :email, presence: :true, etc..., unless: :skip_email_validation?

Bien sûr, j'en ai aussi :

  attr_accessor :skip_first_name_validation, :skip_last_name_validation, etc.

Et ensuite j'utilise des méthodes privées pour vérifier l'état de chacun :

  def skip_first_name_validation?
    skip_first_name_validation
  end

  def skip_last_name_validation?
    skip_last_name_validation
  end

  def skip_email_validation?
    skip_email_validation
  end

  etc..

À partir de là, chaque fois que j'ai besoin de sauter des validations, j'attribue simplement à chacun d'eux une true dans mon contrôleur.


Bien que tout cela fonctionne bien, je me demande s'il n'y a pas un moyen plus élégant ?

L'idéal serait que je puisse utiliser une condition simple comme celle-ci pour chaque attribut de mes modèles :

:skip_validation?

Et dans mes contrôleurs, faites juste quelque chose comme :

skip_validation(:first_name, :last_name, :password) = true

Quelqu'un peut-il me suggérer une façon de programmer ceci ? Je préférerais ne pas utiliser une gemme/bibliothèque existante, mais j'essaie de comprendre comment programmer ce comportement dans rails. Merci.

1voto

TCopple Points 880

Cela pourrait vous aider à définir dynamiquement tous les setters et checkers.

private 
def self.skip_validations_for(*args)

  # this block dynamically defines a setter method 'set_validations' 
  # e.g.
  #   set_validations(true, :email, :last_name, :first_name)
  #   set_validations(false, :email, :last_name, :first_name)
  define_method('set_validations') do |value, *params|
    params.each do |var|
      self.send("skip_#{var.to_s}_validations=", value)
    end
  end

  # this block walks through the passed in fields and defines a checker
  # method `skip_[field_name]_validation?
  args.each do |arg|
    if self.method_defined? arg
      send :define_method, "skip_#{attr.to_s}_validation?" do 
        send "skip_#{attr.to_s}_validation"  
      end
    end
  end
end

Ensuite, dans votre modèle, que ce soit en natif dans la classe ou inclus par le biais d'un module, vous pouvez ajouter :

Class Foo < ActiveRecord::Base
  validate :bar, :presence => true
  validate :baz, :length_of => 10

  skip_validations_for :bar, :baz
end

à ce moment-là, vous aurez accès à

set_validations(true, :bar, :baz)

y

skip_bar_validation? 

mettre à jour :

Où irait ce code ?

Cela dépend de l'ampleur de l'utilisation que vous souhaitez en faire. Si vous voulez simplement remplacer les validations dans un modèle, vous pouvez tout mettre directement dans ce modèle. Si toutefois vous souhaitez créer un Module qui vous permet de faire cela dans chaque classe, alors vous devrez créer une Module le mettre dans le lib/ et s'assurer qu'il est dans le chemin de chargement. Peut-être ce pourrait vous aider.

Pourquoi les choses ont-elles changé si radicalement ?

À l'origine, j'utilisais la méthode _validations sur une instance et c'est une méthode ActiveRecord qui renvoie un hash de validations (nom du champ -> ActiveRecord::Validation). Cette méthode permet d'activer automatiquement la capacité pour chaque champ. C'est plus Rails de vous permettre d'opter pour les champs spécifiques que vous voulez.

0voto

theodorton Points 519

Je ne suis pas un expert en méta-programmation, mais je suppose que vous pourriez l'implémenter de la manière suivante.

class User < ActiveRecord::Base
  ...
  %w(email first_name last_name).each do |attr|
    self.send :attr_accessor, "skip_#{attr}_validation".to_sym
    self.send :alias, "skip_#{attr}_validation?".to_sym, "skip_#{attr}_validation".to_sym
    self.send :validates_presence_of, attr, { unless: "skip_#{attr}_validation?".to_sym }
  end

  def skip_validation_for(*attrs)
    attrs.map{|attr| send "skip_#{attr}_validation=", true }
  end
end

Ce n'est pas vraiment élégant de l'intégrer dans le modèle, mais vous pourriez en faire un module, dont l'utilisation pourrait être quelque chose comme.. :

class User < ActiveRecord::Base
  include SkipValidations
  skip_validations :email, :first_name, :last_name
end

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X