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.
@@ -0,0 +1,8 @@
1
+ class Hash
2
+ def extract_nested_set_attributes!
3
+ slice(*SimpleNestedSet::ATTRIBUTES).tap do
4
+ except!(*SimpleNestedSet::ATTRIBUTES)
5
+ end
6
+ end
7
+ end
8
+
@@ -4,15 +4,16 @@ module SimpleNestedSet
4
4
  return if acts_as_nested_set?
5
5
 
6
6
  include SimpleNestedSet::InstanceMethods
7
- extend SimpleNestedSet::ClassMethods
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 => class_of_active_record_descendant(self).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
- @nested_set ||= nested_set_class.new(self)
6
+ @_nested_set ||= nested_set_class.new(self)
7
7
  end
8
8
 
9
- # TODO refactor
10
- def update_attributes(attributes)
11
- nested_set.with_move_by_attributes(attributes) { super }
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
- # node.save!
22
- nested_set.update_all(query)
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 with_move_by_attributes(attributes, &block)
53
- self.class.with_move_by_attributes(attributes, node, &block)
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
- unless node.rgt && node.lft
78
- max_right = maximum(:rgt) || 0
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
@@ -1,3 +1,3 @@
1
1
  module SimpleNestedSet
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.5"
3
3
  end
@@ -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: 23
4
+ hash: 21
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 4
10
- version: 0.0.4
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