module PostgresTree::ActiveRecordConcern

Public Instance Methods

ancestors() click to toggle source

Get all ancestors

Example:

> parent.ancestors
=> [#<Role id: 1, name: "Grandparent", parent_id: nil>]
# File lib/postgres_tree/concerns/model/active_record.rb, line 17
def ancestors
  self_and_ancestors - [self]
end
descendents() click to toggle source

Get all descendents

Example:

> parent.descendents
=> [#<Role id: 3, name: "Child", parent_id: 2>]
# File lib/postgres_tree/concerns/model/active_record.rb, line 37
def descendents
  self_and_descendents - [self]
end
self_and_ancestors() click to toggle source

Get all ancestors and include self in the returned result

Example:

> parent.self_and_ancestors
=> #<ActiveRecord::Relation [#<Role id: 1, name: "Grandparent", parent_id: nil>, #<Role id: 2, name: "Parent", parent_id: 1>]>
# File lib/postgres_tree/concerns/model/active_record.rb, line 27
def self_and_ancestors
  self_and_ancestors_for(self)
end
self_and_descendents() click to toggle source

Get all descendents and include self in the returned result

Example:

> parent.self_and_descendents
=> #<ActiveRecord::Relation [#<Role id: 2, name: "Parent", parent_id: 1>, #<Role id: 3, name: "Child", parent_id: 2>]>
# File lib/postgres_tree/concerns/model/active_record.rb, line 47
def self_and_descendents
  self_and_descendents_for(self)
end

Private Instance Methods

method_missing(method, *args, &block) click to toggle source
Calls superclass method
# File lib/postgres_tree/concerns/model/active_record.rb, line 53
def method_missing(method, *args, &block)
  if method.to_s =~ /\A(.+)_include\?\z/
    self.send($1.to_sym).include? *args.first
  else
    super
  end
end
respond_to_missing?(method, include_private_methods = false) click to toggle source
Calls superclass method
# File lib/postgres_tree/concerns/model/active_record.rb, line 61
def respond_to_missing?(method, include_private_methods = false)
  method.to_s =~ /\A(.+)_include\?\z/ || super
end
self_and_ancestors_for(instance) click to toggle source

Ancestors

# File lib/postgres_tree/concerns/model/active_record.rb, line 66
def self_and_ancestors_for(instance)
  self.class.where("#{self.class.table_name}.id IN (#{self_and_ancestors_sql_for(instance)})")
end
self_and_ancestors_sql_for(instance) click to toggle source
# File lib/postgres_tree/concerns/model/active_record.rb, line 69
    def self_and_ancestors_sql_for(instance)
      tree_sql = <<-SQL
        WITH RECURSIVE search_tree(id, parent_id, path) AS (
            SELECT id, parent_id, ARRAY[id]
            FROM #{self.class.table_name}
            WHERE id = #{instance.id}
          UNION ALL
            SELECT #{self.class.table_name}.id, #{self.class.table_name}.parent_id, path || #{self.class.table_name}.id
            FROM search_tree
            JOIN #{self.class.table_name} ON #{self.class.table_name}.id = search_tree.parent_id
            WHERE NOT #{self.class.table_name}.id = ANY(path)
        )
        SELECT id FROM search_tree ORDER BY path DESC
      SQL
    end
self_and_descendents_for(instance) click to toggle source

Descendents

# File lib/postgres_tree/concerns/model/active_record.rb, line 86
def self_and_descendents_for(instance)
  self.class.where("#{self.class.table_name}.id IN (#{self_and_descendents_sql_for(instance)})")
end
self_and_descendents_sql_for(instance) click to toggle source
# File lib/postgres_tree/concerns/model/active_record.rb, line 89
    def self_and_descendents_sql_for(instance)
      tree_sql = <<-SQL
        WITH RECURSIVE search_tree(id, path) AS (
            SELECT id, ARRAY[id]
            FROM #{self.class.table_name}
            WHERE id = #{instance.id}
          UNION ALL
            SELECT #{self.class.table_name}.id, path || #{self.class.table_name}.id
            FROM search_tree
            JOIN #{self.class.table_name} ON #{self.class.table_name}.parent_id = search_tree.id
            WHERE NOT #{self.class.table_name}.id = ANY(path)
        )
        SELECT id FROM search_tree ORDER BY path
      SQL
    end