Détecter le changement d'un attribut pour une valeur précise

Posted by ZedTuX 0n R00t on January 19, 2018

Petite découverte de la semaine en Ruby On Rails et les dirty attributes.

Peut-être savez-vous déjà que, par exemple, dans un modèle avec un callback du type before_save vous pouvez accéder aux modifications des attributs de l’instance du modèle avec changes qui vous retourne un Hash avec l’attribut comme clé et la valeur précédente et la nouvelle valeur?

1
person.changes # => { 'name' => ['bill', 'bob'] }

Peut-être savez-vous aussi qu’une méthode du type <nom de l'attribut>_changed? existe pour chacun des attributs de votre modèle?

1
2
person.name_changed? # => true
person.age_changed? # => false

Mais savez-vous comment détecter si un attribut change d’une valeur précise vers une autre?

Disons que vous avez un modèle User qui possède un champs authentication_method, de type :integer qui contient le mode d’authentification de ce dernier. Disons aussi que l’une des méthodes d’authentifications demande à ce que des données soient mise à jour si l’utilisateur change sa méthode d’authentification, par exemple une méthode qui enregistre une clé USB, mais si l’utilisateur change le mode d’authentification, ses clés doivent être effacés.

Prenons la constante suivante:

1
AUTHENTICATION_METHODS = %w[no_method ga_authenticator fido_u2f].freeze

Comment faire pour détecter que authentication_method change de la valeur 2 (position de fido_u2f dans la constante AUTHENTICATION_METHODS) vers une autre valeur?

Voici ce que j’ai trouvé dans la documentation des dirty attributes de Rails:

1
2
3
4
5
6
7
8
9
10
11
12
13
class User
  AUTHENTICATION_METHODS = %w[no_method ga_authenticator fido_u2f].freeze
  
  before_update :destroy_u2f_keys_if_removing_fido_authentification_method
  
  private
  
  def destroy_u2f_keys_if_removing_fido_authentification_method
    return unless authentication_method_changed?(from: fido_u2f_method)
    
    fido_usf_devices.destroy_all
  end
end

Donc la méthode <nom de l'attribut>_changed? peut recevoir les paramètres :from et :to, ici je n’utilise que le paramètre :from puisque seule la valeur précédente m’intéresse ici.

Belle surprise 😀