acts_as_ordered_tree 1.3.1 → 2.0.0.beta3
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 +4 -4
- data/lib/acts_as_ordered_tree.rb +22 -100
- data/lib/acts_as_ordered_tree/adapters.rb +17 -0
- data/lib/acts_as_ordered_tree/adapters/abstract.rb +23 -0
- data/lib/acts_as_ordered_tree/adapters/postgresql.rb +150 -0
- data/lib/acts_as_ordered_tree/adapters/recursive.rb +157 -0
- data/lib/acts_as_ordered_tree/compatibility.rb +22 -0
- data/lib/acts_as_ordered_tree/compatibility/active_record/association_scope.rb +9 -0
- data/lib/acts_as_ordered_tree/compatibility/active_record/default_scoped.rb +19 -0
- data/lib/acts_as_ordered_tree/compatibility/active_record/null_relation.rb +71 -0
- data/lib/acts_as_ordered_tree/compatibility/features.rb +153 -0
- data/lib/acts_as_ordered_tree/deprecate.rb +24 -0
- data/lib/acts_as_ordered_tree/hooks.rb +38 -0
- data/lib/acts_as_ordered_tree/hooks/update.rb +86 -0
- data/lib/acts_as_ordered_tree/instance_methods.rb +92 -453
- data/lib/acts_as_ordered_tree/iterators/arranger.rb +35 -0
- data/lib/acts_as_ordered_tree/iterators/level_calculator.rb +52 -0
- data/lib/acts_as_ordered_tree/iterators/orphans_pruner.rb +58 -0
- data/lib/acts_as_ordered_tree/node.rb +78 -0
- data/lib/acts_as_ordered_tree/node/attributes.rb +48 -0
- data/lib/acts_as_ordered_tree/node/movement.rb +62 -0
- data/lib/acts_as_ordered_tree/node/movements.rb +111 -0
- data/lib/acts_as_ordered_tree/node/predicates.rb +98 -0
- data/lib/acts_as_ordered_tree/node/reloading.rb +49 -0
- data/lib/acts_as_ordered_tree/node/siblings.rb +139 -0
- data/lib/acts_as_ordered_tree/node/traversals.rb +53 -0
- data/lib/acts_as_ordered_tree/persevering_transaction.rb +93 -0
- data/lib/acts_as_ordered_tree/position.rb +143 -0
- data/lib/acts_as_ordered_tree/relation/arrangeable.rb +33 -0
- data/lib/acts_as_ordered_tree/relation/iterable.rb +41 -0
- data/lib/acts_as_ordered_tree/relation/preloaded.rb +46 -11
- data/lib/acts_as_ordered_tree/transaction/base.rb +57 -0
- data/lib/acts_as_ordered_tree/transaction/callbacks.rb +67 -0
- data/lib/acts_as_ordered_tree/transaction/create.rb +68 -0
- data/lib/acts_as_ordered_tree/transaction/destroy.rb +34 -0
- data/lib/acts_as_ordered_tree/transaction/dsl.rb +214 -0
- data/lib/acts_as_ordered_tree/transaction/factory.rb +67 -0
- data/lib/acts_as_ordered_tree/transaction/move.rb +70 -0
- data/lib/acts_as_ordered_tree/transaction/passthrough.rb +12 -0
- data/lib/acts_as_ordered_tree/transaction/reorder.rb +42 -0
- data/lib/acts_as_ordered_tree/transaction/save.rb +64 -0
- data/lib/acts_as_ordered_tree/transaction/update.rb +78 -0
- data/lib/acts_as_ordered_tree/tree.rb +148 -0
- data/lib/acts_as_ordered_tree/tree/association.rb +20 -0
- data/lib/acts_as_ordered_tree/tree/callbacks.rb +57 -0
- data/lib/acts_as_ordered_tree/tree/children_association.rb +120 -0
- data/lib/acts_as_ordered_tree/tree/columns.rb +102 -0
- data/lib/acts_as_ordered_tree/tree/deprecated_columns_accessors.rb +24 -0
- data/lib/acts_as_ordered_tree/tree/parent_association.rb +31 -0
- data/lib/acts_as_ordered_tree/tree/perseverance.rb +19 -0
- data/lib/acts_as_ordered_tree/tree/scopes.rb +56 -0
- data/lib/acts_as_ordered_tree/validators.rb +1 -1
- data/lib/acts_as_ordered_tree/version.rb +1 -1
- data/spec/acts_as_ordered_tree_spec.rb +80 -909
- data/spec/adapters/postgresql_spec.rb +14 -0
- data/spec/adapters/recursive_spec.rb +12 -0
- data/spec/adapters/shared.rb +272 -0
- data/spec/callbacks_spec.rb +177 -0
- data/spec/counter_cache_spec.rb +31 -0
- data/spec/create_spec.rb +110 -0
- data/spec/destroy_spec.rb +57 -0
- data/spec/inheritance_spec.rb +176 -0
- data/spec/move_spec.rb +94 -0
- data/spec/node/movements/concurrent_movements_spec.rb +354 -0
- data/spec/node/movements/move_higher_spec.rb +46 -0
- data/spec/node/movements/move_lower_spec.rb +46 -0
- data/spec/node/movements/move_to_child_of_spec.rb +147 -0
- data/spec/node/movements/move_to_child_with_index_spec.rb +124 -0
- data/spec/node/movements/move_to_child_with_position_spec.rb +85 -0
- data/spec/node/movements/move_to_left_of_spec.rb +120 -0
- data/spec/node/movements/move_to_right_of_spec.rb +120 -0
- data/spec/node/movements/move_to_root_spec.rb +67 -0
- data/spec/node/predicates_spec.rb +211 -0
- data/spec/node/reloading_spec.rb +42 -0
- data/spec/node/siblings_spec.rb +193 -0
- data/spec/node/traversals_spec.rb +71 -0
- data/spec/persevering_transaction_spec.rb +98 -0
- data/spec/relation/arrangeable_spec.rb +88 -0
- data/spec/relation/iterable_spec.rb +104 -0
- data/spec/relation/preloaded_spec.rb +57 -0
- data/spec/reorder_spec.rb +83 -0
- data/spec/spec_helper.rb +30 -38
- data/spec/support/db/boot.rb +22 -0
- data/spec/{db → support/db}/config.travis.yml +2 -0
- data/spec/{db → support/db}/config.yml +1 -0
- data/spec/{db → support/db}/schema.rb +9 -0
- data/spec/support/factories.rb +2 -2
- data/spec/support/matchers.rb +67 -58
- data/spec/support/models.rb +6 -14
- data/spec/support/tree_factory.rb +315 -0
- data/spec/tree/children_association_spec.rb +72 -0
- data/spec/tree/columns_spec.rb +65 -0
- data/spec/tree/scopes_spec.rb +39 -0
- metadata +161 -43
- data/lib/acts_as_ordered_tree/adapters/postgresql_adapter.rb +0 -104
- data/lib/acts_as_ordered_tree/arrangeable.rb +0 -80
- data/lib/acts_as_ordered_tree/class_methods.rb +0 -72
- data/lib/acts_as_ordered_tree/relation/base.rb +0 -26
- data/lib/acts_as_ordered_tree/tenacious_transaction.rb +0 -30
- data/spec/concurrency_support_spec.rb +0 -156
@@ -1,104 +0,0 @@
|
|
1
|
-
require 'acts_as_ordered_tree/arrangeable'
|
2
|
-
require 'acts_as_ordered_tree/relation/preloaded'
|
3
|
-
|
4
|
-
module ActsAsOrderedTree
|
5
|
-
module Adapters
|
6
|
-
module PostgreSQLAdapter
|
7
|
-
# Recursive ancestors fetcher
|
8
|
-
def self_and_ancestors
|
9
|
-
if persisted? && !send("#{parent_column}_changed?")
|
10
|
-
query = <<-QUERY
|
11
|
-
SELECT id, #{parent_column}, 1 AS _depth
|
12
|
-
FROM #{self.class.quoted_table_name}
|
13
|
-
WHERE #{arel[:id].eq(id).to_sql}
|
14
|
-
UNION ALL
|
15
|
-
SELECT alias1.id, alias1.#{parent_column}, _depth + 1
|
16
|
-
FROM #{self.class.quoted_table_name} alias1
|
17
|
-
INNER JOIN self_and_ancestors ON alias1.id = self_and_ancestors.#{parent_column}
|
18
|
-
QUERY
|
19
|
-
|
20
|
-
with_recursive_join(query, 'self_and_ancestors').
|
21
|
-
order('self_and_ancestors._depth DESC').
|
22
|
-
extending(Arrangeable)
|
23
|
-
else
|
24
|
-
(ancestors + [self]).tap { |ary| ary.extend(Arrangeable) }
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
# Recursive ancestors fetcher
|
29
|
-
def ancestors
|
30
|
-
query = <<-QUERY
|
31
|
-
SELECT id, #{parent_column}, 1 AS _depth
|
32
|
-
FROM #{self.class.quoted_table_name}
|
33
|
-
WHERE #{arel[:id].eq(parent.try(:id)).to_sql}
|
34
|
-
UNION ALL
|
35
|
-
SELECT alias1.id, alias1.#{parent_column}, _depth + 1
|
36
|
-
FROM #{self.class.quoted_table_name} alias1
|
37
|
-
INNER JOIN ancestors ON alias1.id = ancestors.#{parent_column}
|
38
|
-
QUERY
|
39
|
-
|
40
|
-
with_recursive_join(query, 'ancestors').
|
41
|
-
order('ancestors._depth DESC').
|
42
|
-
extending(Arrangeable)
|
43
|
-
end
|
44
|
-
|
45
|
-
def root
|
46
|
-
root? ? self : ancestors.first
|
47
|
-
end
|
48
|
-
|
49
|
-
def self_and_descendants
|
50
|
-
query = <<-QUERY
|
51
|
-
SELECT id, #{parent_column}, ARRAY[#{position_column}] AS _positions
|
52
|
-
FROM #{self.class.quoted_table_name}
|
53
|
-
WHERE #{arel[:id].eq(id).to_sql}
|
54
|
-
UNION ALL
|
55
|
-
SELECT alias1.id, alias1.#{parent_column}, _positions || alias1.#{position_column}
|
56
|
-
FROM descendants INNER JOIN
|
57
|
-
#{self.class.quoted_table_name} alias1 ON alias1.parent_id = descendants.id
|
58
|
-
QUERY
|
59
|
-
|
60
|
-
with_recursive_join(query, 'descendants').
|
61
|
-
order('descendants._positions ASC').
|
62
|
-
extending(Arrangeable)
|
63
|
-
end
|
64
|
-
|
65
|
-
def descendants
|
66
|
-
self_and_descendants.where(arel[:id].not_eq(id))
|
67
|
-
end
|
68
|
-
|
69
|
-
private
|
70
|
-
def recursive_scope
|
71
|
-
ActsAsOrderedTree::Relation::Recursive.new(ordered_tree_scope)
|
72
|
-
end
|
73
|
-
|
74
|
-
def with_recursive_join(recursive_query_sql, aliaz)
|
75
|
-
join_sql = 'INNER JOIN (' +
|
76
|
-
"WITH RECURSIVE #{aliaz} AS (" +
|
77
|
-
recursive_query_sql +
|
78
|
-
") SELECT * FROM #{aliaz} " +
|
79
|
-
") #{aliaz} ON #{aliaz}.id = #{self.class.quoted_table_name}.id"
|
80
|
-
|
81
|
-
ordered_tree_scope.joins(join_sql)
|
82
|
-
end
|
83
|
-
|
84
|
-
# Rails 3.0 does not support update_all with joins, so we patch it :(
|
85
|
-
if ActiveRecord::VERSION::STRING <= '3.1.0'
|
86
|
-
module Rails30UpdateAllPatch
|
87
|
-
def update_all(updates, conditions = nil, options = {})
|
88
|
-
relation = except(:joins, :where).
|
89
|
-
where(:id => select(klass.arel_table[:id]).except(:order, :limit).arel)
|
90
|
-
relation.update_all(updates, conditions, options)
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
def with_recursive_join_30(recursive_query_sql, aliaz)
|
95
|
-
relation = with_recursive_join_31(recursive_query_sql, aliaz)
|
96
|
-
relation.extend(Rails30UpdateAllPatch)
|
97
|
-
relation
|
98
|
-
end
|
99
|
-
alias_method :with_recursive_join_31, :with_recursive_join
|
100
|
-
alias_method :with_recursive_join, :with_recursive_join_30
|
101
|
-
end
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
@@ -1,80 +0,0 @@
|
|
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,72 +0,0 @@
|
|
1
|
-
require "acts_as_ordered_tree/adapters/postgresql_adapter"
|
2
|
-
|
3
|
-
module ActsAsOrderedTree
|
4
|
-
module ClassMethods
|
5
|
-
extend ActiveSupport::Concern
|
6
|
-
|
7
|
-
included do
|
8
|
-
scope :preorder, -> { order(arel_table[position_column].asc) }
|
9
|
-
scope :roots, -> { where(arel_table[parent_column].eq(nil)).preorder }
|
10
|
-
|
11
|
-
# add +leaves+ scope only if counter_cache column present
|
12
|
-
scope :leaves, -> { where(arel_table[children_counter_cache_column].eq(0)) } if
|
13
|
-
children_counter_cache?
|
14
|
-
|
15
|
-
# when default value for counter_cache is absent we should set it manually
|
16
|
-
before_create "self.#{children_counter_cache_column} = 0" if children_counter_cache?
|
17
|
-
end
|
18
|
-
|
19
|
-
module ClassMethods
|
20
|
-
# Returns the first root
|
21
|
-
def root
|
22
|
-
roots.first
|
23
|
-
end
|
24
|
-
|
25
|
-
private
|
26
|
-
def children_counter_cache? #:nodoc:
|
27
|
-
children_counter_cache_column && columns_hash.key?(children_counter_cache_column.to_s)
|
28
|
-
end
|
29
|
-
|
30
|
-
def setup_ordered_tree_adapter #:nodoc:
|
31
|
-
include "ActsAsOrderedTree::Adapters::#{connection.class.name.demodulize}".constantize
|
32
|
-
rescue NameError, LoadError
|
33
|
-
# ignore
|
34
|
-
end
|
35
|
-
|
36
|
-
def setup_ordered_tree_callbacks #:nodoc:
|
37
|
-
define_model_callbacks :move, :reorder
|
38
|
-
|
39
|
-
if depth_column
|
40
|
-
before_create :set_depth!
|
41
|
-
before_save :set_depth!, :if => "#{parent_column}_changed?".to_sym
|
42
|
-
around_move :update_descendants_depth
|
43
|
-
end
|
44
|
-
|
45
|
-
if children_counter_cache_column
|
46
|
-
around_move :update_counter_cache
|
47
|
-
end
|
48
|
-
|
49
|
-
unless scope_column_names.empty?
|
50
|
-
before_save :set_scope!, :unless => :root?
|
51
|
-
end
|
52
|
-
|
53
|
-
after_save :move_to_root, :unless => [position_column, parent_column]
|
54
|
-
after_save 'move_to_child_of(parent)', :if => parent_column, :unless => position_column
|
55
|
-
after_save "move_to_child_with_index(parent, #{position_column})",
|
56
|
-
:if => "#{position_column} && (#{position_column}_changed? || #{parent_column}_changed?)"
|
57
|
-
|
58
|
-
before_destroy :flush_descendants
|
59
|
-
after_destroy "decrement_lower_positions(#{parent_column}_was, #{position_column}_was)", :if => position_column
|
60
|
-
end
|
61
|
-
|
62
|
-
def setup_ordered_tree_validations #:nodoc:
|
63
|
-
unless scope_column_names.empty?
|
64
|
-
validates_with Validators::ScopeValidator, :on => :update, :unless => :root?
|
65
|
-
end
|
66
|
-
|
67
|
-
# setup validations
|
68
|
-
validates_with Validators::CyclicReferenceValidator, :on => :update, :if => :parent
|
69
|
-
end
|
70
|
-
end # module ClassMethods
|
71
|
-
end # module ClassMethods
|
72
|
-
end # module ActsAsOrderedTree
|
@@ -1,26 +0,0 @@
|
|
1
|
-
module ActsAsOrderedTree
|
2
|
-
module Relation
|
3
|
-
class Base < ActiveRecord::Relation
|
4
|
-
EMPTY_SCOPE_METHOD = ActiveRecord::VERSION::STRING < '4.0.0' ? :scoped : :all
|
5
|
-
|
6
|
-
# Create from existing +relation+ or from +class+ and +table+
|
7
|
-
def initialize(class_or_relation, table = nil)
|
8
|
-
relation = class_or_relation
|
9
|
-
|
10
|
-
if class_or_relation.is_a?(Class)
|
11
|
-
relation = class_or_relation.send(EMPTY_SCOPE_METHOD)
|
12
|
-
table ||= class_or_relation.arel_table
|
13
|
-
|
14
|
-
super(class_or_relation, table)
|
15
|
-
else
|
16
|
-
super(class_or_relation.klass, class_or_relation.table)
|
17
|
-
end
|
18
|
-
|
19
|
-
# copy instance variables from real relation
|
20
|
-
relation.instance_variables.each do |ivar|
|
21
|
-
instance_variable_set(ivar, relation.instance_variable_get(ivar))
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
module ActsAsOrderedTree
|
2
|
-
module TenaciousTransaction
|
3
|
-
DEADLOCK_MESSAGES = /Deadlock found when trying to get lock|Lock wait timeout exceeded|deadlock detected/.freeze
|
4
|
-
RETRY_COUNT = 10
|
5
|
-
|
6
|
-
# Partially borrowed from awesome_nested_set
|
7
|
-
def tenacious_transaction(&block) #:nodoc:
|
8
|
-
return transaction(&block) if @in_tenacious_transaction
|
9
|
-
|
10
|
-
@in_tenacious_transaction = true
|
11
|
-
retry_count = 0
|
12
|
-
begin
|
13
|
-
transaction(&block)
|
14
|
-
rescue ActiveRecord::StatementInvalid => error
|
15
|
-
raise unless self.class.connection.open_transactions.zero?
|
16
|
-
raise unless error.message =~ DEADLOCK_MESSAGES
|
17
|
-
raise unless retry_count < RETRY_COUNT
|
18
|
-
retry_count += 1
|
19
|
-
|
20
|
-
logger.info "Deadlock detected on retry #{retry_count}, restarting transaction"
|
21
|
-
|
22
|
-
sleep(rand(retry_count)*0.1) # Aloha protocol
|
23
|
-
|
24
|
-
retry
|
25
|
-
ensure
|
26
|
-
@in_tenacious_transaction = false
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
@@ -1,156 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
# FIXME: These tests are buggy on Rails 3.0
|
4
|
-
# Sqlite is not concurrent database
|
5
|
-
if ActiveRecord::VERSION::STRING >= "3.1" && ENV['DB'] != 'sqlite3'
|
6
|
-
describe ActsAsOrderedTree, :non_transactional do
|
7
|
-
module Concurrency
|
8
|
-
# run block in its own thread, create +size+ threads
|
9
|
-
def pool(size)
|
10
|
-
size.times.map { |x|
|
11
|
-
Thread.new do
|
12
|
-
ActiveRecord::Base.connection_pool.with_connection { yield x }
|
13
|
-
end
|
14
|
-
}.each(&:join)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
include Concurrency
|
18
|
-
|
19
|
-
let!(:root) { create :default }
|
20
|
-
|
21
|
-
it "should not create nodes with same position" do
|
22
|
-
pool(3) do
|
23
|
-
create :default, :parent => root
|
24
|
-
end
|
25
|
-
|
26
|
-
root.children.map(&:position).should eq [1, 2, 3]
|
27
|
-
end
|
28
|
-
|
29
|
-
it "should not move nodes to same position when moving to child of certain node" do
|
30
|
-
nodes = create_list :default, 3
|
31
|
-
|
32
|
-
pool(3) do |x|
|
33
|
-
nodes[x].move_to_child_of(root)
|
34
|
-
end
|
35
|
-
|
36
|
-
root.children.map(&:position).should eq [1, 2, 3]
|
37
|
-
end
|
38
|
-
|
39
|
-
it "should not move nodes to same position when moving to left of root node" do
|
40
|
-
nodes = create_list :default, 3, :parent => root
|
41
|
-
|
42
|
-
pool(3) do |x|
|
43
|
-
nodes[x].move_to_left_of(root)
|
44
|
-
end
|
45
|
-
|
46
|
-
Default.roots.map(&:position).should eq [1, 2, 3, 4]
|
47
|
-
end
|
48
|
-
|
49
|
-
it "should not move nodes to same position when moving to left of child node" do
|
50
|
-
child = create :default, :parent => root
|
51
|
-
nodes = create_list :default, 3, :parent => child
|
52
|
-
|
53
|
-
pool(3) do |x|
|
54
|
-
nodes[x].move_to_left_of(child)
|
55
|
-
end
|
56
|
-
|
57
|
-
root.children.map(&:position).should eq [1, 2, 3, 4]
|
58
|
-
root.children.last.should eq child
|
59
|
-
end
|
60
|
-
|
61
|
-
it "should not move nodes to same position when moving to right of child node" do
|
62
|
-
child = create :default, :parent => root
|
63
|
-
nodes = create_list :default, 3, :parent => child
|
64
|
-
|
65
|
-
pool(3) do |x|
|
66
|
-
nodes[x].move_to_right_of(child)
|
67
|
-
end
|
68
|
-
|
69
|
-
root.children.map(&:position).should eq [1, 2, 3, 4]
|
70
|
-
root.children.first.should eq child
|
71
|
-
end
|
72
|
-
|
73
|
-
it "should not move nodes to same position when moving to root" do
|
74
|
-
nodes = create_list :default, 3, :parent => root
|
75
|
-
|
76
|
-
pool(3) do |x|
|
77
|
-
nodes[x].move_to_root
|
78
|
-
end
|
79
|
-
|
80
|
-
Default.roots.map(&:position).should eq [1, 2, 3, 4]
|
81
|
-
end
|
82
|
-
|
83
|
-
# checking deadlock also
|
84
|
-
it "should not move nodes to same position when moving to specified index" do
|
85
|
-
# root
|
86
|
-
# * child1
|
87
|
-
# * nodes1_1
|
88
|
-
# * nodes1_2
|
89
|
-
# * child2
|
90
|
-
# * nodes2_1
|
91
|
-
# * nodes2_2
|
92
|
-
child1, child2 = create_list :default, 2, :parent => root
|
93
|
-
|
94
|
-
nodes1, nodes2 = create_list(:default, 2, :parent => child1),
|
95
|
-
create_list(:default, 2, :parent => child2)
|
96
|
-
|
97
|
-
nodes1_1, nodes1_2 = nodes1
|
98
|
-
nodes2_1, nodes2_2 = nodes2
|
99
|
-
|
100
|
-
# nodes2_2 -> child1[0]
|
101
|
-
thread1 = Thread.new do
|
102
|
-
ActiveRecord::Base.connection_pool.with_connection do
|
103
|
-
nodes2_2.move_to_child_with_index(child1, 0)
|
104
|
-
end
|
105
|
-
end
|
106
|
-
# nodes1_1 -> child2[2]
|
107
|
-
thread2 = Thread.new do
|
108
|
-
ActiveRecord::Base.connection_pool.with_connection do
|
109
|
-
nodes1_1.move_to_child_with_index(child2, 2)
|
110
|
-
end
|
111
|
-
end
|
112
|
-
[thread1, thread2].map(&:join)
|
113
|
-
|
114
|
-
child1.children.reload.should == [nodes2_2, nodes1_2]
|
115
|
-
child2.children.reload.should == [nodes2_1, nodes1_1]
|
116
|
-
end
|
117
|
-
|
118
|
-
it "should not move nodes to same position when moving higher" do
|
119
|
-
child1, child2, child3 = create_list :default, 3, :parent => root
|
120
|
-
|
121
|
-
thread1 = Thread.new do
|
122
|
-
ActiveRecord::Base.connection_pool.with_connection do
|
123
|
-
child2.move_higher
|
124
|
-
end
|
125
|
-
end
|
126
|
-
thread2 = Thread.new do
|
127
|
-
ActiveRecord::Base.connection_pool.with_connection do
|
128
|
-
child3.move_higher
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
[thread1, thread2].map(&:join)
|
133
|
-
|
134
|
-
root.children.map(&:position).should eq [1, 2, 3]
|
135
|
-
end
|
136
|
-
|
137
|
-
it "should not move nodes to same position when moving lower" do
|
138
|
-
child1, child2, child3 = create_list :default, 3, :parent => root
|
139
|
-
|
140
|
-
thread1 = Thread.new do
|
141
|
-
ActiveRecord::Base.connection_pool.with_connection do
|
142
|
-
child1.move_lower
|
143
|
-
end
|
144
|
-
end
|
145
|
-
thread2 = Thread.new do
|
146
|
-
ActiveRecord::Base.connection_pool.with_connection do
|
147
|
-
child2.move_lower
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
[thread1, thread2].map(&:join)
|
152
|
-
|
153
|
-
root.children.map(&:position).should eq [1, 2, 3]
|
154
|
-
end
|
155
|
-
end
|
156
|
-
end
|