ancestry 1.2.5 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,7 +3,7 @@ Gem::Specification.new do |s|
3
3
  s.description = 'Organise ActiveRecord model into a tree structure'
4
4
  s.summary = 'Ancestry allows the records of a ActiveRecord model to be organised in a tree structure, using a single, intuitively formatted database column. It exposes all the standard tree structure relations (ancestors, parent, root, children, siblings, descendants) and all of them can be fetched in a single sql query. Additional features are named_scopes, integrity checking, integrity restoration, arrangement of (sub)tree into hashes and different strategies for dealing with orphaned records.'
5
5
 
6
- s.version = '1.2.5'
6
+ s.version = '1.3.0'
7
7
 
8
8
  s.author = 'Stefan Kroes'
9
9
  s.email = 's.a.kroes@gmail.com'
@@ -22,5 +22,5 @@ Gem::Specification.new do |s|
22
22
  'README.rdoc'
23
23
  ]
24
24
 
25
- s.add_dependency 'activerecord', '>= 2.2.2'
25
+ s.add_dependency 'activerecord', '>= 2.3.14'
26
26
  end
@@ -1 +1,4 @@
1
- require 'ancestry/has_ancestry'
1
+ require File.join(File.expand_path(File.dirname(__FILE__)), 'ancestry/class_methods')
2
+ require File.join(File.expand_path(File.dirname(__FILE__)), 'ancestry/instance_methods')
3
+ require File.join(File.expand_path(File.dirname(__FILE__)), 'ancestry/exceptions')
4
+ require File.join(File.expand_path(File.dirname(__FILE__)), 'ancestry/has_ancestry')
@@ -73,31 +73,34 @@ module Ancestry
73
73
  def check_ancestry_integrity! options = {}
74
74
  parents = {}
75
75
  exceptions = [] if options[:report] == :list
76
- # For each node ...
77
- self.base_class.find_each do |node|
78
- begin
79
- # ... check validity of ancestry column
80
- if !node.valid? and !node.errors[node.class.ancestry_column].blank?
81
- raise Ancestry::AncestryIntegrityException.new("Invalid format for ancestry column of node #{node.id}: #{node.read_attribute node.ancestry_column}.")
82
- end
83
- # ... check that all ancestors exist
84
- node.ancestor_ids.each do |ancestor_id|
85
- unless exists? ancestor_id
86
- raise Ancestry::AncestryIntegrityException.new("Reference to non-existent node in node #{node.id}: #{ancestor_id}.")
76
+
77
+ self.base_class.send(:with_exclusive_scope) do
78
+ # For each node ...
79
+ self.base_class.find_each do |node|
80
+ begin
81
+ # ... check validity of ancestry column
82
+ if !node.valid? and !node.errors[node.class.ancestry_column].blank?
83
+ raise Ancestry::AncestryIntegrityException.new("Invalid format for ancestry column of node #{node.id}: #{node.read_attribute node.ancestry_column}.")
87
84
  end
88
- end
89
- # ... check that all node parents are consistent with values observed earlier
90
- node.path_ids.zip([nil] + node.path_ids).each do |node_id, parent_id|
91
- parents[node_id] = parent_id unless parents.has_key? node_id
92
- unless parents[node_id] == parent_id
93
- 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'}")
85
+ # ... check that all ancestors exist
86
+ node.ancestor_ids.each do |ancestor_id|
87
+ unless exists? ancestor_id
88
+ raise Ancestry::AncestryIntegrityException.new("Reference to non-existent node in node #{node.id}: #{ancestor_id}.")
89
+ end
90
+ end
91
+ # ... check that all node parents are consistent with values observed earlier
92
+ node.path_ids.zip([nil] + node.path_ids).each do |node_id, parent_id|
93
+ parents[node_id] = parent_id unless parents.has_key? node_id
94
+ unless parents[node_id] == parent_id
95
+ 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'}")
96
+ end
97
+ end
98
+ rescue Ancestry::AncestryIntegrityException => integrity_exception
99
+ case options[:report]
100
+ when :list then exceptions << integrity_exception
101
+ when :echo then puts integrity_exception
102
+ else raise integrity_exception
94
103
  end
95
- end
96
- rescue Ancestry::AncestryIntegrityException => integrity_exception
97
- case options[:report]
98
- when :list then exceptions << integrity_exception
99
- when :echo then puts integrity_exception
100
- else raise integrity_exception
101
104
  end
