acts_as_ordered_tree 1.2.1 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
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