closure_tree 3.7.3 → 3.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,65 @@
1
+ # This module is only included if the order column is an integer.
2
+ module ClosureTree
3
+ module DeterministicNumericOrdering
4
+ def self_and_descendants_preordered
5
+ # TODO: raise NotImplementedError if sort_order is not numeric and not null?
6
+ h = connection.select_one(<<-SQL)
7
+ SELECT
8
+ count(*) as total_descendants,
9
+ max(generations) as max_depth
10
+ FROM #{quoted_hierarchy_table_name}
11
+ WHERE ancestor_id = #{ct_quote(self.id)}
12
+ SQL
13
+ join_sql = <<-SQL
14
+ JOIN #{quoted_hierarchy_table_name} anc_hier
15
+ ON anc_hier.descendant_id = #{quoted_hierarchy_table_name}.descendant_id
16
+ JOIN #{quoted_table_name} anc
17
+ ON anc.id = anc_hier.ancestor_id
18
+ JOIN #{quoted_hierarchy_table_name} depths
19
+ ON depths.ancestor_id = #{ct_quote(self.id)} AND depths.descendant_id = anc.id
20
+ SQL
21
+ node_score = "(1 + anc.#{quoted_order_column(false)}) * " +
22
+ "power(#{h['total_descendants']}, #{h['max_depth'].to_i + 1} - depths.generations)"
23
+ order_by = "sum(#{node_score})"
24
+ self_and_descendants.joins(join_sql).group("#{quoted_table_name}.id").reorder(order_by)
25
+ end
26
+
27
+ def append_sibling(sibling_node, use_update_all = true)
28
+ add_sibling(sibling_node, use_update_all, true)
29
+ end
30
+
31
+ def prepend_sibling(sibling_node, use_update_all = true)
32
+ add_sibling(sibling_node, use_update_all, false)
33
+ end
34
+
35
+ def add_sibling(sibling_node, use_update_all = true, add_after = true)
36
+ fail "can't add self as sibling" if self == sibling_node
37
+ # issue 40: we need to lock the parent to prevent deadlocks on parallel sibling additions
38
+ ct_with_advisory_lock do
39
+ # issue 18: we need to set the order_value explicitly so subsequent orders will work.
40
+ update_attribute(:order_value, 0) if self.order_value.nil?
41
+ sibling_node.order_value = self.order_value.to_i + (add_after ? 1 : -1)
42
+ # We need to incr the before_siblings to make room for sibling_node:
43
+ if use_update_all
44
+ col = quoted_order_column(false)
45
+ # issue 21: we have to use the base class, so STI doesn't get in the way of only updating the child class instances:
46
+ ct_base_class.update_all(
47
+ ["#{col} = #{col} #{add_after ? '+' : '-'} 1", "updated_at = now()"],
48
+ ["#{quoted_parent_column_name} = ? AND #{col} #{add_after ? '>=' : '<='} ?",
49
+ ct_parent_id,
50
+ sibling_node.order_value])
51
+ else
52
+ last_value = sibling_node.order_value.to_i
53
+ (add_after ? siblings_after : siblings_before.reverse).each do |ea|
54
+ last_value += (add_after ? 1 : -1)
55
+ ea.order_value = last_value
56
+ ea.save!
57
+ end
58
+ end
59
+ sibling_node.parent = self.parent
60
+ sibling_node.save!
61
+ sibling_node.reload
62
+ end
63
+ end
64
+ end
65
+ end
@@ -1,3 +1,3 @@
1
1
  module ClosureTree
2
- VERSION = "3.7.3" unless defined?(::ClosureTree::VERSION)
2
+ VERSION = "3.8.0" unless defined?(::ClosureTree::VERSION)
3
3
  end
@@ -0,0 +1,18 @@
1
+ require 'with_advisory_lock'
2
+
3
+ module ClosureTree
4
+ module WithAdvisoryLock
5
+ def ct_with_advisory_lock(&block)
6
+ if closure_tree_options[:with_advisory_lock]
7
+ with_advisory_lock("closure_tree") do
8
+ transaction do
9
+ yield
10
+ end
11
+ end
12
+ else
13
+ yield
14
+ end
15
+ end
16
+ end
17
+ end
18
+
data/spec/label_spec.rb CHANGED
@@ -5,7 +5,7 @@ def delete_all_labels
5
5
  Label.delete_all
