forked from JackDanger/immutable_attributes
/
immutable_attributes.rb
75 lines (64 loc) · 1.97 KB
/
immutable_attributes.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
begin
require 'activerecord'
rescue LoadError
require 'active_record'
end
module ImmutableErrors
class ImmutableAttributeError < ActiveRecord::ActiveRecordError
end
end
module ImmutableAttributes
VERSION = "1.0.3"
def attr_immutable(*args)
args.each do |meth|
class_eval do
define_method("#{meth}=") do |value|
new_record? || read_attribute(meth).nil? ?
write_attribute(meth, value) :
raise(ActiveRecord::ImmutableAttributeError, "#{meth} is immutable!")
end
end
end
# handle ActiveRecord::Base#[]=
class_eval <<-RUBY
def []=(attr_name, value)
if #{args}.include? attr_name
raise(ActiveRecord::ImmutableAttributeError, "\#{attr_name} is immutable!")
else
write_attribute(attr_name, value)
end
end
RUBY
end
def validates_immutable(*attr_names)
config = { :on => :update, :if => lambda {|x| true}, :message => "can't be changed" }
config.update(attr_names.extract_options!)
@immutables = attr_names
attr_names.each do |meth|
class_eval do
define_method("original_#{meth}") do
instance_variable_get("@original_#{meth}")
end
end
end
class_eval do
def self.immutables
@immutables
end
def after_initialize; end;
def setup_originals
self.class.immutables.each do |attr_name|
next unless attribute_names.include? attr_name
instance_variable_set("@original_#{attr_name}", send(attr_name.to_s))
end
end
after_initialize :setup_originals
end
validates_each(attr_names, config) do |record, attr_name, value|
next if record.send("original_#{attr_name.to_s}").nil?
record.errors.add(attr_name, config[:message]) if record.send("original_#{attr_name.to_s}") != record.send(attr_name.to_s)
end
end
end
ActiveRecord.send :include, ImmutableErrors
ActiveRecord::Base.extend ImmutableAttributes