Module: Hierarchy

Extended by:
ActiveSupport::Concern
Defined in:
lib/hierarchy.rb,
lib/hierarchy/node.rb,
lib/hierarchy/index_path.rb

Overview

Adds a tree structure to a model. This is very similar to ‘acts_as_nested_set` but uses the PostgreSQL-specific `ltree` feature for schema storage.

Your model must have a ‘path` field of type `ltree`. This field will be a period-delimited list of IDs of records above this one in the hierarchy. In addition, you should also consider the following indexes:

““ sql CREATE INDEX index1 ON table USING gist(path) CREATE INDEX index2 ON table USING btree(path) ““

replacing ‘table` with your table and `index1`/`index2` with appropriate names for these indexes.

Examples:

class MyModel < ActiveRecord::Base
  include Hierarchy
end

Defined Under Namespace

Modules: ClassMethods Classes: IndexPath, Node

Instance Method Summary collapse

Instance Method Details

#ancestors(options = {}) ⇒ Array

Returns an array of ancestors above this object. Note that a) this array is ordered with the most senior ancestor at the beginning of the list, and b) this is an array, not a relation. For that reason, you can pass any additional scope options to the method.

Parameters:

  • options (Hash) (defaults to: {})

    Additional finder options.

Returns:

  • (Array)

    The objects above this one in the hierarchy.



86
87
88
89
90
91
92
# File 'lib/hierarchy.rb', line 86

def ancestors(options={})
  @ancestors ||= begin
    return [] if top_level?
    objects = self.class.ancestors_of(self).scoped(options).group_by(&:id)
    index_path.map { |id| objects[id].first }
  end
end

#bottom_level?true, false

Returns Whether or not this object has no children. Makes a database call.

Returns:

  • (true, false)

    Whether or not this object has no children. Makes a database call.



135
136
137
# File 'lib/hierarchy.rb', line 135

def bottom_level?
  children.empty?
end

#childrenActiveRecord::Relation

Returns The objects directly below this one in the hierarchy.

Returns:

  • (ActiveRecord::Relation)

    The objects directly below this one in the hierarchy.



111
112
113
# File 'lib/hierarchy.rb', line 111

def children
  self.class.children_of self
end

#descendantsActiveRecord::Relation

Returns The objects below this one in the hierarchy.

Returns:

  • (ActiveRecord::Relation)

    The objects below this one in the hierarchy.



97
98
99
# File 'lib/hierarchy.rb', line 97

def descendants
  self.class.descendants_of self
end

#index_pathObject



145
146
147
# File 'lib/hierarchy.rb', line 145

def index_path
  @index_path ||= IndexPath.from_ltree path.to_s
end

#my_pathObject



140
141
142
# File 'lib/hierarchy.rb', line 140

def my_path
  path.blank? ? id.to_s : "#{path}.#{id}"
end

#parentActiveRecord::Base

Returns The object directly above this one in the hierarchy.

Returns:

  • (ActiveRecord::Base)

    The object directly above this one in the hierarchy.



104
105
106
# File 'lib/hierarchy.rb', line 104

def parent
  top_level? ? nil : self.class.parent_of(self).first
end

#parent=(parent) ⇒ Object

Sets the object above this one in the hierarchy.

Parameters:

  • parent (ActiveRecord::Base)

    The parent object.

Raises:

  • (ArgumentError)

    If ‘parent` is an unsaved record with no primary key.



73
74
75
76
# File 'lib/hierarchy.rb', line 73

def parent=(parent)
  raise ArgumentError, "Parent cannot be a new record" if parent.try(:new_record?)
  self.path = parent.try(:my_path)
end

#rootObject

Returns root parent or nil(if current obj is top level).

Returns:

  • root parent or nil(if current obj is top level)



128
129
130
# File 'lib/hierarchy.rb', line 128

def root
  self.top_level? ? nil : self.class.find(self.path.split('.').first)
end

#siblingsArray

Returns The objects at the same hierarchical level of this one.

Returns:

  • (Array)

    The objects at the same hierarchical level of this one.



117
118
119
# File 'lib/hierarchy.rb', line 117

def siblings
  self.class.siblings_of(self) - [ self ]
end

#top_level?true, false

Returns Whether or not this object has no parents.

Returns:

  • (true, false)

    Whether or not this object has no parents.



123
124
125
# File 'lib/hierarchy.rb', line 123

def top_level?
  path.blank?
end