102
105
  end
103
106
  end
@@ -109,33 +112,36 @@ module Ancestry
109
112
  parents = {}
110
113
  # Wrap the whole thing in a transaction ...
111
114
  self.base_class.transaction do
112
- # For each node ...
113
- self.base_class.find_each do |node|
114
- # ... set its ancestry to nil if invalid
115
- if !node.valid? and !node.errors[node.class.ancestry_column].blank?
116
- node.without_ancestry_callbacks do
117
- node.update_attribute node.ancestry_column, nil
115
+ self.base_class.send(:with_exclusive_scope) do
116
+ # For each node ...
117
+ self.base_class.find_each do |node|
118
+ # ... set its ancestry to nil if invalid
119
+ if !node.valid? and !node.errors[node.class.ancestry_column].blank?
120
+ node.without_ancestry_callbacks do
121
+ node.update_attribute node.ancestry_column, nil
122
+ end
118
123
  end
119
- end
120
- # ... save parent of this node in parents array if it exists
121
- parents[node.id] = node.parent_id if exists? node.parent_id
124
+ # ... save parent of this node in parents array if it exists
125
+ parents[node.id] = node.parent_id if exists? node.parent_id
122
126
 
123
- # Reset parent id in array to nil if it introduces a cycle
124
- parent = parents[node.id]
125
- until parent.nil? || parent == node.id
126
- parent = parents[parent]
127
- end
128
- parents[node.id] = nil if parent == node.id
129
- end
130
- # For each node ...
131
- self.base_class.find_each do |node|
132
- # ... rebuild ancestry from parents array
133
- ancestry, parent = nil, parents[node.id]
134
- until parent.nil?
135
- ancestry, parent = if ancestry.nil? then parent else "#{parent}/#{ancestry}" end, parents[parent]
127
+ # Reset parent id in array to nil if it introduces a cycle
128
+ parent = parents[node.id]
129
+ until parent.nil? || parent == node.id
130
+ parent = parents[parent]
131
+ end
132
+ parents[node.id] = nil if parent == node.id
136
133
  end
137
- node.without_ancestry_callbacks do
138
- node.update_attribute node.ancestry_column, ancestry
134
+
135
+ # For each node ...
136
+ self.base_class.find_each do |node|
137
+ # ... rebuild ancestry from parents array
138
+ ancestry, parent = nil, parents[node.id]
139
+ until parent.nil?
140
+ ancestry, parent = if ancestry.nil? then parent else "#{parent}/#{ancestry}" end, parents[parent]
141
+ end
142
+ node.without_ancestry_callbacks do
143
+ node.update_attribute node.ancestry_column, ancestry
144
+ end
139
145
  end
140
146
  end
141
147
  end
@@ -143,19 +149,24 @@ module Ancestry
143
149
 
144
150
  # Build ancestry from parent id's for migration purposes
145
151
  def build_ancestry_from_parent_ids! parent_id = nil, ancestry = nil
146
- self.base_class.find_each(:conditions => {:parent_id => parent_id}) do |node|
147
- node.without_ancestry_callbacks do
148
- node.update_attribute ancestry_column, ancestry
152
+ self.base_class.send(:with_exclusive_scope) do
153
+ self.base_class.find_each(:conditions => {:parent_id => parent_id}) do |node|
154
+ node.without_ancestry_callbacks do
155
+ node.update_attribute ancestry_column, ancestry
156
+ end
157
+ build_ancestry_from_parent_ids! node.id, if ancestry.nil? then "#{node.id}" else "#{ancestry}/#{node.id}" end
149
158
  end
150
- build_ancestry_from_parent_ids! node.id, if ancestry.nil? then "#{node.id}" else "#{ancestry}/#{node.id}" end
151
159
  end
152
160
  end
153
161
 
154
162
  # Rebuild depth cache if it got corrupted or if depth caching was just turned on
155
163
  def rebuild_depth_cache!
156
164
  raise Ancestry::AncestryException.new("Cannot rebuild depth cache for model without depth caching.") unless respond_to? :depth_cache_column
157
- self.base_class.find_each do |node|
158
- node.update_attribute depth_cache_column, node.depth
165
+
166
+ self.base_class.send(:with_exclusive_scope) do
167
+ self.base_class.find_each do |node|
168
+ node.update_attribute depth_cache_column, node.depth
169
+ end
159
170
  end
160
171
  end
