215

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

(N.B. the is_a? test above does not make any sense, as the first comment correctly points out B.new.is_a?(A) would make sense, but is not generally applicable, as not every class has a #initialize method that accepts 0 arguments)

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
  • 16,256
  • 8
  • 46
  • 71

2 Answers2

399

Just use the < operator

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

or use the <= operator

B <= A # => true
A <= A # => true
webwurst
  • 4,830
  • 3
  • 23
  • 32
Marcel Jackwerth
  • 53,948
  • 9
  • 74
  • 88
68

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
  • 296,393
  • 112
  • 651
  • 745
  • 26
    More readable: `B <= B` (same result as `B.ancestors.include? B`). – Marcel Jackwerth Dec 28 '10 at 14:49
  • 1
    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 '<' syntax, and for that reason I prefer it. – 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 `<` methods [loop through the ancestor chain](https://github.com/ruby/ruby/blob/trunk/class.c#L1023). It does this in C, so surely faster than looping through the Ruby array...but practically the two should be indistinguishable. – 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
  • 2
    @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