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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
|
module ActiveRecord::Associations::Builder
class BelongsTo < SingularAssociation #:nodoc:
def macro
:belongs_to
end
def valid_options
super + [:foreign_type, :polymorphic, :touch]
end
def self.valid_dependent_options
[:destroy, :delete]
end
def define_callbacks(model, reflection)
super
add_counter_cache_callbacks(model, reflection) if options[:counter_cache]
add_touch_callbacks(model, reflection) if options[:touch]
end
def define_accessors(mixin, reflection)
super
add_counter_cache_methods mixin
end
private
def add_counter_cache_methods(mixin)
return if mixin.method_defined? :belongs_to_counter_cache_after_create
mixin.class_eval do
def belongs_to_counter_cache_after_create(reflection)
if record = send(reflection.name)
cache_column = reflection.counter_cache_column
record.class.increment_counter(cache_column, record.id)
@_after_create_counter_called = true
end
end
def belongs_to_counter_cache_before_destroy(reflection)
foreign_key = reflection.foreign_key.to_sym
unless destroyed_by_association && destroyed_by_association.foreign_key.to_sym == foreign_key
record = send reflection.name
if record && !self.destroyed?
cache_column = reflection.counter_cache_column
record.class.decrement_counter(cache_column, record.id)
end
end
end
def belongs_to_counter_cache_after_update(reflection)
foreign_key = reflection.foreign_key
cache_column = reflection.counter_cache_column
if (@_after_create_counter_called ||= false)
@_after_create_counter_called = false
elsif attribute_changed?(foreign_key) && !new_record? && reflection.constructable?
model = reflection.klass
foreign_key_was = attribute_was foreign_key
foreign_key = attribute foreign_key
if foreign_key && model.respond_to?(:increment_counter)
model.increment_counter(cache_column, foreign_key)
end
if foreign_key_was && model.respond_to?(:decrement_counter)
model.decrement_counter(cache_column, foreign_key_was)
end
end
end
end
end
def add_counter_cache_callbacks(model, reflection)
cache_column = reflection.counter_cache_column
model.after_create lambda { |record|
record.belongs_to_counter_cache_after_create(reflection)
}
model.before_destroy lambda { |record|
record.belongs_to_counter_cache_before_destroy(reflection)
}
model.after_update lambda { |record|
record.belongs_to_counter_cache_after_update(reflection)
}
klass = reflection.class_name.safe_constantize
klass.attr_readonly cache_column if klass && klass.respond_to?(:attr_readonly)
end
def self.touch_record(o, foreign_key, name, touch) # :nodoc:
old_foreign_id = o.changed_attributes[foreign_key]
if old_foreign_id
klass = o.association(name).klass
old_record = klass.find_by(klass.primary_key => old_foreign_id)
if old_record
if touch != true
old_record.touch touch
else
old_record.touch
end
end
end
record = o.send name
unless record.nil? || record.new_record?
if touch != true
record.touch touch
else
record.touch
end
end
end
def add_touch_callbacks(model, reflection)
foreign_key = reflection.foreign_key
n = name
touch = options[:touch]
callback = lambda { |record|
BelongsTo.touch_record(record, foreign_key, n, touch)
}
model.after_save callback
model.after_touch callback
model.after_destroy callback
end
end
end
|