161
172
  end
@@ -1,7 +1,3 @@
1
- require 'ancestry/class_methods'
2
- require 'ancestry/instance_methods'
3
- require 'ancestry/exceptions'
4
-
5
1
  class << ActiveRecord::Base
6
2
  def has_ancestry options = {}
7
3
  # Check options
@@ -12,7 +12,7 @@ module Ancestry
12
12
  # If node is valid, not a new record and ancestry was updated ...
13
13
  if changed.include?(self.base_class.ancestry_column.to_s) && !new_record? && valid?
14
14
  # ... for each descendant ...
15
- descendants.each do |descendant|
15
+ unscoped_descendants.each do |descendant|
16
16
  # ... replace old ancestry with new ancestry
17
17
  descendant.without_ancestry_callbacks do
18
18
  descendant.update_attribute(
@@ -34,16 +34,16 @@ module Ancestry
34
34
  unless ancestry_callbacks_disabled?
35
35
  # If this isn't a new record ...
36
36
  unless new_record?
37
- # ... make al children root if orphan strategy is rootify
37
+ # ... make all children root if orphan strategy is rootify
38
38
  if self.base_class.orphan_strategy == :rootify
39
- descendants.each do |descendant|
39
+ unscoped_descendants.each do |descendant|
40
40
  descendant.without_ancestry_callbacks do
41
41
  descendant.update_attribute descendant.class.ancestry_column, (if descendant.ancestry == child_ancestry then nil else descendant.ancestry.gsub(/^#{child_ancestry}\//, '') end)
42
42
  end
43
43
  end
44
44
  # ... destroy all descendants if orphan strategy is destroy
45
45
  elsif self.base_class.orphan_strategy == :destroy
46
- descendants.all.each do |descendant|
46
+ unscoped_descendants.each do |descendant|
47
47
  descendant.without_ancestry_callbacks do
48
48
  descendant.destroy
49
49
  end
@@ -228,5 +228,11 @@ module Ancestry
228
228
  def primary_key_type
229
229
  @primary_key_type ||= column_for_attribute(self.class.primary_key).type
230
230
  end
231
+
232
+ def unscoped_descendants
233
+ self.base_class.send(:with_exclusive_scope) do
234
+ self.base_class.all(:conditions => descendant_conditions)
235
+ end
236
+ end
231
237
  end
232
238
  end
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 1
7
- - 2
8
- - 5
9
- version: 1.2.5
7
+ - 3
8
+ - 0
9
+ version: 1.3.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Stefan Kroes
@@ -14,21 +14,22 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2012-03-15 00:00:00 +01:00
17
+ date: 2012-05-04 00:00:00 +02:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: activerecord
22
22
  prerelease: false
23
23
  requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
24
25
  requirements:
25
26
  - - ">="
26
27
  - !ruby/object:Gem::Version
27
28
  segments:
28
29
  - 2
29
- - 2
30
- - 2
31
- version: 2.2.2
30
+ - 3
31
+ - 14
32
+ version: 2.3.14
32
33
  type: :runtime
33
34
  version_requirements: *id001
34
35
  description: Organise ActiveRecord model into a tree structure
@@ -60,6 +61,7 @@ rdoc_options: []
60
61
  require_paths:
61
62
  - lib
62
63
  required_ruby_version: !ruby/object:Gem::Requirement
64
+ none: false
63
65
  requirements:
64
66
  - - ">="
65
67
  - !ruby/object:Gem::Version
@@ -67,6 +69,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
67
69
  - 0
68
70
  version: "0"
69
71
  required_rubygems_version: !ruby/object:Gem::Requirement
72
+ none: false
70
73
  requirements:
71
74
  - - ">="
72
75
  - !ruby/object:Gem::Version
@@ -76,7 +79,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
76
79
  requirements: []
77
80
 
78
81
  rubyforge_project:
79
- rubygems_version: 1.3.6
82
+ rubygems_version: 1.3.7
80
83
  signing_key:
81
84
  specification_version: 3
82
85
  summary: Ancestry allows the records of a ActiveRecord model to be organised in a tree structure, using a single, intuitively formatted database column. It exposes all the standard tree structure relations (ancestors, parent, root, children, siblings, descendants) and all of them can be fetched in a single sql query. Additional features are named_scopes, integrity checking, integrity restoration, arrangement of (sub)tree into hashes and different strategies for dealing with orphaned records.