Last active
August 29, 2015 14:08
-
-
Save danott/d82b3c8fea686371115d to your computer and use it in GitHub Desktop.
Rails' attr_readonly prevents updates from being written to the database, but does not prevent them from being changed in memory. If you have derived values from what's in memory, this can be problematic. For rolling out a new feature, I want to know if any of our code paths are trying to update the field. I don't want it to fail silently and be…
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Creating a whole new concern, introducing the `attr_immutable` macro | |
module ImmutableAttributes | |
class ImmutableAttributeError < StandardError; end | |
extend ActiveSupport::Concern | |
included do | |
class_attribute :_attr_immutable, instance_accessor: false | |
self._attr_immutable = [] | |
end | |
module ClassMethods | |
def attr_immutable(*attributes) | |
self._attr_immutable = Set.new(attributes.map(&:to_s)) + (immutable_attributes) | |
attributes.each do |attribute| | |
define_method("#{attribute}=") do |value| | |
if !new_record? | |
raise ImmutableAttributeError.new("#{self.class}##{attribute} is immutable") | |
else | |
super(value) | |
end | |
end | |
end | |
end | |
def immutable_attributes | |
self._attr_immutable | |
end | |
end | |
def []=(attribute, value) | |
if !new_record? && self.class.immutable_attributes.include?(attribute.to_s) | |
raise ImmutableAttributeError.new("#{self.class}##{attribute} is immutable") | |
else | |
super(attribute, value) | |
end | |
end | |
end | |
class Event | |
include ImmutableAttributes | |
attr_immutable :starts_at | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Piggy backing on Rails' `attr_readonly`, and making it fail loudly when trying to | |
# change the value in memory. | |
module NoisyReadonlyAttributes | |
extend ActiveSupport::Concern | |
class NoisyReadonlyAttributeError < StandardError; end | |
included do | |
after_initialize make_readonly_attrs_noisy | |
end | |
def []=(attribute, value) | |
if !new_record? && self.class.readonly_attributes.include?(attribute.to_s) | |
raise NoisyReadonlyAttributeError.new("#{self.class}##{attribute} is read only") | |
else | |
super(attribute, value) | |
end | |
end | |
private | |
def make_readonly_attrs_noisy | |
self.class.readonly_attributes.each do |attribute| | |
define_singleton_method("#{attribute}=") do |value| | |
if !new_record? | |
raise NoisyReadonlyAttributeError.new("#{self.class}##{attribute} is read only") | |
else | |
super(value) | |
end | |
end | |
end | |
end | |
end | |
# Example Usage | |
class Event | |
include NoisyReadonlyAttributes | |
attr_readonly :starts_at | |
end |
@rmosolgo yep, looking for the better of the two. Or if there's a third that is better than both of them, I wanna know about that!
👍 Great use of a concern.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
👍 seems like a good feature if the Rails built-in isn't cutting it!
Am I supposed to pick a favorite? I prefer
attr_immutable