6
6
  end
7
7
 
8
- def create_label_hierarchy
8
+ def create_label_tree
9
9
  @d1 = Label.find_or_create_by_path %w(a1 b1 c1 d1)
10
10
  @c1 = @d1.parent
11
11
  @b1 = @c1.parent
@@ -19,6 +19,29 @@ def create_label_hierarchy
19
19
  Label.update_all("sort_order = id")
20
20
  end
21
21
 
22
+ def create_preorder_tree
23
+ %w(
24
+ a/l/n/r
25
+ a/l/n/q
26
+ a/l/n/p
27
+ a/l/n/o
28
+ a/l/m
29
+ a/b/h/i/j/k
30
+ a/b/c/d/g
31
+ a/b/c/d/f
32
+ a/b/c/d/e
33
+ ).shuffle.each { |ea| Label.find_or_create_by_path(ea.split '/') }
34
+ a = Label.find_by_path(["a"])
35
+ a.order_value = 0
36
+ a.save!
37
+ a.self_and_descendants.each do |ea|
38
+ ea.children.to_a.sort_by(&:name).each_with_index do |ea, idx|
39
+ ea.order_value = idx
40
+ ea.save!
41
+ end
42
+ end
43
+ end
44
+
22
45
  describe Label do
23
46
  context "Base Label class" do
24
47
  it "should find or create by path" do
@@ -81,7 +104,7 @@ describe Label do
81
104
  context "find_all_by_generation" do
82
105
  before :all do
83
106
  delete_all_labels
84
- create_label_hierarchy
107
+ create_label_tree
85
108
  end
86
109
 
87
110
  it "finds roots from the class method" do
@@ -122,7 +145,7 @@ describe Label do
122
145
  context "loading through self_and_ scopes" do
123
146
  before :all do
124
147
  delete_all_labels
125
- create_label_hierarchy
148
+ create_label_tree
126
149
  end
127
150
 
128
151
  it "self_and_descendants should result in one select" do
@@ -281,4 +304,14 @@ describe Label do
281
304
  labels(:a1).leaves.collect(&:name).should == %w(b2 e2 d2 c1-six c1-seven c1-eight c1-nine)
282
305
  end
283
306
  end
307
+
308
+ context ".self_and_descendants_preordered" do
309
+ it "returns descendants in proper order" do
310
+ delete_all_labels
311
+ create_preorder_tree
312
+ a = Label.root
313
+ a.name.should == "a"
314
+ a.self_and_descendants_preordered.collect { |ea| ea.name }.should == ('a'..'r').to_a
315
+ end
316
+ end unless ENV["DB"] == "sqlite"
284
317
  end
metadata CHANGED
@@ -1,217 +1,212 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: closure_tree
3
- version: !ruby/object:Gem::Version
4
- version: 3.7.3
3
+ version: !ruby/object:Gem::Version
4
+ hash: 39
5
5
  prerelease:
6
+ segments:
7
+ - 3
8
+ - 8
9
+ - 0
10
+ version: 3.8.0
6
11
  platform: ruby
7
- authors:
12
+ authors:
8
13
  - Matthew McEachen
9
14
  autorequire:
10
15
  bindir: bin
11
16
  cert_chain: []
12
- date: 2013-02-19 00:00:00.000000000 Z
13
- dependencies:
14
- - !ruby/object:Gem::Dependency
15
- name: activerecord
16
- requirement: !ruby/object:Gem::Requirement
17
+
18
+ date: 2013-02-23 00:00:00 -08:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ requirement: &id001 !ruby/object:Gem::Requirement
17
23
  none: false
18
- requirements:
19
- - - ! '>='
20
- - !ruby/object:Gem::Version
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ hash: 7
28
+ segments:
29
+ - 3
30
+ - 0
31
+ - 0
21
32
  version: 3.0.0
