In Progress
Unit 1, Lesson 1
In Progress

Class Membership

Video transcript & code

Here is an object. We have sneakily hidden its definition.

mysterio

We would like to index into it using the subscript operator and an integer value.

mysterio[3]

But is this mysterious object an array, to support this indexing operation? How can we find out?

In Ruby, the first answer to that question should always be another question: do we really need to know its class?

You've probably heard the term "duck typing" while working with Ruby. The point of duck-typing is not to ask objects if they are ducks. A truly duck-typed method simply relies on objects to respond to the messages it sends without worrying about their class.

mysterio[3]                     # => "Quack"

If they don't support the message, they'll raise a NoMethodError and we'll know that something's amiss in our code.

In some cases we may need some more certainty about our inputs before working with them. In those cases, it still usually isn't necessary to explicitly check the type of an object. It's often a better idea to perform some kind of conversion at the beginning of the code.

In this case, the arrayifier method will find a way to convert just about any value into an array.

Or, we can insist that only explicitly-convertible objects make it past this line.

Or if we want to be even more strict, we can use implicit conversion to limit the input to actual arrays or objects which are interchangeable with arrays.

If you want to learn more about the difference between implicit and explicit conversion methods, check out episode #210.

Array(mysterio)                 # => ["Moo", "Woof", "Hiss", "Quack"]
mysterio.to_a                   # => ["Moo", "Woof", "Hiss", "Quack"]
mysterio.to_ary                 # => ["Moo", "Woof", "Hiss", "Quack"]

But, for the sake of argument, let's say that for some reason we really do need to know, specifically, whether this object an array. Not just whether it is convertible to one.

Perhaps the most obvious way to test this property is to check the object's special class attribute.

mysterio.class == Array         # => true

There's a fairly serious limitation to this technique, however.

Let's say we have a class that inherits from Array.

And we have an instance of this child class.

Since this object is an instance of an Array subclass, it is an array. But when we ask if its class is equal to Array, the answer is negative. That's because we're simply testing whether its class attribute is specifically equal to the Array class object.

But it's not; it's equal to the MoreAwesomeArray class object.

By the way, there's another, perhaps more idiomatic way to ask this particular question of an object. We can ask it if it's an instance_of? the supplied class.

class MoreAwesomeArray < Array
end

stuff = MoreAwesomeArray.new

stuff.class == Array            # => false
stuff.class == MoreAwesomeArray # => true

stuff.instance_of?(Array)       # => false
stuff.instance_of?(MoreAwesomeArray) # => true

But in most cases when we're testing inclusion in a class, this isn't really what we want. We want to know if the object is an instance of the class or of one of its subclasses.

To do this, we can instead ask the object if it #is_a? Array.

Or, we can ask it if it is a kind_of? Array.

class MoreAwesomeArray < Array
end

stuff = MoreAwesomeArray.new

stuff.is_a?(Array)             # => true
stuff.kind_of?(Array)          # => true

What is the difference between these two messages? Nothing at all. They are aliases for each other. It's just one of those cases where Ruby offers two different ways to say the same thing, and you get to pick the one you prefer. I find myself using is_a? more often, but I can't give you a specific reason why. All I suggest for your own code is that you pick one and use it consistently.

So, the first answer to "how do we test object class membership in Ruby" is: we don't. But if we really do find a need to do it, the is_a? or kind_of? predicate methods are usually what we want.

Now, this isn't the whole story. Eventually, you will run into objects for which these methods either don't exist, or return unreliable information. But that's a topic for an episode on advanced class membership testing. So until then: happy hacking!

Responses