Skip to content
Newer
Older
100644 207 lines (185 sloc) 7.66 KB
ecea76f @stefankroes Version 1.2.0
authored Feb 27, 2010
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 Dec 21, 2011
5 if object.is_a?(self.ancestry_base_class) then object else find(object) end
ad2a8c5 removing whitespace
Sara Trice authored Nov 26, 2013
6 end
7
ecea76f @stefankroes Version 1.2.0
authored Feb 27, 2010
8 # Scope on relative depth options
9 def scope_depth depth_options, depth
4e57f32 @NOX73 base_class replace ancestry_base_class
NOX73 authored Dec 21, 2011
10 depth_options.inject(self.ancestry_base_class) do |scope, option|
ecea76f @stefankroes Version 1.2.0
authored Feb 27, 2010
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 Jun 25, 2012
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 Feb 27, 2010
24 class_variable_set :@@orphan_strategy, orphan_strategy
25 else
94dba1c -- renamed the orphan strategy
unknown authored Jun 25, 2012
26 raise Ancestry::AncestryException.new("Invalid orphan strategy, valid ones are :rootify,:adopt, :restrict and :destroy.")
ecea76f @stefankroes Version 1.2.0
authored Feb 27, 2010
27 end
28 end
ad2a8c5 removing whitespace
Sara Trice authored Nov 26, 2013
29
ecea76f @stefankroes Version 1.2.0
authored Feb 27, 2010
30 # Arrangement
31 def arrange options = {}
32 scope =
33 if options[:order].nil?
4e57f32 @NOX73 base_class replace ancestry_base_class
NOX73 authored Dec 21, 2011
34 self.ancestry_base_class.ordered_by_ancestry
ecea76f @stefankroes Version 1.2.0
authored Feb 27, 2010
35 else
4e57f32 @NOX73 base_class replace ancestry_base_class
NOX73 authored Dec 21, 2011
36 self.ancestry_base_class.ordered_by_ancestry_and options.delete(:order)
ecea76f @stefankroes Version 1.2.0
authored Feb 27, 2010
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 Apr 9, 2013
39 arrange_nodes scope.where(options)
5614a3b @kueda Added class method to sort nodes by ancestry.
kueda authored Nov 25, 2010
40 end
ad2a8c5 removing whitespace
Sara Trice authored Nov 26, 2013
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 Nov 26, 2010
43 # {node => children}, where children = {} if the node has no children
44 def arrange_nodes(nodes)
8a4fb00 @Fryguy Improve performance of arrange_nodes.
Fryguy authored Feb 5, 2016
45 arranged = ActiveSupport::OrderedHash.new
46 min_depth = Float::INFINITY
47 index = Hash.new { |h, k| h[k] = ActiveSupport::OrderedHash.new }
48
49 nodes.each do |node|
50 children = index[node.id]
51 index[node.parent_id][node] = children
52
53 depth = node.depth
54 if depth < min_depth
55 min_depth = depth
56 arranged.clear
57 end
58 arranged[node] = children if depth == min_depth
5614a3b @kueda Added class method to sort nodes by ancestry.
kueda authored Nov 26, 2010
59 end
8a4fb00 @Fryguy Improve performance of arrange_nodes.
Fryguy authored Feb 6, 2016
60
61 arranged
5614a3b @kueda Added class method to sort nodes by ancestry.
kueda authored Nov 26, 2010
62 end
e56807e Added arrange_serializable
Kris Handley authored Nov 20, 2013
63
4cd51a1 give arrange_serializable the ability to take an options hash
Sara Trice authored Nov 26, 2013
64 # Arrangement to nested array
0c14723 @mastfish allow arrange_serializable to accept block
mastfish authored May 13, 2015
65 def arrange_serializable options={}, nodes=nil, &block
4cd51a1 give arrange_serializable the ability to take an options hash
Sara Trice authored Nov 27, 2013
66 nodes = arrange(options) if nodes.nil?
e56807e Added arrange_serializable
Kris Handley authored Nov 20, 2013
67 nodes.map do |parent, children|
0c14723 @mastfish allow arrange_serializable to accept block
mastfish authored May 13, 2015
68 if block_given?
69 yield parent, arrange_serializable(options, children, &block)
70 else
71 parent.serializable_hash.merge 'children' => arrange_serializable(options, children)
72 end
e56807e Added arrange_serializable
Kris Handley authored Nov 20, 2013
73 end
74 end
ad2a8c5 removing whitespace
Sara Trice authored Nov 26, 2013
75
76 # Pseudo-preordered array of nodes. Children will always follow parents,
4b59888 @iliya-gr Added block to sort_by_ancestry class method.
iliya-gr authored Dec 20, 2011
77 # for ordering nodes within a rank provide block, eg. Node.sort_by_ancestry(Node.all) {|a, b| a.rank <=> b.rank}.
78 def sort_by_ancestry(nodes, &block)
79 arranged = nodes if nodes.is_a?(Hash)
ad2a8c5 removing whitespace
Sara Trice authored Nov 26, 2013
80
4b59888 @iliya-gr Added block to sort_by_ancestry class method.
iliya-gr authored Dec 20, 2011
81 unless arranged
82 presorted_nodes = nodes.sort do |a, b|
83 a_cestry, b_cestry = a.ancestry || '0', b.ancestry || '0'
ad2a8c5 removing whitespace
Sara Trice authored Nov 26, 2013
84
4b59888 @iliya-gr Added block to sort_by_ancestry class method.
iliya-gr authored Dec 20, 2011
85 if block_given? && a_cestry == b_cestry
86 yield a, b
87 else
88 a_cestry <=> b_cestry
89 end
90 end
ad2a8c5 removing whitespace
Sara Trice authored Nov 26, 2013
91
4b59888 @iliya-gr Added block to sort_by_ancestry class method.
iliya-gr authored Dec 20, 2011
92 arranged = arrange_nodes(presorted_nodes)
93 end
ad2a8c5 removing whitespace
Sara Trice authored Nov 26, 2013
94
5614a3b @kueda Added class method to sort nodes by ancestry.
kueda authored Nov 26, 2010
95 arranged.inject([]) do |sorted_nodes, pair|
96 node, children = pair
97 sorted_nodes << node
4b59888 @iliya-gr Added block to sort_by_ancestry class method.
iliya-gr authored Dec 20, 2011
98 sorted_nodes += sort_by_ancestry(children, &block) unless children.blank?
5614a3b @kueda Added class method to sort nodes by ancestry.
kueda authored Nov 26, 2010
99 sorted_nodes
ecea76f @stefankroes Version 1.2.0
authored Feb 27, 2010
100 end
101 end
102
103 # Integrity checking
34a13a9 @stefankroes Fixes for 1.2.1
authored Oct 24, 2010
104 def check_ancestry_integrity! options = {}
ecea76f @stefankroes Version 1.2.0
authored Feb 27, 2010
105 parents = {}
34a13a9 @stefankroes Fixes for 1.2.1
authored Oct 24, 2010
106 exceptions = [] if options[:report] == :list
ad2a8c5 removing whitespace
Sara Trice authored Nov 26, 2013
107
108 self.ancestry_base_class.unscoped do
eeadeef @gstokkink ancestry should skip default scopes for some of the node update methods.
gstokkink authored May 4, 2012
109 # For each node ...
a759e6b Merge branch 'master' of git://github.com/kaize/ancestry into kaize-m…
Stefan Henzen authored May 7, 2013
110 self.ancestry_base_class.find_each do |node|
eeadeef @gstokkink ancestry should skip default scopes for some of the node update methods.
gstokkink authored May 4, 2012
111 begin
112 # ... check validity of ancestry column
113 if !node.valid? and !node.errors[node.class.ancestry_column].blank?
114 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 Oct 24, 2010
115 end
eeadeef @gstokkink ancestry should skip default scopes for some of the node update methods.
gstokkink authored May 4, 2012
116 # ... check that all ancestors exist
117 node.ancestor_ids.each do |ancestor_id|
118 unless exists? ancestor_id
119 raise Ancestry::AncestryIntegrityException.new("Reference to non-existent node in node #{node.id}: #{ancestor_id}.")
120 end
121 end
122 # ... check that all node parents are consistent with values observed earlier
123 node.path_ids.zip([nil] + node.path_ids).each do |node_id, parent_id|
124 parents[node_id] = parent_id unless parents.has_key? node_id
125 unless parents[node_id] == parent_id
126 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'}")
127 end
128 end
129 rescue Ancestry::AncestryIntegrityException => integrity_exception
130 case options[:report]
131 when :list then exceptions << integrity_exception
132 when :echo then puts integrity_exception
133 else raise integrity_exception
34a13a9 @stefankroes Fixes for 1.2.1
authored Oct 24, 2010
134 end
ecea76f @stefankroes Version 1.2.0
authored Feb 27, 2010
135 end
136 end
137 end
34a13a9 @stefankroes Fixes for 1.2.1
authored Oct 24, 2010
138 exceptions if options[:report] == :list
ecea76f @stefankroes Version 1.2.0
authored Feb 27, 2010
139 end
140
141 # Integrity restoration
142 def restore_ancestry_integrity!
143 parents = {}
770f2e3 Fix bug in restore_ancestry_integrity!, added test to verify we didn'…
Arthur Holstvoogd authored Jun 8, 2011
144 # Wrap the whole thing in a transaction ...
4e57f32 @NOX73 base_class replace ancestry_base_class
NOX73 authored Dec 21, 2011
145 self.ancestry_base_class.transaction do
ad2a8c5 removing whitespace
Sara Trice authored Nov 26, 2013
146 self.ancestry_base_class.unscoped do
eeadeef @gstokkink ancestry should skip default scopes for some of the node update methods.
gstokkink authored May 4, 2012
147 # For each node ...
a759e6b Merge branch 'master' of git://github.com/kaize/ancestry into kaize-m…
Stefan Henzen authored May 7, 2013
148 self.ancestry_base_class.find_each do |node|
eeadeef @gstokkink ancestry should skip default scopes for some of the node update methods.
gstokkink authored May 4, 2012
149 # ... set its ancestry to nil if invalid
150 if !node.valid? and !node.errors[node.class.ancestry_column].blank?
151 node.without_ancestry_callbacks do
152 node.update_attribute node.ancestry_column, nil
153 end
770f2e3 Fix bug in restore_ancestry_integrity!, added test to verify we didn'…
Arthur Holstvoogd authored Jun 8, 2011
154 end
eeadeef @gstokkink ancestry should skip default scopes for some of the node update methods.
gstokkink authored May 4, 2012
155 # ... save parent of this node in parents array if it exists
156 parents[node.id] = node.parent_id if exists? node.parent_id
ecea76f @stefankroes Version 1.2.0
authored Feb 27, 2010
157
eeadeef @gstokkink ancestry should skip default scopes for some of the node update methods.
gstokkink authored May 4, 2012
158 # Reset parent id in array to nil if it introduces a cycle
159 parent = parents[node.id]
160 until parent.nil? || parent == node.id
161 parent = parents[parent]
162 end
ad2a8c5 removing whitespace
Sara Trice authored Nov 26, 2013
163 parents[node.id] = nil if parent == node.id
770f2e3 Fix bug in restore_ancestry_integrity!, added test to verify we didn'…
Arthur Holstvoogd authored Jun 8, 2011
164 end
ad2a8c5 removing whitespace
Sara Trice authored Nov 26, 2013
165
eeadeef @gstokkink ancestry should skip default scopes for some of the node update methods.
gstokkink authored May 4, 2012
166 # For each node ...
a759e6b Merge branch 'master' of git://github.com/kaize/ancestry into kaize-m…
Stefan Henzen authored May 7, 2013
167 self.ancestry_base_class.find_each do |node|
eeadeef @gstokkink ancestry should skip default scopes for some of the node update methods.
gstokkink authored May 4, 2012
168 # ... rebuild ancestry from parents array
169 ancestry, parent = nil, parents[node.id]
170 until parent.nil?
171 ancestry, parent = if ancestry.nil? then parent else "#{parent}/#{ancestry}" end, parents[parent]
172 end
173 node.without_ancestry_callbacks do
864acf1 fix error in restore_ancestry_integrity! introduced in prev. commit
Stefan Henzen authored Dec 18, 2012
174 node.update_attribute node.ancestry_column, ancestry
eeadeef @gstokkink ancestry should skip default scopes for some of the node update methods.
gstokkink authored May 4, 2012
175 end
770f2e3 Fix bug in restore_ancestry_integrity!, added test to verify we didn'…
Arthur Holstvoogd authored Jun 8, 2011
176 end
ecea76f @stefankroes Version 1.2.0
authored Feb 27, 2010
177 end
178 end
179 end
ad2a8c5 removing whitespace
Sara Trice authored Nov 26, 2013
180
ecea76f @stefankroes Version 1.2.0
authored Feb 27, 2010
181 # Build ancestry from parent id's for migration purposes
182 def build_ancestry_from_parent_ids! parent_id = nil, ancestry = nil
ad2a8c5 removing whitespace
Sara Trice authored Nov 26, 2013
183 self.ancestry_base_class.unscoped do
a759e6b Merge branch 'master' of git://github.com/kaize/ancestry into kaize-m…
Stefan Henzen authored May 7, 2013
184 self.ancestry_base_class.where(:parent_id => parent_id).find_each do |node|
eeadeef @gstokkink ancestry should skip default scopes for some of the node update methods.
gstokkink authored May 4, 2012
185 node.without_ancestry_callbacks do
186 node.update_attribute ancestry_column, ancestry
187 end
188 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 Feb 27, 2010
189 end
190 end
191 end
ad2a8c5 removing whitespace
Sara Trice authored Nov 26, 2013
192
ecea76f @stefankroes Version 1.2.0
authored Feb 27, 2010
193 # Rebuild depth cache if it got corrupted or if depth caching was just turned on
194 def rebuild_depth_cache!
195 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 Nov 26, 2013
196
b8a98e5 @mjc Wrap rebuild_depth_cache! in a transaction
mjc authored Dec 18, 2013
197 self.ancestry_base_class.transaction do
198 self.ancestry_base_class.unscoped do
199 self.ancestry_base_class.find_each do |node|
200 node.update_attribute depth_cache_column, node.depth
201 end
eeadeef @gstokkink ancestry should skip default scopes for some of the node update methods.
gstokkink authored May 4, 2012
202 end
ecea76f @stefankroes Version 1.2.0
authored Feb 27, 2010
203 end
204 end
205 end
5614a3b @kueda Added class method to sort nodes by ancestry.
kueda authored Nov 26, 2010
206 end
Something went wrong with that request. Please try again.