ancestry 1.2.5 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.