closure_tree 3.6.0 → 3.6.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -23,7 +23,7 @@ for a description of different tree storage algorithms.
23
23
  - [Installation](#installation)
24
24
  - [Usage](#usage)
25
25
  - [Accessing Data](#accessing-data)
26
- - [Polymorphic hierarchies with STI](#sti)
26
+ - [Polymorphic hierarchies with STI](#polymorphic-hierarchies-with-sti)
27
27
  - [Deterministic ordering](#deterministic-ordering)
28
28
  - [FAQ](#faq)
29
29
  - [Testing](#testing)
@@ -247,7 +247,7 @@ When you include ```acts_as_tree``` in your model, you can provide a hash to ove
247
247
  * ```tag.find_all_by_generation(2)``` will return the tag's grandchildren, and so on.
248
248
  * ```tag.destroy``` will destroy a node and do <em>something</em> to its children, which is determined by the ```:dependent``` option passed to ```acts_as_tree```.
249
249
 
250
- ## <a id="sti"></a>Polymorphic hierarchies with STI
250
+ ## Polymorphic hierarchies with STI
251
251
 
252
252
  Polymorphic models using single table inheritance (STI) are supported:
253
253
 
@@ -300,37 +300,40 @@ When you enable ```order```, you'll also have the following new methods injected
300
300
 
301
301
  If your ```order``` column is an integer attribute, you'll also have these:
302
302
 
303
- * ```tag.add_sibling_before(sibling_node)``` which will
304
- 1. move ```tag``` to the same parent as ```sibling_node```,
305
- 2. decrement the sort_order values of the nodes before the ```sibling_node``` by one, and
306
- 3. set ```tag```'s order column to 1 less than the ```sibling_node```'s value.
303
+ * ```node1.prepend_sibling(node2)``` which will
304
+ 1. set ```node2``` to the same parent as ```node1```,
305
+ 2. set ```node2```'s order column to 1 less than ```node1```'s value, and
306
+ 3. decrement the order_column of all children of node1's parents whose order_column is <>>= node2's new value by 1.
307
307
 
308
- * ```tag.add_sibling_after(sibling_node)``` which will
309
- 1. move ```tag``` to the same parent as ```sibling_node```,
310
- 2. increment the sort_order values of the nodes after the ```sibling_node``` by one, and
311
- 3. set ```tag```'s order column to 1 more than the ```sibling_node```'s value.
308
+ * ```node1.append_sibling(node2)``` which will
309
+ 1. set ```node2``` to the same parent as ```node1```,
310
+ 2. set ```node2```'s order column to 1 more than ```node1```'s value, and
311
+ 3. increment the order_column of all children of node1's parents whose order_column is >= node2's new value by 1.
312
312
 
313
313
  ```ruby
314
+
314
315
  root = OrderedTag.create(:name => "root")
315
316
  a = OrderedTag.create(:name => "a", :parent => "root")
316
317
  b = OrderedTag.create(:name => "b")
317
318
  c = OrderedTag.create(:name => "c")
318
319
 
320
+ # We have to call 'root.reload.children' because root won't be in sync with the database otherwise:
321
+
319
322
  a.append_sibling(b)
320
- root.children.collect(&:name)
323
+ root.reload.children.collect(&:name)
321
324
  => ["a", "b"]
322
325
 
323
326
  a.prepend_sibling(b)
324
- root.children.collect(&:name)
327
+ root.reload.children.collect(&:name)
325
328
  => ["b", "a"]
326
329
 
327
330
  a.append_sibling(c)
328
- root.children.collect(&:name)
329
- => ["a", "c", "b"]
331
+ root.reload.children.collect(&:name)
332
+ => ["b", "a", "c"]
330
333
 
331
334
  b.append_sibling(c)
332
- root.children.collect(&:name)
333
- => ["a", "b", "c"]
335
+ root.reload.children.collect(&:name)
336
+ => ["b", "c", "a"]
334
337
  ```
335
338
 
336
339
  ## FAQ
@@ -352,12 +355,19 @@ Closure tree is [tested under every combination](http://travis-ci.org/#!/mceache
352
355
 
353
356
  ## Change log
354
357
 
358
+ ### 3.6.1
359
+
360
+ * Fixed [issue 20](https://github.com/mceachen/closure_tree/issues/20), which affected
361
+ deterministic ordering when siblings where different STI classes. Thanks, [edwinramirez](https://github.com/edwinramirez)!
362
+
355
363
  ### 3.6.0
356
364
 
357
- * Added support for:
358
- * ```:hierarchy_class_name``` as an option
359
- * ActiveRecord::Base.table_name_prefix
360
- * ActiveRecord::Base.table_name_suffix
365
+ Added support for:
366
+ * ```:hierarchy_class_name``` as an option
367
+ * ActiveRecord::Base.table_name_prefix
368
+ * ActiveRecord::Base.table_name_suffix
369
+
370
+ This addresses [issue 21](https://github.com/mceachen/closure_tree/issues/21). Thanks, [Judd Blair](https://github.com/juddblair)!
361
371
 
362
372
  ### 3.5.2
363
373
 
@@ -5,6 +5,7 @@ module ClosureTree
5
5
  class_attribute :closure_tree_options
6
6
 
7
7
  self.closure_tree_options = {
8
+ :ct_base_class => self,
8
9
  :parent_column_name => 'parent_id',
9
10
  :dependent => :nullify, # or :destroy or :delete_all -- see the README
10
11
  :name_column => 'name'
@@ -180,7 +181,7 @@ module ClosureTree
180
181
  end
181
182
 
182
183
  def self_and_siblings
183
- s = self.class.scoped.where(parent_column_sym => parent)
184
+ s = ct_base_class.where(parent_column_sym => parent)
184
185
  quoted_order_column ? s.order(quoted_order_column) : s
185
186
  end
186
187
 
@@ -228,14 +229,14 @@ module ClosureTree
228
229
  end
229
230
 
230
231
  def find_all_by_generation(generation_level)
231
- s = self.class.joins(<<-SQL)
232
+ s = ct_base_class.joins(<<-SQL)
232
233
  INNER JOIN (
233
234
  SELECT descendant_id
234
235
  FROM #{quoted_hierarchy_table_name}
235
236
  WHERE ancestor_id = #{self.id}
236
237
  GROUP BY 1
237
238
  HAVING MAX(#{quoted_hierarchy_table_name}.generations) = #{generation_level.to_i}
238
- ) AS descendants ON (#{quoted_table_name}.#{self.class.primary_key} = descendants.descendant_id)
239
+ ) AS descendants ON (#{quoted_table_name}.#{ct_base_class.primary_key} = descendants.descendant_id)
239
240
  SQL
240
241
  order_option ? s.order(order_option) : s
241
242
  end
@@ -317,7 +318,7 @@ module ClosureTree
317
318
  end
318
319
 
319
320
  def without_self(scope)
320
- scope.where(["#{quoted_table_name}.#{self.class.primary_key} != ?", self])
321
+ scope.where(["#{quoted_table_name}.#{ct_base_class.primary_key} != ?", self])
321
322
  end
322
323
 
323
324
  def ids_from(scope)
@@ -433,6 +434,11 @@ module ClosureTree
433
434
  (self.is_a?(Class) ? self : self.class)
434
435
  end
435
436
 
437
+ # This is the "topmost" class. This will only potentially not be ct_class if you are using STI.
438
+ def ct_base_class
439
+ ct_class.closure_tree_options[:ct_base_class]
440
+ end
441
+
436
442
  def ct_subclass?
437
443
  ct_class != ct_class.base_class
438
444
  end
@@ -516,7 +522,8 @@ module ClosureTree
516
522
  # We need to incr the before_siblings to make room for sibling_node:
517
523
  if use_update_all
518
524
  col = quoted_order_column(false)
519
- ct_class.update_all(
525
+ # issue 21: we have to use the base class, so STI doesn't get in the way of only updating the child class instances:
526
+ ct_base_class.update_all(
520
527
  ["#{col} = #{col} #{add_after ? '+' : '-'} 1", "updated_at = now()"],
521
528
  ["#{quoted_parent_column_name} = ? AND #{col} #{add_after ? '>=' : '<='} ?",
522
529
  ct_parent_id,
@@ -531,7 +538,7 @@ module ClosureTree
531
538
  end
532
539
  sibling_node.parent = self.parent
533
540
  sibling_node.save!
534
- sibling_node.reload
541
+ sibling_node.reload # <- because siblings_before and siblings_after will have changed.
535
542
  end
536
543
  end
537
544
  end
@@ -1,3 +1,3 @@
1
1
  module ClosureTree
2
- VERSION = "3.6.0" unless defined?(::ClosureTree::VERSION)
2
+ VERSION = "3.6.1" unless defined?(::ClosureTree::VERSION)
3
3
  end
@@ -115,6 +115,51 @@ describe Label do
115
115
  end
116
116
  end
117
117
 
118
+ context "deterministically orders with polymorphic siblings" do
119
+ before :each do
120
+ @parent = Label.create!(:name => "parent")
121
+ @a = EventLabel.new(:name => "a")
122
+ @b = DirectoryLabel.new(:name => "b")
123
+ @c = DateLabel.new(:name => "c")
124
+ @parent.children << @a
125
+ @a.append_sibling(@b)
126
+ @b.append_sibling(@c)
127
+ end
128
+
129
+ it "when inserted before" do
130
+ @b.append_sibling(@a)
131
+ # Have to reload because the sort_order will have changed out from under the references:
132
+ @b.reload.sort_order.should be < @a.reload.sort_order
133
+ @a.reload.sort_order.should be < @c.reload.sort_order
134
+ end
135
+
136
+ it "when inserted before" do
137
+ @b.append_sibling(@a, use_update_all = false)
138
+ # Have to reload because the sort_order will have changed out from under the references:
139
+ @b.reload.sort_order.should be < @a.reload.sort_order
140
+ @a.reload.sort_order.should be < @c.reload.sort_order
141
+ end
142
+ end
143
+
144
+ it "behaves like the readme" do
145
+ root = Label.create(:name => "root")
146
+ a = Label.create(:name => "a", :parent => root)
147
+ b = Label.create(:name => "b")
148
+ c = Label.create(:name => "c")
149
+
150
+ a.append_sibling(b)
151
+ root.reload.children.collect(&:name).should == %w(a b)
152
+
153
+ a.prepend_sibling(b)
154
+ root.reload.children.collect(&:name).should == %w(b a)
155
+
156
+ a.append_sibling(c)
157
+ root.reload.children.collect(&:name).should == %w(b a c)
158
+
159
+ b.append_sibling(c)
160
+ root.reload.children.collect(&:name).should == %w(b c a)
161
+ end
162
+
118
163
  context "Deterministic siblings sort with custom integer column" do
119
164
  nuke_db
120
165
  fixtures :labels
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: closure_tree
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.6.0
4
+ version: 3.6.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -192,7 +192,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
192
192
  version: '0'
193
193
  segments:
194
194
  - 0
195
- hash: 1756781626803345033
195
+ hash: 4291018222933120120
196
196
  required_rubygems_version: !ruby/object:Gem::Requirement
197
197
  none: false
198
198
  requirements:
@@ -201,7 +201,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
201
201
  version: '0'
202
202
  segments:
203
203
  - 0
204
- hash: 1756781626803345033
204
+ hash: 4291018222933120120
205
205
  requirements: []
206
206
  rubyforge_project:
207
207
  rubygems_version: 1.8.23