Class: Collavre::Creatives::PermissionCacheBuilder

Inherits:
Object
  • Object
show all
Defined in:
app/services/collavre/creatives/permission_cache_builder.rb

Class Method Summary collapse

Class Method Details

.cache_owner(creative) ⇒ Object

Creative 생성 시 소유자 캐시 추가



5
6
7
8
9
10
11
12
13
14
15
16
17
18
# File 'app/services/collavre/creatives/permission_cache_builder.rb', line 5

def self.cache_owner(creative)
  return unless creative.user_id

  CreativeSharesCache.upsert(
    {
      creative_id: creative.id,
      user_id: creative.user_id,
      permission: CreativeShare.permissions[:admin],
      source_share_id: nil
    },
    unique_by: [ :creative_id, :user_id ],
    update_only: [ :permission, :source_share_id ]
  )
end

.propagate_share(creative_share) ⇒ Object

CreativeShare 생성/업데이트 시 호출



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
# File 'app/services/collavre/creatives/permission_cache_builder.rb', line 36

def self.propagate_share(creative_share)
  return if creative_share.destroyed?

  creative = creative_share.creative
  user_id = creative_share.user_id
  permission = creative_share.permission

  # 해당 creative + 모든 자손 ID (closure_tree 사용)
  all_descendant_ids = [ creative.id ] + creative.descendant_ids

  # "closest share wins" 의미론 적용:
  # 이 share의 자손 중 더 가까운 share가 있는 creative는 제외
  # (더 가까운 share가 해당 서브트리를 담당)
  # no_access도 closer share로 인정 (public share보다 우선)
  closer_share_creative_ids = CreativeShare
    .where(user_id: user_id)
    .where(creative_id: creative.descendant_ids)  # 이 creative의 strict descendants에 있는 share들
    .where.not(id: creative_share.id)  # 자기 자신 제외
    .pluck(:creative_id)

  # 더 가까운 share가 있는 creative와 그 자손들은 제외
  excluded_ids = Set.new
  if closer_share_creative_ids.any?
    excluded_ids = CreativeHierarchy
      .where(ancestor_id: closer_share_creative_ids)
      .pluck(:descendant_id)
      .to_set
  end

  ids_to_update = all_descendant_ids.reject { |id| excluded_ids.include?(id) }

  return if ids_to_update.empty?

  permission_value = CreativeShare.permissions[permission]

  # Handle NULL user_id (public shares) separately since SQLite treats NULL as distinct in unique indexes
  if user_id.nil?
    ids_to_update.each do |cid|
      CreativeSharesCache.find_or_initialize_by(creative_id: cid, user_id: nil).tap do |cache|
        cache.assign_attributes(
          permission: permission_value,
          source_share_id: creative_share.id
        )
        cache.save!
      end
    end
  else
    records = ids_to_update.map do |cid|
      {
        creative_id: cid,
        user_id: user_id,
        permission: permission_value,
        source_share_id: creative_share.id
      }
    end

    # Single bulk operation instead of N individual saves
    CreativeSharesCache.upsert_all(
      records,
      unique_by: [ :creative_id, :user_id ],
      update_only: [ :permission, :source_share_id ]
    )
  end
end

.rebuild_for_creative(creative) ⇒ Object

Creative parent_id 변경 시 호출



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'app/services/collavre/creatives/permission_cache_builder.rb', line 137

def self.rebuild_for_creative(creative)
  descendant_ids = creative.self_and_descendant_ids

  # 해당 creative와 자손들의 캐시 삭제 (share 기반만 - owner entries 유지)
  CreativeSharesCache.where(creative_id: descendant_ids)
                     .where.not(source_share_id: nil)
                     .delete_all

  # 새 부모 기준으로 조상 share 전파
  rebuild_from_ancestors_for_subtree(creative)

  # 이동된 서브트리 내의 직접 share들 재적용
  # (closest-share-wins 의미론으로 올바르게 전파됨)
  # no_access도 포함 - 캐시에 저장하여 public share를 override
  CreativeShare.where(creative_id: descendant_ids)
               .find_each { |share| propagate_share(share) }

  # 소유자 캐시 재확인 (혹시 누락된 경우)
  creative.self_and_descendants.each do |c|
    cache_owner(c) if c.user_id
  end
end

.rebuild_from_ancestors_for_user(creative, user_id) ⇒ Object

Public wrapper for rebuild_from_ancestors (used by PermissionCacheJob)



110
111
112
# File 'app/services/collavre/creatives/permission_cache_builder.rb', line 110

def self.rebuild_from_ancestors_for_user(creative, user_id)
  rebuild_from_ancestors(creative, user_id)
end

.rebuild_user_cache_for_subtree(creative, user_id) ⇒ Object

CreativeShare의 creative_id 또는 user_id 변경 시 이전 위치/사용자에 대해 호출특정 사용자의 캐시를 서브트리에서 재구축 (조상 + 서브트리 내 직접 share 모두 고려) user_id는 nil (public share)일 수 있음



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'app/services/collavre/creatives/permission_cache_builder.rb', line 117

def self.rebuild_user_cache_for_subtree(creative, user_id)
  return unless creative

  descendant_ids = [ creative.id ] + creative.descendant_ids

  # 해당 사용자의 캐시만 삭제 (owner entries 유지를 위해 source_share_id가 있는 것만)
  CreativeSharesCache.where(creative_id: descendant_ids, user_id: user_id)
                     .where.not(source_share_id: nil)
                     .delete_all

  # 조상에서 해당 사용자의 share 찾아서 전파
  rebuild_from_ancestors(creative, user_id)

  # 서브트리 내의 해당 사용자 직접 share들도 재적용
  # no_access도 포함 - 캐시에 저장하여 public share를 override
  CreativeShare.where(creative_id: descendant_ids, user_id: user_id)
               .find_each { |share| propagate_share(share) }
end

.remove_share(creative_share) ⇒ Object

CreativeShare 삭제 시 호출



102
103
104
105
106
107
# File 'app/services/collavre/creatives/permission_cache_builder.rb', line 102

def self.remove_share(creative_share)
  CreativeSharesCache.where(source_share_id: creative_share.id).delete_all

  # 삭제 후 조상에서 다른 share가 있으면 다시 전파
  rebuild_from_ancestors(creative_share.creative, creative_share.user_id)
end

.update_owner(creative, old_user_id, new_user_id) ⇒ Object

Creative user_id 변경 시 호출



21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'app/services/collavre/creatives/permission_cache_builder.rb', line 21

def self.update_owner(creative, old_user_id, new_user_id)
  # 기존 소유자의 owner 캐시 삭제 (source_share_id가 nil인 것만)
  if old_user_id
    CreativeSharesCache.where(
      creative_id: creative.id,
      user_id: old_user_id,
      source_share_id: nil
    ).delete_all
  end

  # 새 소유자 캐시 추가
  cache_owner(creative) if new_user_id
end