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
|
# File 'lib/active_record/json_associations.rb', line 20
def belongs_to_many(many, class_name: nil, touch: nil)
one = many.to_s.singularize
one_ids = :"#{one}_ids"
one_ids_equals = :"#{one_ids}="
many_equals = :"#{many}="
many_eh = :"#{many}?"
class_name ||= one.classify
using_json = columns_hash[one_ids.to_s].type == :json
if !using_json
if ActiveRecord.version >= Gem::Version.new("7.1")
serialize one_ids, coder: JSON
else
serialize one_ids, JSON
end
end
if touch
after_commit do
unless no_touching?
method = respond_to?(:saved_changes) ? :saved_changes : :previous_changes
old_ids, new_ids = send(method)[one_ids.to_s]
ids = Array(send(one_ids)) | Array(old_ids) | Array(new_ids)
scope = class_name.constantize.where(self.class.primary_key => ids)
if scope.respond_to?(:touch) scope.touch_all
elsif self.class.respond_to?(:touch_attributes_with_time) scope.update_all self.class.touch_attributes_with_time
else attributes = timestamp_attributes_for_update_in_model.inject({}) do |attributes, key|
attributes.merge(key => current_time_from_proper_timezone)
end
scope.update_all attributes
end
end
end
end
extend Module.new {
define_method :"#{one_ids}_including" do |id|
raise "can't query for a record that does not have an id!" if id.blank?
if id.is_a?(Hash)
Array(id[:any]).inject(none) do |context, id|
context.or(FIELD_INCLUDE_SCOPE_BUILDER_PROC.call(self, one_ids, id))
end
else
FIELD_INCLUDE_SCOPE_BUILDER_PROC.call(self, one_ids, id)
end
end
}
include Module.new {
define_method one_ids do
super().tap do |value|
return send(one_ids_equals, []) if value.nil?
end
end
define_method one_ids_equals do |ids|
super Array(ids).select(&:present?).map(&:to_i)
end
define_method many do
klass = class_name.constantize
scope = klass.all
ids = send(one_ids)
scope.where!(klass.primary_key => ids)
fragments = []
fragments += ["#{klass.primary_key} NOT IN (#{ids.map(&:to_s).join(",")})"] if ids.any?
fragments += ids.reverse.map { |id| "#{klass.primary_key}=#{id}" }
order_by_ids = fragments.join(", ")
scope.order!(Arel.sql(order_by_ids))
end
define_method many_equals do |collection|
send one_ids_equals, collection.map(&:id)
end
define_method many_eh do
send(one_ids).any?
end
}
end
|