Class: SimpleNestedSet::NestedSet

Inherits:
ActiveRecord::Relation
  • Object
show all
Includes:
SqlAbstraction
Defined in:
lib/simple_nested_set/nested_set.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from SqlAbstraction

#group_concat

Constructor Details

#initialize(*args) ⇒ NestedSet

Returns a new instance of NestedSet.



34
35
36
37
38
# File 'lib/simple_nested_set/nested_set.rb', line 34

def initialize(*args)
  super(node_class, node_class.arel_table)
  @node = args.first if args.size == 1
  @where_values = self.class.scope(node).instance_variable_get(:@where_values) if node
end

Instance Attribute Details

#nodeObject (readonly)

Returns the value of attribute node.



32
33
34
# File 'lib/simple_nested_set/nested_set.rb', line 32

def node
  @node
end

Class Method Details

.build_class(model, scopes) ⇒ Object



8
9
10
11
12
13
# File 'lib/simple_nested_set/nested_set.rb', line 8

def build_class(model, scopes)
  model.const_get(:NestedSet) rescue model.const_set(:NestedSet, Class.new(NestedSet)).tap do |node_class|
    node_class.node_class = model
    node_class.scope_names = Array(scopes).map { |s| s.to_s =~ /_id$/ ? s.to_sym : :"#{s}_id" }
  end
end

.extract_attributes!(attributes) ⇒ Object



25
26
27
28
29
# File 'lib/simple_nested_set/nested_set.rb', line 25

def extract_attributes!(attributes)
  attributes.slice(*SimpleNestedSet::ATTRIBUTES).tap do
    attributes.except!(*(SimpleNestedSet::ATTRIBUTES - [:path]))
  end if attributes.respond_to?(:slice)
end

.scope(scope) ⇒ Object



15
16
17
# File 'lib/simple_nested_set/nested_set.rb', line 15

def scope(scope)
  scope.blank? ? node_class.scoped : node_class.where(scope_condition(scope))
end

.scope_condition(scope) ⇒ Object



19
20
21
22
23
# File 'lib/simple_nested_set/nested_set.rb', line 19

def scope_condition(scope)
  scope_names.inject({}) do |c, name|
    c.merge(name => scope.respond_to?(name) ? scope.send(name) : scope[name])
  end
end

Instance Method Details

#attribute_namesObject



73
74
75
# File 'lib/simple_nested_set/nested_set.rb', line 73

def attribute_names
  @attribute_names ||= node.attribute_names.select { |attribute| ATTRIBUTES.include?(attribute.to_sym) }
end

#db_adapterObject



141
142
143
# File 'lib/simple_nested_set/nested_set.rb', line 141

def db_adapter
  node.class.connection.instance_variable_get('@config')[:adapter].to_sym
end

#denormalize!Object

FIXME we don’t always want to call this on after_save, do we? it’s only relevant when either the structure or the slug has changed



49
50
51
52
53
54
# File 'lib/simple_nested_set/nested_set.rb', line 49

def denormalize!
  sql = []
  sql << denormalize_level_query if node.has_attribute?(:level)
  sql << denormalize_path_query  if node.has_attribute?(:path)
  update_all(sql.join(',')) unless sql.blank?
end

#denormalize_level_queryObject



123
124
125
126
127
128
129
130
# File 'lib/simple_nested_set/nested_set.rb', line 123

def denormalize_level_query
  query = arel_table.as(:l)
  query = query.project('count(id)').
          where(query[:lft].lt(arel_table[:lft])).
          where(query[:rgt].gt(arel_table[:rgt])).
          where(where_clauses.map { |clause| clause.gsub(table_name, 'l') })
  "level = (#{query.to_sql})"
end

#denormalize_path_queryObject



132
133
134
135
136
137
138
139
# File 'lib/simple_nested_set/nested_set.rb', line 132

