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.
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.
In most languages, string literals are also immutable, just like numbers and symbols. In Ruby, however, all strings are mutable by default.
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.
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!
I found an inefficient way:
class Object
def primitive?
begin
self.dup
false
rescue TypeError
true
end
end
end
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With