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



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/closure_tree/hierarchy_maintenance.rb', line 43

def _ct_after_save
  scope_changed = _ct.order_is_numeric? && _ct.scope_changed?(self)

  if saved_changes[_ct.parent_column_name] || @was_new_record
    rebuild!
  elsif scope_changed
    # Scope changed without parent change - reorder old scope's siblings
    _ct_reorder_prior_siblings_if_parent_changed
    _ct_reorder_siblings
  elsif _ct.order_option? && saved_changes[_ct.order_column_sym]
    _ct_reorder_siblings(saved_changes[_ct.order_column_sym].min)
  end
  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.
  true # don't cancel anything.
end

#_ct_before_destroyObject



65
66
67
68
69
70
71
72
# File 'lib/closure_tree/hierarchy_maintenance.rb', line 65

def _ct_before_destroy
  _ct.with_advisory_lock do
    _ct_adopt_children_to_grandparent if _ct.options[:dependent] == :adopt
    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



38
39
40
41
# File 'lib/closure_tree/hierarchy_maintenance.rb', line 38

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
23
24
25
# File 'lib/closure_tree/hierarchy_maintenance.rb', line 20

def _ct_skip_sort_order_maintenance!
  ActiveSupport::Deprecation.new.warn(
    '_ct_skip_sort_order_maintenance! is deprecated and will be removed in the next major version. ' \
    'Sort order maintenance is now handled automatically.'
  )
end

#_ct_validateObject



27
28
29
30
31
32
33
34
35
36
# File 'lib/closure_tree/hierarchy_maintenance.rb', line 27

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



113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/closure_tree/hierarchy_maintenance.rb', line 113

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(_ct.to_sql_with_connection(delete_query))
  end
end

#rebuild!(called_by_rebuild = false) ⇒ Object



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

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?\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