acts_as_ordered_tree 1.2.1 → 1.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 86dc7f385344addb7fa9390045711d13e450e08f
4
- data.tar.gz: 00ed12a00031723963094178dc4820cfde4952b2
3
+ metadata.gz: 8d1d945d226f1b6846fa1b58f24a6adab1d41425
4
+ data.tar.gz: a894af6acac2e1d1bbc044cc008effc846b5b42a
5
5
  SHA512:
6
- metadata.gz: 7d7dfc4a087d7cdc79f7f819b4d732cdca4371950421f7b1bad0daf2e355469675a58b5a3f07fa83311dfabbd3892b3db73fd5ffd26fd7a9bb20fbaeea2e4a99
7
- data.tar.gz: 8e4647629a8719db47d2875306a28bcea5932d87c2c0a4c5dfd14a5c7020a1bc84b1e2ee41e6b1ef387aec64c204c15ca253a7dc935ec1ad217a8c50a9ebf713
6
+ metadata.gz: 458af4339a7cdb37ab604344970a7c83264827b62923b90f90e5e607355babd918df26b96897ff91ca2f1b6064f8d0cb6f2d95d1af14c3da6d67cb9cd3ecd060
7
+ data.tar.gz: 62cd0756ebb2c63518cd73ef53b6163eac7bf1716ebb3d3614217bd2bebe2eefa14109dd87a53ae755032f63ce0c75a24a4f43fd1069b8d5dbca0c7edd970f78
@@ -1,8 +1,8 @@
1
- require "active_record"
2
- require "acts_as_ordered_tree/version"
3
- require "acts_as_ordered_tree/class_methods"
4
- require "acts_as_ordered_tree/instance_methods"
5
- require "acts_as_ordered_tree/validators"
1
+ require 'active_record'
2
+ require 'acts_as_ordered_tree/version'
3
+ require 'acts_as_ordered_tree/class_methods'
4
+ require 'acts_as_ordered_tree/instance_methods'
5
+ require 'acts_as_ordered_tree/validators'
6
6
 
7
7
  module ActsAsOrderedTree
8
8
  PROTECTED_ATTRIBUTES_SUPPORTED = ActiveRecord::VERSION::STRING < '4.0.0' ||
@@ -1,3 +1,6 @@
1
+ require 'acts_as_ordered_tree/arrangeable'
2
+ require 'acts_as_ordered_tree/relation/preloaded'
3
+
1
4
  module ActsAsOrderedTree
2
5
  module Adapters
3
6
  module PostgreSQLAdapter
@@ -15,9 +18,10 @@ module ActsAsOrderedTree
15
18
  QUERY
16
19
 
17
20
  with_recursive_join(query, 'self_and_ancestors').
18
- order('self_and_ancestors._depth DESC')
21
+ order('self_and_ancestors._depth DESC').
22
+ extending(Arrangeable)
19
23
  else
20
- ancestors + [self]
24
+ (ancestors + [self]).tap { |ary| ary.extend(Arrangeable) }
21
25
  end
22
26
  end
23
27
 
@@ -33,7 +37,9 @@ module ActsAsOrderedTree
33
37
  INNER JOIN ancestors ON alias1.id = ancestors.#{parent_column}
34
38
  QUERY
35
39
 
36
- with_recursive_join(query, 'ancestors').order('ancestors._depth DESC')
40
+ with_recursive_join(query, 'ancestors').
41
+ order('ancestors._depth DESC').
42
+ extending(Arrangeable)
37
43
  end
38
44
 
39
45
  def root
@@ -52,7 +58,8 @@ module ActsAsOrderedTree
52
58
  QUERY
53
59
 
54
60
  with_recursive_join(query, 'descendants').
55
- order('descendants._positions ASC')
61
+ order('descendants._positions ASC').
62
+ extending(Arrangeable)
56
63
  end
57
64
 
58
65
  def descendants
