closure_tree 3.7.3 → 3.8.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.
@@ -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: