Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 195 lines (176 sloc) 7.781 kB
ecea76f @stefankroes Version 1.2.0
authored
1 module Ancestry
2 module ClassMethods
3 # Fetch tree node if necessary
4 def to_node object
4e57f32 @NOX73 base_class replace ancestry_base_class
NOX73 authored
5 if object.is_a?(self.ancestry_base_class) then object else find(object) end
ad2a8c5 removing whitespace
Sara Trice authored
6 end
7
ecea76f @stefankroes Version 1.2.0
authored
8 # Scope on relative depth options
9 def scope_depth depth_options, depth
4e57f32 @NOX73 base_class replace ancestry_base_class
NOX73 authored
10 depth_options.inject(self.ancestry_base_class) do |scope, option|
ecea76f @stefankroes Version 1.2.0
authored
11 scope_name, relative_depth = option
12 if [:before_depth, :to_depth, :at_depth, :from_depth, :after_depth].include? scope_name
13 scope.send scope_name, depth + relative_depth
14 else
15 raise Ancestry::AncestryException.new("Unknown depth option: #{scope_name}.")
16 end
17 end
18 end
19
20 # Orphan strategy writer
21 def orphan_strategy= orphan_strategy
94dba1c -- renamed the orphan strategy
unknown authored
22 # Check value of orphan strategy, only rootify, adopt, restrict or destroy is allowed
23 if [:rootify, :adopt, :restrict, :destroy].include? orphan_strategy
ecea76f @stefankroes Version 1.2.0
authored
24 class_variable_set :@@orphan_strategy, orphan_strategy
25 else
94dba1c -- renamed the orphan strategy
unknown authored
26 raise Ancestry::AncestryException.new("Invalid orphan strategy, valid ones are :rootify,:adopt, :restrict and :destroy.")
ecea76f @stefankroes Version 1.2.0
authored
27 end
28 end
ad2a8c5 removing whitespace
Sara Trice authored
29
ecea76f @stefankroes Version 1.2.0
authored
30 # Arrangement
31 def arrange options = {}
32 scope =
33 if options[:order].nil?
4e57f32 @NOX73 base_class replace ancestry_base_class
NOX73 authored
34 self.ancestry_base_class.ordered_by_ancestry
ecea76f @stefankroes Version 1.2.0
authored
35 else
4e57f32 @NOX73 base_class replace ancestry_base_class
NOX73 authored
36 self.ancestry_base_class.ordered_by_ancestry_and options.delete(:order)
ecea76f @stefankroes Version 1.2.0
authored
37 end
38 # Get all nodes ordered by ancestry and start sorting them into an empty hash
66fee13 @adammck Fix remaining Rails 4 deprecation warnings
adammck authored
39 arrange_nodes scope.where(options)
5614a3b @kueda Added class method to sort nodes by ancestry.
kueda authored
40 end
ad2a8c5 removing whitespace
Sara Trice authored
41
42 # Arrange array of nodes into a nested hash of the form
5614a3b @kueda Added class method to sort nodes by ancestry.
kueda authored
43 # {node => children}, where children = {} if the node has no children
44 def arrange_nodes(nodes)
45 # Get all nodes ordered by ancestry and start sorting them into an empty hash
46 nodes.inject(ActiveSupport::OrderedHash.new) do |arranged_nodes, node|
ecea76f @stefankroes Version 1.2.0
authored
47 # Find the insertion point for that node by going through its ancestors
48 node.ancestor_ids.inject(arranged_nodes) do |insertion_point, ancestor_id|
49 insertion_point.each do |parent, children|
50 # Change the insertion point to children if node is a descendant of this parent
51 insertion_point = children if ancestor_id == parent.id
5614a3b @kueda Added class method to sort nodes by ancestry.
kueda authored
52 end
53 insertion_point
54 end[node] = ActiveSupport::OrderedHash.new
55 arranged_nodes
56 end
57 end
e56807e Added arrange_serializable
Kris Handley authored
58
59 # Arrangement to nested array
60 def arrange_serializable nodes = arrange
61 nodes.map do |parent, children|
62 parent.serializable_hash.merge 'children' => arrange_serializable(children)
63 end
64 end
ad2a8c5 removing whitespace
Sara Trice authored
65
66 # Pseudo-preordered array of nodes. Children will always follow parents,
4b59888 @iliya-gr Added block to sort_by_ancestry class method.
iliya-gr authored
67 # for ordering nodes within a rank provide block, eg. Node.sort_by_ancestry(Node.all) {|a, b| a.rank <=> b.rank}.
68 def sort_by_ancestry(nodes, &block)
69 arranged = nodes if nodes.is_a?(Hash)
ad2a8c5 removing whitespace
Sara Trice authored
70
4b59888 @iliya-gr Added block to sort_by_ancestry class method.
iliya-gr authored
71 unless arranged
72 presorted_nodes = nodes.sort do |a, b|
73 a_cestry, b_cestry = a.ancestry || '0', b.ancestry || '0'
ad2a8c5 removing whitespace
Sara Trice authored
74
4b59888 @iliya-gr Added block to sort_by_ancestry class method.
iliya-gr authored
75 if block_given? && a_cestry == b_cestry
76 yield a, b
77 else
78 a_cestry <=> b_cestry
79 end
80 end
ad2a8c5 removing whitespace
Sara Trice authored
81
4b59888 @iliya-gr Added block to sort_by_ancestry class method.
iliya-gr authored
82 arranged = arrange_nodes(presorted_nodes)
83 end
ad2a8c5 removing whitespace
Sara Trice authored
84
5614a3b @kueda Added class method to sort nodes by ancestry.
kueda authored
85 arranged.inject([]) do |sorted_nodes, pair|
86 node, children = pair
87 sorted_nodes << node
4b59888 @iliya-gr Added block to sort_by_ancestry class method.
iliya-gr authored
88 sorted_nodes += sort_by_ancestry(children, &block) unless children.blank?
5614a3b @kueda Added class method to sort nodes by ancestry.
kueda authored
89 sorted_nodes
ecea76f @stefankroes Version 1.2.0
authored
90 end
91 end
92
93 # Integrity checking
34a13a9 @stefankroes Fixes for 1.2.1
authored
94 def check_ancestry_integrity! options = {}
ecea76f @stefankroes Version 1.2.0
authored
95 parents = {}
34a13a9 @stefankroes Fixes for 1.2.1
authored
96 exceptions = [] if options[:report] == :list
ad2a8c5 removing whitespace
Sara Trice authored
97
98 self.ancestry_base_class.unscoped do
eeadeef ancestry should skip default scopes for some of the node update methods.
Gerjan Stokkink authored
99 # For each node ...
a759e6b Merge branch 'master' of git://github.com/kaize/ancestry into kaize-m…
Stefan Henzen authored
100 self.ancestry_base_class.find_each do |node|
eeadeef ancestry should skip default scopes for some of the node update methods.
Gerjan Stokkink authored
101 begin
102 # ... check validity of ancestry column
103 if !node.valid? and !node.errors[node.class.ancestry_column].blank?
104 raise Ancestry::AncestryIntegrityException.new("Invalid format for ancestry column of node #{node.id}: #{node.read_attribute node.ancestry_column}.")
34a13a9 @stefankroes Fixes for 1.2.1
authored
105 end
eeadeef ancestry should skip default scopes for some of the node update methods.
Gerjan Stokkink authored
106 # ... check that all ancestors exist
107 node.ancestor_ids.each do |ancestor_id|
108 unless exists? ancestor_id
109 raise Ancestry::AncestryIntegrityException.new("Reference to non-existent node in node #{node.id}: #{ancestor_id}.")
110 end
111 end
112 # ... check that all node parents are consistent with values observed earlier
113 node.path_ids.zip([nil] + node.path_ids).each do |node_id, parent_id|
114 parents[node_id] = parent_id unless parents.has_key? node_id
115 unless parents[node_id] == parent_id
116 raise Ancestry::AncestryIntegrityException.new("Conflicting parent id found in node #{node.id}: #{parent_id || 'nil'} for node #{node_id} while expecting #{parents[node_id] || 'nil'}")
117 end
118 end
119 rescue Ancestry::AncestryIntegrityException => integrity_exception
120 case options[:report]
121 when :list then exceptions << integrity_exception
122 when :echo then puts integrity_exception
123 else raise integrity_exception
34a13a9 @stefankroes Fixes for 1.2.1
authored
124 end
ecea76f @stefankroes Version 1.2.0
authored
125 end
126 end
127 end
34a13a9 @stefankroes Fixes for 1.2.1
authored
128 exceptions if options[:report] == :list
ecea76f @stefankroes Version 1.2.0
authored
129 end
130
131 # Integrity restoration
132 def restore_ancestry_integrity!
133 parents = {}
770f2e3 Fix bug in restore_ancestry_integrity!, added test to verify we didn'…
Arthur Holstvoogd authored
134 # Wrap the whole thing in a transaction ...
4e57f32 @NOX73 base_class replace ancestry_base_class
NOX73 authored
135 self.ancestry_base_class.transaction do
ad2a8c5 removing whitespace
Sara Trice authored
136 self.ancestry_base_class.unscoped do
eeadeef ancestry should skip default scopes for some of the node update methods.
Gerjan Stokkink authored
137 # For each node ...
a759e6b Merge branch 'master' of git://github.com/kaize/ancestry into kaize-m…
Stefan Henzen authored
138 self.ancestry_base_class.find_each do |node|
eeadeef ancestry should skip default scopes for some of the node update methods.
Gerjan Stokkink authored
139 # ... set its ancestry to nil if invalid
140 if !node.valid? and !node.errors[node.class.ancestry_column].blank?
141 node.without_ancestry_callbacks do
142 node.update_attribute node.ancestry_column, nil
143 end
770f2e3 Fix bug in restore_ancestry_integrity!, added test to verify we didn'…
Arthur Holstvoogd authored
144 end
eeadeef ancestry should skip default scopes for some of the node update methods.
Gerjan Stokkink authored
145 # ... save parent of this node in parents array if it exists
146 parents[node.id] = node.parent_id if exists? node.parent_id
ecea76f @stefankroes Version 1.2.0
authored
147
eeadeef ancestry should skip default scopes for some of the node update methods.
Gerjan Stokkink authored
148 # Reset parent id in array to nil if it introduces a cycle
149 parent = parents[node.id]
150 until parent.nil? || parent == node.id
151 parent = parents[parent]
152 end
ad2a8c5 removing whitespace
Sara Trice authored
153 parents[node.id] = nil if parent == node.id
770f2e3 Fix bug in restore_ancestry_integrity!, added test to verify we didn'…
Arthur Holstvoogd authored
154 end
ad2a8c5 removing whitespace
Sara Trice authored
155
eeadeef ancestry should skip default scopes for some of the node update methods.
Gerjan Stokkink authored
156 # For each node ...
a759e6b Merge branch 'master' of git://github.com/kaize/ancestry into kaize-m…
Stefan Henzen authored
157 self.ancestry_base_class.find_each do |node|
eeadeef ancestry should skip default scopes for some of the node update methods.
Gerjan Stokkink authored
158 # ... rebuild ancestry from parents array
159 ancestry, parent = nil, parents[node.id]
160 until parent.nil?
161 ancestry, parent = if ancestry.nil? then parent else "#{parent}/#{ancestry}" end, parents[parent]
162 end
163 node.without_ancestry_callbacks do
864acf1 fix error in restore_ancestry_integrity! introduced in prev. commit
Stefan Henzen authored
164 node.update_attribute node.ancestry_column, ancestry
eeadeef ancestry should skip default scopes for some of the node update methods.
Gerjan Stokkink authored
165 end
770f2e3 Fix bug in restore_ancestry_integrity!, added test to verify we didn'…
Arthur Holstvoogd authored
166 end
ecea76f @stefankroes Version 1.2.0
authored
167 end
168 end
169 end
ad2a8c5 removing whitespace
Sara Trice authored
170
ecea76f @stefankroes Version 1.2.0
authored
171 # Build ancestry from parent id's for migration purposes
172 def build_ancestry_from_parent_ids! parent_id = nil, ancestry = nil
ad2a8c5 removing whitespace
Sara Trice authored
173 self.ancestry_base_class.unscoped do
a759e6b Merge branch 'master' of git://github.com/kaize/ancestry into kaize-m…
Stefan Henzen authored
174 self.ancestry_base_class.where(:parent_id => parent_id).find_each do |node|
eeadeef ancestry should skip default scopes for some of the node update methods.
Gerjan Stokkink authored
175 node.without_ancestry_callbacks do
176 node.update_attribute ancestry_column, ancestry
177 end
178 build_ancestry_from_parent_ids! node.id, if ancestry.nil? then "#{node.id}" else "#{ancestry}/#{node.id}" end
ecea76f @stefankroes Version 1.2.0
authored
179 end
180 end
181 end
ad2a8c5 removing whitespace
Sara Trice authored
182
ecea76f @stefankroes Version 1.2.0
authored
183 # Rebuild depth cache if it got corrupted or if depth caching was just turned on
184 def rebuild_depth_cache!
185 raise Ancestry::AncestryException.new("Cannot rebuild depth cache for model without depth caching.") unless respond_to? :depth_cache_column
ad2a8c5 removing whitespace
Sara Trice authored
186
187 self.ancestry_base_class.unscoped do
a759e6b Merge branch 'master' of git://github.com/kaize/ancestry into kaize-m…
Stefan Henzen authored
188 self.ancestry_base_class.find_each do |node|
eeadeef ancestry should skip default scopes for some of the node update methods.
Gerjan Stokkink authored
189 node.update_attribute depth_cache_column, node.depth
190 end
ecea76f @stefankroes Version 1.2.0
authored
191 end
192 end
193 end
5614a3b @kueda Added class method to sort nodes by ancestry.
kueda authored
194 end
Something went wrong with that request. Please try again.