closure_tree 3.6.0 → 3.6.1

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.
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