197

I would like to test whether a class inherits from another class, but there doesn't seem to exist a method for that.

class A
end

class B < A
end

B.is_a? A 
=> false

B.superclass == A
=> true

A trivial implementation of what I want would be:

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

but I would expect this to exist already.

Confusion
  • 14,331
  • 7
  • 43
  • 71

2 Answers2

372

Just use the < operator

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

or use the <= operator

B <= A # => true
A <= A # => true
webwurst
  • 4,352
  • 2
  • 21
  • 30
Marcel Jackwerth
  • 50,189
  • 8
  • 71
  • 87
61

Also available:

B.ancestors.include? A

This differs slightly from the (shorter) answer of B < A because B is included in B.ancestors:

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

B < B
#=> false

B.ancestors.include? B
#=> true

Whether or not this is desirable depends on your use case.

Phrogz
  • 271,922
  • 98
  • 616
  • 693
  • 25
    More readable: `B <= B` (same result as `B.ancestors.include? B`). – Marcel Jackwerth Dec 28 '10 at 14:49
  • Update: The immediately preceding solution works with 1.9+ whereas there is no "ancestors?" in 1.9. –  Nov 14 '11 at 06:40
  • @Barry I do not see `ancestors?` mentioned in any answer on this page, including this one. To what are you referring? – Phrogz Nov 15 '11 at 16:30
  • Update^2: The immediately preceding solution works with 1.9+ whereas I don't see "ancestors" in 1.9. --- Ambiguity corrected. My bad, I thought I was asking a question while using the overformal "Are these double-quotes sexy?" syntax. –  Nov 18 '11 at 22:54
  • 9
    This will not confuse people not familiar with the ' – Asfand Qazi Nov 18 '13 at 14:42
  • I note that this answer iterates through all ancestors, which might take a while. Is `B < A` meaningfully faster, or do they do the same thing behind the scenes? – Simon Lepkin Jul 10 '15 at 22:18
  • 2
    @SimonLepkin Probably not "a while", unless you can experience microseconds ticking by. ;) Yes, behind the scenes the `include?` and ` – Phrogz Jul 10 '15 at 22:36
  • @Phrogz Could you please explain me why `B.ancestors` returns itself `B` also? Should not it be `[A, Object, Kernel, BasicObject]` ? – Junan Chakma May 05 '18 at 05:45
  • 1
    @JunanChakma Based on how the English word "ancestors" is defined, I agree that the return value should not include `B`. But it does. The [method documentation](http://ruby-doc.org/core-2.5.0/Module.html#method-i-ancestors) says, _"Returns a list of modules included/prepended in mod (**including mod itself**)."_ (emphasis mine). I'm guessing it includes its own class for convenience when using `.include?`, but that's just speculation on my part. – Phrogz May 07 '18 at 02:59
  • @Phrogz Thanks! – Junan Chakma May 11 '18 at 10:55