What is frozen attributes
Frozen is an object or attribute immutable, this means that once an object is frozen, you cannot modify it or its state.
When Rails needs to freeze an object, it typically does so by calling freeze
on the object or its attributes at appropriate times in the object's lifecycle.
Immutability
Immutability refers to the concept that an object, once created, cannot be changed or modified it remains constant throughout its lifecycle.
Advantages of frozen attributes
- Immutability
Freezing attributes can prevent accidental or intentional modifications, which is particularly useful in situations where data integrity is critical. - Thread Safety
Immutable objects are thread-safe since their state cannot be changed, reducing the chances of race conditions and making concurrent code easier to manage. - Performance Improvements
Since frozen objects do not change, certain optimizations can be applied by the Ruby interpreter. For example, frozen strings can be reused across the application.
Disadvantages of frozen attributes
- Complexity in Implementation
Implementing immutability can add complexity to the codebase, especially when dealing with objects that have deeply nested structures or dependencies. - Increased Memory Usage
In some cases, immutability might lead to increased memory usage due to the need to create new objects rather than modifying existing ones. This can be particularly relevant in applications with large data structures. - Difficulty in Debugging
If not documented and managed properly, frozen attributes can lead to confusion and make debugging more challenging, as developers might not expect certain attributes to be immutable.
Is object can be freeze
Yes, in Ruby and Rails, you can freeze an entire object to make it immutable. When you freeze an object, you prevent any modifications to its state.
This can be useful in situations where you want to ensure that the object's state remains consistent and cannot be changed after it has been set.
user = User.new(name: "Rajesh", email: "rajesh.m@gmail.com")
user.freeze
# Attempting to modify the object will raise a RuntimeError
user.name = "Saji Sharma" # => RuntimeError: can't modify frozen User
Custom freezing logic
If you want to ensure that an object is frozen during or after certain operations, you need to implement this logic explicitly.
For instance, you can add a callback to freeze an object after it has been published:
class Product < ApplicationRecord
after_save :freeze_name_if_published
private
def freeze_name_if_published
name.freeze if published?
end
end
Why did I start thinking about the frozen attribute, and what issue did I encounter?
Consider our Payment model, which includes multiple transactions. A payment is marked as "paid" after a successful transaction, updating the status column in the Payment model.
However, if a transaction is removed, the payment is reopened to be marked as "unpaid."
This scenario presents a significant issue.
class Payment < ApplicationRecord
has_many :transactions, dependent: :destroy
end
class Transaction < ApplicationRecord
belongs_to :payment
after_save_commit :mark_payment_as_paid
after_destroy_commit :reopen_the_payment
private
def mark_payment_as_paid
payment.update(status: :paid) if is_successfull_transaction?
end
def reopen_the_payment
payment.update(status: :open) # issue
end
end
What is the issue? When we destroy the payment record, it also deletes the dependent transactions.
However, if the payment is reopened after a transaction, attempting to delete the payment will result in the following error.
FrozenError: can't modify frozen attributes
Rails will freeze the object, preventing further modifications until it is deleted.
How we solved the issue
We need to check if the object is frozen because we don't explicitly freeze it anywhere in the code. Therefore, we used this check to determine its state.
class Transaction < ApplicationRecord
belongs_to :payment
after_destroy_commit :reopen_the_payment
private
def reopen_the_payment
return if payment.frozen?
payment.update(status: :open) # issue
end
end
What is the alternative solutions to solve the above issue
There are alternative solutions available!
Stay tuned, and we'll discuss them in detail later! Thank you!