@@ -0,0 +1,80 @@
1
+ module ActsAsOrderedTree
2
+ module Arrangeable
3
+ # @api private
4
+ class Arranger
5
+ attr_reader :collection, :cache
6
+
7
+ def initialize(collection, options = {})
8
+ @collection = collection
9
+ @discard_orphans = options[:orphans] == :discard
10
+ @min_level = nil
11
+
12
+ if discard_orphans? && !collection.klass.depth_column && ActiveRecord::Base.logger
13
+ ActiveRecord::Base.logger.warn {
14
+ '%s model has no `depth` column, '\
15
+ 'it can lead to N+1 queries during #arrange method invocation' % collection.klass
16
+ }
17
+ end
18
+
19
+ @cache = Hash.new
20
+ @prepared = false
21
+ end
22
+
23
+ def arrange
24
+ prepare unless prepared?
25
+
26
+ @arranged ||= collection.each_with_object(Hash.new) do |node, result|
27
+ ancestors = ancestors(node)
28
+
29
+ if discard_orphans?
30
+ root = ancestors.first || node
31
+
32
+ next if root.level > @min_level
33
+ end
34
+
35
+ insertion_point = result
36
+
37
+ ancestors.each { |a| insertion_point = (insertion_point[a] ||= {}) }
38
+
39
+ insertion_point[node] = {}
40
+ end
41
+ end
42
+
43
+ private
44
+ def prepare
45
+ collection.each do |node|
46
+ cache[node.id] = node if node.id
47
+ @min_level = [@min_level, node.level].compact.min
48
+ end
49
+
50
+ @prepared = true
51
+ end
52
+
53
+ def discard_orphans?
54
+ @discard_orphans
55
+ end
56
+
57
+ def prepared?
58
+ @prepared
59
+ end
60
+
61
+ # get parent node of +node+
62
+ def parent(node)
63
+ cache[node[node.parent_column]]
64
+ end
65
+
66
+ def ancestors(node)
67
+ parent = parent(node)
68
+ parent ? ancestors(parent) + [parent] : []
69
+ end
70
+ end
71
+ private_constant :Arranger
72
+
73
+ # Arrange associated collection into a nested hash of the form
74
+ # {node => children}, where children = {} if the node has no children.
75
+ def arrange(options = {})
76
+ @arranger ||= Arranger.new(self, options)
77
+ @arranger.arrange
78
+ end
79
+ end
80
+ end
@@ -1,6 +1,7 @@
1
1
  # coding: utf-8
2
- require "acts_as_ordered_tree/tenacious_transaction"
3
- require "acts_as_ordered_tree/relation/preloaded"
2
+ require 'acts_as_ordered_tree/tenacious_transaction'
3
+ require 'acts_as_ordered_tree/relation/preloaded'
4
+ require 'acts_as_ordered_tree/arrangeable'
4
5
 
5
6
  module ActsAsOrderedTree
6
7
  module InstanceMethods
@@ -16,7 +17,7 @@ module ActsAsOrderedTree
16
17
  persisted? && if children_counter_cache_column
17
18
  self[children_counter_cache_column] == 0
18
19
  else
19
- children.count == 0
20
+ !children.reorder(nil).exists?
20
21
  end
21
22
  end
22
23
 
@@ -50,7 +51,8 @@ module ActsAsOrderedTree
50
51
 
51
52
  # 3. create fake scope
52
53
  ActsAsOrderedTree::Relation::Preloaded.new(self.class).
53
- where(:id => nodes.map(&:id)).
54
+ where(:id => nodes.map(&:id).compact).
55
+ extending(Arrangeable).
54
56
  records(nodes)
55
57
  end
56
58
 
@@ -93,7 +95,8 @@ module ActsAsOrderedTree
93
95
  records = fetch_self_and_descendants - [self]
94
96
 
95
97
  ActsAsOrderedTree::Relation::Preloaded.new(self.class).
96
- where(:id => records.map(&:id)).
98
+ where(:id => records.map(&:id).compact).
99
+ extending(Arrangeable).
97
100
  records(records)
98
101
  end
99
102
 
@@ -103,6 +106,7 @@ module ActsAsOrderedTree
103
106
 
104
107
  ActsAsOrderedTree::Relation::Preloaded.new(self.class).
105
108
  where(:id => records.map(&:id)).
109
+ extending(Arrangeable).
106
110
  records(records)
107
111
  end
108
112
 
@@ -1,3 +1,3 @@
1
1
  module ActsAsOrderedTree
2
- VERSION = '1.2.1'
2
+ VERSION = '1.3.1'
3
3
  end
@@ -415,6 +415,97 @@ describe ActsAsOrderedTree, :transactional do
415
415
  end
416
416
  end
417
417
 
