Video transcript & code
Here is an object. We have sneakily hidden its definition.
We would like to index into it using the subscript operator and an integer value.
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 # => "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
mysterio.class == Array # => true
There's a fairly serious limitation to this technique, however.
Let's say we have a class that inherits from
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
Or, we can ask it if it is a
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
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!