Ruby Freeze Method

Ruby freeze method

  • freeze - prevents modification to the Hash (returns the frozen object)
  • [] - accesses a value from the hash
  • stat.class.name.underscore.to_sym - I assume this returns a lowercase, snake case version of the given object's class name (underscore is not in the standard library, so I'm not completely sure)
  • call invokes the lambda associated with stat.class.name.underscore.to_sym key.

For instance, passing ['foo', 'bar'] as the argument to track_for would invoke the send(stat[0], stat[1]) lambda.

How to freeze just part of an array or string?

Just because a string is frozen doesn't mean it can't be moved somewhere else in an array. Freezing an object just prevents the object itself from being modified.

What I would do is take out the element that you don't want to move, rotate the array, then put it back in at the same index after the rotation, like this:

arr = ['a', 'b', 'c', 'd', 'e']
index = 0
item = arr.delete_at(index)
arr.rotate!
arr.insert(index, item)

After that arr will be ["a", "c", "d", "e", "b"]

Freezing variables in Ruby doesn't work

You freeze objects, not variables, i.e. you can't update a frozen object but you can assign a new object to the same variable. Consider this:

a = [1,2,3]
a.freeze
a << 4
# RuntimeError: can't modify frozen Array

# `b` and `a` references the same frozen object
b = a
b << 4
# RuntimeError: can't modify frozen Array

# You can replace the object referenced by `a` with an unfrozen one
a = [4, 5, 6]
a << 7
# => [4, 5, 6, 7]

As an aside: it is quite useless to freeze Fixnums, since they are immutable objects.

How to unfreeze an object in Ruby?

No, according to the documentation for Object#freeze:

There is no way to unfreeze a frozen object.

The frozen state is stored within the object. Calling freeze sets the frozen state and thereby prevents further modification. This includes modifications to the object's frozen state.

Regarding your example, you could assign a new string instead:

script = 'Do you want to build a snowman?'
script.freeze

script = script.dup if script.frozen?
script[/snowman/] = 'castle of ice'
script #=> "Do you want to build a castle of ice?"

Ruby 2.3 introduced String#+@, so you can write +str instead of str.dup if str.frozen?

What is the use or effect of freezing Symbols and Numbers in Ruby?

The answer is no. Those data types are immutable. There is no reason to freeze those datatypes. The reason Ruby does not report those datatypes as frozen is because the obj.frozen? method returns the freeze status of the object and it is set to false initially for immutable datatypes. Calling obj.freeze will set the freeze status to true for that object.

The bottom line is that calling freeze on an immutable datatype sets the freeze status of the obj to true, but does nothing because the object is already immutable.

Freeze in Python?

You could always subclass list and add the "frozen" flag which would block __setitem__ doing anything:

class freezablelist(list):
def __init__(self,*args,**kwargs):
list.__init__(self, *args)
self.frozen = kwargs.get('frozen', False)

def __setitem__(self, i, y):
if self.frozen:
raise TypeError("can't modify frozen list")
return list.__setitem__(self, i, y)

def __setslice__(self, i, j, y):
if self.frozen:
raise TypeError("can't modify frozen list")
return list.__setslice__(self, i, j, y)

def freeze(self):
self.frozen = True

def thaw(self):
self.frozen = False

Then playing with it:

>>> from freeze import freezablelist as fl
>>> a = fl([1,2,3])
>>> a[1] = 'chicken'
>>> a.freeze()
>>> a[1] = 'tuna'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "freeze.py", line 10, in __setitem__
raise TypeError("can't modify frozen list")
TypeError: can't modify frozen list
>>> a[1:1] = 'tuna'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "freeze.py", line 16, in __setslice__
raise TypeError("can't modify frozen list")
TypeError: can't modify frozen list
>>>

Freezing HashWithIndifferentAccess in Ruby

So we're looking at how the nested hash behaves differently between

v_1 = { a: { b: 2 } }.with_indifferent_access
v_2 = { a: { b: 2 }.with_indifferent_access }.with_indifferent_access

When you call Hash#with_indifferent_access, it creates a new ActiveSupport::HashWithIndifferentAccess object; and then calls #update to insert all of the key/value pairs from the original hash into the new object (ActiveSupport::HashWithIndifferentAccess#update), which calls #convert_values with the nested hash:

def convert_value(value, options = {})
if value.is_a? Hash
if options[:for] == :to_hash
value.to_hash
else
value.nested_under_indifferent_access
end
...

So both { b: 2 } and { b: 2 }.with_indifferent_access will have #nested_under_indifferent_access called on them. But that’s a different method for Hash than it is for HashWithIndifferentAccess. In the core_ext file, Hash#nested_under_indifferent_access calls HashWithIndifferentAccess.new(self), but HashWithIndifferentAccess#nested_under_indifferent_access just returns self.

So {'b' => 2}.nested_under_indifferent_access returns a new object, but {'b' => 2}.with_indifferent_access.nested_under_indifferent_access doesn’t do anything to the object. Which is why if the first one is frozen, you get back a different (unfrozen by default) object, and if the second one is frozen, it stays frozen.



Related Topics



Leave a reply



Submit