Module: ClosureTree::HierarchyMaintenance

Extended by:
ActiveSupport::Concern
Defined in:
lib/closure_tree/hierarchy_maintenance.rb

Instance Method Summary collapse

Instance Method Details

#_ct_after_saveObject



40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/closure_tree/hierarchy_maintenance.rb', line 40

def _ct_after_save
  rebuild! if saved_changes[_ct.parent_column_name] || @was_new_record
  if saved_changes[_ct.parent_column_name] && !@was_new_record
    # Resetting the ancestral collections addresses
    # https://github.com/mceachen/closure_tree/issues/68
    ancestor_hierarchies.reload
    self_and_ancestors.reload
  end
  @was_new_record = false # we aren't new anymore.
  @_ct_skip_sort_order_maintenance = false # only skip once.
  true # don't cancel anything.
end

#_ct_before_destroyObject



53
54
55
56
57
58
59
# File 'lib/closure_tree/hierarchy_maintenance.rb', line 53

def _ct_before_destroy
  _ct.with_advisory_lock do
    delete_hierarchy_references
    self.class.find(id).children.find_each(&:rebuild!) if _ct.options[:dependent] == :nullify
  end
  true # don't prevent destruction
end

#_ct_before_saveObject



35
36
37
38
# File 'lib/closure_tree/hierarchy_maintenance.rb', line 35

def _ct_before_save
  @was_new_record = new_record?
  true # don't cancel the save
end

#_ct_skip_cycle_detection!Object



16
17
18
# File 'lib/closure_tree/hierarchy_maintenance.rb', line 16

def _ct_skip_cycle_detection!
  @_ct_skip_cycle_detection = true
end

#_ct_skip_sort_order_maintenance!Object



20
21
22
# File 'lib/closure_tree/hierarchy_maintenance.rb', line 20

def _ct_skip_sort_order_maintenance!
  @_ct_skip_sort_order_maintenance = true
end

#_ct_validateObject



24
25
26
27
28
29
30
31
32
33
# File 'lib/closure_tree/hierarchy_maintenance.rb', line 24

def _ct_validate
  if !(defined? @_ct_skip_cycle_detection) &&
     !new_record? && # don't validate for cycles if we're a new record
     changes[_ct.parent_column_name] && # don't validate for cycles if we didn't change our parent
     parent.present? && # don't validate if we're root
     parent.self_and_ancestors.include?(self) # < this is expensive :     errors.add(_ct.parent_column_sym,
               I18n.t('closure_tree.loop_error', default: 'You cannot add an ancestor as a descendant'))
  end
end

#delete_hierarchy_referencesObject



87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/closure_tree/hierarchy_maintenance.rb', line 87

def delete_hierarchy_references
  _ct.with_advisory_lock do
    # The crazy double-wrapped sub-subselect works around MySQL's limitation of subselects on the same table that is being mutated.
    # It shouldn't affect performance of postgresql.
    # See http://dev.mysql.com/doc/refman/5.0/en/subquery-errors.html
    # Also: PostgreSQL doesn't support INNER JOIN on DELETE, so we can't use that.

    hierarchy_table = hierarchy_class.arel_table
    delete_query = _ct.build_hierarchy_delete_query(hierarchy_table, id)
    _ct.connection.execute(delete_query.to_sql)
  end
end

#rebuild!(called_by_rebuild = false) ⇒ Object



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
# File 'lib/closure_tree/hierarchy_maintenance.rb', line 61

def rebuild!(called_by_rebuild = false)
  _ct.with_advisory_lock do
    delete_hierarchy_references unless (defined? @was_new_record) && @was_new_record
    hierarchy_class.create!(ancestor: self, descendant: self, generations: 0)
    unless root?
      _ct.connection.execute "        INSERT INTO \#{_ct.quoted_hierarchy_table_name}\n          (ancestor_id, descendant_id, generations)\n        SELECT x.ancestor_id, \#{_ct.quote(_ct_id)}, x.generations + 1\n        FROM \#{_ct.quoted_hierarchy_table_name} x\n        WHERE x.descendant_id = \#{_ct.quote(_ct_parent_id)}\n      SQL\n    end\n\n    if _ct.order_is_numeric? && !@_ct_skip_sort_order_maintenance\n      _ct_reorder_prior_siblings_if_parent_changed\n      # Prevent double-reordering of siblings:\n      _ct_reorder_siblings unless called_by_rebuild\n    end\n\n    children.find_each { |c| c.rebuild!(true) }\n\n    _ct_reorder_children if _ct.order_is_numeric? && children.present?\n  end\nend\n".squish