forked from mongodb/mongoid
/
synchronization.rb
113 lines (104 loc) · 3.13 KB
/
synchronization.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
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
# encoding: utf-8
module Mongoid # :nodoc:
module Relations #:nodoc:
# This module handles the behaviour for synchronizing foreign keys between
# both sides of a many to many relations.
module Synchronization
extend ActiveSupport::Concern
# Is the document able to be synced on the inverse side? This is only if
# the key has changed and the relation bindings have not been run.
#
# @example Are the foreign keys syncable?
# document.syncable?(metadata)
#
# @param [ Metadata ] metadata The relation metadata.
#
# @return [ true, false ] If we can sync.
#
# @since 2.1.0
def syncable?(metadata)
!synced?(metadata.foreign_key) && send(metadata.foreign_key_check)
end
# Get the synced foreign keys.
#
# @example Get the synced foreign keys.
# document.synced
#
# @return [ Hash ] The synced foreign keys.
#
# @since 2.1.0
def synced
@synced ||= {}
end
# Has the document been synced for the foreign key?
#
# @todo Change the sync to be key based.
#
# @example Has the document been synced?
# document.synced?
#
# @param [ String ] foreign_key The foreign key.
#
# @return [ true, false ] If we can sync.
#
# @since 2.1.0
def synced?(foreign_key)
!!synced[foreign_key]
end
# Update the inverse keys for the relation.
#
# @example Update the inverse keys
# document.update_inverse_keys(metadata)
#
# @param [ Metadata ] meta The document metadata.
#
# @return [ Object ] The updated values.
#
# @since 2.1.0
def update_inverse_keys(meta)
old, new = changes[meta.foreign_key]
meta.criteria(new - old).add_to_set(meta.inverse_foreign_key, id)
meta.criteria(old - new).pull(meta.inverse_foreign_key, id)
end
module ClassMethods #:nodoc:
# Set up the syncing of many to many foreign keys.
#
# @example Set up the syncing.
# Person.synced(metadata)
#
# @param [ Metadata ] metadata The relation metadata.
#
# @since 2.1.0
def synced(metadata)
synced_save(metadata)
end
private
# Set up the sync of inverse keys that needs to happen on a save.
#
# If the foreign key field has changed and the document is not
# synced, $addToSet the new ids, $pull the ones no longer in the
# array from the inverse side.
#
# @example Set up the save syncing.
# Person.synced_save(metadata)
#
# @param [ Metadata ] metadata The relation metadata.
#
# @return [ Class ] The class getting set up.
#
# @since 2.1.0
def synced_save(metadata)
tap do
set_callback(
:save,
:after,
:if => lambda { |doc| doc.syncable?(metadata) }
) do |doc|
doc.update_inverse_keys(metadata)
end
end
end
end
end
end
end