418
+ describe '#arrange' do
419
+ shared_examples 'arrangeable' do
420
+ let(:child_1) { root.children.first }
421
+ let(:child_2) { root.children.last }
422
+ let(:grandchild_11) { child_1.children.first }
423
+ let(:grandchild_12) { child_1.children.last }
424
+ let(:grandchild_21) { child_2.children.first }
425
+ let(:grandchild_22) { child_2.children.last }
426
+
427
+ specify '#descendants scope should be arrangeable' do
428
+ expect(root.descendants.arrange).to eq Hash[
429
+ child_1 => {
430
+ grandchild_11 => {},
431
+ grandchild_12 => {}
432
+ },
433
+ child_2 => {
434
+ grandchild_21 => {},
435
+ grandchild_22 => {}
436
+ }
437
+ ]
438
+ end
439
+
440
+ specify '#self_and_descendants should be arrangeable' do
441
+ expect(root.self_and_descendants.arrange).to eq Hash[
442
+ root => {
443
+ child_1 => {
444
+ grandchild_11 => {},
445
+ grandchild_12 => {}
446
+ },
447
+ child_2 => {
448
+ grandchild_21 => {},
449
+ grandchild_22 => {}
450
+ }
451
+ }
452
+ ]
453
+ end
454
+
455
+ specify '#ancestors should be arrangeable' do
456
+ expect(grandchild_11.ancestors.arrange).to eq Hash[
457
+ root => {
458
+ child_1 => {}
459
+ }
460
+ ]
461
+ end
462
+
463
+ specify '#self_and_ancestors should be arrangeable' do
464
+ expect(grandchild_11.self_and_ancestors.arrange).to eq Hash[
465
+ root => {
466
+ child_1 => {
467
+ grandchild_11 => {}
468
+ }
469
+ }
470
+ ]
471
+ end
472
+
473
+ it 'should not discard orphaned nodes by default' do
474
+ relation = root.descendants.where(root.class.arel_table[:id].not_eq(child_1.id))
475
+
476
+ expect(relation.arrange).to eq Hash[
477
+ grandchild_11 => {},
478
+ grandchild_12 => {},
479
+ child_2 => {
480
+ grandchild_21 => {},
481
+ grandchild_22 => {}
482
+ }
483
+ ]
484
+ end
485
+
486
+ it 'should discard orphans if option :discard passed' do
487
+ relation = root.descendants.where(root.class.arel_table[:id].not_eq(child_1.id))
488
+
489
+ expect(relation.arrange(:orphans => :discard)).to eq Hash[
490
+ child_2 => {
491
+ grandchild_21 => {},
492
+ grandchild_22 => {}
493
+ }
494
+ ]
495
+ end
496
+ end
497
+
498
+ context 'when persisted tree given' do
499
+ it_should_behave_like 'arrangeable' do
500
+ let(:root) { create :default }
501
+
502
+ before { create_list :default, 2, :parent => root }
503
+ before { create_list :default, 2, :parent => root.children.first }
504
+ before { create_list :default, 2, :parent => root.children.last }
505
+ end
506
+ end
507
+ end
508
+
418
509
  describe "move actions" do
419
510
  let!(:root) { create :default_with_counter_cache, :name => 'root' }
420
511
  let!(:child_1) { create :default_with_counter_cache, :parent => root, :name => 'child_1' }
@@ -28,6 +28,7 @@ ActiveRecord::Base.configurations = YAML::load(ERB.new(IO.read(File.join(test_di
28
28
  ActiveRecord::Base.establish_connection(ENV['DB'])
29
29
  ActiveRecord::Base.logger = Logger.new(ENV['DEBUG'] ? $stderr : '/dev/null')
30
30
  ActiveRecord::Migration.verbose = false
31
+ I18n.enforce_available_locales = false if I18n.respond_to?(:enforce_available_locales=)
31
32
  load(File.join(test_dir, 'db', 'schema.rb'))
32
33
 
33
34
  require 'shoulda-matchers'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: acts_as_ordered_tree
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: 1.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexei Mikhailov
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-12-23 00:00:00.000000000 Z
12
+ date: 2014-02-18 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -73,14 +73,14 @@ dependencies:
73
73
  requirements:
74
74
  - - '>='
75
75
  - !ruby/object:Gem::Version
76
- version: 1.2.0
76
+ version: 2.4.0
77
77
  type: :development
78
78
  prerelease: false
79
79
  version_requirements: !ruby/object:Gem::Requirement
80
80
  requirements:
81
81
  - - '>='
82
82
  - !ruby/object:Gem::Version
83
- version: 1.2.0
83
+ version: 2.4.0
84
84
  - !ruby/object:Gem::Dependency
85
85
  name: factory_girl
86
86
  requirement: !ruby/object:Gem::Requirement
@@ -119,6 +119,7 @@ extra_rdoc_files: []
119
119
  files:
120
120
  - lib/acts_as_ordered_tree.rb
121
121
  - lib/acts_as_ordered_tree/adapters/postgresql_adapter.rb
122
+ - lib/acts_as_ordered_tree/arrangeable.rb
122
123
  - lib/acts_as_ordered_tree/class_methods.rb
123
124
  - lib/acts_as_ordered_tree/instance_methods.rb
124
125
  - lib/acts_as_ordered_tree/relation/base.rb