Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test whether a Ruby object is immutable?

Is there an easy way to test whether an object is a immutable (numbers, nil) or not (Array, Hash, objects)? In other words, could it be changed by side effects from other code?

Motivation: I want to create a versioned value store, but some of the data is arrays. Some of the arrays will store custom objects, and I could invert the relationship by storing the 'in' property and searching for it. But I'd also like to be able to store arrays of symbols, other arrays, etc.

like image 622
Justin Love Avatar asked Jan 18 '09 16:01

Justin Love


People also ask

Are objects immutable in Ruby?

Unlike numbers, booleans, and a few other types, most objects in Ruby are mutable; they are objects of a class that permit changes to the object's state in some way.

Is Ruby string immutable?

In most languages, string literals are also immutable, just like numbers and symbols. In Ruby, however, all strings are mutable by default.

Are floats immutable in Ruby?

Integers and floats are frozen by default, while booleans are not. So while some primitives are not frozen, Ruby does not provide mutation methods for them and they usually can be treated as immutable objects.

What is mutable in Ruby?

Don't let fancy words confuse you, “mutability” just means that an object's internal state can be changed. This is the default of all objects, excluding those that have been frozen, or those that are part of a list of special objects. In other words, not all objects in Ruby are mutable!


3 Answers

I found an inefficient way:

class Object
  def primitive?
    begin
      self.dup
      false
    rescue TypeError
      true
    end
  end
end
like image 133
Justin Love Avatar answered Sep 26 '22 18:09

Justin Love


There are no primitive objects in Ruby. This can therefore not be detected in a straightforward manner.

Can't you simply use Marshal or YAML for your versioned store? Then you'll get loading and saving of all object types for free. Why reinvent the wheel?

I don't know what you want to achieve exactly, but looking at the source of YAML may be interesting to see how they handle this problem. The Ruby YAML encoding implementation simply implements the to_yaml method for all relevant classes. See yaml/rubytypes.rb.

like image 38
wvanbergen Avatar answered Sep 25 '22 18:09

wvanbergen


The idea of mutability doesn't really apply in Ruby the same way as in other languages. The only immutable object is a frozen one. You can even add methods and instance variables to Fixnums. For example:

class Fixnum
  attr_accessor :name
end
1.name = "one"
2.name = "two"

Obviously, the vast majority of the time, people aren't going to be pathological enough to add attributes to Fixnum, but the point is, no unfrozen object is truly immutable.

If you can come up with a cannonical list of classes that you want to assume are immutable, you could just go through and give them all an immutable?() method that returns true (and Object a version that returns false). But like wvanbergen said, the best way to make sure your copy of an object doesn't change is to deep-copy it with Marshal.

like image 26
Chuck Avatar answered Sep 24 '22 18:09

Chuck