Module: Ancestry::ClassMethods

Defined in:
lib/ancestry/class_methods.rb

Instance Method Summary collapse

Instance Method Details

#arrange(options = {}) ⇒ Object

Arrangement



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/ancestry/class_methods.rb', line 31

def arrange options = {}
  scope =
    if options[:order].nil?
      self.base_class.ordered_by_ancestry
    else
      self.base_class.ordered_by_ancestry_and options.delete(:order)
    end
  # Get all nodes ordered by ancestry and start sorting them into an empty hash
  scope.all(options).inject({}) do |arranged_nodes, node|
    # Find the insertion point for that node by going through its ancestors
    node.ancestor_ids.inject(arranged_nodes) do |insertion_point, ancestor_id|
      insertion_point.each do |parent, children|
        # Change the insertion point to children if node is a descendant of this parent
        insertion_point = children if ancestor_id == parent.id
      end; insertion_point
    end[node] = {}; arranged_nodes
  end
end

#build_ancestry_from_parent_ids!(parent_id = nil, ancestry = nil) ⇒ Object

Build ancestry from parent id’s for migration purposes



110
111
112
113
114
115
116
117
# File 'lib/ancestry/class_methods.rb', line 110

def build_ancestry_from_parent_ids! parent_id = nil, ancestry = nil
  self.base_class.all(:conditions => {:parent_id => parent_id}).each do |node|
    node.without_ancestry_callbacks do
      node.update_attribute ancestry_column, ancestry
    end
    build_ancestry_from_parent_ids! node.id, if ancestry.nil? then "#{node.id}" else "#{ancestry}/#{node.id}" end
  end
end

#check_ancestry_integrity!Object

Integrity checking



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/ancestry/class_methods.rb', line 51

def check_ancestry_integrity!
  parents = {}
  # For each node ...
  self.base_class.all.each do |node|
    # ... check validity of ancestry column
    if !node.valid? and node.errors[node.class.ancestry_column].any?
      raise Ancestry::AncestryIntegrityException.new("Invalid format for ancestry column of node #{node.id}: #{node.read_attribute node.ancestry_column}.")
    end
    # ... check that all ancestors exist
    node.ancestor_ids.each do |ancestor_id|
      unless exists? ancestor_id
        raise Ancestry::AncestryIntegrityException.new("Reference to non-existent node in node #{node.id}: #{ancestor_id}.")
      end
    end
    # ... check that all node parents are consistent with values observed earlier
    node.path_ids.zip([nil] + node.path_ids).each do |node_id, parent_id|
      parents[node_id] = parent_id unless parents.has_key? node_id
      unless parents[node_id] == parent_id
        raise Ancestry::AncestryIntegrityException.new("Conflicting parent id in node #{node.id}: #{parent_id || 'nil'} for node #{node_id}, expecting #{parents[node_id] || 'nil'}")
      end
    end
  end
end

#orphan_strategy=(orphan_strategy) ⇒ Object

Orphan strategy writer



21
22
23
24
25
26
27
28
# File 'lib/ancestry/class_methods.rb', line 21

def orphan_strategy= orphan_strategy
  # Check value of orphan strategy, only rootify, restrict or destroy is allowed
  if [:rootify, :restrict, :destroy].include? orphan_strategy
    class_variable_set :@@orphan_strategy, orphan_strategy
  else
    raise Ancestry::AncestryException.new("Invalid orphan strategy, valid ones are :rootify, :restrict and :destroy.")
  end
end

#rebuild_depth_cache!Object

Rebuild depth cache if it got corrupted or if depth caching was just turned on



120
121
122
123
124
125
# File 'lib/ancestry/class_methods.rb', line 120

def rebuild_depth_cache!
  raise Ancestry::AncestryException.new("Cannot rebuild depth cache for model without depth caching.") unless respond_to? :depth_cache_column
  self.base_class.all.each do |node|
    node.update_attribute depth_cache_column, node.depth
  end
end

#restore_ancestry_integrity!Object

Integrity restoration



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/ancestry/class_methods.rb', line 76

def restore_ancestry_integrity!
  parents = {}
  # For each node ...
  self.base_class.all.each do |node|
    # ... set its ancestry to nil if invalid
    if node.errors[node.class.ancestry_column].any?
      node.without_ancestry_callbacks do
        node.update_attribute(node.class.ancestry_column, nil)
      end
    end
    # ... save parent of this node in parents array if it exists
    parents[node.id] = node.parent_id if exists? node.parent_id

    # Reset parent id in array to nil if it introduces a cycle
    parent = parents[node.id]
    until parent.nil? || parent == node.id
      parent = parents[parent]
    end
    parents[node.id] = nil if parent == node.id
  end
  # For each node ...
  self.base_class.all.each do |node|
    # ... rebuild ancestry from parents array
    ancestry, parent = nil, parents[node.id]
    until parent.nil?
      ancestry, parent = if ancestry.nil? then parent else "#{parent}/#{ancestry}" end, parents[parent]
    end
    node.without_ancestry_callbacks do
      node.update_attribute(node.class.ancestry_column, ancestry)
    end
  end
end

#scope_depth(depth_options, depth) ⇒ Object

Scope on relative depth options



9
10
11
12
13
14
15
16
17
18
# File 'lib/ancestry/class_methods.rb', line 9

def scope_depth depth_options, depth
  depth_options.inject(self.base_class) do |scope, option|
    scope_name, relative_depth = option
    if [:before_depth, :to_depth, :at_depth, :from_depth, :after_depth].include? scope_name
      scope.send scope_name, depth + relative_depth
    else
      raise Ancestry::AncestryException.new("Unknown depth option: #{scope_name}.")
    end
  end
end

#to_node(object) ⇒ Object

Fetch tree node if necessary



4
5
6
# File 'lib/ancestry/class_methods.rb', line 4

def to_node object
  if object.is_a?(self.base_class) then object else find(object) end
end