Object Equality & States in Ruby

A good area to spend some time for someone new to Ruby is to understand about objects. This serves as a basic wrap-around over view of what you need to know to get going.

First off remember to use the .class method on any object too (in your IRB, for example), giving you its class name. If you’re playing along here, that’ll help.

You will need to know what object orientation is in programming for this to make sense, and I hope it is geared to people coming from an object orientation learned in a different environment.

Everything in Ruby is an object. Even true and false have their own objects. Occasionally operators (like << and ||) are language contructs, but that's really insignificant for the purpose of this lessen. When we say nearly everything in Ruby we mean everything you think is an object and then some. Other languages have what are called primitives -- numbers, integers or floats; strings; booleans, etc. These things are see by the compiler and treated as what they are. But in Ruby all the primitives are objects too: numbers, strings, even boolean operators have their own classes. So, you can do things like ask an object if it is a nil object. Since it is used to return a negative meaning to an operation or a non-result, it's a good way to catch edge cases. False and nil are trip-ups for programmers coming from other languages where because false does not evaluate to 0 and true doesn't eval to 1.

// only nil is nil! everything will give you false
?> “”.nil?
=> false
>> 0.nil?
=> false
>> nil.nil?
=> true

Instead, get used to the idea of there being object which are TrueClass, FalseClass, and NilClass. Since variables can change classes, at a whim, you are freed from having to worry about this most of the time.

// demonstrates that you can change a variable to a different class
>> a = false
=> false
>> a.class
=> FalseClass
>> a = true
=> true
>> a.class
=> TrueClass

So the net affect of just describe above is that an object can be true or false. And you can compare objects just like that, assign them to true or false, without really digging deep. But, this will help you understand the chart.

Pass & Assign by reference. Most things are passed and assigned by reference. So let’s try to create a general metaphore for any programmer and then we’ll get into how this contracts with C++ programming

[MORE ABOUT PASSING & ASSIGNING BY REFERENCE]

Underlying objects. So outfall of all this is that we get a bunch of ways to test for equality. Test for the variables to refer to the very same objects. Take a look at these two

equal?
a.object_id == b.object_id

Remember you know that 2 variables point to the same object if their object_id(s) are the same. So equal? is an alias of this, more complicated syntax.

==
eql?

Next we get the distilled operators most programmers from other language would recognize. These as the two operands if they are equal in value, after they get evaluated.

But, one more snarl, the object might return classes of different types. In this case you will need the == operator, because that will try to coerce the second operand into the first. (Throwing an exception if it can’t)

>> 1 == 1.0
>> true
>> 1.eql?(1.0)
>> false

This is because 1 is an integer and 1.0 is a float. The == sign can easily convert the float to an integer. But the eql? operator won’t do type conversion, so those two objects (one an integer and one a float) are not seen as equal.

Here’s a complete table for reference with a few other equal-sign-like symbols for completeness sake.

Symbol Definition
equal? Tests if two values refer to the exact same object.
a.object_id == b.object_id same as a.equal?(b)
== Tests if two objects have equal values. Usually does type conversion (Works this way for strings, numbers, arrays, & hashes, except hashe values are compared with the eql? method)
eql? Tests if two objects have equal values but does not do type conversion. 1 == 1.0 is true; 1.eql?(1.0) is false
=== Used for testing whether target value of a case statement matches any when clause; For many classes, case equality (===) is the same as value equality (==). Some exceptions to this are Range, Regexp, and Class which define a broader case equality than the == comparison.
=~ Defined by String and Regexp to perform pattern matching (really not an operator at all).
!~ opposite of =~
<=> Returns -1, 0, or 1 depending if a is less than, equal to , or greater than b, respectively; Uses the Comparable mixin module.
?> a = “2”
=> “2”
>> b = a
=> “2”
>> a.object_id
=> 17396400
>> b.object_id
=> 17396400
>> b
=> “2”
>> a.equal? b
=> true
>> b.equal? a
=> true
>> c = “2”
=> “2”
>> c.object_id
=> 17364870
>> a.equal? c
=> false
>> b.equal? c
=> false
>> a == c
=> true
>> b == c
=> true

Here a basic primer on freezing, tainting, copying, cloning, duping, and marshalling.

Term What it does Note
Freezing Makes the object immutable — none of its internal state may be changed. Once frozen, the object is frozen forever — there is no way to “thaw” an object See dup below. which will let you create an unfrozen copy
Tainting Used to keep track of whether the object might contain untrusted user input. Can be untained using untaint method — only do this if you have examined the object and are convinced that it presents no security risks. User input, command-line arguments, environmental variables, and strings read with gets are all automatically tainted. When used with the $SAFE global variable, Ruby restricts various built-in methods so that they will not work with tainted data.
Shallow copy Using either clone or dup (see below) will return a returns a shallow copy: If the copied object refers to other objects, only the references are copied, not the referenced objects.
Clone Copies both frozen and tainted state. Returns a shallow copy.
Dup Copies only the tainted state. Calling dup on a frozen object returns an unfrozen object. Returns a shallow copy.
Marshaling Used to save the state of an object for later use (turns it into binary output for saving to a string or I/O stream, like a file). Pass it to the class Marshal.dump. To restore a marshaled object, pass a string or I/O stream containing the object to Marshal.load. YAML is a common alternative to Marshal; NOTE: Marshal in Ruby is spelled with only one “L” even though in English both “marshal” and “marshall” are considered correct spellings.

Leave a Reply





Your email address will not be published. Required fields are marked *

Previous post Arrays in Ruby
Next post Configure git so that it shows green & red in my terminal