22
- type: :runtime
33
+ version_requirements: *id001
34
+ name: activerecord
23
35
  prerelease: false
24
- version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
- requirements:
27
- - - ! '>='
28
- - !ruby/object:Gem::Version
29
- version: 3.0.0
30
- - !ruby/object:Gem::Dependency
31
- name: with_advisory_lock
32
- requirement: !ruby/object:Gem::Requirement
36
+ type: :runtime
37
+ - !ruby/object:Gem::Dependency
38
+ requirement: &id002 !ruby/object:Gem::Requirement
33
39
  none: false
34
- requirements:
35
- - - ! '>='
36
- - !ruby/object:Gem::Version
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 19
44
+ segments:
45
+ - 0
46
+ - 0
47
+ - 6
37
48
  version: 0.0.6
38
- type: :runtime
49
+ version_requirements: *id002
50
+ name: with_advisory_lock
39
51
  prerelease: false
40
- version_requirements: !ruby/object:Gem::Requirement
52
+ type: :runtime
53
+ - !ruby/object:Gem::Dependency
54
+ requirement: &id003 !ruby/object:Gem::Requirement
41
55
  none: false
42
- requirements:
43
- - - ! '>='
44
- - !ruby/object:Gem::Version
45
- version: 0.0.6
46
- - !ruby/object:Gem::Dependency
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ version_requirements: *id003
47
64
  name: rake
48
- requirement: !ruby/object:Gem::Requirement
49
- none: false
50
- requirements:
51
- - - ! '>='
52
- - !ruby/object:Gem::Version
53
- version: '0'
54
- type: :development
55
65
  prerelease: false
56
- version_requirements: !ruby/object:Gem::Requirement
66
+ type: :development
67
+ - !ruby/object:Gem::Dependency
68
+ requirement: &id004 !ruby/object:Gem::Requirement
57
69
  none: false
58
- requirements:
59
- - - ! '>='
60
- - !ruby/object:Gem::Version
61
- version: '0'
62
- - !ruby/object:Gem::Dependency
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ hash: 3
74
+ segments:
75
+ - 0
76
+ version: "0"
77
+ version_requirements: *id004
63
78
  name: yard
64
- requirement: !ruby/object:Gem::Requirement
65
- none: false
66
- requirements:
67
- - - ! '>='
68
- - !ruby/object:Gem::Version
69
- version: '0'
70
- type: :development
71
79
  prerelease: false
72
- version_requirements: !ruby/object:Gem::Requirement
80
+ type: :development
81
+ - !ruby/object:Gem::Dependency
82
+ requirement: &id005 !ruby/object:Gem::Requirement
73
83
  none: false
74
- requirements:
75
- - - ! '>='
76
- - !ruby/object:Gem::Version
77
- version: '0'
78
- - !ruby/object:Gem::Dependency
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ hash: 3
88
+ segments:
89
+ - 0
90
+ version: "0"
91
+ version_requirements: *id005
79
92
  name: rspec
80
- requirement: !ruby/object:Gem::Requirement
81
- none: false
82
- requirements:
83
- - - ! '>='
84
- - !ruby/object:Gem::Version
85
- version: '0'
86
- type: :development
87
93
  prerelease: false
88
- version_requirements: !ruby/object:Gem::Requirement
94
+ type: :development
95
+ - !ruby/object:Gem::Dependency
96
+ requirement: &id006 !ruby/object:Gem::Requirement
89
97
  none: false
90
- requirements:
91
- - - ! '>='
92
- - !ruby/object:Gem::Version
93
- version: '0'
94
- - !ruby/object:Gem::Dependency
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ hash: 3
102
+ segments:
103
+ - 0
104
+ version: "0"
105
+ version_requirements: *id006
95
106
  name: rails
96
- requirement: !ruby/object:Gem::Requirement
97
- none: false
98
- requirements:
99
- - - ! '>='
100
- - !ruby/object:Gem::Version
101
- version: '0'
102
- type: :development
103
107
  prerelease: false
104
- version_requirements: !ruby/object:Gem::Requirement
108
+ type: :development
109
+ - !ruby/object:Gem::Dependency
110
+ requirement: &id007 !ruby/object:Gem::Requirement
105
111
  none: false