def denormalize_path_query
  query = arel_table.as(:l)
  query = query.project(group_concat(db_adapter, :slug)).
          where(query[:lft].lteq(arel_table[:lft])).
          where(query[:rgt].gteq(arel_table[:rgt])).
          where(where_clauses.map { |clause| clause.gsub(table_name, 'l') })
  "path = (#{query.to_sql})"
end

#init_as_nodeObject

before validation set lft and rgt to the end of the tree



87
88
89
90
# File 'lib/simple_nested_set/nested_set.rb', line 87

def init_as_node
  max_right = maximum(:rgt) || 0
  node.lft, node.rgt = max_right + 1, max_right + 2
end

#move_by_attributes(attributes) ⇒ Object



105
106
107
# File 'lib/simple_nested_set/nested_set.rb', line 105

def move_by_attributes(attributes)
  Move::ByAttributes.new(node, attributes).perform
end

#move_to(target, position) ⇒ Object



115
116
117
# File 'lib/simple_nested_set/nested_set.rb', line 115

def move_to(target, position)
  Move::ToTarget.new(node, target, position).perform
end

#move_to_path(path) ⇒ Object



109
110
111
112
113
# File 'lib/simple_nested_set/nested_set.rb', line 109

def move_to_path(path)
  node.path, parent_path = path, path.split('/')[0..-2].join('/')
  parent = parent_path.empty? ? nil : node.nested_set.where(:path => parent_path).first
  node.move_to_child_of(parent)
end

#populate_associations(nodes) ⇒ Object



77
78
79
80
81
82
83
84
# File 'lib/simple_nested_set/nested_set.rb', line 77

def populate_associations(nodes)
  node.children.target = nodes.select do |child|
    next unless child.parent_id == node.id
    nodes.delete(child)
    child.nested_set.populate_associations(nodes)
    child.parent = node
  end
end

#prune_branchObject

Prunes a branch off of the tree, shifting all of the elements on the right back to the left so the counts still work.



94
95
96
97
98
99
100
101
102
103
# File 'lib/simple_nested_set/nested_set.rb', line 94

def prune_branch
  if node.rgt && node.lft
    transaction do
      diff = node.rgt - node.lft + 1
      delete_all(['lft > ? AND rgt < ?', node.lft, node.rgt])
      update_all(['lft = (lft - ?)', diff], ['lft >= ?', node.rgt])
      update_all(['rgt = (rgt - ?)', diff], ['rgt >= ?', node.rgt])
    end
  end
end

#rebuild_from_paths!Object



119
120
121
# File 'lib/simple_nested_set/nested_set.rb', line 119

def rebuild_from_paths!
  Rebuild::FromPaths.new.run(self)
end

#reloadObject

reload nested set attributes



62
63
64
65
66
67
68
69
70
71
# File 'lib/simple_nested_set/nested_set.rb', line 62

def reload
  columns  = [:parent_id, :lft, :rgt]
  columns << :level if node.has_attribute?(:level)
  columns << :path  if node.has_attribute?(:path)

  reloaded = unscoped { find(node.id, :select => columns) }
  node.instance_eval { @attributes.merge!(reloaded.instance_variable_get(:@attributes)) }
  node.parent = nil if node.parent_id.nil?
  node.children.reset
end

#same_scope?(other) ⇒ Boolean

Returns true if the node has the same scope as the given node

Returns:

  • (Boolean)


57
58
59
# File 'lib/simple_nested_set/nested_set.rb', line 57

def same_scope?(other)
  scope_names.all? { |scope| node.send(scope) == other.send(scope) }
end

#save!Object



40
41
42
43
44
45
# File 'lib/simple_nested_set/nested_set.rb', line 40

def save!
  attributes = node.instance_variable_get(:@_nested_set_attributes)
  node.instance_variable_set(:@_nested_set_attributes, nil)
  move_by_attributes(attributes) unless attributes.blank?
  denormalize!
end