Class: ProjectUnification::TaxonNameHandler

Inherits:
Object
  • Object
show all
Defined in:
lib/project_unification/taxon_name_handler.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(source_project_id:, target_project_id:, options: {}) ⇒ TaxonNameHandler

Returns a new instance of TaxonNameHandler.



13
14
15
16
17
# File 'lib/project_unification/taxon_name_handler.rb', line 13

def initialize(source_project_id:, target_project_id:, options: {})
  @source_project_id = source_project_id
  @target_project_id = target_project_id
  @options = options
end

Instance Attribute Details

#optionsObject (readonly)

Returns the value of attribute options.



11
12
13
# File 'lib/project_unification/taxon_name_handler.rb', line 11

def options
  @options
end

#source_project_idObject (readonly)

Returns the value of attribute source_project_id.



11
12
13
# File 'lib/project_unification/taxon_name_handler.rb', line 11

def source_project_id
  @source_project_id
end

#target_project_idObject (readonly)

Returns the value of attribute target_project_id.



11
12
13
# File 'lib/project_unification/taxon_name_handler.rb', line 11

def target_project_id
  @target_project_id
end

Instance Method Details

#count_descendants(source_root) ⇒ Object (private)

Count all descendants of source root (excluding root itself)



88
89
90
# File 'lib/project_unification/taxon_name_handler.rb', line 88

def count_descendants(source_root)
  source_root.descendants.unscope(:order).count
end

#determine_target_parent(target_project) ⇒ Object (private)

Determine where to attach the source hierarchy in the target project



79
80
81
82
83
84
85
# File 'lib/project_unification/taxon_name_handler.rb', line 79

def determine_target_parent(target_project)
  if options[:root_taxon_name_id]
    TaxonName.find(options[:root_taxon_name_id])
  else
    target_project.root_taxon_name
  end
end

#disable_cached_callbacksObject (private)

Temporarily disable cached field callbacks for performance



118
119
120
121
122
123
# File 'lib/project_unification/taxon_name_handler.rb', line 118

def disable_cached_callbacks
  TaxonName.no_cached_thread = true
  yield
ensure
  TaxonName.no_cached_thread = nil
end

#migrateHash

Migrate TaxonName hierarchy from source to target project

Returns:

  • (Hash)

    Migration results



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
# File 'lib/project_unification/taxon_name_handler.rb', line 21

def migrate
  source_project = Project.find(source_project_id)
  target_project = Project.find(target_project_id)

  source_root = source_project.root_taxon_name
  target_parent = determine_target_parent(target_project)

  stats = {
    track: :special,
    model: 'TaxonName',
    migrated: 0,
    source_root_id: source_root.id,
    target_parent_id: target_parent.id,
    closure_tree_rebuilt: false,
    errors: []
  }

  begin
    # Count all descendants before moving (excluding root)
    total_count = count_descendants(source_root)

    # Skip if there are no children to migrate (only root exists)
    if total_count == 0
      stats[:migrated] = 0
      stats[:note] = 'No TaxonNames to migrate (only root exists)'
      return stats
    end

    # Temporarily disable cached field updates for performance
    disable_cached_callbacks do
      move_children_to_target(source_root, target_parent)
      update_all_descendants_project_id(source_root)

      stats[:migrated] = total_count

      # Rebuild only target_parent's subtree — all source names now hang off it.
      # TaxonName.rebuild! is O(all projects); instance rebuild! is O(moved subtree).
      #
      # Ancestors of target_parent are correctly handled: closure_tree's instance
      # rebuild! seeds each node's ancestor entries by copying rows where
      # descendant_id = parent_id, which already includes all ancestors above
      # target_parent. No separate rebuild of those nodes is needed.
      target_parent.rebuild!
      stats[:closure_tree_rebuilt] = true
    end
  rescue => e
    stats[:errors] << {
      error: e.message,
      backtrace: e.backtrace.first(3)
    }
  end

  stats
end

#move_children_to_target(source_root, target_parent) ⇒ Object (private)

Move all direct children of source root to target parent Updates both parent_id AND project_id atomically



94
95
96
97
98
99
100
101
102
103
# File 'lib/project_unification/taxon_name_handler.rb', line 94

def move_children_to_target(source_root, target_parent)
  children = source_root.children.unscope(:order)

  children.each do |child|
    child.update_columns(
      parent_id: target_parent.id,
      project_id: target_project_id
    )
  end
end

#update_all_descendants_project_id(source_root) ⇒ Object (private)

Update project_id for all descendants in one bulk operation



106
107
108
109
110
111
112
113
114
115
# File 'lib/project_unification/taxon_name_handler.rb', line 106

def update_all_descendants_project_id(source_root)
  # Get all descendants except direct children (already updated in move_children_to_target)
  # Use closure_tree's descendants method with unscope for performance
  descendants = source_root.descendants.unscope(:order).where.not(parent_id: source_root.id)

  return if descendants.empty?

  # Bulk update all descendants' project_id via ActiveRecord for performance
  TaxonName.where(id: descendants.select(:id)).update_all(project_id: target_project_id)
end