106
- requirements:
107
- - - ! '>='
108
- - !ruby/object:Gem::Version
109
- version: '0'
110
- - !ruby/object:Gem::Dependency
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ hash: 3
116
+ segments:
117
+ - 0
118
+ version: "0"
119
+ version_requirements: *id007
111
120
  name: rspec-rails
112
- requirement: !ruby/object:Gem::Requirement
113
- none: false
114
- requirements:
115
- - - ! '>='
116
- - !ruby/object:Gem::Version
117
- version: '0'
118
- type: :development
119
121
  prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
122
+ type: :development
123
+ - !ruby/object:Gem::Dependency
124
+ requirement: &id008 !ruby/object:Gem::Requirement
121
125
  none: false
122
- requirements:
123
- - - ! '>='
124
- - !ruby/object:Gem::Version
125
- version: '0'
126
- - !ruby/object:Gem::Dependency
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ hash: 3
130
+ segments:
131
+ - 0
132
+ version: "0"
133
+ version_requirements: *id008
127
134
  name: mysql2
128
- requirement: !ruby/object:Gem::Requirement
129
- none: false
130
- requirements:
131
- - - ! '>='
132
- - !ruby/object:Gem::Version
133
- version: '0'
134
- type: :development
135
135
  prerelease: false
136
- version_requirements: !ruby/object:Gem::Requirement
136
+ type: :development
137
+ - !ruby/object:Gem::Dependency
138
+ requirement: &id009 !ruby/object:Gem::Requirement
137
139
  none: false
138
- requirements:
139
- - - ! '>='
140
- - !ruby/object:Gem::Version
141
- version: '0'
142
- - !ruby/object:Gem::Dependency
140
+ requirements:
141
+ - - ">="
142
+ - !ruby/object:Gem::Version
143
+ hash: 3
144
+ segments:
145
+ - 0
146
+ version: "0"
147
+ version_requirements: *id009
143
148
  name: pg
144
- requirement: !ruby/object:Gem::Requirement
145
- none: false
146
- requirements:
147
- - - ! '>='
148
- - !ruby/object:Gem::Version
149
- version: '0'
150
- type: :development
151
149
  prerelease: false
152
- version_requirements: !ruby/object:Gem::Requirement
150
+ type: :development
151
+ - !ruby/object:Gem::Dependency
152
+ requirement: &id010 !ruby/object:Gem::Requirement
153
153
  none: false
154
- requirements:
155
- - - ! '>='
156
- - !ruby/object:Gem::Version
157
- version: '0'
158
- - !ruby/object:Gem::Dependency
154
+ requirements:
155
+ - - ">="
156
+ - !ruby/object:Gem::Version
157
+ hash: 3
158
+ segments:
159
+ - 0
160
+ version: "0"
161
+ version_requirements: *id010
159
162
  name: sqlite3
160
- requirement: !ruby/object:Gem::Requirement
161
- none: false
162
- requirements:
163
- - - ! '>='
164
- - !ruby/object:Gem::Version
165
- version: '0'
166
- type: :development
167
163
  prerelease: false
168
- version_requirements: !ruby/object:Gem::Requirement
164
+ type: :development
165
+ - !ruby/object:Gem::Dependency
166
+ requirement: &id011 !ruby/object:Gem::Requirement
169
167
  none: false
170
- requirements:
171
- - - ! '>='
172
- - !ruby/object:Gem::Version
173
- version: '0'
174
- - !ruby/object:Gem::Dependency
168
+ requirements:
169
+ - - ">="
170
+ - !ruby/object:Gem::Version
171
+ hash: 3
172
+ segments:
173
+ - 0
174
+ version: "0"
175
+ version_requirements: *id011
175
176
  name: uuidtools
176
- requirement: !ruby/object:Gem::Requirement
177
- none: false
178
- requirements:
179
- - - ! '>='
180
- - !ruby/object:Gem::Version
181
- version: '0'
182
- type: :development
183
177
  prerelease: false
