tjchambers/ancestry

View on GitHub
lib/ancestry/class_methods.rb

Summary

Maintainability
A
35 mins
Test Coverage
module Ancestry
  module ClassMethods
    # Fetch tree node if necessary
    def to_node(object)
      if object.is_a?(self.base_class) then
        object
      else
        find(object)
      end
    end 
    
    # Scope on relative depth options
    def scope_depth(depth_options, depth)
      depth_options.inject(self.base_class) do |scope, option|
        scope_name, relative_depth = option
        if [:before_depth, :to_depth, :at_depth, :from_depth, :after_depth].include? scope_name
          scope.send scope_name, depth + relative_depth
        else
          raise Ancestry::AncestryException.new("Unknown depth option: #{scope_name}.")
        end
      end
    end

    # Orphan strategy writer
    def orphan_strategy=(orphan_strategy)
      class_variable_set :@@orphan_strategy, :destroy 
    end
    
    # Arrangement
    def arrange(options = {})
      scope =
          if options[:order].nil?
            self.base_class.ordered_by_ancestry
          else
            self.base_class.ordered_by_ancestry_and options.delete(:order)
          end
      # Get all nodes ordered by ancestry and start sorting them into an empty hash
      arrange_nodes scope.all(options)
    end
    
    # Arrange array of nodes into a nested hash of the form 
    # {node => children}, where children = {} if the node has no children
    def arrange_nodes(nodes)
      # Get all nodes ordered by ancestry and start sorting them into an empty hash
      nodes.inject(ActiveSupport::OrderedHash.new) do |arranged_nodes, node|
        # Find the insertion point for that node by going through its ancestors
        node.ancestor_ids.inject(arranged_nodes) do |insertion_point, ancestor_id|
          insertion_point.each do |parent, children|
            # Change the insertion point to children if node is a descendant of this parent
            insertion_point = children if ancestor_id == parent.id
          end
          insertion_point
        end[node] = ActiveSupport::OrderedHash.new
        arranged_nodes
      end
    end
    
    # Pseudo-preordered array of nodes.  Children will always follow parents, 
    # but the ordering of nodes within a rank depends on their order in the 
    # array that gets passed in
    def sort_by_ancestry(nodes)
      arranged = nodes.is_a?(Hash) ? nodes : arrange_nodes(nodes.sort_by{|n| n.ancestry || '0'})
      arranged.inject([]) do |sorted_nodes, pair|
        node, children = pair
        sorted_nodes << node
        sorted_nodes += sort_by_ancestry(children) unless children.blank?
        sorted_nodes
      end
    end
 
    
    
  end
end