ruby-on-railsnested-setsawesome-nested-set

Awesome nestset set ancestor class method


Awesome nested set includes an ancestors instance method: https://github.com/collectiveidea/awesome_nested_set/wiki/Awesome-nested-set-cheat-sheet

@john = Group.where(name: "John").first
@tree = @john.ancestors

I am looking for a class method that would return an array or AR relation of ancestors for every group called "John"

@johns = Group.where(name: "John")
@tree  = @johns.ancestors

Presently I am doing this by looping through an AR relation and running the instance method for each row.

Update1

class Group < ApplicationRecord
  acts_as_nested_set    :counter_cache => :children_count

  def self.build_tree(groups)
    groups.collect(&:ancestors).flatten!
  end
end

class GroupsController < ApplicationController
    def index
        @johns = Group.where(name: "John")
        @tree  = Group.build_tree(@johns)
    end
end

Error:

undefined method `collect' for #<Class:0x00000002a28378>

Update2

There appears to be a problem with the Ancestor => Group relationship.

class Group < ApplicationRecord
  acts_as_nested_set    :counter_cache => :children_count
  has_many :ancestors

  def self.build_tree(objects)
     objects.collect(&:ancestors).flatten!
  end
End

class Ancestor < ActiveRecord::Base
  belongs_to :group
  scope :with_group, -> (name) { joins(:group).where("groups.name = ?", name) }
end


2.4.0 :008 > Ancestor.joins(:group)
ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR:  relation "ancestors" does not exist
LINE 1: SELECT  "ancestors".* FROM "ancestors" INNER JOIN "groups" O...
                                   ^
: SELECT  "ancestors".* FROM "ancestors" INNER JOIN "groups" ON "groups"."id" = "ancestors"."group_id" LIMIT $1


2.4.0 :009 > Ancestor.includes(:group)
ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR:  relation "ancestors" does not exist
LINE 1: SELECT  "ancestors".* FROM "ancestors" LIMIT $1
                                   ^
: SELECT  "ancestors".* FROM "ancestors" LIMIT $1

Solution

  • You are almost there, try following

    @johns = People.where(name: "John")
    @tree  = @johns.collect(&:ancestors).flatten!
    

    or you can use join query

    Ancestor.joins(:people).where("peoples.name = ?", 'John')
    

    According your code base you can just pass the records, but this is not a good way.

    class People < ActiveRecord::Base
       acts_as_nested_set    :counter_cache => :children_count
    
       def self.build_tree(peoples)
         peoples.collect(&:ancestors).flatten!
       end
    end
    
    
    class PeopleController < ApplicationController
       def index
          @johns = People.where(name: "John")
          @tree  = People.build_tree(@johns)
      end
    end
    

    Example with scope, good one

     class People < ActiveRecord::Base
        has_many :ancestors
        #your codes goes here  
     end
    
    
     class Ancestor < ActiveRecord::Base
         belongs_to :people
         scope :with_people, -> (name) { joins(:people).where("peoples.name = ?", name) }
     end
    
    
    class PeopleController < ApplicationController
       def index
          @tree = Ancestor.with_people("John")
       end
     end