184
- version_requirements: !ruby/object:Gem::Requirement
178
+ type: :development
179
+ - !ruby/object:Gem::Dependency
180
+ requirement: &id012 !ruby/object:Gem::Requirement
185
181
  none: false
186
- requirements:
187
- - - ! '>='
188
- - !ruby/object:Gem::Version
189
- version: '0'
190
- - !ruby/object:Gem::Dependency
182
+ requirements:
183
+ - - ">="
184
+ - !ruby/object:Gem::Version
185
+ hash: 3
186
+ segments:
187
+ - 0
188
+ version: "0"
189
+ version_requirements: *id012
191
190
  name: strong_parameters
192
- requirement: !ruby/object:Gem::Requirement
193
- none: false
194
- requirements:
195
- - - ! '>='
196
- - !ruby/object:Gem::Version
197
- version: '0'
198
- type: :development
199
191
  prerelease: false
200
- version_requirements: !ruby/object:Gem::Requirement
201
- none: false
202
- requirements:
203
- - - ! '>='
204
- - !ruby/object:Gem::Version
205
- version: '0'
192
+ type: :development
206
193
  description: Easily and efficiently make your ActiveRecord model support hierarchies
207
- email:
194
+ email:
208
195
  - matthew-github@mceachen.org
209
196
  executables: []
197
+
210
198
  extensions: []
199
+
211
200
  extra_rdoc_files: []
212
- files:
201
+
202
+ files:
213
203
  - lib/closure_tree/acts_as_tree.rb
204
+ - lib/closure_tree/columns.rb
205
+ - lib/closure_tree/deterministic_ordering.rb
206
+ - lib/closure_tree/model.rb
207
+ - lib/closure_tree/numeric_deterministic_ordering.rb
214
208
  - lib/closure_tree/version.rb
209
+ - lib/closure_tree/with_advisory_lock.rb
215
210
  - lib/closure_tree.rb
216
211
  - MIT-LICENSE
217
212
  - Rakefile
@@ -229,37 +224,41 @@ files:
229
224
  - spec/support/models.rb
230
225
  - spec/tag_spec.rb
231
226
  - spec/user_spec.rb
227
+ has_rdoc: true
232
228
  homepage: http://matthew.mceachen.us/closure_tree
233
229
  licenses: []
230
+
234
231
  post_install_message:
235
232
  rdoc_options: []
236
- require_paths:
233
+
234
+ require_paths:
237
235
  - lib
238
- required_ruby_version: !ruby/object:Gem::Requirement
236
+ required_ruby_version: !ruby/object:Gem::Requirement
239
237
  none: false
240
- requirements:
241
- - - ! '>='
242
- - !ruby/object:Gem::Version
243
- version: '0'
244
- segments:
238
+ requirements:
239
+ - - ">="
240
+ - !ruby/object:Gem::Version
241
+ hash: 3
242
+ segments:
245
243
  - 0
246
- hash: -1398803141248485966
247
- required_rubygems_version: !ruby/object:Gem::Requirement
244
+ version: "0"
245
+ required_rubygems_version: !ruby/object:Gem::Requirement
248
246
  none: false
249
- requirements:
250
- - - ! '>='
251
- - !ruby/object:Gem::Version
252
- version: '0'
253
- segments:
247
+ requirements:
248
+ - - ">="
249
+ - !ruby/object:Gem::Version
250
+ hash: 3
251
+ segments:
254
252
  - 0
255
- hash: -1398803141248485966
253
+ version: "0"
256
254
  requirements: []
255
+
257
256
  rubyforge_project:
258
- rubygems_version: 1.8.23
257
+ rubygems_version: 1.6.2
259
258
  signing_key:
260
259
  specification_version: 3
261
260
  summary: Easily and efficiently make your ActiveRecord model support hierarchies
262
- test_files:
261
+ test_files:
263
262
  - spec/cuisine_type_spec.rb
264
263
  - spec/db/database.yml
265
264
  - spec/db/schema.rb
@@ -273,4 +272,3 @@ test_files:
273
272
  - spec/support/models.rb
274
273
  - spec/tag_spec.rb
275
274
  - spec/user_spec.rb
276
- has_rdoc: