Class: Collavre::Creatives::ChildrenIndex

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

Overview

Per-level batch answer to "which children does this node show?" for the browse tree, replacing a per-node children_with_permission call.

Presence (has_children?) is resolved for every node in a level with one plucked scan plus one batched PermissionFilter; the full child rows are materialized only for the nodes that actually render them (expanded, or a filter forcing the subtree open). Presence and content are derived from the same candidate set, so an expand toggle can never open an empty branch or hide a reachable subtree — a drift that would also leak the existence of children the user cannot see.

Children of a linked shell live under its effective origin, so every lookup resolves the shell to its origin first. Preload :origin on the level before indexing it, or that resolution costs a query per shell.

Instance Method Summary collapse

Constructor Details

#initialize(user:, show_archived:, allowed_creative_ids: nil) ⇒ ChildrenIndex

Returns a new instance of ChildrenIndex.



18
19
20
21
22
23
24
# File 'app/services/collavre/creatives/children_index.rb', line 18

def initialize(user:, show_archived:, allowed_creative_ids: nil)
  @user = user
  @show_archived = show_archived
  @allowed_creative_ids = allowed_creative_ids
  @child_ids_by_creative = {}
  @rows_by_child_id = {}
end

Instance Method Details

#children_for(creative) ⇒ Object

Sequence-ordered, permission- and archive-filtered children. Empty unless load has materialized them.



59
60
61
# File 'app/services/collavre/creatives/children_index.rb', line 59

def children_for(creative)
  child_ids(creative).filter_map { |id| @rows_by_child_id[id] }
end

#has_children?(creative) ⇒ Boolean

Returns:

  • (Boolean)


44
45
46
# File 'app/services/collavre/creatives/children_index.rb', line 44

def has_children?(creative)
  child_ids(creative).any?
end

#index(creatives) ⇒ Object

Scans a level. Only creatives not seen before cost anything, so the recursive descent pays two queries per level, not per node.



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'app/services/collavre/creatives/children_index.rb', line 28

def index(creatives)
  pending = creatives.reject { |c| @child_ids_by_creative.key?(c.id) }
  return if pending.empty?

  origin_id_by_id = pending.to_h { |c| [ c.id, c.effective_origin.id ] }

  candidates = Creative.where(parent_id: origin_id_by_id.values.uniq)
  candidates = candidates.where(archived_at: nil) unless show_archived
  rows = candidates.order(:sequence).pluck(:id, :parent_id)

  visible_by_origin = visible_child_ids_by_origin(rows)
  pending.each do |creative|
    @child_ids_by_creative[creative.id] = visible_by_origin[origin_id_by_id[creative.id]] || []
  end
end

#load(creatives) ⇒ Object

Materializes the child rows of creatives in a single query. Called only for the nodes whose children actually render.



50
51
52
53
54
55
# File 'app/services/collavre/creatives/children_index.rb', line 50

def load(creatives)
  wanted = creatives.flat_map { |c| child_ids(c) }.uniq - @rows_by_child_id.keys
  return if wanted.empty?

  Creative.where(id: wanted).each { |child| @rows_by_child_id[child.id] = child }
end