Teste se uma classe Ruby é uma subclasse de outra classe

187

Eu gostaria de testar se uma classe herda de outra classe, mas parece não existir um método para isso.

class A
end

class B < A
end

B.is_a? A 
=> false

B.superclass == A
=> true

Uma implementação trivial do que eu quero seria:

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

mas eu esperaria que isso já existisse.

Confusão
fonte
2
A.class #=> Class. É por isso que B.is_a? Aretorna falso.
Wen
que tal #kind_of?
akostadinov
1
kind_of?testa se um objeto é uma instância de uma classe. Não se o objeto herda de uma classe.
Confusion
5
kind_of?é um alias deis_a?
coreyward

Respostas:

355

Basta usar o <operador

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

ou use o <=operador

B <= A # => true
A <= A # => true
Marcel Jackwerth
fonte
13
@ Brian Porque is_a?traduz para é instância de . Bnão é uma instância de A, B.newé embora ( B.new.is_a? A # => true).
Marcel Jackwerth
4
Hmm, sintaxe estranha (não teria sido meu primeiro palpite), mas obrigado pelo esclarecimento!
Brian Armstrong
2
Para obter documentação, consulte API / módulo principal / # < .
Webwurst
2
Meu relacionamento de amor / ódio com Ruby continua… Por que fornecer uma função diferente para um operador usado para declarar relacionamentos de classe E fornecer duas maneiras diferentes de fazer isso?
Ben Gotow
4
@BenGotow - 1. Como <não é um operador, é um método. 2. Porque <verifica apenas uma subclasse e <= também inclui self.
superluminary
59

Também disponível:

B.ancestors.include? A

Isso difere um pouco da resposta (mais curta) do B < Aporque Bestá incluído em B.ancestors:

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

B < B
#=> false

B.ancestors.include? B
#=> true

Se isso é desejável ou não, depende do seu caso de uso.

Phrogz
fonte
24
Mais legível: B <= B(mesmo resultado que B.ancestors.include? B).
Marcel Jackwerth
Atualização: a solução imediatamente anterior funciona com 1.9+, enquanto não há "antepassados?" em 1.9.
8
Isso não confundirá pessoas que não estão familiarizadas com a sintaxe '<' e, por esse motivo, prefiro.
Asfand Qazi
2
@SimonLepkin Provavelmente não "um tempo", a menos que você possa experimentar microssegundos passando. ;) Sim, nos bastidores, os métodos include?e passam pela cadeia de ancestrais . Ele faz isso em C, com certeza mais rápido do que percorrer o array Ruby ... mas praticamente os dois devem ser indistinguíveis. <
21815 Phrogz
1
@JunanChakma Com base em como a palavra em inglês "antepassados" é definida, concordo que o valor de retorno não deve ser incluído B. Mas sim. A documentação do método diz: "Retorna uma lista de módulos incluídos / anexados no mod ( incluindo o próprio mod )". (ênfase minha). Acho que inclui sua própria classe por conveniência ao usar .include?, mas isso é apenas especulação da minha parte.
Phrogz