Tester si une classe Ruby est une sous-classe d'une autre classe

192

Je voudrais tester si une classe hérite d'une autre classe, mais il ne semble pas exister de méthode pour cela.

class A
end

class B < A
end

B.is_a? A 
=> false

B.superclass == A
=> true

Une implémentation triviale de ce que je veux serait:

class Class
  def is_subclass_of?(clazz)
    return true if superclass == clazz
    return false if self == Object
    superclass.is_subclass_of?(clazz)
  end
end

mais je m'attendrais à ce que cela existe déjà.

Confusion
la source
2
A.class #=> Class. C'est pourquoi B.is_a? Arenvoie false.
Wen
Qu'enkind_of?
akostadinov
1
kind_of?teste si un objet est une instance d'une classe. Pas si l'objet hérite d'une classe.
Confusion
5
kind_of?est un alias deis_a?
coreyward

Réponses:

361

Utilisez simplement l' <opérateur

B < A # => true
A < A # => false

ou utilisez l' <=opérateur

B <= A # => true
A <= A # => true
Marcel Jackwerth
la source
13
@Brian Car se is_a?traduit par une instance de . Bn'est pas une instance de A, B.newest si ( B.new.is_a? A # => true).
Marcel Jackwerth
4
Hmm, syntaxe étrange (n'aurait pas été ma première supposition), mais merci pour la clarification!
Brian Armstrong
2
Pour la documentation, voir Core API / Module / # < .
webwurst
2
Ma relation d'amour / haine avec Ruby se poursuit… Pourquoi fournir une fonction différente à un opérateur utilisé pour déclarer des relations de classe ET fournir deux façons différentes de le faire?
Ben Gotow
4
@BenGotow - 1. Puisque <n'est pas un opérateur, c'est une méthode. 2. Parce que <ne vérifie qu'une sous-classe, et <= inclut également self.
superluminaire
61

Aussi disponible:

B.ancestors.include? A

Cela diffère légèrement de la réponse (plus courte) de B < Acar elle Best incluse dans B.ancestors:

B.ancestors
#=> [B, A, Object, Kernel, BasicObject]

B < B
#=> false

B.ancestors.include? B
#=> true

Que cela soit souhaitable ou non dépend de votre cas d'utilisation.

Phrogz
la source
25
Plus lisible: B <= B(même résultat que B.ancestors.include? B).
Marcel Jackwerth
Mise à jour: La solution immédiatement précédente fonctionne avec 1.9+ alors qu'il n'y a pas "d'ancêtres?" dans 1.9.
9
Cela ne déroutera pas les personnes qui ne sont pas familières avec la syntaxe «<», et c'est pour cette raison que je la préfère.
Asfand Qazi
2
@SimonLepkin Probablement pas "pendant un certain temps", à moins que vous ne puissiez ressentir des microsecondes. ;) Oui, dans les coulisses, les méthodes include?et parcourent la chaîne des ancêtres . Il le fait en C, donc sûrement plus vite que de boucler le tableau Ruby ... mais pratiquement les deux devraient être indiscernables. <
Phrogz
1
@JunanChakma D'après la définition du mot anglais «ancestors», je suis d'accord que la valeur de retour ne doit pas inclure B. Mais c'est le cas. La documentation de la méthode dit: "Renvoie une liste des modules inclus / ajoutés au début du mod ( y compris le mod lui-même )." (c'est moi qui souligne). Je suppose qu'il inclut sa propre classe pour plus de commodité lors de son utilisation .include?, mais ce n'est que de la spéculation de ma part.
Phrogz