simple_nested_set 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/core_ext/hash/extract_nested_set_attributes.rb +8 -0
- data/lib/simple_nested_set/act_macro.rb +4 -3
- data/lib/simple_nested_set/class_methods.rb +0 -8
- data/lib/simple_nested_set/instance_methods.rb +4 -13
- data/lib/simple_nested_set/move/by_attributes.rb +4 -0
- data/lib/simple_nested_set/move/to_target.rb +52 -30
- data/lib/simple_nested_set/nested_set.rb +8 -29
- data/lib/simple_nested_set/version.rb +1 -1
- data/lib/simple_nested_set.rb +3 -0
- metadata +4 -3
@@ -4,15 +4,16 @@ module SimpleNestedSet
|
|
4
4
|
return if acts_as_nested_set?
|
5
5
|
|
6
6
|
include SimpleNestedSet::InstanceMethods
|
7
|
-
extend
|
7
|
+
extend SimpleNestedSet::ClassMethods
|
8
8
|
|
9
9
|
define_callbacks :move, :terminator => "result == false"
|
10
10
|
|
11
|
-
before_validation lambda { |r| r.nested_set.init_as_node }
|
11
|
+
before_validation lambda { |r| r.nested_set.init_as_node }, :if => :new_record?
|
12
12
|
before_destroy lambda { |r| r.nested_set.prune_branch }
|
13
|
+
after_save lambda { |r| r.nested_set.save! }
|
13
14
|
|
14
15
|
belongs_to :parent, :class_name => self.name
|
15
|
-
has_many :children, :foreign_key => :parent_id, :class_name =>
|
16
|
+
has_many :children, :foreign_key => :parent_id, :class_name => self.name
|
16
17
|
|
17
18
|
default_scope :order => :lft
|
18
19
|
|
@@ -10,14 +10,6 @@ module SimpleNestedSet
|
|
10
10
|
set_callback(:move, :after, *args, &block)
|
11
11
|
end
|
12
12
|
|
13
|
-
def create(attributes)
|
14
|
-
nested_set_class.with_move_by_attributes(attributes) { super }
|
15
|
-
end
|
16
|
-
|
17
|
-
def create!(attributes)
|
18
|
-
nested_set_class.with_move_by_attributes(attributes) { super }
|
19
|
-
end
|
20
|
-
|
21
13
|
# Returns the first root node (with the given scope if any)
|
22
14
|
def root(scope = nil)
|
23
15
|
roots(scope).first
|
@@ -3,16 +3,12 @@ require 'active_support/core_ext/hash/keys'
|
|
3
3
|
module SimpleNestedSet
|
4
4
|
module InstanceMethods
|
5
5
|
def nested_set
|
6
|
-
@
|
6
|
+
@_nested_set ||= nested_set_class.new(self)
|
7
7
|
end
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
end
|
13
|
-
|
14
|
-
def update_attributes!(attributes)
|
15
|
-
nested_set.with_move_by_attributes(attributes) { super }
|
9
|
+
def attributes=(attributes)
|
10
|
+
@_nested_set_attributes = attributes.extract_nested_set_attributes!
|
11
|
+
super
|
16
12
|
end
|
17
13
|
|
18
14
|
# recursively populates the parent and children associations of self and
|
@@ -22,11 +18,6 @@ module SimpleNestedSet
|
|
22
18
|
self
|
23
19
|
end
|
24
20
|
|
25
|
-
# Returns the level of this object in the tree, root level is 0
|
26
|
-
def level
|
27
|
-
parent_id.nil? ? 0 : ancestors.count
|
28
|
-
end
|
29
|
-
|
30
21
|
# Returns true if this is a root node.
|
31
22
|
def root?
|
32
23
|
parent_id.blank?
|
@@ -38,6 +38,10 @@ module SimpleNestedSet
|
|
38
38
|
attributes.symbolize_keys!
|
39
39
|
attributes.each { |key, value| attributes[key] = nil if value == 'null' }
|
40
40
|
|
41
|
+
[:parent, :left, :right].each do |key|
|
42
|
+
attributes[:"#{key}_id"] = attributes.delete(key).id if attributes.key?(key)
|
43
|
+
end
|
44
|
+
|
41
45
|
# if left_id is given but blank, set right_id to leftmost sibling
|
42
46
|
attributes[:right_id] = siblings.first.id if blank_given?(:left_id) && siblings.any?
|
43
47
|
|
@@ -15,17 +15,66 @@ module SimpleNestedSet
|
|
15
15
|
|
16
16
|
def perform
|
17
17
|
node.run_callbacks(:move) do
|
18
|
-
# reload
|
19
18
|
unless bound == node.rgt || bound == node.lft # there would be no change
|
20
19
|
nested_set.transaction do
|
21
|
-
#
|
22
|
-
|
20
|
+
# puts ActiveRecord::Base.send(:sanitize_sql_array, query)
|
21
|
+
update_structure!
|
22
|
+
update_denormalizations!
|
23
23
|
end
|
24
24
|
end
|
25
25
|
reload
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
+
def update_structure!
|
30
|
+
sql = <<-sql
|
31
|
+
lft = CASE
|
32
|
+
WHEN lft BETWEEN :a AND :b THEN lft + :d - :b
|
33
|
+
WHEN lft BETWEEN :c AND :d THEN lft + :a - :c
|
34
|
+
ELSE lft END,
|
35
|
+
|
36
|
+
rgt = CASE
|
37
|
+
WHEN rgt BETWEEN :a AND :b THEN rgt + :d - :b
|
38
|
+
WHEN rgt BETWEEN :c AND :d THEN rgt + :a - :c
|
39
|
+
ELSE rgt END,
|
40
|
+
|
41
|
+
parent_id = CASE
|
42
|
+
WHEN id = :id THEN :parent_id
|
43
|
+
ELSE parent_id END
|
44
|
+
sql
|
45
|
+
|
46
|
+
a, b, c, d = boundaries
|
47
|
+
sql = [sql, { :a => a, :b => b, :c => c, :d => d, :id => id, :parent_id => parent_id }]
|
48
|
+
nested_set.update_all(sql)
|
49
|
+
end
|
50
|
+
|
51
|
+
def update_denormalizations!
|
52
|
+
sql = []
|
53
|
+
sql << denormalize_level_query if node.has_attribute?(:level)
|
54
|
+
sql << denormalize_path_query if node.has_attribute?(:path)
|
55
|
+
nested_set.update_all(sql.join(',')) unless sql.blank?
|
56
|
+
end
|
57
|
+
|
58
|
+
def denormalize_level_query
|
59
|
+
<<-sql
|
60
|
+
level = (
|
61
|
+
SELECT count(id)
|
62
|
+
FROM #{table_name} as l
|
63
|
+
WHERE l.lft < #{table_name}.lft AND l.rgt > #{table_name}.rgt
|
64
|
+
)
|
65
|
+
sql
|
66
|
+
end
|
67
|
+
|
68
|
+
def denormalize_path_query
|
69
|
+
<<-sql
|
70
|
+
path = (
|
71
|
+
SELECT GROUP_CONCAT(slug, '/')
|
72
|
+
FROM #{table_name} as l
|
73
|
+
WHERE l.lft <= #{table_name}.lft AND l.rgt >= #{table_name}.rgt AND parent_id IS NOT NULL
|
74
|
+
)
|
75
|
+
sql
|
76
|
+
end
|
77
|
+
|
29
78
|
def reload
|
30
79
|
target.nested_set.reload if target
|
31
80
|
node.nested_set.reload
|
@@ -66,33 +115,6 @@ module SimpleNestedSet
|
|
66
115
|
@roots ||= node.nested_set.roots
|
67
116
|
end
|
68
117
|
|
69
|
-
def query
|
70
|
-
sql = <<-sql
|
71
|
-
lft = CASE
|
72
|
-
WHEN lft BETWEEN :a AND :b THEN lft + :d - :b
|
73
|
-
WHEN lft BETWEEN :c AND :d THEN lft + :a - :c
|
74
|
-
ELSE lft END,
|
75
|
-
|
76
|
-
rgt = CASE
|
77
|
-
WHEN rgt BETWEEN :a AND :b THEN rgt + :d - :b
|
78
|
-
WHEN rgt BETWEEN :c AND :d THEN rgt + :a - :c
|
79
|
-
ELSE rgt END,
|
80
|
-
|
81
|
-
parent_id = CASE
|
82
|
-
WHEN id = :id THEN :parent_id
|
83
|
-
ELSE parent_id END,
|
84
|
-
|
85
|
-
level = (
|
86
|
-
SELECT count(id)
|
87
|
-
FROM #{table_name} as t
|
88
|
-
WHERE t.lft < #{table_name}.lft AND rgt > #{table_name}.rgt
|
89
|
-
)
|
90
|
-
sql
|
91
|
-
# TODO name a, b, c, d in a more reasonable way
|
92
|
-
a, b, c, d = boundaries
|
93
|
-
[sql, { :a => a, :b => b, :c => c, :d => d, :id => id, :parent_id => parent_id }]
|
94
|
-
end
|
95
|
-
|
96
118
|
def table_name
|
97
119
|
node.class.quoted_table_name
|
98
120
|
end
|
@@ -1,7 +1,5 @@
|
|
1
1
|
module SimpleNestedSet
|
2
2
|
class NestedSet < ActiveRecord::Relation
|
3
|
-
NESTED_SET_ATTRIBUTES = [:parent_id, :left_id, :right_id]
|
4
|
-
|
5
3
|
class_inheritable_accessor :node_class, :scope_names
|
6
4
|
|
7
5
|
class << self
|
@@ -21,24 +19,6 @@ module SimpleNestedSet
|
|
21
19
|
c.merge(name => scope.respond_to?(name) ? scope.send(name) : scope[name])
|
22
20
|
end
|
23
21
|
end
|
24
|
-
|
25
|
-
def with_move_by_attributes(attributes, node = nil)
|
26
|
-
node_class.transaction do
|
27
|
-
nested_set_attributes = extract_nested_set_attributes!(attributes)
|
28
|
-
yield.tap do |result|
|
29
|
-
unless nested_set_attributes.empty?
|
30
|
-
node ||= result
|
31
|
-
node.nested_set.move_by_attributes(nested_set_attributes)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def extract_nested_set_attributes!(attributes)
|
38
|
-
result = attributes.slice(*NESTED_SET_ATTRIBUTES)
|
39
|
-
attributes.except!(*NESTED_SET_ATTRIBUTES)
|
40
|
-
result
|
41
|
-
end
|
42
22
|
end
|
43
23
|
|
44
24
|
attr_reader :node
|
@@ -48,9 +28,11 @@ module SimpleNestedSet
|
|
48
28
|
@node = args.first if args.size == 1
|
49
29
|
@where_values = self.class.scope(node).instance_variable_get(:@where_values) if node
|
50
30
|
end
|
51
|
-
|
52
|
-
def
|
53
|
-
|
31
|
+
|
32
|
+
def save!
|
33
|
+
attributes = node.instance_variable_get(:@_nested_set_attributes)
|
34
|
+
node.instance_variable_set(:@_nested_set_attributes, nil)
|
35
|
+
move_by_attributes(attributes) unless attributes.blank?
|
54
36
|
end
|
55
37
|
|
56
38
|
# Returns true if the node has the same scope as the given node
|
@@ -60,7 +42,7 @@ module SimpleNestedSet
|
|
60
42
|
|
61
43
|
# reload left, right, and parent
|
62
44
|
def reload
|
63
|
-
node.reload(:select => 'lft, rgt, parent_id')
|
45
|
+
node.reload(:select => 'lft, rgt, parent_id') unless node.new_record?
|
64
46
|
end
|
65
47
|
|
66
48
|
def populate_associations(nodes)
|
@@ -74,11 +56,8 @@ module SimpleNestedSet
|
|
74
56
|
|
75
57
|
# before validation set lft and rgt to the end of the tree
|
76
58
|
def init_as_node
|
77
|
-
|
78
|
-
|
79
|
-
node.lft, node.rgt = max_right + 1, max_right + 2
|
80
|
-
end
|
81
|
-
true
|
59
|
+
max_right = maximum(:rgt) || 0
|
60
|
+
node.lft, node.rgt = max_right + 1, max_right + 2
|
82
61
|
end
|
83
62
|
|
84
63
|
# Prunes a branch off of the tree, shifting all of the elements on the right
|
data/lib/simple_nested_set.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
require 'active_record'
|
2
|
+
require 'core_ext/hash/extract_nested_set_attributes'
|
2
3
|
|
3
4
|
module SimpleNestedSet
|
5
|
+
ATTRIBUTES = [:parent, :parent_id, :left_id, :right_id]
|
6
|
+
|
4
7
|
autoload :ActMacro, 'simple_nested_set/act_macro'
|
5
8
|
autoload :ClassMethods, 'simple_nested_set/class_methods'
|
6
9
|
autoload :InstanceMethods, 'simple_nested_set/instance_methods'
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: simple_nested_set
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 21
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 5
|
10
|
+
version: 0.0.5
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Sven Fuchs
|
@@ -100,6 +100,7 @@ extensions: []
|
|
100
100
|
extra_rdoc_files: []
|
101
101
|
|
102
102
|
files:
|
103
|
+
- lib/core_ext/hash/extract_nested_set_attributes.rb
|
103
104
|
- lib/simple_nested_set.rb
|
104
105
|
- lib/simple_nested_set/act_macro.rb
|
105
106
|
- lib/simple_nested_set/class_methods.rb
|